Change exchange problem solving
We usually go out to buy goods. When we pay at the store, if we want to change, we don't want the salesperson to get back a pile of change. It's very inconvenient to store it. Especially when we buy goods on the vending machine, we find that the vending machine is very intelligent now. When we don't change for you, we find a lot of coins. How can we do that?
For example, if a customer buys 37 yuan of goods and gives 100 yuan to them, if they want to find 63 yuan, the minimum amount is 50 yuan for one piece, 10 yuan for one piece, and 1 yuan for three pieces, a total of 4 pieces.
Method 1: greedy strategy
The most intuitive way to solve this problem is to use greedy strategy. We will start with the largest denomination and use the largest amount. If there is a balance to the next maximum face value, the maximum amount will be used until 1 yuan.
def chage_give(coins_list, change): solutions =  s_list = sorted(coins_list, reverse=True) for coin in s_list: coins_num = change // coin solutions += [coin,] * coins_num change = change - coin * coins_num if coins_num < 0: break return len(solutions) if __name__ == '__main__': print(chage_give([1, 5, 10, 20, 50, 100], 63))
Run the program and output the printing result:
The greedy strategy performs well under the RMB system, but if there is a face value of 21 yuan, the greedy strategy will fail. Because the optimal solution of 63 yuan is 3 denominations of 21 yuan.
Method 2: recursive call
Since the greedy strategy will fail under special denominations, let's use recursion to solve this problem.
Three first conditions of recursion, we first determine the basic end condition: the remaining change to be exchanged is exactly equal to a certain face value. For example, change 10 yuan, and the answer is 10 yuan for one piece.
The second is to reduce the scale of the problem:
After subtracting 1 yuan from the change, find the minimum amount of change to exchange (call itself recursively); Subtract 5 yuan from the change to find the minimum amount of change; Subtract 10 yuan from the change to find the minimum amount of change; Subtract 20 yuan from the change to find the minimum amount of change; Subtract 50 yuan from the change to find the minimum amount of change; Select the smallest of the above 5 items
def change_give_recursion(coins_list, change): min_coins = change # When the value of the change to be exchanged is exactly equal to one of the items in the face value list, it directly returns 1 if change in coins_list: return 1 else: # Try to call itself recursively for all kinds of face values, but choose the smallest one. for i in [ c for c in coins_list if c < change]: coin_num = 1 + change_give_recursion(coins_list, change - i) if coin_num < min_coins: min_coins = coin_num return min_coins if __name__ == '__main__': print(change_give_recursion([1, 5, 10, 20, 50, 100], 63))
Run the program and output the printing result:
Although the above recursive solution can solve the problem, the biggest problem is: very inefficient! For example, more than 60 million recursive calls are needed for the exchange of 63 yuan, and there are too many repeated calculations. So to optimize this algorithm, we need to eliminate the duplicate computation.
We can use a table to save the calculated intermediate results, and check the table to see if it has been calculated before calculation. The intermediate result of this algorithm is the optimal solution of partial zero finding. Before the recursive call, first look up whether there is an optimal solution of partial zero finding in the table. If there is one, return the optimal solution directly instead of the recursive call. If not, the recursive call can be made.
Since there are problems, we need to make improvements. The improvements are as follows:
def change_give_recursion(coins_list, change, known_result): min_coins = change if change in coins_list: # If the change is exactly equal to one of the currencies, record the result first known_result[change] = 1 return 1 elif known_result[change] > 0: # If the result record of the change value already exists, it will be returned directly return known_result[change] else: for i in [ c for c in coins_list if c < change]: coin_num = 1 + change_give_recursion(coins_list, change - i, known_result) if coin_num < min_coins: min_coins = coin_num # Save the result and call later for later check known_result[change] = min_coins return min_coins if __name__ == '__main__': print(change_give_recursion([1, 5, 10, 20, 50, 100], 63,  * 64))
The improved solution greatly reduces the number of recursive calls. Only 221 recursive calls are needed for the exchange of 63 yuan, which is one-third of the 300000 before improvement. This method of recording intermediate results is called "memory / function value buffer" technology, which improves the performance of recursive solution, such as buffer.
Method 3: dynamic programming solution
Dynamic programming (DP) is mainly used to solve some optimization problems that want to find the optimal solution
The dynamic programming algorithm of change exchange starts from the most simple "1 yuan change" optimal solution, and gradually increases until we need to find zero. In the process of zero finding and increasing, try to keep every penny's increasing is the optimal solution, and add it to the solution of zero finding, and naturally get the optimal solution.
The key to keep the optimal solution is that it depends on the calculation of the optimal solution with less money, and the optimal solution with less money has been obtained. The optimal solution of the problem contains the optimal solution of the smaller model problem, which is a necessary condition for an optimal problem to be solved by dynamic programming strategy.
To calculate the exchange method of 11 cents, we do the following steps: 1. Subtract 1 cent, and the best solution for the remaining 10 cents is 1 2. Then subtract 5 cents, and the best solution for the remaining 6 cents is 2 3. At last, subtract 10 cents, and the best solution is 1 From the above minimum value to the optimal solution: 2 coins
def change_give_dynamic(coins_list, change, min_coins): for cents in range(1, change + 1): coins_count = cents for j in [c for c in coins_list if c < cents]: if min_coins[cents - j] + 1 < coins_count: coins_count = min_coins[cents - j] + 1 min_coins[cents] = coins_count return min_coins[change] if __name__ == '__main__': print(change_give_dynamic([1, 5, 10, 20, 50, 100], 63,  * 64))