Gần đây tôi đã bắt đầu học Clojure và quyết định thực hành các vấn đề về Euler để thu thập các cấu trúc dữ liệu có sẵn và thực hành đệ quy và lặp lại.Tại sao Clojure chậm hơn 10 lần so với Python đối với giải pháp tương đương Euler 50?
Tôi đã thử các cách tiếp cận khác nhau để Problem 50, nhưng không có vấn đề gì tôi đã làm, việc tìm kiếm giải pháp cho 1000000 chưa bao giờ kết thúc. Sau khi tôi tra cứu những gì người khác đã làm, tôi đoán những gì tôi đang làm không nên mất mãi mãi, vì vậy tôi gõ vào thuật toán tương đương trong Python để xem liệu vấn đề nằm trong sự thiếu hiểu biết của tôi về một số điều Clojure, hoặc thiết lập Java. Python hoàn thành trong 10 giây. Đối với số nguyên tố dưới 100000, phiên bản Python đã hoàn thành sau 0,5 giây, Clojure ở 5.
Tôi đăng phiên bản Clojure được tạo riêng để phù hợp với mã Python. Bạn có thể giúp tôi hiểu tại sao có sự khác biệt về hiệu suất như vậy không? Tôi có nên sử dụng bỏ chọn, thêm gợi ý, nguyên thủy (nhưng ở đâu?) Hoặc cái gì?
Vì vậy, đây là Clojure:
(defn prime? [n]
(let [r (int (Math/sqrt n))]
(loop [d 2]
(cond
(= n 1) false
(> d r) true
(zero? (rem n d)) false
:other (recur (inc d))))))
(defn primes []
(filter prime? (iterate inc 2)))
(defn cumulative-sum [s]
(reduce
(fn [v, x] (conj v (+ (last v) x)))
[(first s)]
(rest s)))
(defn longest-seq-under [n]
"Longest prime seq with sum under n"
(let [ps (vec (take-while #(< % n) (primes))) ; prime numbers up to n
prime-set (set ps) ; set for testing of inclusion
cs (cumulative-sum ps)
cnt (count ps)
max-len (count (take-while #(< % n) cs)) ; cannot have longer sequences
sub-sum (fn [i j] ; sum of primes between the i-th and j-th
(- (cs j) (get cs (dec i) 0)))
seq-with-len (fn [m] ; try m length prime sequences and return the first where the sum is prime
(loop [i 0] ; try with the lowest sum
(if (> i (- cnt m)) ; there are no more elements for and m length sequence
nil ; could not find any
(let [j (+ i (dec m)) ; fix length
s (sub-sum i j)]
(if (>= s n) ; overshoot
nil
(if (prime-set s) ; sum is prime
[i (inc j)] ; we just looked for the first
(recur (inc i))))))))] ; shift window
(loop [m max-len] ; try with the longest sequence
(if (not (zero? m))
(let [[i j] (seq-with-len m) ]
(if j
(subvec ps i j)
(recur (dec m))))))))
(assert (= [2 3 5 7 11 13] (longest-seq-under 100)))
(let [s1000 (longest-seq-under 1000)]
(assert (= 21 (count s1000)))
(assert (= 953 (reduce + s1000))))
; (time (reduce + (longest-seq-under 100000))) ; "Elapsed time: 5707.784369 msecs"
Và đây là như nhau trong Python:
from math import sqrt
from itertools import takewhile
def is_prime(n) :
for i in xrange(2, int(sqrt(n))+1) :
if n % i == 0 :
return False
return True
def next_prime(n):
while not is_prime(n) :
n += 1
return n
def primes() :
i = 1
while True :
i = next_prime(i+1)
yield i
def cumulative_sum(s):
cs = []
css = 0
for si in s :
css += si
cs.append(css)
return cs
def longest_seq_under(n) :
ps = list(takewhile(lambda p : p < n, primes()))
pss = set(ps)
cs = cumulative_sum(ps)
cnt = len(ps)
max_len = len(list(takewhile(lambda s : s < n, cs)))
def subsum(i, j):
return cs[j] - (cs[i-1] if i > 0 else 0)
def interval_with_length(m) :
for i in xrange(0, cnt-m+1) :
j = i + m - 1
sij = subsum(i,j)
if sij >= n :
return None, None
if sij in pss : # prime
return i, j+1
return None, None
for m in xrange(max_len, 0, -1) :
f, t = interval_with_length(m)
if t :
return ps[f:t]
assert longest_seq_under(100) == [2, 3, 5, 7, 11, 13]
assert sum(longest_seq_under(1000)) == 953
# import timeit
# timeit.Timer("sum(longest_seq_under(100000))", "from __main__ import longest_seq_under").timeit(1) # 0.51235757617223499
Cảm ơn
Bạn đang sử dụng phiên bản Clojure nào? 1.2.x hoặc 1.3? –
Tôi đã tìm ra thủ phạm chính là gì: cách tính tổng số tiền tích lũy. Tôi không bao giờ kiểm tra những gì nó đã làm cho các vectơ lớn hơn, tôi giả định rằng một 'cuối' của một véc tơ được sử dụng truy cập mảng, nhưng' (nguồn cuối cùng) 'cho thấy rằng nó là đệ quy. Mã của tôi không bao giờ đạt đến phần chính với 78000 số nguyên tố trong vector. –
Các phiên bản sau sẽ làm việc: '(defn tích lũy-sum-2 [s] (loop [[x & xs] s ss 0 acc []] (nếu x (hãy [SSX (+ ss x)] (recur xs ssx (conj acc ssx))) acc))) ' hoặc ' (defn cumulative-sum-3 [s] (giảm (fn [v, x] (conj v (+ (v (dec (đếm v))) x))) [(s đầu tiên] (còn lại))) ' Sử dụng một trong các giải pháp này vẫn chậm hơn khoảng 3 lần so với Python tương đương, nhưng điều đó có thể được giảm nhẹ với transients hoặc một số kỹ thuật tôi chưa làm chủ. –