2

STArray がどのように機能するかを学ぼうとしましたが、できませんでした。(Docは貧弱です、または少なくとも私が見つけたものです)。

いずれにせよ、次のアルゴリズムがありますが、多くの !! を使用しており、遅いです。STArrayモナドを使用するように変換するにはどうすればよいですか?

-- The Algorithm prints the primes present in [1 .. n]

main :: IO ()
main = print $ primesUpTo 100

type Nat = Int

primesUpTo :: Nat -> [Nat]
primesUpTo n = primesUpToAux n 2 [1]

primesUpToAux :: Nat -> Nat -> [Nat] -> [Nat]
primesUpToAux n current primes = 
  if current > n
  then primes
  else primesUpToAux n (current + 1) newAcum
  where newAcum = case isPrime current primes of
                  True  -> primes++[current]
                  False -> primes

isPrime :: Nat -> [Nat] -> Bool
isPrime 1 _ = True
isPrime 2 _ = True
isPrime x neededPrimes = isPrimeAux x neededPrimes 1

isPrimeAux x neededPrimes currentPrimeIndex = 
  if sqrtOfX < currentPrime
  then True
  else if mod x currentPrime == 0
       then False
       else isPrimeAux x neededPrimes (currentPrimeIndex + 1)
  where
        sqrtOfX = sqrtNat x
        currentPrime = neededPrimes !! currentPrimeIndex

sqrtNat :: Nat -> Nat
sqrtNat = floor . sqrt . fromIntegral

編集

おっと、!! 問題ではありませんでした。アルゴリズムの次のバージョン (以下) では、!!; の使用を削除しました。また、@pedrorodriguesが指摘したように、1が素数ではないことを修正しました

main :: IO ()
main = print $ primesUpTo 20000

type Nat = Int

primesUpTo :: Nat -> [Nat]
primesUpTo n = primesUpToAux n 1 []

primesUpToAux :: Nat -> Nat -> [Nat] -> [Nat]
primesUpToAux n current primesAcum = 
    if current > n
    then primesAcum
    else primesUpToAux n (current + 1) newPrimesAcum
    where newPrimesAcum = case isPrime current primesAcum of
                          True  -> primesAcum++[current]
                          False -> primesAcum

isPrime :: Nat -> [Nat] -> Bool
isPrime 1 _ = False
isPrime 2 _ = True
isPrime x neededPrimes =
    if sqrtOfX < currentPrime
    then True
    else if mod x currentPrime == 0
         then False
         else isPrime x restOfPrimes
    where
          sqrtOfX = sqrtNat x
          currentPrime:restOfPrimes = neededPrimes

sqrtNat :: Nat -> Nat
sqrtNat = floor . sqrt . fromIntegral

今、この質問は実際には約2つの質問です:

1.- リストの代わりに配列を使用するようにこのアルゴリズムを変換する方法は? (Haskellで状態と配列を処理する方法を学ぶためのものです)コメントで誰かがすでに答えていますが、あまりよく説明されていない例を指しています。

2.- 新しい素数が見つかるたびにリストの連結をなくすには?

True -> primesAcum++[現在]

4

1 に答える 1

1

これは、ボックス化されていない整数の配列を操作するためのコードの多かれ少なかれ直接的な翻訳です。

import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.Array.Unboxed
import Control.Arrow

main :: IO ()
main = print . (length &&& last) . primesUpTo $ 1299709

primesUpTo :: Int -> [Int]
primesUpTo = takeWhile (> 0) . elems . primesUpToUA 

primesUpToUA :: Int -> UArray Int Int
primesUpToUA n = runSTUArray $ do
  let k = floor( 1.25506 * fromIntegral n / log (fromIntegral n)) -- WP formula
  ar <- newArray (0,k+1) 0            -- all zeroes initially, two extra spaces 
  let 
    loop c i | c > n = return ar           -- upper limit is reached 
             | otherwise = do              -- `i` primes currently in the array
         b <- isPrime 0 i c                -- is `c` a prime?
         if  b  then do { writeArray ar i c ; loop (c+1) (i+1) }
         else   loop (c+1) i
    isPrime j i c | j >= i = return True   -- `i` primes 
                  | otherwise = do         --   currently in the array 
            p <- readArray ar j
            if   p*p > c           then return True 
            else  if rem c p == 0  then return False 
            else                   isPrime (j+1) i c
  loop 2 0

これは、一度に 1 ステートメントずつゆっくりと読むと、多かれ少なかれ一目瞭然です。

配列を使用すると、リストがないため、リストの連結に問題はありません。新しい項目を追加する際に、素数の配列を使用します。

もちろん、リストベースのコードを書き直して、動作を改善することもできます。最も簡単な書き直し

ps :: [Int]
ps = 2 : [i | i <- [3..],  
              and [rem i p > 0 | p <- takeWhile ((<=i).(^2)) ps]]
primesTo n = takeWhile (<= n) ps

重要なのは、再帰的思考からコアカーシブ思考に切り替えることです。最後に明示的に追加する方法ではなく、リストを生成する方法を定義し、残りは怠惰なセマンティクスに任せます。

于 2014-10-17T12:29:31.100 に答える