有没有一种有效的方法来找到数字n,给定数字N(可能大到10 ^ 18),对于某些n和r,它等于nCr?我们如何找到n的相应最小值?例如
f(20)=6 (20=6C3)
f(21)= 7 (21=7C2)
f(22)= 22 (22=22C1)
答案 0 :(得分:0)
要找到最小值n与使用r<=n/2
找到最大值r相同。由于nCr
的下限是(2r)Cr=(2r)!/r!
,因此要检查的r的数量是有限的。对于10^18
,最大值r为31,因为64C32=1.8*10^18
。
要找到n,对于给定的r,满足N = nCr,可以检查N*r!
形式的n*(n-1)*...*(n-r+1)
。 n接近(N*r!)^(1/r) + r/2
。我认为只需几张支票即可进行测试。
<强>更新强>
简单的python实现:
from operator import mul # or mul=lambda x,y:x*y
from fractions import Fraction
def nCk(n,k):
return int(reduce(mul, (Fraction(n-i, i+1) for i in range(k)), 1))
def M(f,t):
return reduce(mul, range(f,t+1), 1)
def find_n_r(N):
for r in range(2, 50): # omit 1, since it is trivial solution
if nCk(2*r, r) > N:
return False
Nr = N * M(1,r)
nn = pow(Nr, 1./r) + r*0.5
n = int(nn)
if M(n-r+1,n) == Nr:
return n, r
n += 1
if M(n-r+1,n) == Nr:
return n, r
print find_n_r(nCk(31,7))
print find_n_r(nCk(31,7) + 12)
打印:
(31, 7)
False
答案 1 :(得分:0)
我会给出一个伪代码。该算法在O((N log N) 2 )时间内完成任务。它不够快,但我列出了一些可以显着提高速度的优化。也许有人可以进一步优化这一点。
这里的关键是我们可以确定分割n的素数p的最高幂!对于某些n,p而不计算n!的值,只需在O(log n)时间内查看n。我们表示分割n的p的最高功率!通过hdp(n,p)。 hdp(n,p)可以计算为: hdp(n,p)= floor(n / p)+ floor(n / p 2 )+ ....因此hdp(n,p)可以在log p <中计算/ sub> n time。
首先,我们计算小于或等于n的素数列表。 然后,我们找到N的所有素因子。这将更容易,因为您在步骤1中计算了高达N的素数。设N = p 1 a 1 < /sup>p2a2...pkak子>
n的上限和下限由Ante在他的解决方案中给出。我们分别称它们为n max 和n min 。然后我们迭代n min 和n max 之间的所有n,对于1到n之间的所有r。现在,N = n C r =(n!)/((nr)!* r!)当且仅当hdp(n,p i < / sub>) - hdp(k,p i ) - hdp(nk,p i )= a i 表示所有i。因此,n-r对最多可以在O((log n) 2 )时间内被修剪。从最大的素因子开始应该是更好的想法,因为程度应该很小。
一些优化:
,你可以找到r所在的范围,而不是迭代所有r从1到n。使用不等式,如 n C r &gt; (n / r) r 等。
无需考虑n小于p k 的值,其中p k 是N的最大素因子。
分别求解 n C 2 = N(这只是一个二次方程)。现在,只考虑小于此解决方案的n值。该解决方案将是O(N 1/2 ),因此将时间复杂度降低到O(N(log n) 2 )。为 n C 2 = N和 n C 3 = N执行此操作会将时间复杂度降低到O (N 2/3 (log n) 2 )依此类推。