我们可以改进 SICP 的素数筛代码吗

2024-01-18

最近问答入口 https://stackoverflow.com/questions/73689215/need-help-to-understand-some-of-the-sicp-streams-examples展示了使用惰性流从 SICP 生成代码的以下素数:

(define (sieve stream)
  (cons-stream
   (stream-car stream)
   (sieve (stream-filter
            (lambda (x)
              (not (divisible? x (stream-car stream))))
            (stream-cdr stream)))))

(define primes (sieve (integers-starting-from 2)))

一个答案 https://stackoverflow.com/a/73695449/849891有显示primes除其他可能性外,等同于以下内容:

  (cons-stream 2
   (cons-stream 3
    (cons-stream 5
     (cons-stream 7
       (sieve 
         (stream-filter (lambda (x) (not (divisible? x 7)))
          (stream-filter (lambda (x) (not (divisible? x 5)))
           (stream-filter (lambda (x) (not (divisible? x 3)))
            (stream-filter (lambda (x) (not (divisible? x 2)))
             (integers-starting-from 9))))))))))

看起来这里的过滤流太多了——例如 7 是通过对输入数字进行 2、3 和 5 过滤而产生的,而实际上只需要单独测试 2——只有 9 以上的数字才需要真正进行测试除以 3,更不用说除以 5 等等。

随着我们不断产生素数流,这个问题变得越来越明显。总体而言,生产第一n素数需要O(n^2)用这个代码。

我们可以做得更好吗?


事实上,我们只需要开始过滤掉素数的倍数square在输入中遇到。

为此,我们将使用素数及其平方。我们将使用same生成这些素数的代码,我们需要生成这些素数our primes:

(define (pprimes) 
  (cons-stream 2
    (psieve (stream-map (lambda (x) (cons x (* x x)))
                        (pprimes))     ;; here 
            (integers-starting-from 3))))

(define (psieve pr-sqrs numbers)       ;; prime+square pairs
  (if (< (stream-car numbers)          
         (cdr (stream-car pr-sqrs)))   ;; prime's square
    (cons-stream 
       (stream-car numbers)
       (psieve pr-sqrs                 ;; same prime+square pair
               (stream-cdr numbers)))  ;;   for the next number
    (psieve 
       (stream-cdr pr-sqrs)            ;; advance prime+square's stream
       (stream-filter                  ;; and start filtering 
          (let ((p  (car (stream-car pr-sqrs)))) ;; by this prime now
               (lambda (x)
                   (not (divisible? x p))))
          (stream-cdr numbers)))))

Now this导致

  (pprimes)
=
  ....
=
  (cons-stream 2
   (cons-stream 3
    (cons-stream 5
     (cons-stream 7
      (cons-stream 11
       (cons-stream 13
        (cons-stream 17
         (cons-stream 19
           (psieve (cons-stream 5 ... )
                   (cons-stream 25 ... )
              (stream-filter (lambda (x) (not (divisible? x 3)))
               (stream-filter (lambda (x) (not (divisible? x 2)))
                  (integers-starting-from 20))))))))))))
=
  ....

毫无疑问,这要好得多。 25以下的数字不会被5测试,以此类推。

这还是审判庭,并运行大约n^1.5. True sieve埃拉托斯特尼的应该运行在n log n log log n根据经验,这通常接近于n^1.1..1.2或左右。但是这个n^1.5也比原来的二次算法有了很大的改进,并且在实践中绝对运行速度也比它快得多。

顺便说一句,改变(not (divisible? x n)) to ((not-divisible-by n) x) https://stackoverflow.com/questions/66348123/sieve-of-eratosthenes-in-scheme-using-mutation-of-local-state-in-its-filtering-p/849891将我们的代码打开到全新的一行算法的改进,仅使用添加(并且没有尝试划分),就像原始版本一样,从大约 2000 年前开始。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我们可以改进 SICP 的素数筛代码吗 的相关文章

  • R-莱曼素性测试中的模数警告

    我花了一点时间破解莱曼素性测试的 R 实现 我借鉴的功能设计http davidkendal net articles 2011 12 lehmann primality test http davidkendal net articles
  • 编写一个带有两个参数的 forAll 过程:系列的开始值和结束值,并将给定过程应用于该系列

    我正在尝试编写一个带有两个参数的 forAll 过程 一系列的开始值和结束值 生成的闭包还需要两个参数 一个应用于系列中所有元素的操作 以及一个初始值 这就是我所拥有的 我似乎遗漏了一些东西或者我不理解闭包背后的概念 define forA
  • 本地球拍

    我正在书中阅读有关本地定义的内容 并且遇到了这个例子 local define f x x 5 define g alon cond empty alon empty else cons f first alon g rest alon g
  • mapM 的惰性版本

    假设我在使用 IO 时收到了大量的项目列表 as lt getLargeList 现在我正在尝试申请fn a gt IO b onto as as lt getLargeList bs lt mapM fn as mapM有类型mapM M
  • 如何在方案中调试gimp的script-fu脚本?

    我尝试使用 script fu scheme 为 gimp 制作一些脚本 当然 作为一个初学者 会有很多错误和误解 现在我正在寻找一种调试这些脚本的方法 我找到了 gimp message 但结果没有显示 我不知道是否有可能将调试消息打印到
  • 方案let语句

    在函数式编程语言scheme中 没有赋值语句 但在一个let陈述 let x 2 x 3 您正在分配2 to x 那么为什么这不违反函数式编程中没有赋值语句的原则呢 Scheme 是一种函数式编程语言 这一说法是不正确的 在Scheme中
  • 对方案中的列表进行排序

    如何编写一个排序算法 以升序返回列表 ex 1 3 5 2 9 回报 1 2 3 5 9 大多数Scheme 实现都附带一个对列表进行排序的过程 如果您的实现没有提供这一功能 那么为您提供一个并不困难 下面是快速排序算法的实现 gt def
  • 小阴谋家 - 从哪里开始?

    我刚刚打开 小阴谋家 我觉得我错过了一些东西 第一个问题问 这是一个原子吗 但我没有看到原子是什么的任何定义 我想我可以通过问题的答案推导出什么是原子 但随后它继续问 l 的 car 是什么 l 的 cdr 是什么 我不知道在问什么 这本书
  • 查找 lambda 表达式中的自由变量

    有谁知道如何找出 lambda 表达式中的自由变量 自由变量是不属于 lambda 参数的变量 我当前的方法 这对我毫无帮助 是简单地使用 car 和 cdr 来遍历表达式 我的主要问题是确定一个值是否是一个变量或者它是否是方案原语之一 有
  • 如何找到 MIT 方案中出现错误的地方?

    当你在 MIT 方案中遇到错误时 它不会告诉你错误发生在哪里 例如 它只打印如下内容 Unbound variable top left To continue call RESTART with an option number REST
  • 传递给过程的列表转换为过程内列表的列表

    我正在 DrRacket 上调试这段代码 lang racket define last element on list lambda l cond null l null cdr l car l else last element on
  • 展开方案中的函数

    Goal 实施unfold仅使用两个参数的函数 论据 第一个参数是 f 它接受某种类型 I 的初始值并返回 nil 或两个元素的 cons 对 这两个元素中的第一个是某种类型 A 的列表中的下一个元素 下一个初始值又是某些类型 I 第二个参
  • DJB 哈希函数中数字 5381 的原因是什么?

    谁能告诉我为什么 DJB 哈希函数中使用数字 5381 DJB 哈希函数定义为 h 0 5381 h i 33h i 1 s i 这是一个 C 实现 unsigned int DJBHash char str unsigned int le
  • 不知道如何解决 SICP 练习 1.11

    练习1 11 http mitpress mit edu sicp full text book book Z H 11 html thm 1 11 一个功能f由以下规则定义f n n if n lt 3 and f n f n 1 2f
  • 如何获取 SICP、Scheme、练习 2.78 等中的 put 和 get 函数

    我正在尝试在 SICP 中做练习 2 78 但 put 和 get 函数未知 我尝试过多种语言 比如相当大 racket r5rs mit scheme mzscheme等 我什至下载了SICP支持 http www neilvandyke
  • 从数组中打印素数

    我想用方法从数组中打印出所有素数 我可以用一个 int 来完成 但不知道如何从数组中返回某些数字 感谢帮助 public static boolean isPrime int tab boolean prime true for int i
  • Python while 循环查找素数

    作为 Python 的第一个练习 我尝试编写一个使用循环来查找素数的程序 一切都适用于 for 循环 所以我尝试使用 while 循环 这可行 但程序返回一些不正确的数字 import math looking for all primes
  • Lisp 中的 (定义 (平均 ....))

    我只是在玩scheme lisp 并正在考虑如何纠正我自己的定义average 我不确定如何做一些我认为需要的事情 定义一个接受任意数量参数的过程 计算这些参数 将参数列表传递给 以将它们加在一起 有人有定义的例子吗average 我似乎对
  • F# 正确使用序列缓存

    我正在尝试将 Seq cache 与我制作的函数一起使用 该函数返回最多为 N 的素数序列 不包括数字 1 我无法弄清楚如何将缓存的序列保留在范围内 但仍然使用它在我的定义中 let rec primesNot1 n 2 n gt Seq
  • Java 8 Stream,获取头部和尾部

    Java 8 引入了Stream http download java net jdk8 docs api java util stream Stream html类似于 Scala 的类Stream http www scala lang

随机推荐