如何以不平凡的方式组合两个生成器
我有一个生成器,它生成所有 2 的幂的正整数,另一个生成器生成所有 3 的幂的整数。我现在需要使用它们生成 2^i*3^j 形式的整数,其中 i,j >=0,0 按升序排列。
我认为使用生成器的目的是减少内存消耗。我已经尝试这样做有一段时间了,但没有成功。请帮忙。
I have a generator which produces all positive integers that are powers of 2, and another which produces all integers that are powers of 3. I now need to use those to produce integers of the form 2^i*3^j where i,j >=0,0 in the increasing order.
The point of using generators is to reduce memory consumption, I think. I have been trying to do this for a while now to no avail. Please help out.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
使用自读流
您可以使用自读流来解决这个问题:
第一个流产生 2 的幂,
而第二个则确保所有生成的数字
乘以 3 并重新注入到输出中。
合并运算符确保输出已排序。
请注意,我们必须用 1 为输出流“播种”,
或者第一个元素在求值时会尝试生成自身。
这是代码:
与我的其他提案相比,它非常简单且快速,
因为它除了单调性之外还使用了问题的算术属性。我错了,它也可以被推广(即将推出)
使用生成器和队列
我们也可以使用 python 的生成器来实现它,
但我们需要使用队列来存储在反馈循环中等待的数字:
这有点不太优雅(恕我直言),
但生成器更加轻量级:
为了值得,我做了一个方案实现
(使用闭包作为生成器)
这显示了大致相同的性能。
Using a self-reading stream
You can solve this using a self-read stream:
The first stream produces the powers of two,
while the second one ensures all the generated numbers
are multiplied by 3 and reinjected into the output.
The merge operator ensures that the output is sorted.
Note that we must "seed" the output stream with 1,
or the first element will try to produce itself when evaluated.
Here is the code:
It's quite simple and fast compared to my other proposal,
because it uses arithmetic properties of the problem besides monotonicity.I'm wrong, it can be generalized just as well (upcoming)
Using generators and a queue
We can also implement this with python's generators,
but we need to use a queue to store the numbers waiting in the feedback loop:
This is somewhat less elegant (IMHO),
but the generators are much more lightweight:
For what is worth, I have done a scheme implementation
(using closures as generators)
which shows roughly the same performance.
我对发电机不太了解
但是我可以提出一个基于流的解决方案(延迟构建,
可能是无限列表),这有点相似。
我的方法是创建一个流
其“状态”本身就是一串串流。
个体的、内在的数字流,
我们称它们为 3 流,
表示从 1 开始的 3 的连续幂的列表,
乘以给定的二的幂。
然后我们可以组装无限个这样的 3 流,
从 1 开始,每个连续的 2 次幂 1。
我们称之为 2 流。
在 ascii-art 中,初始状态是这样的:
现在,我们要操纵它,以便在任何时刻,
3 个流将在 2 个流内排序
关于他们的第一个要素。
结果是下一个最小的生成数
将始终是第一个 3 流的第一个元素。
因此,要获得您希望获得的序列中的下一个数字,
我们要拉出第一个 3 流,
取出它的第一个元素(这是我们感兴趣的数字),
然后将3流重新插入2流中
位于由其新第一个元素确定的位置。
提取第一个数字 (1) 后的新状态将是:
请注意,此方法不具体依赖于 2^i、3^j 或乘法
(只是 2^i * 3^j 随着 i 和 j 单调递增)。
我已经发布了另一个答案,并且。结果是更加简单和快速
不要相信我:它与数学无关
下面是一个示例实现,使用 SRFI-41 流:
使用 plt-scheme(在我相当蹩脚的硬件上):
我想可以用生成器来实现这个,
但棘手的部分是实现
(insert)
。你可以通过组合生成器来做到这一点,
但每次拉出一个数字时,你最终都会添加一个“层”,
而使用
(insert)
创建的流与原始流共享其尾部(“层”最终合并)。
I don't know much about generators,
however I can propose a solution based on streams (lazily constructed,
possibly infinite lists), which are somewhat similar.
My approach would be to create a stream
whose "state" itself would be a stream of streams.
The individual, inner streams of numbers,
let's call them the 3-streams,
would represent lists of the successive powers of 3, starting with 1,
multiplied by a given power of two.
We can then assemble an infinity of such 3-streams,
one for each successive power of 2, starting with 1.
Let's call this the 2-stream.
The initial state, in ascii-art, is this:
Now, we're going to manipulate this so that at any moment,
the 3-streams will be ordered within the 2-stream
with regards to their first elements.
As a consequence the next smallest generated number
will always be the first element of the first 3-stream.
So, to get the next number in the sequence you wish to obtain,
we're going to pull out the first 3-stream,
pull out its first element (which is the number we're interested in),
and then re-insert the 3-stream in the 2-stream
at a position determined by its new first element.
The new state after the first number (1) has been extracted would be:
Note that this method does not depend on 2^i, 3^j or multiplication specifically
(just on 2^i * 3^j being monotonically increasing with i and j).
I have posted another answer which does, and.is much more simple and fast as a result
don't trust me: it has nothing to do with the math
Below is an example implementation, using SRFI-41 streams:
Using plt-scheme (on my rather crappy hardware):
Implementing this with generators could be done I guess,
but the tricky part would be implementing
(insert)
.You could do so by composing generators,
but you would end up adding one "layer" every time a number is pulled,
whereas a stream created with
(insert)
shares its tail with the original one(the "layers" eventually merge).
只需合并从此处提取的两个有序列表即可
。
Just merge the two ordered lists a la
lifted from here.
没有任何示例的简单解决方案是创建一个新的解决方案。
编辑:X 是你想要运行它的时间,假设你想要输出 0-100,输入 100。
这有点乱,但应该可以。
编辑:计数器应该以 1 结束,否则它会给你 1001 个项目。
The simple solution w/o any examples is creating a new one.
edit: X is how long you want to run it say you want output 0-100 put 100.
It's a little messy but should work.
edit: The counter should end at 1 or it will give you 1001 items.
至少如果我理解你的问题,你只需要合并两个生成器的结果:
如果两个生成器产生相等的值,将其作为输出,并从每个生成器生成下一个值。
请注意,虽然它通常用于对现有数据进行排序而不是生成新数据,但这与普通合并排序中使用的合并类似,但我假设您不想要重复项,而合并排序通常会保留重复项重复。
编辑:感谢 lpthnc,我重新阅读了这个问题,我认为他是对的——我误读了原来的问题。为了获得正确的输出,您需要创建第三个生成器并生成(在本例中)六的倍数,并在该结果集与其他两个生成器的结果集之间使用三向合并。
我没怎么玩过它,但我相信最近迭代的 PLT 方案中的惰性语言级别(或惰性模块)将让您编写代码来生成整个无限序列,理论上这将使用无限的时间和内存,但是仅根据需要评估其中的有限子集。
At least if I understand your question, you just need to merge the results from the two generators:
If the two generators produce equal values, produce that as the output, and generate the next value from each generator.
Note that although it's typically used for sorting existing data instead of generating new data, this is similar to the merge used in a normal merge sort, with the exception that I've assumed you don't want duplicates, where a merge sort normally retains duplicates.
Edit: Thanks to lpthnc, I've reread the question, and I think he's right -- I misread the original question. To get the correct output, you'd need to create a third generator and produces the multiples of (in this case) six, and use a three-way merge between that result set and those from the other two generators.
I haven't played with it much, but I believe the Lazy language level (or lazy module) in recent iterations of PLT Scheme would let you write your code to generate the entire infinite sequence, which would theoretically use infinite time and memory, but only evaluate a finite subset of that as needed.
这在 Haskell 中非常简单:
产生以下结果:
虽然我想有一种更优雅的方法来做到这一点......
This is pretty easy in Haskell:
yields the following:
though I imagine there's a more elegant way to do it...
已编辑。我越看这个问题,就越觉得我完全错了——而其他人似乎已经有了更好的答案。
抱歉,这些都不是方案中的,只是伪代码...
以下代码与我从您的问题中获得的思维过程相匹配:
编辑:修改伪代码现在我意识到它是“2^i*3^j”,不是“2^i, 3^j”
Redacted. The more I look at this, the more I think I've got it all wrong - and others appear to have better answers, already.
Sorry, none of this is in scheme, just pseudocode...
The following code matches the thought process I garner from your question:
EDIT: revised pseudocode now that I realize it's "2^i*3^j", not "2^i, 3^j"