Scheme 如何以非平凡的方式组合两个生成器
我有一个生成器,它生成2的幂的所有正整数,还有一个生成器生成3的幂的所有整数。我现在需要用这些来生成形式为2^I*3^j的整数,其中I,j>=0,0,以递增的顺序Scheme 如何以非平凡的方式组合两个生成器,scheme,puzzle,generator,Scheme,Puzzle,Generator,我有一个生成器,它生成2的幂的所有正整数,还有一个生成器生成3的幂的所有整数。我现在需要用这些来生成形式为2^I*3^j的整数,其中I,j>=0,0,以递增的顺序 我认为,使用生成器的目的是减少内存消耗。我已经试着这样做了一段时间,但没有结果。请提供帮助。至少如果我理解您的问题,您只需要合并两个生成器的结果: 从每个生成器生成一个输出 生成两者中较小的一个作为下一个输出 从该生成器生成下一个输出 返回步骤2 如果两个生成器生成相等的值,则生成该值作为输出,并从每个生成器生成下一个值 请注意,尽管
我认为,使用生成器的目的是减少内存消耗。我已经试着这样做了一段时间,但没有结果。请提供帮助。至少如果我理解您的问题,您只需要合并两个生成器的结果:
我没怎么玩过它,但我相信在最近的PLT方案迭代中的惰性语言级别(或惰性模块)将允许您编写代码来生成整个无限序列,理论上这将使用无限的时间和内存,但只根据需要计算其中的有限子集。没有任何示例的简单解决方案正在创建一个新的解决方案
for (i = 0; i < X; i++)
{
if (i%2 or i%3)
{
cout << i
}
}
(i=0;i
{
如果(i%2或i%3)
{
只需将两个有序列表合并一个la即可
摘自。编辑。我看得越多,就越觉得自己搞错了——其他人似乎已经有了更好的答案
抱歉,这些都不在scheme中,只是伪代码
以下代码与我从您的问题中获得的思考过程相匹配:
编辑:修改了伪代码,现在我意识到它是“2^I*3^j”,而不是“2^I,3^j”
//如果我靠近了,这次,
//输入min-i=0,max-i=2,min-j=0,max-j=2
//应该得到像这样的输出
// 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
输入min-i、max-i、min-j、max-j
让当前值=1
对于i=最小-i到最大-i
对于j=最小-j到最大-j DO
打印“2^”.i.*j^.j.=”当前值
当前值*=3;
完成//结束j循环
当前值*=2
完成//结束i循环
这在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..])
虽然我想有一种更优雅的方式来做…我对发电机不太了解,
但是,我可以提出一个基于流的解决方案(延迟构造,
可能是无限列表),它们有些相似
我的方法是创建一个流
其“状态”本身就是一股溪流
个体内部的数字流,
让我们称之为三流,
将代表3的连续幂的列表,从1开始,
乘以给定的二次幂。
然后我们可以集合无限多这样的三条流,
从1开始,每连续2次方一次。
让我们称之为2流
ascii技术中的初始状态为:
---------------------- --- -- -
| 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流的第一个元素
因此,要获得您希望获得的序列中的下一个数字,
我们要拿出第一条三流,
拉出它的第一个元素(我们感兴趣的数字),
然后在2流中重新插入3流
位于由其新的第一个元素确定的位置。
提取第一个数字(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单调增加时)。
我已经发布了另一个答案,而且
其结果是更加简单和快速。
别相信我,这和数学无关
下面是一个使用流的示例实现:
用生成器实现这一点我想是可以做到的,
但棘手的部分是实现(insert)
。
你可以通过组合生成器来实现,
但每次拉取一个数字时,都会增加一个“层”,
而使用(insert)
创建的流与原始流共享其尾部
(层最终合并)。使用自读流
您可以使用自读流解决此问题:
----------- -----------
| pow 2 |------->| |
----------- | |
| merge |-------+------------>
----------- | | |
.->| x 3 |------->| | |
| ----------- ----------- |
\_______________________________________/
第一股水流产生两股力量,
而第二个则确保生成的所有数字
乘以3并重新输入到输出中。
merge操作符确保对输出进行排序
请注意,我们必须使用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))
使用生成器和队列
我们也可以用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()
为了有价值,我做了一个方案实施
(使用闭合器作为生成器)
这显示了大致相同的性能。@Greg,你的发型很漂亮!你想要的东西能以2或3的倍数递增的顺序找到所有东西吗?我不知道你在找什么。你在解析什么东西吗?还是像2,3,4,6,8,9这样的直接计数器。对不起,2或3的幂递增的顺序。每次我调用它,我
---------------------- --- -- -
| The 2-stream ...
---|----|----|----|---- --- -- -
V V V V
| 2| | 3| | 4| | 8|
| 6| | 9| |12| |24| ...
|18| |27| |36| |72| The 3-streams
: : : :
(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))
$ 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
----------- -----------
| pow 2 |------->| |
----------- | |
| merge |-------+------------>
----------- | | |
.->| x 3 |------->| | |
| ----------- ----------- |
\_______________________________________/
(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
# 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