我有一个生成所有正整数的生成器,它是2的幂,另一个生成所有3的幂的整数。我现在需要用它们来生成2 ^ i * 3 ^ j形式的整数i,j> = 0,0按升序排列。
我认为使用生成器的目的是减少内存消耗。我一直试图这样做一段时间但无济于事。请帮忙。
答案 0 :(得分:6)
您可以使用自读流来解决此问题:
----------- -----------
| pow 2 |------->| |
----------- | |
| merge |-------+------------>
----------- | | |
.->| x 3 |------->| | |
| ----------- ----------- |
\_______________________________________/
第一个流产生两个幂, 而第二个确保所有生成的数字 乘以3并重新注入输出。 合并运算符确保输出已排序。
请注意,我们必须使用1“播种”输出流, 或者第一个元素在评估时会尝试自我产生。
以下是代码:
(require srfi/41)
(define (merge s1 s2)
(stream-match s1 ((x . xs)
(stream-match s2 ((y . ys)
(if (< x y)
(stream-cons x (merge xs s2))
(stream-cons y (merge ys s1))))))))
(define (the-stream)
(letrec ((s
(stream-cons 1 (merge (stream-map (lambda (x) (* 3 x)) s)
(stream-iterate (lambda (x) (* 2 x)) 2)))))
s))
与我的其他提案相比,它非常简单快捷,
因为除了单调性之外它还使用问题的算术属性。
我错了,它也可以推广(即将推出)
$ mzscheme -f feedback.scm -e '(display (stream->list (stream-take 20 (the-stream))))'
(1 2 3 4 6 8 9 12 16 18 24 27 32 36 48 54 64 72 81 96)
$ time mzscheme -f feedback.scm -e '(display (stream-ref (the-stream) 10000))'
161968247347450370721577384417107686788864605658546176
real 0m1.746s
user 0m1.344s
sys 0m0.156s
我们也可以用python的生成器实现这个, 但我们需要使用队列来存储在反馈循环中等待的数字:
# Merge the output of two generators
def merge(g1, g2):
v1 = g1.next()
v2 = g2.next()
while 1:
if v1 < v2:
yield v1
v1 = g1.next()
else:
yield v2
v2 = g2.next()
# Generates the powers of 2, starting with n
def pow2(n):
while 1: yield n; n *= 2
# Generates values shifted from the given 'q' and multiplied by 3
def mul3(q):
while 1: yield q.pop(0) * 3
# The generator we want
def pow23():
q = []
v = 1
g = merge(pow2(2), mul3(q))
while 1:
yield v
q.append(v)
v = g.next()
g23 = pow23()
for i in range(10000): g23.next()
print g23.next()
这有点不太优雅(恕我直言), 但是发电机更轻巧:
$ time python feedback.py
161968247347450370721577384417107686788864605658546176
real 0m0.150s
user 0m0.112s
sys 0m0.012s
值得一提的是,我已经完成了一个方案实施 (使用闭包作为发电机) 表现出大致相同的表现。
答案 1 :(得分:3)
我对发电机知之甚少, 但我可以提出一个基于 streams 的解决方案(懒惰构建, 可能是无限的列表),有些相似。
我的方法是创建一个流 其“状态”本身就是一股溪流。
个人,内在的数字流, 让我们称他们为3流, 将代表3的连续幂的列表,从1开始, 乘以给定的2的幂。 然后我们可以组装这样的3流的无限, 每个连续2次幂的一个,从1开始。 我们称之为2流。
ascii-art中的初始状态是:
---------------------- --- -- -
| The 2-stream ...
--|----|----|----|---- --- -- -
V V V V
|1| | 2| | 4| | 8|
|3| | 6| |12| |24| ...
|9| |18| |36| |72| The 3-streams
: : : :
现在,我们要操纵这个,以便随时 3流将在2流中订购 关于他们的第一要素。 结果是下一个最小的生成数 将始终是第一个3流的第一个元素。
因此,要获得您希望获得的序列中的下一个数字, 我们将推出第一个3流, 拉出它的第一个元素(这是我们感兴趣的数字), 然后在2流中重新插入3流 在由 new 第一个元素确定的位置。 第一个数字(1)被提取后的新状态将是:
---------------------- --- -- -
| The 2-stream ...
---|----|----|----|---- --- -- -
V V V V
| 2| | 3| | 4| | 8|
| 6| | 9| |12| |24| ...
|18| |27| |36| |72| The 3-streams
: : : :
请注意,此方法不依赖于2 ^ i,3 ^ j或乘法
(仅在2 ^ i * 3 ^ j随着i和j单调增加)。
我发布了另一个答案,并且
结果更加简单快捷。
不相信我:它与数学无关
以下是使用SRFI-41流的示例实现:
(require srfi/41)
; Geometric sequence with initial value 'init', and ratio 'r'
(define (make-geoseq init r)
(stream-cons
init
(make-geoseq (* r init) r)))
; Your power generators
(define pow2 (make-geoseq 1 2))
(define pow3 (make-geoseq 1 3))
; Construct a 3-stream from the pow3 sequence
(define (make-3stream mult)
(stream-map (lambda (x) (* mult x)) pow3))
; Construct the (initial) 2-stream from the pow2 sequence
(define initial-2stream
(stream-map make-3stream pow2))
; Insert a modified 3-stream into the given 2-stream, at the right position
(define (insert two-stream three-stream)
(if (< (stream-car three-stream)
(stream-car (stream-car two-stream)))
; we have the smallest 3-stream, put it at the front
(stream-cons
three-stream
two-stream)
; otherwise, recurse
(stream-cons
(stream-car two-stream)
(insert (stream-cdr two-stream) three-stream))))
; Construct a 2^n * 3^p stream with the given 2-stream as its "state"
(define (make-the-stream current-2stream)
(let*
; pull out the first 3-stream
((first-3s (stream-car current-2stream))
(other-3s (stream-cdr current-2stream))
; use its first element as our next value
(next-val (stream-car first-3s))
; reinsert its tail into the 2-stream's tail
(next-2s (insert other-3s (stream-cdr first-3s))))
; and use the resulting 2-stream to construct the (outer) stream's tail
(stream-cons
next-val
(make-the-stream next-2s))))
; Now, we can construct the stream we want
(define the-stream (make-the-stream initial-2stream))
使用plt-scheme(在我相当蹩脚的硬件上):
$ mzscheme -f pow23.scm -e '(display (stream->list (stream-take 20 the-stream)))'
(1 2 3 4 6 8 9 12 16 18 24 27 32 36 48 54 64 72 81 96)
$ time mzscheme -f pow23.scm -e '(display (stream-ref the-stream 10000))'
161968247347450370721577384417107686788864605658546176
real 0m12.550s
user 0m11.005s
sys 0m0.340s
我想,可以用生成器实现这个,
但棘手的部分是实施(insert)
。
你可以通过组合发电机来做到这一点,
但是每次拉出一个数字时你最终都会添加一个“图层”,
而使用(insert)
创建的流与原始流共享尾部
(“层”最终合并)。
答案 2 :(得分:1)
至少如果我理解你的问题,你只需要合并两个生成器的结果:
如果两个生成器生成相等的值,则将其生成为输出,并从每个生成器生成下一个值。
请注意,虽然它通常用于排序现有数据而不是生成新数据,但这类似于普通合并排序中使用的合并,除了我假设您不需要重复项,其中合并排序通常会保留重复。
编辑:感谢lpthnc,我重读了这个问题,我认为他是对的 - 我误读了原来的问题。要获得正确的输出,您需要创建第三个生成器并生成(在这种情况下)6的倍数,并使用该结果集与其他两个生成器之间的三向合并。
我没有玩过多少,但我相信PLT Scheme最近迭代中的懒惰语言级别(或懒惰模块)会让你编写代码来生成整个无限序列,这理论上会使用无限时间和记忆,但只根据需要评估其有限子集。
答案 3 :(得分:1)
没有任何示例的简单解决方案是创建一个新的。
for (i = 0; i < X; i++)
{
if (i%2 or i%3)
{
cout << i
}
}
编辑:X是你要运行多长时间,说你想要输出0-100把100。
int counter = 1000;
bool done = false;
while(!done)
{
if (i%2 or i%3)
{
cout << i;
counter--;
if(counter <= 1)
{
done = true;
}
}
i++;
}
这有点乱,但应该有用。
编辑:计数器应该以1结束,否则它将为您提供1001项。
答案 4 :(得分:1)
只需合并两个有序列表 a la
(define merge
(lambda (pred ls1 ls2)
(cond
[(null? ls1) ls2]
[(null? ls2) ls1]
[(pred (car ls1) (car ls2))
(cons (car ls1) (merge pred (cdr ls1) ls2))]
[else (cons (car ls2) (merge pred ls1 (cdr ls2)))])))
从here解除。
答案 5 :(得分:1)
数据被编辑。我越看这一点,我就越认为我错了 - 其他人似乎已经有了更好的答案。
对不起,这些都不是方案,只是伪代码......
以下代码与我从您的问题中获得的思维过程相匹配:
编辑:修改后的伪代码现在我意识到它是“2 ^ i * 3 ^ j”,而不是“2 ^ i,3 ^ j”
// If i got close, this time, // inputs min-i=0, max-i=2, min-j=0, max-j=2 // should get output like // 2^0 * 3^0 = 1 // 2^0 * 3^1 = 3 // 2^0 * 3^2 = 6 // 2^1 * 3^0 = 2 // 2^1 * 3^1 = 6 // 2^1 * 3^2 = 12 // 2^2 * 3^0 = 4 // 2^2 * 3^1 = 12 // 2^2 * 3^2 = 24 LET min-i, max-i, min-j, max-j be input LET current-value = 1 FOR i = min-i to max-i FOR j = min-j to max-j DO PRINT "2^" . i . " * j^" . j . " = " . current-value current-value *= 3; DONE // end j loop current-value *= 2 DONE // end i loop
答案 6 :(得分:1)
在Haskell中这很容易:
merge as bs =
case (as, bs) of
([], _) -> bs
(_, []) -> as
((a:as'), (b:bs')) ->
if a <= b
then a : (merge as' bs)
else b : (merge as bs')
rmDups as =
case as of
[] -> []
[a] -> [a]
(a:bs@(b:_)) ->
if a == b
then rmDups bs
else a:(rmDups bs)
take 25 $ rmDups $ merge (map (2^) [1..]) (map (3^) [1..])
产生以下结果:
[2,3,4,8,9,16,27,32,64,81,128,243,256,512,729,1024,2048,2187,4096,6561,8192,16384,19683,32768,59049]
虽然我认为有更优雅的方式来做...