如何计算硬币问题的可能组合
我正在尝试实现一个硬币问题,问题规范如下
创建一个函数来计算可用于给定数量的所有可能的硬币组合。
All possible combinations for given amount=15, coin types=1 6 7
1) 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2) 1,1,1,1,1,1,1,1,1,6,
3) 1,1,1,1,1,1,1,1,7,
4) 1,1,1,6,6,
5) 1,1,6,7,
6) 1,7,7,
函数原型:
int findCombinationsCount(int amount, int coins[])
假设硬币数组已排序。对于上面的例子,这个函数应该返回 6。
有人指导我如何实现这个吗?
I am trying to implement a coin problem, Problem specification is like this
Create a function to count all possible combination of coins which can be used for given amount.
All possible combinations for given amount=15, coin types=1 6 7
1) 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2) 1,1,1,1,1,1,1,1,1,6,
3) 1,1,1,1,1,1,1,1,7,
4) 1,1,1,6,6,
5) 1,1,6,7,
6) 1,7,7,
function prototype:
int findCombinationsCount(int amount, int coins[])
assume that coin array is sorted. for above example this function should return 6.
Anyone guide me how to implement this??
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(18)
使用递归。
不过你应该检查这个实现。我这里没有Java IDE,而且我有点生疏,所以可能会有一些错误。
Use recursion.
You should check this implementation though. I don't have a Java IDE here, and I'm a little rusty, so it may have some errors.
尽管递归可以发挥作用,并且通常是一些大学水平的算法和递归课程中需要实施的作业。数据结构,我相信“动态编程”实现效率更高。
Although recursion can work and is often an assignment to implement in some college level courses on Algorithms & Data Structures, I believe the "dynamic programming" implementation is more efficient.
您可以使用生成函数方法来给出使用复数的快速算法。
给定硬币值 c1, c2, .., ck,要获得 n 求和的方法数,您需要的是 x^n 中的系数,
中查找 x^n 的系数相同
这与现在使用复数 数字,x^a - 1 = (x-w1)(x-w2)...(x-wa) 其中 w1、w2 等是复数单位根。
所以
可以写成
可以用部分分数重写为
可以很容易地找到其中的 x^n 系数:
计算机程序应该能够轻松找到 Ai 和 ai (可以是复数)。当然,这可能涉及浮点计算。
对于较大的 n,这可能比枚举所有可能的组合更快。
希望有帮助。
You can use generating function methods to give fast algorithms, which use complex numbers.
Given the coin values c1, c2, .., ck, to get the number of ways to sum n, what you need is the coefficient of x^n in
Which is the same as finding the coefficient of x^n in
Now using complex numbers, x^a - 1 = (x-w1)(x-w2)...(x-wa) where w1, w2 etc are the complex roots of unity.
So
can be written as
which can be rewritten using partial fractions are
The coefficient of x^n in this can be easily found:
A computer program should easily be able to find Ai and ai (which could be complex numbers). Of course, this might involve floating point computations.
For large n, this will be probably faster than enumerating all the possible combinations.
Hope that helps.
递归非常简单:
这是 SCALA 中的示例
Very simple with recursion:
This is example in SCALA
Aryabhatta 的回答
计算用固定的硬币找零的方式的数量
面额很可爱,但实施起来也不切实际
描述的。我们将使用模块化,而不是使用复数
算术,类似于数论变换如何替换
用于乘以整数多项式的傅里叶变换。
令
D
为硬币面额的最小公倍数。经过狄利克雷算术级数定理,存在无限
许多质数
p
使得D
整除p - 1
。 (如果运气好的话,它们甚至会以我们可以找到它们的方式分发
有效地。)我们将计算对某些
p
取模的方式数满足这个条件。通过以某种方式获得粗略的界限(例如,
n + k - 1
选择k - 1
,其中n
是总数,k
是数字的面额),用几个不同的重复此过程
乘积超过该界限的素数,并应用中文
余数定理,我们可以恢复出精确的数。
测试候选者
1 + k*D
是否为整数k > 0
直到我们找到素数p
。令g
为原根模p
(生成候选随机并应用标准测试)。对于每个面额
d
,表达多项式
x**d - 1
模p
作为因子的乘积:请注意,
d
除以D
除p-1
,所以指数确实是整数。
令
m
为面额之和。收集所有常数g**((p-1)*i/d)
为a(0), ..., a(m-1)
。下一步是找到一个部分分数分解
A(0), ..., A(m-1)
使得如果存在偶数,则其中
sign
为1
宗派数量和-1
如果面额为奇数。推导出一个系统通过计算给定两边的值来得到
A(j)
的线性方程不同
x
值的方程,然后用高斯求解消除。如果存在重复,生活就会变得复杂;选择另一个素数可能是最简单的。
给定这个设置,我们可以计算路数(模
p
,当然)进行总计
n
的更改为Aryabhatta’s answer for
counting the number of ways to make change with coins of fixed
denominations is very cute but also impractical to implement as
described. Rather than use complex numbers, we’ll use modular
arithmetic, similar to how the number-theoretic transform replaces a
Fourier transform for multiplying integer polynomials.
Let
D
be the least common multiple of the coin denominations. ByDirichlet’s theorem on arithmetic progressions, there exist infinitely
many prime numbers
p
such thatD
dividesp - 1
. (With any luck,they’ll even be distributed in a way such that we can find them
efficiently.) We’ll compute the number of ways modulo some
p
satisfying this condition. By obtaining a crude bound somehow (e.g.,
n + k - 1
choosek - 1
wheren
is the total andk
is the numberof denominations), repeating this procedure with several different
primes whose product exceeds that bound, and applying the Chinese
remainder theorem, we can recover the exact number.
Test candidates
1 + k*D
for integersk > 0
until we find a primep
. Letg
be a primitive root modulop
(generate candidates atrandom and apply the standard test). For each denomination
d
, expressthe polynomial
x**d - 1
modulop
as a product of factors:Note that
d
dividesD
dividesp-1
, so the exponent indeed is aninteger.
Let
m
be the sum of denominations. Gather all of the constantsg**((p-1)*i/d)
asa(0), ..., a(m-1)
. The next step is to find apartial fraction decomposition
A(0), ..., A(m-1)
such thatwhere
sign
is1
if there are an even number of denominations and-1
if there are an odd number of denominations. Derive a system oflinear equations for
A(j)
by evaluating both sides of the givenequation for different values of
x
, then solve it with Gaussianelimination. Life gets complicated if there are duplicates; it's probably easiest just to pick another prime.
Given this setup, we can compute the number of ways (modulo
p
, ofcourse) to make change amounting to
n
as提到的递归解决方案是可行的,但如果您添加更多硬币面额和/或显着增加目标值,它们将会非常慢。
您需要加快速度的是实现动态编程解决方案。查看背包问题。您可以采用那里提到的 DP 解决方案来解决您的问题,方法是记录可以达到总数的方式数量,而不是所需的最小硬币数量。
The recursive solutions mentioned will work, but they're going to be horrendously slow if you add more coin denominations and/or increase the target value significantly.
What you need to speed it up is to implement a dynamic programming solution. Have a look at the knapsack problem. You can adapt the DP solution mentioned there to solve your problem by keeping a count of the number of ways a total can be reached rather than the minimum number of coins required.
递归解决方案可能是正确的答案:
警告:我还没有测试甚至编译上面的内容。
A recursive solution might be the right answer here:
Warning: I haven't tested or even compiled the above.
@Jordi 提供的解决方案很好,但运行速度非常慢。您可以尝试在该解决方案中输入 600 并查看它有多慢。
我的想法是使用自下而上的动态规划。
请注意,一般来说,money=m 和 coin{a,b,c} 的可能组合等于
如果没有可用的币或可用的币不能满足所需的金额,则应相应地向区块填充0。如果金额为0,则应填写1。
The solution provided by @Jordi is nice but runs extremely slow. You can try input 600 to that solution and see how slow it is.
My idea is to use bottom-up dynamic programming.
Note that generally, the possible combination for money=m and coins{a,b,c} equals combination for
If no coins are available or available coins can not cover the required amount of money, it should fill in 0 to the block accordingly. If the amount of money is 0, it should fill in 1.
该代码基于 JeremyP 提供的解决方案,该解决方案工作完美,我只是通过使用动态编程对其进行了增强以优化性能。我无法对 JeremyP 帖子发表评论,因为我没有足够的声誉:)
This code is based on the solution provided by JeremyP which is working perfect and I just enhanced it to optimize the performance by using dynamic programming.I couldn't comment on the JeremyP post because I don't have enough reputation :)
第一个想法:(
在这种情况下“<=”是多余的,但如果您决定更改参数,则需要更通用的解决方案)
First idea:
(the '<=' is superfluous in this case, but is needed for a more general solution, if you decide to change your parameters)
下面是带有记忆化java解决方案的递归。对于下面的一个,我们有 1,2,3,5 个硬币,200 个作为目标数量。
Below is recursion with memoization java solution. for below one we have 1,2,3,5 as coins and 200 as the target amount.
再次使用递归作为经过测试的解决方案,尽管可能不是最优雅的代码。 (请注意,它返回要使用的每种硬币的数量,而不是重复实际的硬币数量 n 次)。
Again using recursion a tested solution, though probably not the most elegant code. (note it returns the number of each coin to use rather than repeating the actual coin ammount n times).
动态规划解决方案
给定一系列面额
D = {d1, d2, d3, ... , dm}
和目标金额W
。请注意,D
不需要排序。令
T(i, j)
为仅使用第i
左边的面额组成金额j
的组合数量(可以包含自身)在D
中。我们有:
T(0, 0) = 1
:由于金额为0
,因此只有 1 个有效组合可以组成0
code>,这是空集。T(i, j) = T(i - 1, j)
如果D[i] > j
T(i, j) = T(i - 1, j) + T(i, j - D[i])
ifD[i] <= j
Dynamic Programming Solution
Given an array of denominations
D = {d1, d2, d3, ... , dm}
and a target amountW
. Note thatD
doesn't need to be sorted.Let
T(i, j)
be the number of combinations that make up amountj
using only denominations on the left of theith
one (can include itself) inD
.We have:
T(0, 0) = 1
: since the amount is0
, there is only 1 valid combination that makes up0
, which is the empty set.T(i, j) = T(i - 1, j)
ifD[i] > j
T(i, j) = T(i - 1, j) + T(i, j - D[i])
ifD[i] <= j
下面是我创建的递归回溯解决方案,它列出并计算了所有可能的面额(硬币)组合,总计达到给定金额。
面额和金额都可以是动态的
输出:
1)[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
2)[1, 1, 1, 1, 1, 1, 1, 1, 1, 6]
3)[1, 1, 1, 1, 1, 1, 1, 1, 7]
4)[1, 1, 1, 6, 6]
5)[1, 1, 6, 7]
6)[1, 7, 7]
Below is a recursive backtracking solution I created, It lists and counts all possible combination of denominations (coins) that would add up to a given amount.
Both denominations and the amounts can be dynamic
Output:
1)[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
2)[1, 1, 1, 1, 1, 1, 1, 1, 1, 6]
3)[1, 1, 1, 1, 1, 1, 1, 1, 7]
4)[1, 1, 1, 6, 6]
5)[1, 1, 6, 7]
6)[1, 7, 7]
对于硬币(1,5,10,25,50)的相同问题有以下解决方案之一。
解应满足以下方程:
1*a + 5*b + 10*c + 25*d + 50*e == cents
这可以针对任何通用解决方案进行修改。
The same problem for coins(1,5,10,25,50) has one of below solutions.
The solution should satisfy below equation:
1*a + 5*b + 10*c + 25*d + 50*e == cents
This can be modified for any general solutions.