Algorithm 计算n的值选择k

Algorithm 计算n的值选择k,algorithm,language-agnostic,combinations,Algorithm,Language Agnostic,Combinations,评估n和k值的最有效方法是什么? 我认为蛮力的方法是找到n阶乘/k阶乘/(n-k)阶乘 根据这一点,更好的策略可能是使用dp。有没有其他更好的方法来评估n选择k “最有效”是一个糟糕的请求。你想让什么更有效率?那一堆?记忆?速度总的来说,我的观点是递归方法是最有效的,因为它只使用加法(一种廉价的操作),并且递归在大多数情况下不会太糟糕。功能是: nchoosek(n, k) { if(k==0) return 1; if(n==0) return 0; return nc

评估n和k值的最有效方法是什么? 我认为蛮力的方法是找到n阶乘/k阶乘/(n-k)阶乘

根据这一点,更好的策略可能是使用dp。有没有其他更好的方法来评估n选择k

“最有效”是一个糟糕的请求。你想让什么更有效率?那一堆?记忆?速度总的来说,我的观点是递归方法是最有效的,因为它只使用加法(一种廉价的操作),并且递归在大多数情况下不会太糟糕。功能是:

nchoosek(n, k)
{
    if(k==0) return 1;
    if(n==0) return 0;
    return nchoosek(n-1, k-1)+nchoosek(n-1,k);
}

如果你有一个阶乘查找表,那么C(n,k)的计算会非常快。

问题在于
n/K(n-k)方法与其说是成本,不如说是
的问题
增长非常迅速,因此即使对于
nCk
的值(其完全在64位整数的范围内),中间计算也不适用。如果您不喜欢kainaw的递归加法方法,可以尝试乘法方法:

nCk==产品(i=1..k)(n-(k-i))/i


其中,
product(i=1..k)
表示当
i
取值
1,2,…,k
时所有项的乘积。计算二项式系数
(n选择k)
而不溢出的最简单方法可能是使用帕斯卡三角形。不需要分数或乘法<代码>(n选择k)
。帕斯卡三角形的第n行和第k行给出值


。这是一个只有加法的
O(n^2)
操作,可以用动态规划求解。对于任何可以放入64位整数的数字来说,它都将是闪电般的快。

您可以使用乘法公式:


如果要计算许多这样的组合,计算帕斯卡三角形肯定是最好的选择。正如您已经知道的递归公式,我想我可以在这里通过一些代码:

MAX\u N=100
最大值K=100
C=[[1]+[0]*范围内i的MAX_K(MAX_N+1)]
对于范围内的i(1,最大N+1):
对于范围内的j(1,最大K+1):
C[i][j]=C[i-1][j-1]+C[i-1][j];
打印C[10][2]
打印C[10][8]
打印C[10][3]

这是我的版本,它只对整数起作用(被k除总是产生整数商),并且在O(k)处速度很快:


我递归地编写它,因为它非常简单和漂亮,但是如果你愿意,你可以将它转换为迭代解。

最快的方法可能是使用公式,而不是帕斯卡三角形。当我们知道以后要除以同一个数时,我们就开始不做乘法了。 如果k 至少使用这种技术,你永远不会被以前用来乘法的数字除。有(n-k)乘法和(n-k)除法



我在想一种避免所有除法的方法,在我们必须相乘的数字和我们必须除法的数字之间找到GCD。我稍后会尝试编辑。

这是一个树递归,对于n和k的大值,它可能根本不会完成。它是
O(2^n)
time和
O(n)
space。阶乘计算是
O(n)
时间和
O(1)
空间。这绝对是指数的。计算nchoosek(n,k)的复杂性至少是nchoosek(n,k),因为基本情况是0和1。如果您对动态编程也这样做,您将得到n^2的复杂性,在这里您要多次计算相同的结果。@AndrewMao每次调用此函数都会在递归树中产生2个节点。递归在n个步骤后停止(我假设k这是一个糟糕的方法,即使使用memonization.C(10^12,2)需要一万亿次加法,但乘法公式是瞬时的。阶乘计算在空间和时间方面都比递归方法有效得多。首先,您可以用
n*(n-1)*(n-2)*……*(k+1)替换
n!/k!
n!和
k!
当许多因素抵消时,完全计算
n!
没有意义。你在考虑n的范围是多少?@AndrewMorton我必须计算n选择k,其中n是@M42:这个问题不是你链接的问题的重复。这个问题要求n中k元素的所有组合,而这个任务ion只需要这些组合的数量。对于大量的n和k,该查找表可能是禁止的。此外,该表之外的值应该有一个选项。@Pedrom在问题中没有提到对数字大小的限制。它被标记为
语言不可知
算法
。它不是O(k);k严格小于n,因此您不能忽略n对运行时的贡献。充其量,您可以说它是O(km(n)),其中M(n)是乘法算法的速度。正确,但迂腐。上述函数使O(k)乘法和除法。我忽略了运算本身的位复杂性。这个函数计算
n!/k!
。这不是问题所在about@icepack:不,不是。分子范围从n到n-k+1。分母范围从k到1。因此,选择(9,4)=(9*8*7*6)/(4*3*2*1)=126,这是正确的。相比之下,9!/4!=362880/24=15120。这是递归形式的乘法方法。它确实是O(k),并且是它能得到的最快的方法,除非对k的估计足够好()。使用斯特林近似法()有一个分治版本的阶乘可能也有帮助()找到GCD肯定会减少运算量。不幸的是,GCD本身的发现将是一项更为繁重的任务。是的,我很害怕。但是当乘法有一个大的数时,GCD将以小的数计算。实际上我不确定GCD是否比除法更难。我倾向于怀疑,但事实上确实如此
function choose(n, k)
    if k == 0 return 1
    return (n * choose(n - 1, k - 1)) / k
n! / (k! x (n-k)!) = (product of numbers between (k+1) and n) / (n-k)!