2009-05-22 21 views
6

Bu yüzden, tembel bir şekilde prim üretmek için çalışıyordum ve bunların hepsi eşdeğer bir şekilde çalışan bu üç tanımla ortaya çıkmıştı - sadece her yeni tam sayı 'un önceki tüm faktörler arasında bir faktör olup olmadığını kontrol ediyor asal: bu asal sayısı sırasına çalışma gerektirir ben düşünüyorum her tamsayı (için f_ = f (const True) recomputing önler beriHaskell stili/verimlilik

primes1 :: [Integer] 
primes1 = mkPrimes id [2..] 
    where mkPrimes f (x:xs) = 
      if f (const True) x 
      then 
      let g h y = y `mod` x > 0 && h y in 
      x : mkPrimes (f . g) xs 
      else 
      mkPrimes f xs 

primes2 :: [Integer] 
primes2 = mkPrimes id (const True) [2..] 
    where mkPrimes f f_ (x:xs) = 
      if f_ x 
      then 
      let g h y = y `mod` x > 0 && h y in 
      x : mkPrimes (f . g) (f $ g $ const True) xs 
      else 
      mkPrimes f f_ xs 

primes3 :: [Integer] 
primes3 = mkPrimes [] [2..] 
    where mkPrimes ps (x:xs) = 
      if all (\p -> x `mod` p > 0) ps 
      then 
      x : mkPrimes (ps ++ [x]) xs 
      else 
      mkPrimes ps xs 

Yani bana öyle geliyor primes2, primes1 biraz daha hızlı olmalı biz şimdiye kadar bulduğumuz, ve sadece ne zaman şifrelediğimizi yeni bir baştan çıkarmak.

Sadece bilimsel olmayan testlerden (GHCi içinde take 1000 çalıştıran) o primes3 çalışır hızlı primes2 daha gibi görünüyor.

Ben bundan bir ders alır ve ben bir dizide bir operasyon olarak bir işlev temsil edebilir, ben verimlilik için ikinci bir biçimde uygulamak gerektiğini varsayalım, veya başka bir şey oluyor burada var mıdır ?

+0

all' muazzam overkill 'çağırarak burada primes3'' in (Şimdiye kadar biliyoruz eminim) - böylece aynı asal listesi kullanılabilir - yalnızca alarak yukarıda '' ait sqrt' x' yeterli değildir asal inşa edildiği - fonksiyonu basit bir filtre haline gelir: 'primes4 = 2: filtre (\ x> tüm ((/ = 0) (rEM x)) $ takeWhile ((<= x) (^ 2)) primes4..) [3,5 ..] '(n^1.45)' ampirik, 'de n 'asal üretilen O' yaklaşık çalışan - * sorunuzu her üç * versiyonları kuadratik bakmak - olursa olsun onlar hala işlevlerini nasıl inşa Sadece sqt x'nin altındakiler yerine * tüm * primleri ile test edin. –

cevap

9

f için gereken ikinci argüman nedir? Bence, bu alternatiflerin de geri soruya,


neyse ... daha okunabilir ve performansı önemli ölçüde etkilemez. Bazen veri yapıları olarak işlevler kullanmak, belirli bir görev için en iyi gösterimdir ve bazen de değildir. Performans açısından "kolaylık" ve "en iyi" açısından "En İyi" her zaman aynı şey değildir. "Işlevleri veri yapıları olarak" tekniği runtime compilation esastır, ama bu sayfa uyardığı gibi,

Süre derleme bazen size önemli verimlilik artışları kazanabilir, ama çoğu zaman da artmış stres maliyetle hemen hemen hiçbir şey kazanabilir ve azaltılmış verimlilik. Senin durumunda

, her f :: Integer -> ... -> Bool inşa etmenin havai all ... ps karşı f ... x çağrılırken az veya hiç fark ile her ps :: [Integer], inşa etmenin yükü önemli ölçüde daha yüksek olması olasıdır.


sonsuz asal eleğin üzerinden döngüleri sıkmak için mod çağrı kurtulmak! Tamsayı çarpma, bölme ve modulus, tamsayı toplama ve çıkarma işleminden çok daha yavaştır. Makinemde, bu uygulama ilk 1000 primini hesaplarken% 40 daha hızlı saatler (GHC 6.10.3 -O2). (JSON-imsi sözdizimi biraz kullanarak) eylem olarak

import qualified Data.Map as M 
primes' :: [Integer] 
primes' = mkPrimes 2 M.empty 
    where 
    mkPrimes n m = case (M.null m, M.findMin m) of 
     (False, (n', skips)) | n == n' -> 
      mkPrimes (succ n) (addSkips n (M.deleteMin m) skips) 
     _ -> n : mkPrimes (succ n) (addSkip n m n) 
    addSkip n m s = M.alter (Just . maybe [s] (s:)) (n+s) m 
    addSkips = foldl' . addSkip 

,

mkPrimes 2 {} 
=> 2 : mkPrimes 3 {4: [2]} 
=> 2 : 3 : mkPrimes 4 {4: [2], 6: [3]} 
=> 2 : 3 : mkPrimes 5 {6: [2, 3]} 
=> 2 : 3 : 5 : mkPrimes 6 {6: [2, 3], 10: [5]} 
=> 2 : 3 : 5 : mkPrimes 7 {8: [2], 9: [3], 10: [5]} 
=> 2 : 3 : 5 : 7 : mkPrimes 8 {8: [2], 9: [3], 10: [5], 14: [7]} 
=> 2 : 3 : 5 : 7 : mkPrimes 9 {9: [3], 10: [2, 5], 14: [7]} 
=> 2 : 3 : 5 : 7 : mkPrimes 10 {10: [2, 5], 12: [3], 14: [7]} 
=> 2 : 3 : 5 : 7 : mkPrimes 11 {12: [2, 3], 14: [7], 15: [5]} 
... 

haritası ek başka bir şey kullanarak, gelecekteki katları izler.

+0

Teşekkürler! Bu, umut ettiğim detaylı bir cevaptı. – rampion

+0

bir sidenote Bu [önemli ölçüde geliştirilebilir] (http://hpaste.org/79571) bu daha ilk kare girişi görülmeyene kadar, örneğin görüldüğü gibi, harita her bir asal ilave geciktirerek [burada Python'da] (http://stackoverflow.com/a/10733621/849891). ayrıca http://stackoverflow.com/a/13895347/849891'i de karşılaştırın. –

1

primes3'un ps++[x] değerini (x:ps) olarak değiştirerek daha verimli hale getirilebileceğini unutmayın.Çalışan (++), sol argümanının uzunluğunda doğrusaldır, ancak sağ argüman uzunluğunda sabittir.

+3

Aslında, bu kasıtlıydı. 2, 173'ten çok daha fazla bir faktördür, bu nedenle küçük uçtan büyük uçtan başlayarak başladığımızda, ilkelliği kontrol ederken daha erken çıkışlar elde ederiz. – rampion