Three examples to understand dynamic programming (knapsack (basic, optimization time, optimization space), currency maximization, change problem)

Overview of dynamic programming

  • It can be divided into multiple related subproblems, and the solution of the subproblem can be reused (in the example below, F array and value array are used to retain the solution of the subproblem), which is also the key to improve the efficiency compared with recursive violence. The first two examples below will simply compare the efficiency, and the second example is different because the data is not representative
  • The key of dynamic programming is how to write the recursive relation from the optimal solution of the previous sub problem to the current situation according to the conditions
  • Then there will be many ways to find the optimal solution of the current situation through min or max according to the problem. Of course, the current situation may also appear in the sub problems of the current situation
  • Write the initial conditions
  • (misunderstanding of my previous understanding) don't just pay attention to the recursive relationship in the understanding. For example, in the knapsack problem, if I was wrong, I always felt that the total value of the combination including the i-th item must be greater than that of the combination excluding the i-th item. The mistake of this understanding is that I didn't really understand the bottom-up of dynamic programming, but only saw the recursive relationship, In fact, f (I, j) here changes not only I, but also j, which is equivalent to that you do not include the ith item, so you have more space to choose others. In short, the sub combination of the previous sub combination containing the ith item is different from that without the ith item. (for the misunderstanding of knapsack problem that I first came into contact with before, I have read the dynamic planning system this time. Welcome to communicate)
  • Can't you read it? Look directly at the code!

Reference book: algorithm analysis and design

Reference blog: https://blog.csdn.net/qq_34178562/article/details/79959380

github(jupyter source code): https://github.com/Justin3go/BasicComputerNotes/tree/main/ Algorithm analysis and design

Currency maximization

Problem Description: given a row of coins, these integers are not necessarily the same. So that the amount of the selected coins is the largest when their original positions are not adjacent to each other.

Algorithm idea:

  • Including the last one: c_n + F(n-2)
  • Excluding the last one: F(n-1)

Dynamic programming implementation

def CoinRow1(coins):
	F = [0]
	F.append(coins[0])
	for i in range(1, len(coins)):
		F.append(max(coins[i] + F[i-1], F[i])) 

	return F[len(coins)]

test_coin = [5, 1, 2, 10, 6, 2]
CoinRow1(test_coin)
17

Recursive implementation

def CoinRow2(coins, i):
	if i == 0:
		return 0
	if i == 1:
		return coins[0]
	return max((coins[i] + CoinRow2(coins, i - 2)), CoinRow2(coins, i - 1))

test_coin = [5, 1, 2, 10, 6, 2]
CoinRow2(test_coin, len(test_coin)-1)

17

Efficiency comparison

import matplotlib.pyplot as plt
import numpy as np
import time

cost_time_log1 = []
cost_time_log2 = []
for i in range(1, 6):
    print(
        "**********************************The first%s Data scale********************************"
        % i)
    # Data generation
    test_coins = list(np.random.randint(0, 1000, i * 6))
    t1 = time.time()
    print("CoinRow1", CoinRow1(test_coins))
    t2 = time.time()
    print("CoinRow2", CoinRow2(test_coins, len(test_coins) - 1))
    t3 = time.time()
    cost_time_log1.append(t2 - t1)
    cost_time_log2.append(t3 - t2)

print(cost_time_log1)
print(cost_time_log2)

plt.plot(range(1, 6), cost_time_log1)
plt.plot(range(1, 6), cost_time_log2)

**********************************Chapter 1 data scale********************************
CoinRow1 1985
CoinRow2 1693
**********************************Chapter 2 data scale********************************
CoinRow1 3830
CoinRow2 3660
**********************************Chapter 3 data scale********************************
CoinRow1 4547
CoinRow2 4476
**********************************Chapter 4 data scale********************************
CoinRow1 7088
CoinRow2 6718
**********************************Chapter 5 data scale********************************
CoinRow1 10291
CoinRow2 9797
[0.0009958744049072266, 0.0, 0.0, 0.0009632110595703125, 0.0009703636169433594]
[0.0, 0.0, 0.0020334720611572266, 0.030029296875, 0.4920010566711426]





[<matplotlib.lines.Line2D at 0x24a8d902520>]

Change problem

Problem Description: there are m currency values, and the change amount is n. how many coins should be used at least

Algorithm idea:

dynamic programming

def ChangeMaking1(D, n):
	F = [0]
	for i in range(1, n+1):
		temp = float('inf')
		j = 0
		while(j < len(D) and i >= D[j]):
			temp = min(F[i-D[j]], temp)
			j += 1
		F.append(temp + 1)

	return F[n]

ChangeMaking1([1,3,4], 6)

	
2

Recursive implementation

def ChangeMaking2(D, n):
	min_count = n
	if n in D:
		return 1
	for value in [i for i in D if i <= n]:
		count = 1 + ChangeMaking2(D, n - value)
		if count < min_count:
			min_count = count	
	
	return min_count

ChangeMaking2([1,3,4], 6)
2

Efficiency comparison

import matplotlib.pyplot as plt
import numpy as np
import time

cost_time_log1 = []
cost_time_log2 = []
for i in range(1, 6):
    print(
        "**********************************The first%s Data scale********************************"
        % i)
    # Data generation
    test_coins = list(np.random.randint(0, 100, i * 1))
    test_coins.append(1)  # Make sure you can find the change anyway
    t1 = time.time()
    print("ChangeMaking1", ChangeMaking1(test_coins, 86))
    t2 = time.time()
    print("ChangeMaking2", ChangeMaking2(test_coins, 86))
    t3 = time.time()
    cost_time_log1.append(t2 - t1)
    cost_time_log2.append(t3 - t2)

print(cost_time_log1)
print(cost_time_log2)

# Here, because the test data is not easy to generate, the final results do not meet the expectations and are only for reference
plt.plot(range(1, 6), cost_time_log1)
plt.plot(range(1, 6), cost_time_log2)
**********************************Chapter 1 data scale********************************
ChangeMaking1 24
ChangeMaking2 24
**********************************Chapter 2 data scale********************************
ChangeMaking1 14
ChangeMaking2 14
**********************************Chapter 3 data scale********************************
ChangeMaking1 2
ChangeMaking2 2
**********************************Chapter 4 data scale********************************
ChangeMaking1 4
ChangeMaking2 4
**********************************Chapter 5 data scale********************************
ChangeMaking1 3
ChangeMaking2 3
[0.0, 0.0, 0.0009989738464355469, 0.0, 0.0]
[0.002011537551879883, 0.07396841049194336, 0.12302207946777344, 0.08499431610107422, 0.009998559951782227]





[<matplotlib.lines.Line2D at 0x1f5abbda640>]

knapsack problem

Solution of basic knapsack problem

def bag(n, c, w, v):
    # Zero indicates the initial state
    value = [[0 for j in range(c + 1)] for i in range(n + 1)]
    for i in range(1, n + 1):
        for j in range(1, c + 1):
            value[i][j] = value[i - 1][j]
            # The total capacity of the knapsack is enough to hold the current object. It traverses the previous state and considers whether to replace it, which is equivalent to the max operation in the recursive relationship
            if j >= w[i - 1] and value[i][j] < value[i - 1][j - w[i - 1]] + v[i - 1]:
                value[i][j] = value[i - 1][j - w[i - 1]] + v[i - 1]
    return value
# test
n = 4  # Number of items,
c = 5 # The weight that the schoolbag can bear,
w = [2, 1, 3, 2] # The weight of each item,
v = [12, 10, 20, 15] # Value of each item

value = bag(n, c, w, v)
for i in value:
    print(i)
print(value[-1][-1])
 
[0, 0, 0, 0, 0, 0]
[0, 0, 12, 12, 12, 12]
[0, 10, 12, 22, 22, 22]
[0, 10, 12, 22, 30, 32]
[0, 10, 15, 25, 30, 37]
37

Backtracking diagram

def show(n, c, w, value):
    print('The maximum value is:', value[n][c])
    x = [False for i in range(n)]
    j = c
    # to flash back
    for i in range(n, 0, -1):
        if value[i][j] > value[i - 1][j]:
            x[i - 1] = True
            j -= w[i - 1]
    print('The items in the backpack are:')
    for i in range(n):
        if x[i]:
            print('The first', i+1, 'individual', end=',')

show(n, c, w, value)
The maximum value is: 37
 The items in the backpack are:
1st,2nd,4th,

Solution of knapsack problem with memory function

import numpy as np
# Generate a two-dimensional array with the first column, the first line is 0, and the rest are null(-1)
value = np.zeros([n+1,c+1])
value[:], value[0,:], value[:,0] = -1, 0, 0  # The broadcast mechanism of numpy is used
value = list(value)
# From top to bottom, start to find the sub problem solution you need from I = 4 and j = 5
def bag_imporve_time(i, j, w, v):
    if value[i][j] < 0:
        if j < w[i-1]:
            temp = bag_imporve_time(i-1, j, w, v)
        else:
            temp = max(bag_imporve_time(i-1, j, w, v), bag_imporve_time(i-1, j-w[i-1], w, v) + v[i-1])
        value[i][j] = temp

    return value[i][j]

# test
n = 4  # Number of items,
c = 5 # The weight that the schoolbag can bear,
w = [2, 1, 3, 2] # The weight of each item,
v = [12, 10, 20, 15] # Value of each item
bag_imporve_time(n, c, w, v)
37.0
# -1 represents the reduced calculation
value
[array([0., 0., 0., 0., 0., 0.]),
 array([ 0.,  0., 12., 12., 12., 12.]),
 array([ 0., -1., 12., 22., -1., 22.]),
 array([ 0., -1., -1., 22., -1., 32.]),
 array([ 0., -1., -1., -1., -1., 37.])]

Tail iterative solution of optimization space

def bag_imporve_space(n, c, w, v):
    # Record only the optimal solution
    values = [0 for i in range(c+1)]
    for i in range(1, n + 1):
        for j in range(c, 0, -1):
            # The total capacity of the knapsack is enough to hold the current object. Traverse the previous state and consider whether to replace it
            if j >= w[i-1]:
                values[j] = max(values[j-w[i-1]]+v[i-1], values[j])
    
    return values

n = 4  # Number of items,
c = 5 # The weight that the schoolbag can bear,
w = [2, 1, 3, 2] # The weight of each item,
v = [12, 10, 20, 15] # Value of each item
bag_imporve_space(n, c, w, v)
[0, 10, 15, 25, 30, 37]

Tags: Algorithm Dynamic Programming

Posted on Sun, 10 Oct 2021 06:29:24 -0400 by suz_1234