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