繁体   English   中英

满足条件时就地修改python列表

[英]In-place modification of a python list when condition is met

我有一个数字列表,我还计算了这个列表中两个的所有可能组合。 我想要做的是检查第一个组合的总和是否小于 5,如果是,从组合中选择 2 个随机数,从初始列表中删除该数字并重新计算组合等.

import itertools
import random

numbers = [1,2,3,4,5,6,7,8,9,10]

all_combos = list(itertools.combinations(numbers, 2))

for combo in all_combos:
    if combo[0] + combo[1] < 5:
        choice = random.choice([combo[0],combo[1]])
        numbers.remove(choice)

我得到的错误

ValueError: list.remove(x): x not in list

最初我认为是列表的就地更改与 for 循环相结合导致了这个,所以我也尝试实现一个 while 循环,但它总是以无限循环结束,因为我似乎无法找到正确的条件为了它。

如果您itertools.combinations问题中所说的那样实际重新计算组合,则需要将itertools.combinations调用放在循环中的某个位置,并在改变numbers后重新生成它。

一种方法是使用 while 循环,只有在您完成组合后才中断,而没有找到更多符合您标准的对:

import itertools
import random

numbers = [1,2,3,4,5,6,7,8,9,10]


while True:    
    all_combos = itertools.combinations(numbers, 2)
    try:
        pair = next(p for p in all_combos if sum(p) < 5)
    except StopIteration:
        break
    choice = random.choice(pair)
    numbers.remove(choice)

这将对选择的统计数据产生很小的影响,因为在第一次迭代中删除2会删除以后在对(2, 3)中删除3所有可能性,其中简单地从numbers删除它并继续在组合中留下该选择。 这意味着您的号码列表中剩余3的几率高于其他方式。

由于它是随机的,您最终可能会尝试删除已删除的数字。

只需包含一个try / except

    try:
        numbers.remove(choice)
    except ValueError:
        pass

或者,请求许可:

    if choice in numbers: 
        numbers.remove(choice)

如前所述,您可能会多次选择相同的数字,从而导致错误。 您可以将要删除的数字放在一个集合中,该集合会删除重复项,然后再进行删除。

import itertools
import random

numbers = [1,2,3,4,5,6,7,8,9,10]

all_combos = list(itertools.combinations(numbers, 2))

remove_this = {}
for combo in all_combos:
    if combo[0] + combo[1] < 5:
        choice = random.choice([combo[0],combo[1]])
        remove_this.add(choice)
for choice in remove_this:
    numbers.remove(choice)

这可以减少到

remove_this = {random.choice(combo)
        for combo in itertools.combinations(numbers, 2)
        if sum(combo) < 5}
for choice in remove_this:
    numbers.remove(choice)
print(numbers)

现在,您正在从numbers中删除一个数字,但不更新all_combos 这意味着该数字几乎肯定会再次出现在all_combos ,但会从numbers丢失。 您不需要在每次删除数字时重新生成所有可能的组合,但您确实需要过滤all_combos并相应地调整您的位置。 这样做的一个简单方法是维护一个布尔值列表,每次丢弃一个数字时都会更新该列表:

from itertools import combinations
from random import choice

numbers = list(range(1, 11))

all_combos = list(combinations(numbers, 2))

valid = [True] * len(all_combos)
for i, combo in enumerate(all_combos):
    if not valid[i]: continue
    if sum(combo) < 5:
        c = random.choice(combo)
        valid[i + 1:] = (valid[j] and c not in all_combos[j] for j in range(i + 1, len(all_combos)))

该算法比在每一步重新生成整个组合列表便宜得多,因为它只是跳过无效的组合。 您可以通过使用set来跟踪丢弃的元素并决定要即时跳过哪些元素,从而进一步降低O(n 2 )的复杂性(因为您在每一步都会重新访问all_combos的整个尾部)。 这将是O(n 2 )因为集合查找表面上是O(1)

from itertools import combinations
from random import choice

numbers = list(range(1, 11))

all_combos = list(combinations(numbers, 2))

dropped = set()
for i, combo in enumerate(all_combos):
    if any(x in dropped for x in dropped): continue
    if sum(combo) < 5:
        dropped.add(random.choice(combo))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM