所以我最近对计算阶乘的兴趣非常快,我写了一个,并从互联网上得到了一些其他人来测试。这是代码(Python 2.7):
def prime_sieve(n):
"""A prime sieve, takes an int n, and returns all the primes < n"""
odds = range(3,n+1,2) #make a list of odd numbers starting at 3
i = 0 #an index counter
sqrt_n = int(n**0.5) #upper limit for sieving is isqrt(n)
d = len(odds) #the length of the list
while odds[i]<=sqrt_n:
if odds[i]: #odds[i]!=0
step = odds[i] #the ith odd which is nonzero is the next prime
start = (i+1)*step + i #this is the location of odds[i]^2
while start<d: #zero out stepth odd from start.
odds[start]=0
start+=step
i+=1
return [2]+filter(None, odds) #return a list of primes
#This one I wrote, and the stuff above
def factorial(n):
"""A factorial function which computes n! using it's prime factors"""
factors = [[i, 0] for i in prime_sieve(n)] #a list holding factor-exp pairs
for i in xrange(len(factors)): #find the exponents
m=float(n)
while factors[i][0]<m:
m/=factors[i][0]
factors[i][1]+=int(m)
result = 1 << factors[0][1] #start off by shifting result, instead of x 2^m
for i in factors[1:]: #multiply result by every prime factor^exp
result*=i[0]**i[1]
return result
#the next two are the obvious ones
def naive_factorial(n):
"""Calculates n! with a simple while loop"""
result = n
while (n-1)>1: #n! = n*(n-1)*(n-2)...*2*1
result*=(n-1)
n-=1
return result
def recursive_fact(n):
"""Calculates n! recursively"""
if n<=1: return 1 #n! = n*(n-1)!
return n*recursive_fact(n-1)
def factorial2(n):
"""Another factorial claiming to be fast, pulled from the internet
I have no idea how this works"""
result = 1
buf = 1
power2 = 0
stack = []
while n > 1:
stack.append(n)
n /= 2
power2 += n
for n1 in stack[::-1]:
while n <= n1:
buf *= n
n += 2
result *= buf
result <<= power2
return result
def multiply_range(n, m):
if n == m:
return n
if m < n:
return 1
else:
return multiply_range(n, (n+m)/2) * multiply_range((n+m)/2+1, m)
def factorial3(n):
""""Also from the internet, the logic seems reasonable, 'divide and conquer'. """
return multiply_range(1, n)
if __name__=="__main__":
import math
import cProfile
print "Calculating 70000! using naive_factorial."
cProfile.run("naive_factorial(70000)")
print "\nCalculating 70000! using math.factorial."
cProfile.run("math.factorial(70000)")
print "\nCalculating 70000! using factorial."
cProfile.run("factorial(70000)")
print "\nCalculating 70000! using factorial2."
cProfile.run("factorial2(70000)")
print "\nCalculating 70000! using factorial3."
cProfile.run("factorial3(70000)")
我不是python的新手,我已经使用它作为我的主要语言几年,但结果有点出乎意料......
Calculating 70000! using naive_factorial.
3 function calls in 19.842 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 19.842 19.842 <string>:1(<module>)
1 19.842 19.842 19.842 19.842 factorial.py:30(naive_factorial)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Calculating 70000! using math.factorial.
3 function calls in 22.372 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 22.372 22.372 <string>:1(<module>)
1 22.372 22.372 22.372 22.372 {math.factorial}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Calculating 70000! using factorial.
8 function calls in 9.092 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.003 0.003 9.092 9.092 <string>:1(<module>)
1 0.020 0.020 0.024 0.024 factorial.py:1(prime_sieve)
1 9.066 9.066 9.090 9.090 factorial.py:17(factorial)
1 0.002 0.002 0.002 0.002 {filter}
2 0.000 0.000 0.000 0.000 {len}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.002 0.002 0.002 0.002 {range}
Calculating 70000! using factorial2.
19 function calls in 5.791 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 5.791 5.791 <string>:1(<module>)
1 5.791 5.791 5.791 5.791 factorial.py:43(factorial2)
16 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Calculating 70000! using factorial3.
140002 function calls (4 primitive calls) in 1.147 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 1.147 1.147 <string>:1(<module>)
139999/1 1.147 0.000 1.147 1.147 factorial.py:65(multiply_range)
1 0.000 0.000 1.147 1.147 factorial.py:73(factorial3)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
我的问题是,最后一个怎么能比Soo快得多?!看看所有的递归!我以为Python不喜欢递归!那为什么它这么快? factorial2如何工作?此外,众所周知,我检查过这些都是为几个不同的值产生正确的输出,所以我很确定这些都能正常工作。
答案 0 :(得分:0)
“factorial2()
如何工作的简短回答:它只是计算结果必须乘以2的两倍(变量)才能找出所有奇数power2
,仅将奇数相加,并在最后一步中将结果的二进制表示移位(实际上,将其乘以2**power2
)。
至于“为什么这么快”,我不清楚你是否正确地解释了你的结果:140002 function calls
的数字似乎包括multiply_range()
的来电。它仍然很快(我使用factorial2
在5次运行中得到1.6s而4.4s与n=70000
,但不是那么令人愤慨。