所以我对Python和Julia编写的Eratosthenes函数有一点筛子,我正在比较运行时间。
这是Python代码:
import time
def get_primes(n):
numbers = set(range(n, 1, -1))
primes = []
while numbers:
p = numbers.pop()
primes.append(p)
numbers.difference_update(set(range(p*2, n+1, p)))
return primes
start = time.time()
get_primes(10000)
print time.time() - start
这是Julia代码:
function get_primes(n)
numbers = [2:n]
primes = Int[]
while numbers != []
p = numbers[1]
push!(primes, p)
numbers = setdiff(numbers, [p*i for i=1:int(n/p)])
end
return primes
end
@time get_primes(10000);
Python代码在大约.005秒内运行,而Julia代码大约需要0.5秒,因此这意味着Python运行速度提高了大约100倍。这可能是一个完全合乎逻辑的理由,但我真的不知道我在做什么。
答案 0 :(得分:8)
主要的不同之处在于,在Python中你分配了一个集number
,然后在适当的位置修改它,而在Julia中,你在循环的每次迭代中分配一个新数组。另一个区别是你在Python中使用了一个集合,在Julia中使用了一个数组(Python称之为“列表”)。让我们更改Julia代码以消除这两个差异:
function get_primes(n)
numbers = IntSet(2:n)
primes = Int[]
while !isempty(numbers)
p = shift!(numbers)
push!(primes, p)
setdiff!(numbers, p:p:n)
end
return primes
end
通过此更改,在我的系统上,Julia代码比Python代码快10倍(0.0005对0.0048秒)。请注意,我还使用了一个范围作为setdiff!
的第二个参数 - 它比构造一个具有理解力的数组更简洁,更快(1.5倍)。
在朱莉娅写一个更惯用的写作方式是使用一系列布尔值,打开和关闭它们:
function eratosthenes(n)
primes = fill(true,n)
primes[1] = false
for p = 2:n
primes[p] || continue
for i = 2:div(n,p)
primes[p*i] = false
end
end
find(primes)
end
最后的find
返回向量非零(即为真)的索引。在我的机器上,这比其他Julia版本快5倍(0.0001秒),比Python版本快45倍。
答案 1 :(得分:2)
使用此代码
function get_primes(n)
numbers::Set{Int} = Set(2:n)
primes::Array{Int64,1} = []
while !isempty(numbers)
p = minimum(numbers)
push!(primes,p);
setdiff!(numbers,Set(p:p:n))
end
return primes
end
我得到了
julia> @time get_primes(10000);
elapsed time: 0.029556727 seconds (1656448 bytes allocated)
另一个(原始)版本确实很糟糕,因为它在计算后大部分时间花在setdiff re-hashing上 - 我的未更改版本的时间与OP类似。所以看起来 setdiff!可能比 setdiff 快100倍,但只接受 集 而不是 < EM>阵列 强>
这仍然比python慢6倍,但比使用 setdiff 快13倍。但是,如果有某种方法来维护有序集并始终采用第一个元素,那么它可能会快得多,因为几乎90%的时间(209/235)用于查找集合的最小值。
235 task.jl; anonymous; line: 96
235 REPL.jl; eval_user_input; line: 54
235 profile.jl; anonymous; line: 14
209 /Users/jeffw/src/errata/julia/sive.jl; get_primes; line: 5
2 reduce.jl; mapfoldl; line: 75
2 dict.jl; skip_deleted; line: 669
207 reduce.jl; mapfoldl; line: 81
1 reduce.jl; mapfoldl_impl; line: 54
1 dict.jl; skip_deleted; line: 670
199 reduce.jl; mapfoldl_impl; line: 57
10 dict.jl; skip_deleted; line: 668
132 dict.jl; skip_deleted; line: 669
12 dict.jl; skip_deleted; line: 670
27 dict.jl; skip_deleted; line: 672
7 reduce.jl; mapfoldl_impl; line: 58
1 /Users/jeffw/src/errata/julia/sive.jl; get_primes; line: 6
25 /Users/jeffw/src/errata/julia/sive.jl; get_primes; line: 7
14 set.jl; setdiff!; line: 24
1 dict.jl; skip_deleted; line: 669
function get_primes(n)
numbers::IntSet = IntSet(2:n)
primes::Array{Int64,1} = []
while !isempty(numbers)
p = shift!(numbers)
push!(primes,p);
setdiff!(numbers,Set(p:p:n))
end
return primes
end
julia> @time get_primes(10000);
elapsed time: 0.003691856 seconds (1463152 bytes allocated)
答案 2 :(得分:2)
我测试了很多方法,我发现这个方法在8种不同的测试中是最快的。
# Julia 0.4.0 [Execution time = 95us (After warm up!!)]
function get_primes(n::Int64)
numbers = fill(true,n)
numbers[1] = false
for i = 2:isqrt(n)
if numbers[i]
for j = i:div(n,i)
numbers[i*j] = false
end
end
end
primes = find(numbers) # t=3-5us
return primes
end
@time primes = get_primes(10000)
println(get_primes(100))
这个页面上的第一个Julia代码在我的电脑上计算n = 10000,大约 1'000'000us ,而这个代码大约 95us ,速度提高了10'000倍。
# Python 3.4 [Execution time = 5ms (Every time)]
def get_primes(n):
m = n+1
numbers = [True for i in range(m)]
for i in range(2, int(math.sqrt(n))):
if numbers[i]:
for j in range(i*i, m, i):
numbers[j] = False
primes = []
for i in range(2, m):
if numbers[i]:
primes.append(i)
return primes
start = time.time()
primes = get_primes(10000)
print(time.time() - start)
print(get_primes(100))
Python测试
# Python n=100e6 [Execution time 52.906s]
start = time.time()
get_primes(int(100e6))
print(time.time() - start)
朱莉娅测试
# Julia n=100e6 [Execution time 3.694s]
@time get_primes(convert(Int64,100e6))
差异现在减少了。 Julia vs Python的速度提高了约12倍。