如果给出一些美元价值,如何找到所有硬币组合

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如果给出一些美元价值,如何找到所有硬币组合相关的知识,希望对你有一定的参考价值。

几个月前,我找到了一段代码,我正在编写面试。

根据我的评论,它试图解决这个问题:

给定美分值(例如200 = 2美元,1000 = 10美元),找到构成美元价值的所有硬币组合。只有便士(1¢),镍(5¢),角钱(10¢)和四分之一(25¢)。

例如,如果给出100,答案应该是:

4 quarter(s) 0 dime(s) 0 nickel(s) 0 pennies  
3 quarter(s) 1 dime(s) 0 nickel(s) 15 pennies  
etc.

我相信这可以通过迭代和递归方式解决。我的递归解决方案非常错误,我想知道其他人如何解决这个问题。这个问题的难点在于尽可能提高效率。

答案

我很久以前就看过这个,你可以读我的little write-up on it。这是Mathematica source

通过使用生成函数,您可以获得问题的封闭形式的恒定时间解决方案。格雷厄姆,克努特和帕塔什尼克的混凝土数学就是这本书的书,并且对这个问题进行了相当广泛的讨论。基本上,您定义了一个多项式,其中第n个系数是为n美元进行更改的方式的数量。

这篇文章的第4-5页显示了如何使用Mathematica(或任何其他方便的计算机代数系统)在三行代码中在几秒钟内计算10 ^ 10 ^ 6美元的答案。

(而且这已经很久以前在75Mhz Pentium上只有几秒钟......)

另一答案

半黑客绕过独特的组合问题 - 强制降序:

$denoms = [1,5,10,25]
def all_combs(sum,last) 
  return 1 if sum == 0
  return $denoms.select|d| d &le sum && d &le last.inject(0) |total,denom|
           total+all_combs(sum-denom,denom)
end

这将运行缓慢,因为它不会被记忆,但你明白了。

另一答案
# short and sweet with O(n) table memory    

#include <iostream>
#include <vector>

int count( std::vector<int> s, int n )

  std::vector<int> table(n+1,0);

  table[0] = 1;
  for ( auto& k : s )
    for(int j=k; j<=n; ++j)
      table[j] += table[j-k];

  return table[n];


int main()

  std::cout <<  count(25, 10, 5, 1, 100) << std::endl;
  return 0;

另一答案

这是我在Python中的答案。它不使用递归:

def crossprod (list1, list2):
    output = 0
    for i in range(0,len(list1)):
        output += list1[i]*list2[i]

    return output

def breakit(target, coins):
    coinslimit = [(target / coins[i]) for i in range(0,len(coins))]
    count = 0
    temp = []
    for i in range(0,len(coins)):
        temp.append([j for j in range(0,coinslimit[i]+1)])


    r=[[]]
    for x in temp:
        t = []
        for y in x:
            for i in r:
                t.append(i+[y])
        r = t

    for targets in r:
        if crossprod(targets, coins) == target:
            print targets
            count +=1
    return count




if __name__ == "__main__":
    coins = [25,10,5,1]
    target = 78
    print breakit(target, coins)

示例输出

    ...
    1 ( 10 cents)  2 ( 5 cents)  58 ( 1 cents)  
    4 ( 5 cents)  58 ( 1 cents)  
    1 ( 10 cents)  1 ( 5 cents)  63 ( 1 cents)  
    3 ( 5 cents)  63 ( 1 cents)  
    1 ( 10 cents)  68 ( 1 cents)  
    2 ( 5 cents)  68 ( 1 cents)  
    1 ( 5 cents)  73 ( 1 cents)  
    78 ( 1 cents)  
    Number of solutions =  121
另一答案
var countChange = function (money,coins) 
  function countChangeSub(money,coins,n) 
    if(money==0) return 1;
    if(money<0 || coins.length ==n) return 0;
    return countChangeSub(money-coins[n],coins,n) + countChangeSub(money,coins,n+1);
  
  return countChangeSub(money,coins,0);

另一答案

两者:迭代所有面额从高到低,取一个面额,从需求总和中减去,然后递归余数(将可用面额限制为等于或低于当前迭代值。)

另一答案

如果货币系统允许它,一个简单的greedy algorithm,从最高价值的货币开始,尽可能多的每个硬币。

否则,需要动态编程才能快速找到最佳解决方案,因为这个问题基本上就是knapsack problem

例如,如果一个货币系统有硬币:13, 8, 1,贪婪的解决方案将改变24为13, 8, 1, 1, 1,但真正的最佳解决方案是8, 8, 8

编辑:我认为我们正在以最佳方式进行变革,而不是列出所有改变美元的方法。我最近的采访询问如何进行改变,所以我在完成阅读问题之前跳了起来。

另一答案

我知道这是一个非常古老的问题。我正在寻找正确的答案,找不到任何简单和令人满意的东西。花了我一些时间,但能够记下一些东西。

function denomination(coins, original_amount)
    var original_amount = original_amount;
    var original_best = [ ];

    for(var i=0;i<coins.length; i++)
      var amount = original_amount;
      var best = [ ];
      var tempBest = [ ]
      while(coins[i]<=amount)
        amount = amount - coins[i];
        best.push(coins[i]);
      
      if(amount>0 && coins.length>1)
        tempBest = denomination(coins.slice(0,i).concat(coins.slice(i+1,coins.length)), amount);
        //best = best.concat(denomination(coins.splice(i,1), amount));
      
      if(tempBest.length!=0 || (best.length!=0 && amount==0))
        best = best.concat(tempBest);
        if(original_best.length==0 )
          original_best = best
        else if(original_best.length > best.length )
          original_best = best;
          
      
    
    return original_best;  
  
  denomination( [1,10,3,9] , 19 );

这是一个javascript解决方案,并使用递归。

另一答案

在Scala编程语言中,我会这样做:

 def countChange(money: Int, coins: List[Int]): Int = 

       money match 
           case 0 => 1
           case x if x < 0 => 0
           case x if x >= 1 && coins.isEmpty => 0
           case _ => countChange(money, coins.tail) + countChange(money - coins.head, coins)

       

  
另一答案

呃,我现在觉得很傻。下面是一个过于复杂的解决方案,我会保留它,因为它毕竟是一个解决方案。一个简单的解决方案是:

// Generate a pretty string
val coinNames = List(("quarter", "quarters"), 
                     ("dime", "dimes"), 
                     ("nickel", "nickels"), 
                     ("penny", "pennies"))
def coinsString = 
  Function.tupled((quarters: Int, dimes: Int, nickels:Int, pennies: Int) => (
    List(quarters, dimes, nickels, pennies) 
    zip coinNames // join with names
    map (t => (if (t._1 != 1) (t._1, t._2._2) else (t._1, t._2._1))) // correct for number
    map (t => t._1 + " " + t._2) // qty name
    mkString " "
  ))

def allCombinations(amount: Int) = 
 (forquarters <- 0 to (amount / 25)
      dimes <- 0 to ((amount - 25*quarters) / 10)
      nickels <- 0 to ((amount - 25*quarters - 10*dimes) / 5)
   yield (quarters, dimes, nickels, amount - 25*quarters - 10*dimes - 5*nickels)
 ) map coinsString mkString "\n"

这是另一种解决方案。该解决方案基于观察到每个硬币是其他硬币的倍数,因此它们可以用它们来表示。

// Just to make things a bit more readable, as these routines will access
// arrays a lot
val coinValues = List(25, 10, 5, 1)
val coinNames = List(("quarter", "quarters"), 
                     ("dime", "dimes"), 
                     ("nickel", "nickels"), 
                     ("penny", "pennies"))
val List(quarter, dime, nickel, penny) = coinValues.indices.toList


// Find the combination that uses the least amount of coins
def leastCoins(amount: Int): Array[Int] =
  ((List(amount) /: coinValues) (list, coinValue) =>
    val currentAmount = list.head
    val numberOfCoins = currentAmount / coinValue
    val remainingAmount = currentAmount % coinValue
    remainingAmount :: numberOfCoins :: list.tail
  ).tail.reverse.toArray

// Helper function. Adjust a certain amount of coins by
// adding or subtracting coins of each type; this could
// be made to receive a list of adjustments, but for so
// few types of coins, it's not worth it.
def adjust(base: Array[Int], 
           quarters: Int, 
           dimes: Int, 
           nickels: Int, 
           pennies: Int): Array[Int] =
  Array(base(quarter) + quarters, 
        base(dime) + dimes, 
        base(nickel) + nickels, 
        base(penny) + pennies)

// We decrease the amount of quarters by one this way
def decreaseQuarter(base: Array[Int]): Array[Int] =
  adjust(base, -1, +2, +1, 0)

// Dimes are decreased this way
def decreaseDime(base: Array[Int]): Array[Int] =
  adjust(base, 0, -1, +2, 0)

// And here is how we decrease Nickels
def decreaseNickel(base: Array[Int]): Array[Int] =
  adjust(base, 0, 0, -1, +5)

// This will help us find the proper decrease function
val decrease = Map(quarter -> decreaseQuarter _,
                   dime -> decreaseDime _,
                   nickel -> decreaseNickel _)

// Given a base amount of c

以上是关于如果给出一些美元价值,如何找到所有硬币组合的主要内容,如果未能解决你的问题,请参考以下文章

动态硬币变化算法(最佳结果)

如何计算硬币数量的变化?

如何将美元平价的所有硬币获取到 Binance API?

确定硬币组合的算法

多重背包

给定硬币的所有可能总和