[英]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.