【问题标题】:Generalizing prime pairs in SICP在 SICP 中推广素数对
【发布时间】:2021-06-19 22:46:10
【问题描述】:

我花了一些时间从 SICP 第 2.2.3 节 - 作为常规接口的序列 中生成“素数对”,例如:

  • (1 3) 不,因为 sum = 4
  • (1 4) 是的,因为 sum = 5(素数)

这是我从头开始的,它有效:

#lang sicp
; RANGE helper function
(define (range a b) ; a to b
 (if (> a b) nil
     (cons a (range (+ 1 a) b))
 ))
; FILTER helper function
(define (filter func sequence)
  (cond ((null? sequence) nil)
        ((func (car sequence)) (cons (car sequence) (filter func (cdr sequence))))
        (else (filter func (cdr sequence)))))
; PRIME helper function
(define (prime? n)
 (cond ((or (= n 1) (= n 2)) #t)
       ((=(modulo n 2) 0) #f)
        (else ; see if no modulos=0 under length of sqrt(N)
         (= 0 
            (length (filter (lambda (i) (eq? i #t))
               (map (lambda (d) (= (modulo n d) 0)) (range 3 (sqrt n)))))))))
; MAP helper
(define (flatmap func seq)
  (if (null? seq) nil (append (func (car seq)) (flatmap func (cdr seq)))))

以及实际功能:

; generate pair of two numbers with  1 <=  i < j <= N
(define (get-pairs n)
  (flatmap (lambda (i)
         (map (lambda (j)
                (list i j))
              (range 1 (- i 1))))
       (range 1 n)))

(define (get-prime-pairs n)
  (filter (lambda (p) (prime? (apply + p)))
          (get-pairs n)))

(get-prime-pairs 4)
; ((2 1) (3 2) (4 1) (4 3))

相当整洁。现在我正在尝试编写相同的函数,而不是仅仅做对,而是做一个size 的元组。到目前为止,这是我所拥有的:

(define (get-n-tuples size n)
  (define (get-n-tuples-internal i args)
    (cond ((= i size) (map (lambda (i) (cons i args)) (range 1 n)))
          (else
           (flatmap (lambda (k)
                      (get-n-tuples-internal (+ i 1) (cons k args)))
                    (range 1 n)))))
  (get-n-tuples-internal 1 '()))

(define (get-prime-seqs size num)
  (filter (lambda (p) (prime? (apply + p)))
          (get-n-tuples size num)))

(get-prime-seqs 4 4)
; ...
; (3 2 4 4)
; (2 3 4 4)
; (1 4 4 4))

我发现困难的一件事是通过执行(range i (- (min args) 1)) 之类的操作来删除函数本身中的重复项。因此,我对所有循环都使用了相同的 (range 1 n)。

我将如何正确转换它以模拟初始函数,以便列表中的每个连续数字都必须更小,即如果序列中有三个数字,那么1 &lt;= num1 &lt; num2 &lt; num3 &lt;= N,并且有效的一对将是(4 2 1) 但不是 (1 2 4) 或 (5 1 1) ?

【问题讨论】:

    标签: scheme lisp racket primes sicp


    【解决方案1】:

    您的 2D 案例相当于两个嵌套循环:

        for i in 1 to n
          for j in 1 to (i-1)
            yield (i,j)
    

    flatmap 只是实现机制,为此。是的,这就是 "monads" 的精髓:第二个循环的 "shape"(范围)取决于 value(@987654324 @) 由第一个产生;此外,从最内层循环产生的所有值都以一个顺序出现,无论循环的深度如何(这里是 2)。

    这也是回溯的本质,因为当我们对给定的i 处理完所有js 后,控制权会返回外部循环,next i 依次尝试。

    回到我们的业务。自然,3D 案例涉及三个嵌套循环:

        for i in 1 to n
          for j in 1 to (i-1)
            for k in 1 to (j-1)
              yield (i,j,k)
    

    一般来说,您希望将其推广到 m 嵌套循环,m = 2,3,4,...。

    构建m 嵌套循环的标准方法是使用递归。如果我们要使用flatmap,那么我们只需要意识到外循环内的所有结构都代表m-1嵌套循环计算:

    (define (tuplesND_ ND max_idx)
      (define (loopvals m imax)
        (if (= m 0)      ; at the deepest level
          (list '())     ; no more indices to collect
          (flatmap                 ; splice in, 
               (lambda (i)         ;   for each i...
                 (map (lambda (tup)             ;   (back from
                           (cons i tup))        ;    recursion)
                      (loopvals (- m 1)     ; all the results from
                                (- i 1))))  ;   the m-1 inner loops
             (range 1 imax))))     ;   ...in the proper range
      (loopvals ND max_idx))
    

    似乎正在工作:

    > (display (tuplesND_ 2 3))
    ((2 1) (3 1) (3 2))
    > (display (tuplesND_ 2 4))
    ((2 1) (3 1) (3 2) (4 1) (4 2) (4 3))
    > (display (tuplesND_ 3 3))
    ((3 2 1))
    > (display (tuplesND_ 3 4))
    ((3 2 1) (4 2 1) (4 3 1) (4 3 2))
    > (display (tuplesND_ 4 5))
    ((4 3 2 1) (5 3 2 1) (5 4 2 1) (5 4 3 1) (5 4 3 2))
    

    当然,flatmap 在性能方面很糟糕(除非它可用作低级原语),其所有常量结构都与所有 appends 进行复制和重新分配。

    当然,只有在受到可变性挑战的语言中才需要它。另一方面,Scheme 拥有最大的原语:set-cdr!。它具有以自上而下的方式构建列表的能力,一次完成,无需复制,无需重新分配(这也是假设的内置 flatmap 的操作方式)。否则无法观察到的突变并没有错!

    通过在我们进入递归的过程中构建元组,传递一个回调从最深层调用,我们让it做我们需要什么:只需在构造每个元组时打印它,append! 将其打印到增长列表的右端(将其引用保持为隐藏状态以提高效率,因此可以在 O(1) 时间),或者我们选择的任何其他内容。

    所以,不用多说,

    (define ((tuplesND yield) ND max_idx)
      (define (loops m imax tup)
        (if (= m 0)                  ; at the deepest level,
            (yield (reverse tup))    ; give callback the value
            (for-each                ; otherwise, looping
               (lambda (i)                ; for each i...       
                  (loops (- m 1) (- i 1)      ; going into
                         (cons i tup)))       ;   the recursion
               (range 1 imax)))           ; ...in the proper range
        "")       ; (some unimportant value that displays as nothing)  
      (loops ND max_idx '())         ; no indices collected at first
      "")       ; (some unimportant value that displays as nothing)
    

    这将在 in 的方式上构建元组,进入最深层次的递归,而不是像在之前的版本中那样在 out 的方式上构建它。

    【讨论】:

      猜你喜欢
      • 2010-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-14
      • 2020-09-26
      • 1970-01-01
      • 2015-02-23
      相关资源
      最近更新 更多