Arrays 如何通过1)使用数组,2)避免列表串联(惰性列表?)来改进该算法?

Arrays 如何通过1)使用数组,2)避免列表串联(惰性列表?)来改进该算法?,arrays,haskell,monads,st-monad,starray,Arrays,Haskell,Monads,St Monad,Starray,我试着去学习星空是如何工作的,但我做不到。(医生很差,或者至少是我找到的那个) 无论如何,我有下一个算法,但它使用了很多!!,这很慢。如何将其转换为使用STArray monad -- The Algorithm prints the primes present in [1 .. n] main :: IO () main = print $ primesUpTo 100 type Nat = Int primesUpTo :: Nat -> [Nat] primesUpTo n

我试着去学习星空是如何工作的,但我做不到。(医生很差,或者至少是我找到的那个)

无论如何,我有下一个算法,但它使用了很多!!,这很慢。如何将其转换为使用STArray monad

-- 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
--算法打印[1..n]中的素数
main::IO()
main=打印$primesup到100
类型Nat=Int
primesUpTo::Nat->[Nat]
primesUpTo n=primesUpToAux n 2[1]
primesUpToAux::Nat->Nat->[Nat]->[Nat]
primesUpToAux n当前primes=
如果当前>n
然后素数
else primesUpToAux n(当前+1)newAcum
其中newAcum=情况isPrime当前素数
True->primes++[当前]
假->素数
iPrime::Nat->[Nat]->Bool
iPrime 1=真
iPrime 2=真
isPrime x neededPrimes=isPrimeAux x neededPrimes 1
isPrimeAux x所需素数currentPrimeIndex=
如果sqrtOfXNat
sqrtNat=地板。sqrt。从积分
编辑

哎哟,这个!!这不是问题所在;在下一个版本的算法(如下)中,我删除了!!;此外,我还将1固定为素数,正如@pedrorodrigues所指出的,它不是素数

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
main::IO()
main=print$primesUpTo 20000
类型Nat=Int
primesUpTo::Nat->[Nat]
primesUpTo n=primesUpToAux n 1[]
primesUpToAux::Nat->Nat->[Nat]->[Nat]
primesUpToAux n当前primesAcum=
如果当前>n
然后是primesAcum
else primesUpToAux n(当前+1)NEWSPRIMESACUM
其中newPrimesAcum=案例i当前primesAcum
True->primesAcum++[当前]
假->素数
iPrime::Nat->[Nat]->Bool
iPrime 1=错误
iPrime 2=真
isPrime x所需素数=
如果sqrtOfXNat
sqrtNat=地板。sqrt。从积分
现在这个问题实际上是关于两个问题:

1.-如何将此算法转换为使用数组而不是列表?(是为了学习如何在Haskell中处理状态和数组) 有人在评论中已经回答了这个问题,但指出了一个不太好的例子

2.-如何在每次发现新素数时消除列表的串联?

True->primesAcum++[当前]


下面是您的代码到使用非固定整数数组的大致直接转换:

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
import-Control.Monad
进口管制站
导入Data.Array.ST
导入Data.Array.unbox
导入控制。箭头
main::IO()
main=打印。(长度和最后一个)。价格降至1299709美元
primesUpTo::Int->[Int]
primesUpTo=takeWhile(>0)。元素。普莱姆苏普图亚酒店
primesUpToUA::Int->UArray Int
primesUpToUA n=runSTUArray$do
设k=地板(1.25506*从积分n/对数(从积分n))--WP公式
ar n=返回ar——达到上限
|否则=do--`i`当前在数组中的素数
b=i=返回真--`i`素数
|否则=do--当前在数组中
然后返回True
否则,如果rem c p==0,则返回False
else isPrime(j+1)i c
环路20
这或多或少是不言自明的,当你慢慢读的时候,一次读一句话

使用数组时,列表连接没有问题,因为没有列表。我们在添加新项时使用素数数组

当然,您可以重新编写基于列表的代码,使其表现得更好;是

ps::[Int]

ps=2:[i | i 0 | p此处不需要STArray,该算法只是尝试除法。您应该看看标准的列表操作函数(
filter
takeWhile
all
any
,等等)首先,我认为,因为使用它们可以以最佳方式和惯用方式解决问题,而在这里,您走了相当多的弯路。您希望以函数式的方式快速实现吗?请参阅前面的评论(并确保避免盲目地用列表表示集合的谬误)或者:你想学习使用STArray,然后准确地描述你对STArray的问题所在。你意识到你必须编写“命令式”代码(所有内容都在ST monad中)顺便说一句,1不是素数。Haskell wiki有一个使用ST数组计算素数的例子:来解决你的第二个问题:与其做
primesAcum++[current]
,也就是O(n^2),不如做
current:primesAcum
,并反转最后的列表,也就是O(n)非常感谢。有一个问题,我知道newArray(0,k+1)0生成一个sts(STUArray s Int Int),对吗?s(state)的类型是什么?我也不知道isPrime的类型。要查看任何实体的类型,例如
isPrime
,请添加
let x=isPrime==True
,并读取有关
Bool
-和您想要知道的类型之间类型不匹配的错误消息。--我们知道
runSTUArray::Ix I=>(对于所有s.ST s(stu数组I-e))->UArray i e
,因此其中的
do
表达式是
::Ix i=>(对于所有s.ST s(STU数组i e))
。(…cont)(…contd)因此,do表达式中的计算是在
ST s
单子中。
s
被所有s的
封装,所以我们不知道它是什么,也一定不知道。所以
m~ST s
与不可见的
s
一起
ps :: [Int]
ps = 2 : [i | i <- [3..],  
              and [rem i p > 0 | p <- takeWhile ((<=i).(^2)) ps]]
primesTo n = takeWhile (<= n) ps