Find the least number of coins required that can make any change from 1 to 99 cents

后端 未结 27 2010
生来不讨喜
生来不讨喜 2020-12-07 10:08

Recently I challenged my co-worker to write an algorithm to solve this problem:

Find the least number of coins required that can make any change from

相关标签:
27条回答
  • 2020-12-07 10:26

    Here's my take. One Interesting thing is that we need to check min coins needed to form up to coin_with_max_value(25 in our case) - 1 only. After that just calculate the sum of these min coins. From that point we just need to add certain number of coin_with_max_value, to form any number up to the total cost, depending on the difference of total cost and the sum found out. That's it.

    So for values we have take, once min coins for 24 is found out: [1, 2, 2, 5, 10, 10]. We just need to keep adding a 25 coin for every 25 values exceeding 30(sum of min coins). Final answer for 99 is:
    [1, 2, 2, 5, 10, 10, 25, 25, 25]
    9

    import itertools
    import math
    
    
    def ByCurrentCoins(val, coins):
      for i in range(1, len(coins) + 1):
        combinations = itertools.combinations(coins, i)
        for combination in combinations:
          if sum(combination) == val:
            return True
    
      return False
    
    def ExtraCoin(val, all_coins, curr_coins):
      for c in all_coins:
        if ByCurrentCoins(val, curr_coins + [c]):
          return c
    
    def main():
      cost = 99
      coins = sorted([1, 2, 5, 10, 25], reverse=True)
      max_coin = coins[0]
    
      curr_coins = []
      for c in range(1, min(max_coin, cost+1)):
        if ByCurrentCoins(c, curr_coins):
          continue
    
        extra_coin = ExtraCoin(c, coins, curr_coins)
        if not extra_coin:
          print -1
          return
    
        curr_coins.append(extra_coin)
    
      curr_sum = sum(curr_coins)
      if cost > curr_sum:
        extra_max_coins = int(math.ceil((cost - curr_sum)/float(max_coin)))
        curr_coins.extend([max_coin for _ in range(extra_max_coins)])
    
      print curr_coins
      print len(curr_coins)
    
    0 讨论(0)
  • 2020-12-07 10:29

    What you are looking for is Dynamic Programming.

    You don't actually have to enumerate all the possible combinations for every possible values, because you can build it on top of previous answers.

    You algorithm need to take 2 parameters:

    • The list of possible coin values, here [1, 5, 10, 25]
    • The range to cover, here [1, 99]

    And the goal is to compute the minimal set of coins required for this range.

    The simplest way is to proceed in a bottom-up fashion:

    Range     Number of coins (in the minimal set)
              1   5   10   25
    [1,1]     1
    [1,2]     2
    [1,3]     3
    [1,4]     4
    [1,5]     5
    [1,5]*    4   1             * two solutions here
    [1,6]     4   1
    [1,9]     4   1
    [1,10]    5   1             * experience tells us it's not the most viable one :p
    [1,10]    4   2             * not so viable either
    [1,10]    4   1   1
    [1,11]    4   1   1
    [1,19]    4   1   1
    [1,20]    5   1   1         * not viable (in the long run)
    [1,20]    4   2   1         * not viable (in the long run)
    [1,20]    4   1   2
    

    It is somewhat easy, at each step we can proceed by adding at most one coin, we just need to know where. This boils down to the fact that the range [x,y] is included in [x,y+1] thus the minimal set for [x,y+1] should include the minimal set for [x,y].

    As you may have noticed though, sometimes there are indecisions, ie multiple sets have the same number of coins. In this case, it can only be decided later on which one should be discarded.

    It should be possible to improve its running time, when noticing that adding a coin usually allows you to cover a far greater range that the one you added it for, I think.

    For example, note that:

     [1,5]    4*1  1*5
     [1,9]    4*1  1*5
    

    we add a nickel to cover [1,5] but this gives us up to [1,9] for free!

    However, when dealing with outrageous input sets [2,3,5,10,25] to cover [2,99], I am unsure as how to check quickly the range covered by the new set, or it would be actually more efficient.

    0 讨论(0)
  • 2020-12-07 10:29

    I've been learning about dynamic programming today, and here's the result:

    coins = [1,5,10,25]
    d = {} # stores tuples of the form (# of coins, [coin list])
    
    # finds the minimum # of coins needed to
    # make change for some number of cents
    def m(cents):
        if cents in d.keys():
            return d[cents]
        elif cents > 0:
            choices = [(m(cents - x)[0] + 1, m(cents - x)[1] + [x]) for x in coins if cents >= x]
    
            # given a list of tuples, python's min function
            # uses the first element of each tuple for comparison
            d[cents] = min(choices)
            return d[cents]
        else:
            d[0] = (0, [])
            return d[0]
    
    for x in range(1, 100):
        val = m(x)
        print x, "cents requires", val[0], "coins:", val[1]
    

    Dynamic programming really is magical.

    0 讨论(0)
  • 2020-12-07 10:29

    For this problem, Greedy approach gives a better solution than DP or others. Greedy approach: Find the largest denomination that is lesser than the required value and add it to the set of coins to be delivered. Lower the required cents by the denomination just added and repeat until the required cents becomes zero.

    My solution (greedy approach) in java solution:

    public class MinimumCoinDenomination {
    
        private static final int[] coinsDenominations = {1, 5, 10, 25, 50, 100};
    
        public static Map<Integer, Integer> giveCoins(int requiredCents) {
            if(requiredCents <= 0) {
                return null;
            }
            Map<Integer, Integer> denominations = new HashMap<Integer, Integer>();
    
            int dollar = requiredCents/100;
            if(dollar>0) {
                denominations.put(100, dollar);
            }
            requiredCents = requiredCents - (dollar * 100);
    
            //int sum = 0;
            while(requiredCents > 0) {
                for(int i = 1; i<coinsDenominations.length; i++) {
                    if(requiredCents < coinsDenominations[i]) {
                        //sum = sum +coinsDenominations[i-1];
                        if(denominations.containsKey(coinsDenominations[i-1])) {
                            int c = denominations.get(coinsDenominations[i-1]);
                            denominations.put(coinsDenominations[i-1], c+1);
                        } else {
                            denominations.put(coinsDenominations[i-1], 1);
                        }
                        requiredCents = requiredCents - coinsDenominations[i-1];
                        break;
                    }
                }
            }
            return denominations;
        }
    
        public static void main(String[] args) {
            System.out.println(giveCoins(199));
        }
    
    }
    
    0 讨论(0)
  • 2020-12-07 10:30

    There are a couple of similar answers up there but my solution with Java seems a little easier to understand. Check this out.

    public static int findMinimumNumberOfCoins(int inputCents) {
    
         // Error Check, If the input is 0 or lower, return 0.
         if(inputCents <= 0) return 0;
    
         // Create the List of Coins that We need to loop through. Start from highest to lowewst.
         // 25-10-5-1
         int[] mCoinsArray = getCoinsArray();
    
         // Number of Total Coins.
         int totalNumberOfCoins = 0;
    
         for(int i=0; i < mCoinsArray.length; i++) {
    
             // Get the Coin from Array.
             int coin = mCoinsArray[i];
    
             // If there is no inputCoin Left, simply break the for-loop
             if(inputCents == 0) break;
    
             // Check If we have a smaller input than our coin
             // If it's, we need to go the Next one in our Coins Array.
             // e.g, if we have 8, but the current index of array is 10, we need to go to 5.
             if(inputCents < coin) continue;
    
             int quotient = inputCents/coin;
             int remainder = inputCents%coin;
    
             // Add qutient to number of total coins.
             totalNumberOfCoins += quotient;
    
             // Update the input with Remainder.
             inputCents = remainder;
         }
    
         return totalNumberOfCoins;
     }
    
     // Create a Coins Array, from 25 to 1. Highest is first.
     public static int[] getCoinsArray() {
    
         int[] mCoinsArray = new int[4];
         mCoinsArray[0] = 25;
         mCoinsArray[1] = 10;
         mCoinsArray[2] = 5;
         mCoinsArray[3] = 1;
    
         return mCoinsArray;
     }
    
    0 讨论(0)
  • 2020-12-07 10:32

    Assuming you're talking about US currency, you would want a Greedy Algorithm: http://en.wikipedia.org/wiki/Greedy_algorithm

    In essence, you try all denominations from highest-to-lowest, taking as many coins as posible from each one until you've got nothing left.

    For the general case see http://en.wikipedia.org/wiki/Change-making_problem, because you would want to use dynamic programming or linear programming to find the answer for arbitrary denominations where a greedy algorithm wouldn't work.

    0 讨论(0)
提交回复
热议问题