有理数生成器(Python)

时间:2014-01-27 19:15:12

标签: python

  1. 为什么要删除[1,3],[3,2]条目并将其替换为[2,3],[3,1]?

  2. 注意,[2,3],[3,1]是正确的分支,但它不应该覆盖前一个条目,它应该附加到它。

    def rational():
        treecount = 0
        tree = [[[1,1]]]
        left=[0,0]
        right=[0,0]
        while(True):
            treecount+=1
            tree.append([])
            left=[0,0]
            right=[0,0]
            for z in range(len(tree[treecount-1])):
                left[0] = tree[treecount-1][z][0]
                right[0] = tree[treecount-1][z][0] + tree[treecount-1][z][1]
                left[1] = tree[treecount-1][z][1] + tree[treecount-1][z][0]
                right[1] = tree[treecount-1][z][1]
                tree[treecount].append(left)
                tree[treecount].append(right)
                yield( tree)
    
  3. 输出如下:

    [[[1, 1]], [[1, 2], [2, 1]]] [[[1, 1]], [[1, 2], [2, 1]], [[1, 3], [3, 2]]] [[[1, 1]], [[1, 2], [2, 1]], [[2, 3], [3, 1], [2, 3], [3, 1]]]
    

2 个答案:

答案 0 :(得分:2)

你正在遇到Python对象模型的一个基本方面,即Python永远不会隐式地复制对象。

在您的代码中,您在进入left循环之前创建了两个新列表rightfor。然后,您反复修改这两个列表,并将它们附加到您的树中。这很好,除了你正在做的所有事情都是重复地附加对相同两个列表的引用。

这是一个更简单的例子。我们将创建一个列表left并将其附加到另一个列表outer

>>> outer = []
>>> left = [1, 3]
>>> outer.append(left)
>>> outer
[[1, 3]]

到目前为止一切顺利:一切都像预期的那样。但现在让我们修改left并再次将其附加到outer

>>> left[0] = 4
>>> outer.append(left)
>>> outer
[[4, 3], [4, 3]]

注意outer的第一个条目是如何更改的?那是因为outer此时不包含两个独立的列表:相反,它包含对相同列表的两个引用。这就是上面代码中发生的事情。

可以直接修复:在left循环的每次迭代中重新创建rightfor

for z in range(len(tree[treecount-1])):
    left=[0, 0]
    right=[0, 0]
    left[0] = tree[treecount-1][z][0]
    right[0] = tree[treecount-1][z][0] + tree[treecount-1][z][1]
    left[1] = tree[treecount-1][z][1] + tree[treecount-1][z][0]
    <and so on>

或者,您可以在附加leftright之前制作副本:

tree[treecount].append(list(left))
tree[treecount].append(list(right))

顺便提一下,如果你更好地利用Python的一些习语,你可以大大简化你的代码。首先,迭代range(len(something))通常是不必要的,特别是当您要将值直接用作索引时。直接在列表上迭代。其次,您可以直接在tree[treecount-1]语句中解压缩for值。然后,您可以使用负索引从列表末尾开始索引,从而节省维护treecount的需要。通过这些首次更改,您的代码如下所示:

def rational():
    tree = [[[1,1]]]
    while True:
        tree.append([])
        for a, b in tree[-2]:
            left = [a, b + a]
            right = [a + b, b]
            tree[-1].extend([left, right])
            yield tree

仍有改进的余地,但这比原始代码更具可读性。

答案 1 :(得分:0)

如果您实际上不需要树,而只是想遍历正向理性,则可以使用此生成器。输出与Calkin-Wilf树的广度优先遍历顺序相同,但无需增加队列的开销。

from fractions import Fraction
def Calkin_Wilf():
   x = Fraction(1, 1)
   yield x
   while True:
      x = Fraction(1, 2*Fraction(int(x))-x+1)
      yield x

使用生成器创建一个迭代器,然后使用next()进行迭代,例如:

rational = Calkin_Wilf()
for _ in range(100):
   print(next(rational))