有没有一种有效的方法来找到数字 n,给定一个数字 N(可能大到 10^18),对于某些 n 和 r 等于 nCr?我们如何找到 n 的对应最小值?例如
f(20)=6 (20=6C3)
f(21)= 7 (21=7C2)
f(22)= 22 (22=22C1)
有没有一种有效的方法来找到数字 n,给定一个数字 N(可能大到 10^18),对于某些 n 和 r 等于 nCr?我们如何找到 n 的对应最小值?例如
f(20)=6 (20=6C3)
f(21)= 7 (21=7C2)
f(22)= 22 (22=22C1)
求最小值 n 与求最大值 r 相同r<=n/2
。由于nCr
从下方以 为界,因此(2r)Cr=(2r)!/r!
要检查的 r 的数量是有限的。10^18
最大 r 为 31,因为64C32=1.8*10^18
.
对于给定的 r,要找到满足 N=nCr 的 n,可以检查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
我将给出一个伪代码。该算法在 O((N log N) 2 ) 时间内完成任务。它不够快,但我列出了一些可以显着提高速度的优化。也许有人可以进一步优化这一点。
这里的关键是我们可以确定素数 p 除以 n 的最大幂!对于一些 n,p 而不计算 n! 的值,只需查看 n,在 O(log n) 时间内。我们表示除 n 的 p 的最高幂!通过 hdp(n,p)。hdp(n,p) 可以计算为: hdp(n,p) = floor(n/p) + floor(n/p 2 )+...。因此 hdp(n,p) 可以在 log p中计算n 时间。
首先,我们计算小于或等于 n 的素数列表。然后,我们找到 N 的所有素数。这会更容易,因为您在步骤 1 中计算了最多 N 的素数。令 N = p 1 a 1 p 2 a 2 ...p k a k。
n 的上限和下限由 Ante 在他的解决方案中给出。我们分别称它们为 n max和 n min。然后我们对 n min和 n max之间的所有 n以及 1 到 n 之间的所有 r进行迭代。现在,N = n C r = (n!)/((nr)!*r!) 当且仅当 hdp(n, pi ) - hdp(k, pi ) - hdp(nk, pi ) = a i代表所有 i。因此,最多可以在 O((log n) 2 ) 时间内修剪 nr 对。从最大的主要因素开始应该是更好的主意,因为程度应该很小。
一些优化:
对于任何 n,您可以找到 r 可以位于的范围,而不是对所有 r 从 1 到 n 进行迭代。使用n C r > (n/r) r等不等式。
无需考虑小于 p k的 n 值,其中 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 ) 等等。