Eratosthenes的Haskell Sieve与复合材料列表

时间:2015-05-14 02:52:15

标签: list haskell primes sieve-of-eratosthenes

我必须在Haskell中为一个项目实现Eratosthenes筛选的经典问题。而不是计算每个素数我只需要比较列表之间的数字。例如,我传递了一个潜在素数列表(参数1)和一个复合列表(列表2)。列表sieve [2..10] []的结果为[2,3,5,7]

我认为我非常接近并且它编译,但它将每个项目都附加到主要列表而不是丢弃复合材料。我的想法是,它将采用所有数字2..10或其他任何数据的列表x,并且复合材料列表y使用elem来查看列表x的列表是否在列表y中找到,如果是,则附加到列出z并打印。提前谢谢!

目前我的代码返回第一个列表中的所有内容并拒绝排序。 sieve [2..10] []会产生[2,3,4,5,6,7,8,9,10]

sieve ::[Int]->[Int]->[Int]
z = []
sieve [] [] = []
sieve x [] = x
sieve [] y = y
sieve xs ys = if ((elem (head xs)) ys) then (sieve (tail xs) ys)
    else ((head xs):z)

2 个答案:

答案 0 :(得分:1)

您展示的程序没有多大意义,首先将始终使用sieve x [],此外,您应检查元素是否可被其他列表分割。最后,您应该使调用是递归的,而head xs : z则不需要这样做,因为z被定义为空列表。

让我们从基本情况开始:如果左侧列表为空,则无论第二个列表的内容如何,​​都会返回空列表。筛选什么都不会导致任何结果:

sieve [] _ = []

接下来,我们使用as pattern:

来寻找归纳案例
sieve (x:xs) ds = ...

现在我们需要枚举已找到的元素列表。从找到的元素的any分钟x开始,我们就知道数字不是(相对)素数。这种情况形式化为:

(==) 0 . mod x :: Integral b => b -> Bool

或者迭代ds列表:

any ((==) 0 . mod x) ds

如果存在这样的元素,我们只需跳过元素,并使用sieve xs ds调用归纳案例。

如果没有这样的元素,我们将它添加到ds列表并发出它。结果是:x : sieve xs (x:ds)。因此,归纳案例是:

sieve (x:xs) ds | any ((==) 0 . mod x) ds = sieve xs ds
                | otherwise = x : sieve xs (x:ds)

我们可以通过为sieve xs

创建特定变量来缩短这一点
sieve (x:xs) ds | any ((==) 0 . mod x) ds = rec ds
                | otherwise = x : rec (x:ds)
                where rec = sieve xs

因此完整的功能是:

sieve [] _ = []
sieve (x:xs) ds | any ((==) 0 . mod x) ds = rec ds
                | otherwise = x : rec (x:ds)
                where rec = sieve xs

您可以通过两种方式提升效果:

  • x末尾添加ds。这确实是一项更昂贵的操作。但过了一段时间,你不经常添加数字。这很有意思,因为在这种情况下ys看起来像[2,3,5,7,11]而不是[11,7,5,3,2]。现在,2(50%)可以对一个数字进行整除的可能性大于可被11整除的数字(9.9%)。最好尝试尝试最有可能成功的测试。

  • 此外,您可以在到达要测试的数字的平方根的分隔符后结束检查:如果某个数字不能被小于该数字的数字整除,则它绝对不能被数字整除大于平方根。

因此,更有效的方法是:

sieve [] _ = []
sieve (x:xs) ds | any ((==) 0 . mod x) $ takeWhile (\y -> y*y <= x) ds = rec ds
                | otherwise = x : rec (ds++[x])
                where rec = sieve xs

答案 1 :(得分:1)

你所谓的sieve通常称为minus,从第一个列表中减去第二个列表,假设两者都是有序的,增加了数字列表。那么仅仅比较两个头元素就足够了,没有任何elem个调用。

但如果您为z提供了正确的定义,它仍然有用。 z=[]只是一个占位符,可以编译(对吗?);这不是正确的定义。应该是:

sieve :: [Int] -> [Int] -> [Int]
-- z = []
sieve [] [] = []
sieve x [] = x
sieve [] y = y
sieve xs ys = if ((elem (head xs)) ys) then (sieve (tail xs) z)
    else ((head xs) : sieve (tail xs) ys )
      where
         z = ... -- need to remove (head xs) from ys

对于最后一条评论的任务,您可以使用例如delete功能。

这仍然不会产生没有复合列表的素数列表,因此初始调用可以将第二个列表清空(否则,首先会得到相同的正如你所做的那样,因为sieve x [] = x方程式):

primesAmong input = sieve input composites

但是composites是什么? Eratosthenes的答案是,为什么,它们是素数的倍数!(和试验分子说:复合材料有其他素数作为它们的除数)。

鉴于素数,比如说2,我们只计算: 2,4,6,... ;对于3,比方说,它是 3,6,9,12,...... ;找到它的倍数。让我们把它写下来:

composites = mutliplesOf primes
mutliplesOf primes = [ mult | p <- primes, mult <- [...] ]

这不太合适:这个multiplesOf需要一个参数:

primes = primesAmong input
primesAmong input = sieve input (mutliplesOf primes)

我们似乎在追逐自己的尾巴;我们还没有primes;我们可以用什么呢?找到多个非素数以及素数是否有害?

确实有正在运行的代码之后,尝试找一种方法来使用primes