一尘不染

使用一组质数以升序生成整数

algorithm

我有一组素数,我必须仅使用那些素数按升序生成整数。

例如,如果集合为 p = {2,5},则我的整数应为1、2、4、5、8、10、16、20、25 …

有没有有效的算法来解决这个问题?


阅读 244

收藏
2020-07-28

共1个答案

一尘不染

基本思想是1是集合的成员,对于集合 n的 每个成员,2 n 和5 n
也是集合的成员。因此,您首先输出1,然后将2和5推入优先级队列。然后,您反复弹出优先级队列的前一项,如果它与先前的输出不同,则将其输出,然后将该数字按2倍和5倍推入优先级队列。

对于“ Hamming Number”或“ Regular
number”,请使用Google或访问A003592以了解更多信息。

-----之后添加-----

我决定在午餐时间花费几分钟来编写一个使用Scheme编程语言实现上述算法的程序。首先,是使用配对堆算法的优先级队列的库实现:

(define pq-empty '())
(define pq-empty? null?)

(define (pq-first pq)
  (if (null? pq)
      (error 'pq-first "can't extract minimum from null queue")
      (car pq)))

(define (pq-merge lt? p1 p2)
  (cond ((null? p1) p2)
        ((null? p2) p1)
        ((lt? (car p2) (car p1))
          (cons (car p2) (cons p1 (cdr p2))))
        (else (cons (car p1) (cons p2 (cdr p1))))))

(define (pq-insert lt? x pq)
  (pq-merge lt? (list x) pq))

(define (pq-merge-pairs lt? ps)
  (cond ((null? ps) '())
        ((null? (cdr ps)) (car ps))
        (else (pq-merge lt? (pq-merge lt? (car ps) (cadr ps))
                            (pq-merge-pairs lt? (cddr ps))))))

(define (pq-rest lt? pq)
  (if (null? pq)
      (error 'pq-rest "can't delete minimum from null queue")
      (pq-merge-pairs lt? (cdr pq))))

现在介绍算法。函数f采用两个参数,即一组 ps 中的数字列表以及要从输出的开头输出的项数 n
。算法略有变化;通过按1初始化优先级队列,然后开始提取步骤。变量 p 是前一个输出值(最初为0), pq 是优先级队列,而 xs
是输出列表,其输出顺序相反。这是代码:

(define (f ps n)
  (let loop ((n n) (p 0) (pq (pq-insert < 1 pq-empty)) (xs (list)))
    (cond ((zero? n) (reverse xs))
          ((= (pq-first pq) p) (loop n p (pq-rest < pq) xs))
          (else (loop (- n 1) (pq-first pq) (update < pq ps)
                      (cons (pq-first pq) xs))))))

对于那些不熟悉Scheme的人来说,loop是一个局部定义的函数,它被递归调用,并且cond是if-
else链的头。在这种情况下,有三个cond子句,每个子句都有一个谓词和一个结果谓词,因此对谓词为true的第一个子句求值。谓词(zero? n)终止递归并以正确的顺序返回输出列表。该谓词(= (pq-first pq) p)指示优先级队列的当前头先前已输出,因此通过在第一项之后重复优先级队列的其余部分来跳过它。最后,else
谓词(始终为true)标识要输出的新数字,因此它将递减计数器,将优先级队列的当前头保存为新的先前值,更新优先级队列以添加当前数字的新子项,并且将优先级队列的当前头插入累加输出中。

由于更新优先级队列以添加当前编号的新子代并非易事,因此将该操作提取到单独的函数中:

(define (update lt? pq ps)
  (let loop ((ps ps) (pq pq))
    (if (null? ps) (pq-rest lt? pq)
      (loop (cdr ps) (pq-insert lt? (* (pq-first pq) (car ps)) pq)))))

函数遍历集合中的元素,ps依次将每个元素插入优先级队列。在if返回更新后的优先级队列,减去其老脸,当ps名单被耗尽。递归步骤使用剥离ps列表cdr的头部,并将优先级队列的头部和ps列表的头部的乘积插入优先级队列。

这是该算法的两个示例:

> (f '(2 5) 20)
(1 2 4 5 8 10 16 20 25 32 40 50 64 80 100 125 128 160 200 250)
> (f '(2 3 5) 20)
(1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36)

您可以在http://ideone.com/sA1nn上运行该程序。

2020-07-28