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]