用于在 Scheme 中查找素数的修改筛

问题描述

我正在研究使用埃拉托色尼筛法提供素数列表的解决方案。所以程序应该找到直到特定数字“n”的素数。我相信我想出了一个不完整的解决方案,但不知道如何从这里开始。

;;;This is a helper function
(define sievehelper 
   (lambda (list)
      ;;; This is the base condition where we are comparing 
      ;;; that the divisor is less than the square root of n""
      (if (> (car list) (sqrt (car (reverse list))))
          '()
          ;;; If the base condition has not be reached,then send it through 
          ;;; this filter function where not-divisible by will go through
          ;;; the specified list and only output the list which contains
          ;;; the numbers that are not divisible by (car list)
          (filterfunc (not-divisible-by (car list)) 
                      list)))

我已经单独测试了另一个辅助函数 fliterfunc 并且它工作正常。

;;;; This is the main function that calls the above helper function
(define sieve 
   (lambda (n)
      ;;; `makelist` is a helper function to generate the list from 2 to n
      (sievehelper (makelist n))))

我已经单独测试了 makelist 辅助函数,它工作正常。

我的问题是关于辅助函数sievehelper”如何迭代列表中的不同元素作为除数。

感谢任何帮助。谢谢。

解决方法

导致卡住的一段代码是 (if ( > (car list) (sqrt (car(reverse list)))),它看起来很像你可能在其他语言中使用的循环条件(“迭代”这个词也暗示着偷看其他语言) .
我建议您重新开始,采用不同的策略。

当您使用列表时,您通常只想递归它们的结构。

假设您有所有整数的列表,从 2 开始。
作为第一步,您希望保留这两个,并从列表的其余部分中删除其所有倍数。
现在,删除的结果将为您提供一个以下一个素数(即 3)开头的列表,因此您可以使用该部分结果重复该过程,这将删除 3 的所有倍数,并再次重复该部分结果,并且依此类推,直到没有更多列表。

(请注意,这远没有达到应有的效率,而是更像是“开始递归思考”级别的建议。阅读 Will 的答案以了解更多信息。)

应用一些一厢情愿的想法并假设有一个过程 remove-multiples-of 可以实现它听起来像的样子,这可能看起来像这样:

(define (my-sieve-helper ls)
  (if (null? ls)
      '()
      (cons (car ls)
            (my-sieve-helper (remove-multiples-of (car ls) (cdr ls))))))

所以,remove-multiples-of...
这与保留所有不能被数字整除的数字相同,所以让我们想象另一个函数:

(define (remove-multiples-of x ls) (filter (not-divisible-by x) ls))

其中 (not-divisible-by x) 是一个过程,它接受一个数字并返回该数字是否不能被 x 整除。

(define (not-divisible-by n) (lambda (x) (not (zero? (remainder x n)))))

现在我们可以添加一个合适的包装器。
我这样做了:

(define (from-to m n)
  (if (> m n)
      '()
      (cons m (from-to (+ m 1) n))))

(define (my-sieve n) (my-sieve-helper (from-to 2 n)))

测试:

> (my-sieve 100)
'(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)
,

好吧,您的问题提出了一个有趣的规范不足案例,这可能是有利的,延迟了实际规范——不仅仅是像往常一样的实现——您的代码段使用的子例程。

这里我们有 sievehelper,它使用未实现的非指定 filterfuncnot-divisible-by。尽管它们具有暗示性的名称,但这些函数可以做任何事情,只要它们一起使用时可以工作,并使使用它们的函数 sievehelper 也能按预期工作。 Delayed specification for the win!

那么让我们首先看看给定的 sievehelper 可以做什么,它有什么作用?假设这两个子程序的含义显而易见,它似乎打算对其工作序列执行一步过滤,从其“头”元素的任何倍数中剔除它,即位于 car 位置的元素。

它会通过返回 () 来表示停止条件。对于 a*a > z 的输入列表,该停止条件是 [a,b,c,...,z]

否则,它不会执行循环,而只是执行其中的一步。您的 sieve 根本没有考虑到这一点,因此需要更改以不断调用此帮助程序,像筛选算法一样一步一步地执行,直到第一个元素的平方大于最后一个元素的平方工作序列中的元素,当停止剔除确实是安全的,因为序列中的所有倍数都已经作为较小素数的倍数从中删除...... 提供那些较小的素数出现在初始序列中。

所以这个发现的需求落在使用中的第三个未实现的子路由makelist上。您确实提到它必须创建从 2n 的连续自然数列表,现在我们明白了为什么我们需要它这样做。

那么,为了迭代输入列表的不同版本,因为每个除数依次从中过滤掉,使用您的{{ 1}} 定义 as given,您的 sievehelper 函数必须更改为 eg

sieve

这段代码是从使用 pair 的角度来看的——当前序列及其下一个迭代。在每个步骤中,在其 head 元素的所有倍数被 (define sieve (lambda (n) (let ((init (makelist n))) (let loop ((this init ) (next (sievehelper init))) (if (null? next) this (cons (car this) (loop next (sievehelper next)))))))) (包括头元素本身,下一个找到的素数)从列表中删除后,找到列表的下一个版本——或者当所有已知列表中的数字已经是质数时,返回空列表以表示处理结束,通过构造

在 Racket 中试用:

sievehelper

(以上代码将 the fixad-absurdum 从后续 Q&A entry 合并到此代码最初包含的错误中)。

要运行它,必须进行进一步的定义:

> (sieve 50)
'(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47)

像我们在这里所做的那样定义 (define filterfunc filter) (define (not-divisible-by n) (let ((m n)) (lambda (x) (let ((ret (not (= x m)))) (if (>= x m) (set! m (+ m n)) #f) ret)))) (define (makelist n) (range 2 (+ 1 n))) ;; in Racket 使它成为 Eratosthenes 的筛子,实际上,只能通过添加和比较来工作。像在原始代码中那样尽早停止可以防止其时间复杂度变得比应有的更糟。