web-dev-qa-db-ja.com

素数の怠惰なリスト

素数のリストをHaskellに実装して、それらを怠惰に取得できるようにするにはどうすればよいでしょうか?

Haskellは初めてですが、遅延評価機能の実用的な使い方を学びたいと思います。

25
Mantas Vidutis

これは、 Literate Programs: から素数を列挙する短いHaskell関数です。

primes :: [Integer]
primes = sieve (2 : [3, 5..])
  where
    sieve (p:xs) = p : sieve [x|x <- xs, x `mod` p > 0]

どうやら、これはnotエラトステネスのふるいです(ありがとう、Landei)。 Haskellで非常にエレガントで短いコードを書くことができ、間違ったデータ構造を選択すると効率が大幅に低下する可能性があることを示す、これはまだ有益な例だと思います。

22
Niki

このペーパーから実装の1つを取ることをお勧めします: http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf

18
Landei

Haskell wikiには、素数シーケンスを遅延生成するためのソリューションがいくつかあります。最初で最も単純なのは 延期されたターナーふるい : (古いリビジョン... NB)

primes :: [Integer]
primes = 2: 3: sieve (tail primes) [5,7..]
 where 
  sieve (p:ps) xs = h ++ sieve ps [x | x <- t, x `rem` p /= 0]  
                                -- or:  filter ((/=0).(`rem`p)) t
                  where (h,~(_:t)) = span (< p*p) xs
8
sleepynate

@nikieから受け入れられた回答はあまり効率的ではなく、数千を超えると比較的遅くなりますが、@ sleepynateの回答ははるかに優れています。それを理解するのに少し時間がかかりました。したがって、ここに同じコードがありますが、変数の名前がより明確になっています。

lazyPrimes :: [Integer]
lazyPrimes = 2: 3: calcNextPrimes (tail lazyPrimes) [5, 7 .. ]
  where
    calcNextPrimes (p:ps) candidates =
      let (smallerSquareP, (_:biggerSquareP)) = span (< p * p) candidates in
      smallerSquareP ++ calcNextPrimes ps [c | c <- biggerSquareP, rem c p /= 0]

主な考え方は、次の素数の候補には、関数に与えられた最初の素数よりも小さい素数で割り切れる数がすでに含まれていないということです。だからあなたが呼ぶなら

calcNextPrimes (5:ps) [11,13,17..]

候補リストには番号が含まれていません。つまり、2または3で割り切れます。つまり、最初の非素数候補は5 * 5になり、5* 25 * 3および5 * 4はすでに削除されています。これにより、5の平方根よりも小さいすべての候補を取得し、それらをすぐに素数に追加し、残りをふるいにかけて5で割り切れるすべての数を削除することができます。

2
Daria
_primes = 2 : [x | x <- [3..], all (\y -> x `mod` y /= 0) 
                   (takeWhile (<= (floor . sqrt $ fromIntegral x)) primes)]
_

最初にリストに2があり、整数ごとにx2より大きい場合は、すべてy in primes次のようにy <= sqrt(x)_x mod y != 0_ =保持します。つまり、xには、1とそれ自体以外の要素はありません。

1
Tianren Li