我有一些代码可以计算排列和组合,并且我正在尝试使其更适合大量使用。
我发现了一种更好的置换算法,可以避免较大的中间结果,但是我仍然认为我可以对组合做得更好。
到目前为止,我已经提出了一种特殊情况来反映nCr的对称性,但是我仍然想找到一种更好的算法来避免调用factorial(r),这是不必要的大中间结果。没有这种优化,最后的doctest尝试计算阶乘(99000)会花费很长时间。
谁能建议一种更有效的组合计数方法?
from math import factorial def product(iterable): prod = 1 for n in iterable: prod *= n return prod def npr(n, r): """ Calculate the number of ordered permutations of r items taken from a population of size n. >>> npr(3, 2) 6 >>> npr(100, 20) 1303995018204712451095685346159820800000 """ assert 0 <= r <= n return product(range(n - r + 1, n + 1)) def ncr(n, r): """ Calculate the number of unordered combinations of r items taken from a population of size n. >>> ncr(3, 2) 3 >>> ncr(100, 20) 535983370403809682970 >>> ncr(100000, 1000) == ncr(100000, 99000) True """ assert 0 <= r <= n if r > n // 2: r = n - r return npr(n, r) // factorial(r)
如果n离r不远,则使用递归组合定义可能会更好,因为xC0 == 1,那么您将只有几次迭代:
这里相关的递归定义是:
nCr =(n-1)C(r-1)* n / r
可以使用尾部递归结合以下列表来很好地计算出此值:
[(n-r,0),(n-r + 1,1),(n-r + 2,2),…,(n-1,r-1),(n,r)]
注意,这当然很容易在Python中生成(自nC0 = 1以来,我们省略了第一个条目),izip(xrange(n - r + 1, n+1), xrange(1, r+1))请注意,这假设r <= n,您需要检查该值,如果不是,请交换它们。为了优化使用,如果r <n / 2,则r = n-r。
izip(xrange(n - r + 1, n+1), xrange(1, r+1))
现在,我们只需要使用带有reduce的尾部递归来应用递归步骤。我们从1开始,因为nC0为1,然后将当前值乘以列表中的下一个条目,如下所示。
from itertools import izip reduce(lambda x, y: x * y[0] / y[1], izip(xrange(n - r + 1, n+1), xrange(1, r+1)), 1)