Recursion Eratosthenes方案的筛选
我一直在网上搜索scheme中Eratosthenes筛子的实现,尽管我提出了很多内容,但似乎没有一个内容让我觉得我需要完成它 问题是大多数算法要么使用静态结束,要么使用迭代。再加上我对这门语言的缺乏,我向你们所有人寻求帮助 我需要一个Sieve的实现,它接受一个参数(要筛选的数字),只使用递归,并且有一个带有Recursion Eratosthenes方案的筛选,recursion,scheme,primes,sieve-of-eratosthenes,imperative,Recursion,Scheme,Primes,Sieve Of Eratosthenes,Imperative,我一直在网上搜索scheme中Eratosthenes筛子的实现,尽管我提出了很多内容,但似乎没有一个内容让我觉得我需要完成它 问题是大多数算法要么使用静态结束,要么使用迭代。再加上我对这门语言的缺乏,我向你们所有人寻求帮助 我需要一个Sieve的实现,它接受一个参数(要筛选的数字),只使用递归,并且有一个带有\t(true)或\f(false)的数字的“cons”列表 因此,基本上,算法是这样的: 从2开始制作列表-输入的编号,每个编号以true开头 递归地遍历并标记每个可被2整除的数字fal
\t
(true)或\f
(false)的数字的“cons”列表
因此,基本上,算法是这样的:
(define (makeList n)
(if (> n 2)
(append (makeList (- n 1)) (list (cons n (and))))
(list (cons 2 (and)))))
这将返回一个列表,其中除数的每个倍数都标记为false
(define (mark-off-multiples numbers divisor)
(if (null? numbers)
'()
(append
(list (cons (car (car numbers))
(not (zero? (modulo (car (car numbers)) divisor)))))
(mark-off-multiples (cdr numbers) divisor))))
现在这是我遇到麻烦的函数,它似乎应该可以工作,我已经手动检查了三次,但我不明白为什么它不能返回我需要的
(define (call-mark-off-multiples-for-each-true-number numbers)
(if (null? numbers)
'()
(if (cdr (car numbers))
(append (list (car numbers))
(call-mark-off-multiples-for-each-true-number
(mark-off-multiples (cdr numbers) (car (car numbers)))))
(append (list (car numbers))
(call-mark-off-multiples-for-each-true-number
(cdr numbers))))))
我想让它做的是,正如函数名所暗示的那样,为列表中仍然标记为true的每个数字调用标记倍数。因此,您传入((3.#t)(4.#t)(5.#t))
,然后它调用标记2的倍数,并返回(3.#t)(4.#f)(5.#t)
,然后向其追加(2.#t)
。然后它再次调用自己传入(3.#t)(4.#f)(5.#t)
,并调用标记倍数,使列表的cdr返回(4.#f)(5.#t)
,并继续向下搜索列表
然后返回的输出是一个包含所有true的列表
希望这能帮助您更好地理解我的困境。代码和解释可以在SICP 3.5.2有限流中找到
这里有一个有效的解决方案
(define (divides? m n)
(if (eq? (modulo n m) 0)
#t
#f))
(define (mark-true n)
(cons n #t))
(define (mark-divisors n ns)
(cond ((null? ns) '())
((and (unmarked? (car ns))
(divides? n (car ns)))
(cons (cons (car ns) #f) (mark-divisors n (cdr ns))))
(else (cons (car ns) (mark-divisors n (cdr ns))))))
(define (unmarked? n)
(not (pair? n)))
(define (eratosthenes x)
(cond ((null? x) '())
((unmarked? (car x))
(cons (mark-true (car x))
(eratosthenes (mark-divisors (car x) (cdr x)))))
(else (cons (car x) (eratosthenes (cdr x))))))
(eratosthenes (list 2 3 4 5 6))
我使用了很多helper函数,但是如果需要,可以将它们添加到eratosthenes函数中。我想这会让整件事更具可读性
mark true
将值保存到#t
上<代码>标记除数
接受一个数字n
和一个数字列表,并使用n
除以a#f
的所有数字。几乎所有其他事情都是不言自明的。Eratosthenes工作正常,如果第一个数字是“未标记”的,它会将其标记为“真”或“素数”,然后从列表的其余部分“划掉”其所有倍数,然后对列表中的每个后续“未标记”数字重复。我的eratosthenes函数基本上就是你想用你的函数做的。我不确定你的问题是什么,但作为一项规则,让助手帮助你的文章更具可读性是很有帮助的
我是在DrRacket用Neil Van Dyke的SICP软件包做的。我不知道你在用什么方案。如果您在实现此功能时遇到问题,请告诉我。好的,因此SoE的重点不是测试任何可除性,而是一次按p数计数:
(define (make-list n) ; list of unmarked numbers 2 ... n
(let loop ((i n)
(a '()))
(if (= i 1)
a ; (cons '(2 . #t) (cons (3 . #t) ... (list '(n . #t))...))
(loop (- i 1) (cons (cons i #t) a)))))
(define (skip2t xs) ; skip to first unmarked number
(if (cdar xs) xs (skip2t (cdr xs))))
(define (mark-each! k n i xs) ; destructive update of list xs -
(set-cdr! (car xs) #f) ; mark each k-th elem,
(if (<= (+ i k) n) ; head is i, last is n
(mark-each! k n (+ i k)
(list-tail xs k))))
(define (erat-sieve n)
(let ((r (sqrt n)) ; unmarked multiples start at prime's square
(xs (make-list n)))
(let loop ((a xs))
(let ((p (caar a))) ; next prime
(cond ((<= p r)
(mark-each! p n (* p p) (list-tail a (- (* p p) p)))
(loop (skip2t (cdr a)))))))
xs))
在麻省理工学院计划中测试
(define (prime-sieve-to n)
(let* ((sz (quotient n 2)) (sv (make-vector sz 1)) (lm (integer-sqrt n)))
(for ((i (in-range 1 lm)))
(cond ((vector-ref sv i)
(let ((v (+ 1 (* 2 i))))
(for ((i (in-range (+ i (* v (/ (- v 1) 2))) sz v)))
(vector-set! sv i 0))))))
(cons 2
(for/list ((i (in-range 1 sz))
#:when (and (> (vector-ref sv i) 0) (> i 0)))
(+ 1 (* 2 i))))))
这是另一个在骗局方言的计划,但工程高达100000000。除此之外,我不会担保它的效率。注意,与书中所说的相反,这不是一个埃拉托斯坦筛。算法是Turner筛,虽然简洁但效率极低;Turner筛的通常列表理解公式来自1983年修订版,带有“ZF表达式”。Davie 1992将其命名为
primes=sieve[2..];筛子(p:nos)=p:siever(移除(多个p)个)
。(参见“更新”),它允许两对remove/multsof
实现。它也可以调整为延迟,尽管这种伪代码还没有变成我所知道的任何语言的代码。:)虽然这种方法可行,但它并不是一种埃拉托斯烯的筛子。筛选算法的要点是通过连续筛选出已知素数的倍数来避免进行代价高昂的素性测试。你所需要的只是重复的加法。你是对的,这个函数的名字有误导性。然而,OP提出问题的方式完全违背了筛选的目的。他希望返回一个列表,其中包含所有原始元素,并用t或f标记。要做到这一点,他必须保留两个列表,我认为,然后使用筛选生成的一个列表来标记第一个列表。而且,素性测试不需要花费太多。虽然我不认为它像筛子一样便宜,但米勒·拉宾相当快。当然比测试所有的除数到sqrt(n)要好。虽然米勒-拉宾测试不是确定性的,正如苏斯曼所说,“宇宙辐射更可能导致你的计算机出现故障并给出错误的答案”,而不是一些伪素数傻瓜为了足够的碱基而欺骗米勒-拉宾。我修改了我的答案,按照OP的要求去做。嗨,乔希,很抱歉耽搁了。但是我看了你的答案,我的电脑不工作。不管怎样,它比我的口味复杂得多。我修改了这个问题,向你展示了更多我想要的东西。抱歉,没有更明确的说明。在每个循环的外部(if(vector ref sv i)
始终为真。在Scheme中,0代表真值,而不是假值,如在C中--(map…(2i+1)…(filter…
C)
(define (prime-sieve-to n)
(let* ((sz (quotient n 2)) (sv (make-vector sz 1)) (lm (integer-sqrt n)))
(for ((i (in-range 1 lm)))
(cond ((vector-ref sv i)
(let ((v (+ 1 (* 2 i))))
(for ((i (in-range (+ i (* v (/ (- v 1) 2))) sz v)))
(vector-set! sv i 0))))))
(cons 2
(for/list ((i (in-range 1 sz))
#:when (and (> (vector-ref sv i) 0) (> i 0)))
(+ 1 (* 2 i))))))