我正在学习一个客户端拼图协议,我有一个关于找到解决方案可能性的问题。这里有一个场景,而不是进入干燥的协议事实:
假设我有x个人,我有y苹果:
是否有计算方案数量的公式?
实施例: 4人[x],6个苹果[y],15个MAX苹果[z]
没有。手工计算的情景:10。
如果我的数字很大,我希望用公式计算它。
感谢您的帮助。
答案 0 :(得分:1)
您的问题等同于"通过将x
个数字加在一起,找到可以获得z
的方式的数量,每个数字位于min
和{{1}之间}&#34。示例Python实现:
max
结果:
def possible_sums(x, z, min, max):
if min*z > x or max*z < x:
return 0
if z == 1:
if x >= min and x <= max:
return 1
else:
return 0
total = 0
#iterate from min, up to and including max
for i in range(min, max+1):
total += possible_sums(x-i, z-1, min, max)
return total
print possible_sums(6, 4, 1, 15)
使用大数字调用此函数会变得非常昂贵,但使用memoization可以改善运行时间。如何实现这一点取决于语言,但传统的Python方法是将先前计算的值存储在字典中。
10
现在def memoize(fn):
results = {}
def f(*args):
if args not in results:
results[args] = fn(*args)
return results[args]
return f
@memoize
def possible_sums(x, z, min, max):
#rest of code goes here
需要花费很长时间才能计算,会立即返回print possible_sums(60, 40, 1, 150)
。
答案 1 :(得分:0)
有数学方法可以做到这一点。它类似于询问有多少种方法可以在3个6面骰子上滚动总共10个(x = 3,y = 10,z = 6)。您可以通过几种不同的方式实现此目的。
一种方法是使用包含 - 排除。将y作为x正数之和而没有最大值的方法的数量是y-1,由stars-and-bars argument选择x-1。您可以计算将y作为x正数之和的方式的数量,以便如果yx-sz为负,则y的一个特定的s组至少为z + 1:0,并且y-1-sz选择x- 1如果它是非负的。然后你可以使用inclusion-exclusion将计数写为s的非负值的总和,使yx-sz为(-1)^ s(x选择s)的非负(y-1-sz选择x-1) )。
您可以使用生成功能。你可以让一些变量的权力,比如t,保持总数,系数表示总数的组合数。然后你要求(t + t ^ 2 + ... + t ^ z)^ x中的t ^ y系数。您可以通过几种方式计算出来。
一种方法是动态编程,计算k到x的(t + t ^ 2 + ... + t ^ z)^ k的系数。天真的方法可能足够快:你可以计算k = 1,2,3,...,x。使用诸如重复平方之类的东西要快一点,例如,计算第87次幂,你可以将二进制扩展为87 + 64 + 16 + 4 + 2 + 1 = 0b1010111(写成二进制文字)。您可以通过平方和乘法计算第1,第2,第4,第16和第64功率,或者您可以通过squaring and multiplying计算0b1,0b10,0b101,0b1010,0b10101,0b101011和0b1010111功率来保存小空间。
另一种方法是使用二项式定理两次。
(t+t^2+...+t^z)^x = t^x ((t^z-1)/(t-1))^x
= t^x (t^z-1)^x (t-1)^-x.
带有指数x的二项式定理让我们将(t ^ z-1)^ x重写为(-1)^ st ^(z(xs))之和(x选择s),其中s的范围从0到x 。它还允许我们将(t-1)^ - x重写为(r + x-1选择x-1)t ^ r与非负r的无限和。然后我们可以选出有助于t ^ y系数的有限项集(r = y-x-sz),并得到与上面的包含 - 排除相同的和。
例如,假设我们有x = 1000,y = 1100,z = 30。值是
= 1.29 x 10 ^ 144。