SORU
7 HAZİRAN 2009, Pazar


Clojure hızlı Asal Sayı Üretimi

Clojure Project Euler sorunları daha iyi çözme üzerinde çalışıyorum, ve zaten asal sayı üretimi içine bir kaç kez yaptım. Benim sorunum sadece çok uzadı. Biri bana Clojure-y bir şekilde bunu yapmak için etkili bir yol bulmak yardım edeceğini umuyordum.

Ben yumruk yaptı, ben kaba zorunlu. Bunu yapmak kolay oldu. Ama 10001 asal sayı hesaplama " çok çekirdekli 2.33 GHz 2 dakika bu şekilde, genel kural, ve çok uzun süre çok uzun sürdü. Burada algoritması:

(defn next-prime-slow
    "Find the next prime number, checking against our already existing list"
    ([sofar guess]
        (if (not-any? #(zero? (mod guess %)) sofar)
            guess                         ; Then we have a prime
            (recur sofar (  guess 2)))))  ; Try again                               

(defn find-primes-slow
    "Finds prime numbers, slowly"
    ([]
        (find-primes-slow 10001 [2 3]))   ; How many we need, initial prime seeds
    ([needed sofar]
        (if (<= needed (count sofar))
            sofar                         ; Found enough, we're done
            (recur needed (concat sofar [(next-prime-slow sofar (last sofar))])))))

Sonraki prime-yavaş dikkate bazı ek kurallar aldı yeni bir rutin (6n /- 1 özellik gibi) ile değiştirerek şeyler 70 saniye hızlandırmak için mümkün oldu.

Önümüzdeki saf Clojure içinde Demek ki bir elek yapmaya çalıştım. Tüm böcekler var sanmıyorum, ama sadece çok yavaş (yukarıda daha kötü bence) çünkü ben vazgeçtim.

(defn clean-sieve
    "Clean the sieve of what we know isn't prime based"
    [seeds-left sieve]
    (if (zero? (count seeds-left))
        sieve              ; Nothing left to filter the list against
        (recur
            (rest seeds-left)    ; The numbers we haven't checked against
            (filter #(> (mod % (first seeds-left)) 0) sieve)))) ; Filter out multiples

(defn self-clean-sieve  ; This seems to be REALLY slow
    "Remove the stuff in the sieve that isn't prime based on it's self"
    ([sieve]
        (self-clean-sieve (rest sieve) (take 1 sieve)))
    ([sieve clean]
        (if (zero? (count sieve))
            clean
            (let [cleaned (filter #(> (mod % (last clean)) 0) sieve)]
                (recur (rest cleaned) (into clean [(first cleaned)]))))))

(defn find-primes
    "Finds prime numbers, hopefully faster"
    ([]
        (find-primes 10001 [2]))
    ([needed seeds]
        (if (>= (count seeds) needed)
            seeds        ; We have enough
            (recur       ; Recalculate
                needed
                (into
                    seeds    ; Stuff we've already found
                    (let [start (last seeds)
                            end-range (  start 150000)]   ; NOTE HERE
                        (reverse                                                
                            (self-clean-sieve
                            (clean-sieve seeds (range (inc start) end-range))))))))))

Bu çok kötü oldu. Ayrıca eğer sayı 150000 küçükse yığın taşması neden olur. Tekrarlayabilir kullanıyorum gerçeğine rağmen. Bu benim hatam olabilir.

Önümüzdeki bir elek, bir Java ArrayList Java yöntemleri kullanarak denedim. O zaman, bellek ve biraz aldı.

Benim son girişimi bir elek karma harita Clojure kullanarak, asal olmayan elek sonra dissoc bence bu sayılar tüm sayıları ekleme. Sonunda buldum asal sayıların hangi anahtar listesini alır. Yaklaşık 10-12 saniye 10000 asal sayıları bulmak için zaman alır. Henüz tam olarak debug emin değilim. Lispy olmaya çalışıyorum beri de (tekrarlama ve döngü kullanarak) özyinelemeli.

Bu yüzden bu tür bir sorun, sorun 10 (2000000 altındaki tüm asal özetle) beni öldürüyor. Benim en hızlı kod geldi doğru cevap, ama aldı 105 saniye yap ve gerekirse biraz bellek (verdiğim 512 MB çok yaptırmazdım için yaygara ile). Benim diğer algoritmalar ben her zaman onları durdurma sona erdi çok uzun zaman önce.

Java veya C birçok asal sayıları oldukça hızlı ve çok fazla bellek kullanmadan hesaplamak için bir elek kullanabilirim. Soruna neden olan Clojure/Lisp benim tarzında bir şeyleri kaçırıyor olmalıyım biliyorum.

Yapıyorum bir şeyler gerçekten yanlış mı? Clojure sadece büyük dizileri ile biraz yavaş? Projenin bazı insanlar okuma altında 100 Salise içinde diğer Lisps ilk 10000 asal hesaplanan görüşmeleri, Euler. JVM şeyler yavaşlatabilir farkındayım ve Clojure nispeten genç, ama 100 kez bir fark beklemiyorum.

Birisi Clojure içinde asal sayıları hesaplamak için hızlı bir yol denedi.

CEVAP
29 EKİM 2011, CUMARTESİ


İşte Clojure Java birlikte çalışabilirlik kutlayan başka bir yaklaşım. Bu 2.4 Ghz Core 2 Duo (tek dişli çalışan) 374ms alır. Asallık çeki ile Java#isProbablePrime Bigınteger anlaşma Miller-Rabin uygulama verimli izin verdim.

(def certainty 5)

(defn prime? [n]
      (.isProbablePrime (BigInteger/valueOf n) certainty))

(take 10001 
   (filter prime? 
      (take-nth 2 
         (range 1 Integer/MAX_VALUE)))) 

5 Miller-Rabin kesinlik muhtemelen bu sayı çok daha büyük için çok iyi değil. Bu o kadar açık ki Başbakan bu 96.875% belli eşit (- .5^1 kesinlik)

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Ash100HD

    Ash100HD

    29 EKİM 2011
  • echifitness

    echifitness

    9 Kasım 2008
  • Kai Moosmann

    Kai Moosmann

    5 Temmuz 2006