python中大数的阶乘
以下是我的阶乘方法:python中大数的阶乘,python,algorithm,factorial,Python,Algorithm,Factorial,以下是我的阶乘方法: def factorial(n): '''Returns factorial of n''' r = 1 for i in range(1, n + 1): r *= i return r 我认为这很简单,虽然我想你可以做一些更有效的东西,因为像100000这样的大数字需要时间。我的问题是,有吗?factorial()也不好,它花费的时间大致相同。您确实知道factorial(100000)是2.8242294080×10^
def factorial(n):
'''Returns factorial of n'''
r = 1
for i in range(1, n + 1):
r *= i
return r
我认为这很简单,虽然我想你可以做一些更有效的东西,因为像100000这样的大数字需要时间。我的问题是,有吗?factorial()也不好,它花费的时间大致相同。您确实知道factorial(100000)是2.8242294080×10^456573
这就是为什么它很慢,而且很大。如果需要较短的执行时间并且不需要尽可能高的精度,可以使用近似公式,例如,可以返回gamma函数(
math.gamma(x)
),但是,用for循环生成阶乘可能会更快,因为四次方效应会导致速度减慢:当n变大时,你必须进行更多的乘法,但你也必须乘以更大的数
找到更好的算法并不容易。您可以尝试利用对称性(如FFT)。以不同的顺序进行乘法也很有好处,结果是中间的,因此最后只需要乘以几个非常大的数字,但我一直没有想到这一点。在任何情况下,你都必须找到一条可以利用的法律
寻找进一步的灵感。阶乘变得非常大,因此处理数字的对数通常更好 许多语言都有一个lgama库函数,用于计算n-1的阶乘的自然对数 这意味着您可以通过lgama(n+1)计算阶乘(n)的自然对数 可以除以log10将其转换为以10为底的对数 因此,如果您只需要位数,那么此Python代码将立即给出答案:
from math import *
print ceil(lgamma(100000+1)/log(10))
将数字按顺序相乘
r = 1
for i in range(1, n + 1):
r *= i
return r
非常快地创建一个大的数字(比如数万位),然后你有一个大的数字和一个小的数字的大量乘法。至少有一个因素是巨大的乘法是缓慢的
例如,通过减少涉及大量数字的乘法次数,可以大大加快运算速度
def range_prod(lo,hi):
if lo+1 < hi:
mid = (hi+lo)//2
return range_prod(lo,mid) * range_prod(mid+1,hi)
if lo == hi:
return lo
return lo*hi
def treefactorial(n):
if n < 2:
return 1
return range_prod(1,n)
因此,
100000的加速比超过10倍代码>如果您只需要一个近似值,Ramanujan的阶乘近似值应该比Stirling的更精确
如果您需要(或想要)一些精确的东西,您可以尝试GMP,GNU多精度库。我已经成功地将它用于Python中大数的素性测试。您可以使用reduce函数而不是显式循环,因此:
>>> from functools import reduce
>>> mul = int.__mul__
>>> len(str(reduce(mul, range(2,100001), 1)))
456574
>>>
在Python2中,您需要使用long:long.\uu mul\uuu
和len(str(reduce(mul,range(2L,100001L),1L))
真正需要真正的阶乘值n实际上是不寻常的!在许多应用领域。通常,使用阶乘的自然对数更为现实。我想不出有哪个应用程序不能将日志用作更好的替代方案,因为阶乘最常用于计算相关值
选择事物组合的概率
通常要计算的是基于因子的概率,例如选择二项系数(nk)=n!/(k!(n-k)!)。假设这是一个阶乘比率,那么log(nk)=log(n!)-log(k!)-log((n-k)!)是使用各种对数阶乘近似之一可靠计算的。如果你做了大量的概率数学,通常最好还是在对数域中进行(以分贝为单位测量概率),因为它通常涉及非常广泛的小于1的数字范围,因此如果不使用对数版本,使用常用的浮点表示法,数学精度会很快下降
E.T.Jaynes是一位著名的数学家和概率论专家,我推荐他的书《概率论:科学的逻辑》作为这一主题以及使用对数概率的贝叶斯推理和信息理论的可读资料。我完成了2000000!(200万),Chez方案9.5.4在Lispide运行。计算一小时,在屏幕上打印19分钟。非常令人印象深刻。
Francesco使用xrange而不是range,至少可以节省一些内存(如果不是时间的话)。xrange是一个生成器,不需要预先分配整个列表。谢谢,但我使用的是Python 3,range()现在是一个生成器。您是否想过使用内置的math.factorial
?它比上面的代码要快一点。我需要的是得到阶乘的长度,但在我可以这样做之前,我需要生成数字(或者至少我是这么认为的)。len(str(factorial(100000)))实际上在大约一分钟后返回456574。logamma
在Python2.7.x和Python3.2+中(但不是在3.0.x或3.1.x中)。有趣的事实:看看源代码,Python使用Lanczos近似值作为gamma函数(Modules/mathmodules.c
)。“以不同的顺序进行乘法也很有好处,结果是中间的,这样你最后只需要乘以几个非常大的数字“我已经尝试过了,在Haskell中我得到了很大的加速,但是我在Python中的尝试并没有带来很大的不同。不知道为什么,哼!这不是算法,而是转换成字符串的速度太慢了math.gamma(100000)
不幸引发了溢出错误
;该函数仅适用于高达170的值。您同意在BSD许可的SciPy中使用该函数吗@当然可以。不过,我对代码中的错误不承担任何责任。关于这个算法,我也不能说有什么独创性,因为它太旧了。所以这个算9!作为((2*3)*(4*5))*((6*7)*(8*9))
?最好使用((2*9)*(3*8))*((4*7)*(5*6))
将它们保持更小的长度?@endolith在中间产品的大小方面没有太大的差异。当我测试时(很久以前,在哈斯克尔),我
>>> from functools import reduce
>>> mul = int.__mul__
>>> len(str(reduce(mul, range(2,100001), 1)))
456574
>>>