创建条件迭代列表

时间:2012-04-17 17:54:05

标签: python list

我有一个列表B = [0,0,0,0,0,0,0,0,0]但它可以是任何长度。

我试图迭代我可以通过迭代放置在B中的所有可能值。当满足某些条件C时,我想“重置”我刚迭代的元素并将下一个项目向上移动1.类似于二进制:

000变为001,但是当我们增加到002时,满足条件C,因此我们将其降为0并增加下一列:002变为010等。

很抱歉,如果我解释得不好。

所以B可能来自

B=[0,0,0,0,1,2,5]
to
B=[0,0,0,0,1,2,6]
to
B=[0,0,0,0,1,2,7]

等等。

但是当满足条件C时,我想以这种方式重置:

B=[0,0,0,0,1,2,96]
...attempt to increment
B=[0,0,0,0,1,2,97]
...attempt to increment
Condition C met
B=[0,0,0,0,1,3,0]

能够做到这一点,直到我最终击中最左边元素的条件C(相当于击中1111111并且无法再增加它)。

为了便于编码,我们假设条件C =所有数字的总和超过100。

我的尝试(按照agf的要求):

B=[0,0,0,0,0,0,0,0]
lenB=len(B)

while sum(B)<=100: #I think I have to somehow account for having tried incrementing the far left instead
    B[lenB-1]+=1 #increment last value in B
    while sum(B)>100: #if the sum is greater than 100
        B[lenB-1]=0 #reset the far right element
        B[lenB-2]+=1 #increment the next element
        #but this is wrong because it needs to perform this check again and again
        #for every column, while also checking if B[len-1] or B[len-2] even exists

编辑:我的条件C实际上 MUCH 比简单地检查Sum(B)&gt; 100更复杂。我只是将它用作虚拟条件,因为我可以简单地将“if sum(B)&gt; 100”替换为更复杂的条件函数。

6 个答案:

答案 0 :(得分:3)

编辑:我似乎已经为一个不同的,更复杂的问题创建了一个解决方案。以下是我在评论中通过agf澄清的问题的解决方案:

def uphill(initial=None):
    """Yields a tuple of integers. On each iteration, add one to the last column
    . If True is sent then reset the column, and begin iterating the previous
    column, until the first column is matched."""
    b = initial
    column = len(initial)-1
    while True:
        if (yield tuple(b)):
            b[column] = 0
            if column > 0:
                column -= 1
                b[column] += 1
            else:
                yield None
                raise StopIteration
            yield None
        else:
            b[column] += 1

gen = uphill([1, 2, 0])
for b in gen:
    print(b)
    if sum(b) >= 4:
        gen.send(True)

给我们:

(1, 2, 0)
(1, 2, 1)
(1, 3, 0)
(2, 0, 0)
(3, 0, 0)
(4, 0, 0)

旧解决方案:

我们可以使用生成器和鲜为人知的generator.send()

创建一个非常优雅的解决方案
def waterfall(columns):
    """Yields a tuple of integers. On each iteration, adds one to the last list
    item. The consumer can send column numbers to the waterfall during iteration
     - when this is done, the specified column is reset to 0 and the previous 
    column is incremented. When the first column is reset, the iterator ends."""
    b = [0]*columns
    while True:
        reset = (yield tuple(b))
        if not reset == None:
            while not reset == None:
                b[reset] = 0
                if reset > 0:
                    b[reset-1] +=1
                else:
                    yield None
                    raise StopIteration
                reset = (yield None)
        else:
            b[-1] += 1

gen = waterfall(3)
for b in gen:
    print(b)
    if b[2] >= 3:
        gen.send(2)
    if b[1] >= 2:
        gen.send(1)
    if b[0] >= 1:
        gen.send(0)

这给了我们:

(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 0, 3)
(0, 1, 0)
(0, 1, 1)
(0, 1, 2)
(0, 1, 3)
(0, 2, 0)
(1, 0, 0)

您可以愉快地将这些条件更改为任何内容。只需满足您选择的条件,您只需向生成器发送要重置的列的索引(自动将其上方的值增加1)。重置最后一列时,它将完成生成器。

值得注意的是,您可以随时使用gen.close()来停止它,而无需到达最后一列。 (gen.send(0)gen.close())相同。

具有不同条件的示例:

gen = waterfall(2)
for b in gen:
    print(b)
    if sum(b) >= 3:
        gen.send(1)
    if b[0] >= 3:
        gen.send(0)

给我们:

(0, 0)
(0, 1)
(0, 2)
(0, 3)
(1, 0)
(1, 1)
(1, 2)
(2, 0)
(2, 1)
(3, 0)

答案 1 :(得分:1)

def increment(box, condition):
    # last index in the list
    maxindex = index = len(box) - 1
    while True:
        # so you can see it's correct
        print box
        # increment the last digit
        box[-1] += 1
        # while the overflow condition is True
        while condition(box):
            # reset the current digit
            box[index] = 0
            # and move to the next index left
            index -= 1
            # if we're past the end of the list
            if index < 0:
                # stop
                return
            # increment the current digit
            box[index] += 1
        # back to the rightmost digit
        index = maxindex

increment([0] * 3, lambda box: sum(box) > 4)

答案 2 :(得分:0)

您可以使用列表推导和range()(在此处使用小值来停止输出非常大)来执行此操作:

>>> [(a, b, c) for a in range(2) for b in range(4) for c in range(3)]
[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 2, 0), (0, 2, 1), (0, 2, 2), (0, 3, 0), (0, 3, 1), (0, 3, 2), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 2, 0), (1, 2, 1), (1, 2, 2), (1, 3, 0), (1, 3, 1), (1, 3, 2)]

请注意,如果您想循环它,而不是在开头生成一个大列表,使用生成器表达式只会根据需要创建项目:

for a, b, c in ((a, b, c) for a in range(2) for b in range(4) for c in range(3)):
    ...

请注意,在Python 2.x中,您需要使用xrange()而不是range来获取生成器而不是列表。

答案 3 :(得分:0)

这样做你想要的吗?

B=[0,1,0]

def check(ll,callback):
    """
    This function only works if you 
    increment the last element in the list.
    All other incrementing is done in this function.
    """
    for idx in reversed(range(len(ll))):
        if(callback(ll)):
            ll[idx]=0
            ll[idx-1]+=1
        else:
            break  #this index wasn't updated, so the next one won't be either.

    #a check to see if every element is 1
    return all(map(lambda x: x==1,ll))

def checksum(ll):
    return True if sum(ll)>100 else False

count=0
while True:
    count+=1
    B[-1]+=1
    if(check(B,checksum)): break
    print B

print B   # [1,1,1]
print count

即使对于这个例子,我们在[1,1,1]

之前经历了超过5000次迭代

修改

添加了一个简单的break语句,因为只要在上一次迭代期间更改了列表,就必须检查该列表。

答案 4 :(得分:0)

紧凑但效率低下的解决方案

如果您的条件不是特定于数字的,请尝试使用类似于LattyWare的解决方案,但使用过滤器仅提供预测结果。

如果您有一个谓词函数列表,将(a, b, c)映射到bool

for a, b, c in ((a, b, c) for a in range(2) for b in range(4) for c in range(3)):
    if all [p(a,b,c) for p in predicates]:
        yield a, b, c

请注意,如果您无法对各个数字设置合理的初始界限(搜索空间变得太大),则会变得站不住脚。

答案 5 :(得分:0)

这似乎可以解决您所期望的递增问题:

b=[0,0,0,0,0,0,0,1,7]

def incr(arr, condition, pred):
   arr[-1] += 1
   element = -1
   while condition(arr) > pred:

      if arr[element]:
         arr[element] = 0
         arr[element-1] += 1
      else:
         element -= 1
      if abs(element) == len(arr):
         break
   return arr

print b
for i in xrange(0,10):
   b = incr(b, sum, 15)
   print b

函数接受列表和函数条件(例如,sum)以及增量应该结转的点。

因此它为示例sum(15)返回这样的结果:

>>> 
[0, 0, 0, 0, 0, 0, 0, 1, 7]
[0, 0, 0, 0, 0, 0, 0, 1, 8]
[0, 0, 0, 0, 0, 0, 0, 1, 9]
[0, 0, 0, 0, 0, 0, 0, 1, 10]
[0, 0, 0, 0, 0, 0, 0, 1, 11]
[0, 0, 0, 0, 0, 0, 0, 1, 12]
[0, 0, 0, 0, 0, 0, 0, 1, 13]
[0, 0, 0, 0, 0, 0, 0, 1, 14]
[0, 0, 0, 0, 0, 0, 0, 2, 0]
[0, 0, 0, 0, 0, 0, 0, 2, 1]
[0, 0, 0, 0, 0, 0, 0, 2, 2]