我正在尝试使用另一个列表的随机结果生成一个列表,并且我希望这样做时不存在重复项(一个例外)。当我检查重复项时会发生问题-找到一个重复项并且不知道为什么会自动中断循环。
据我所知,一切似乎都是正确的。我已经通过pythontutor.com/visualise运行了代码,我尝试了不同的代码来检查重复项,将循环更改为for循环,而while循环是range循环。我测试了turning_point的定义,甚至将其复制粘贴到了循环本身中,而不是将其用作函数,我尝试更改了“ if”语句的位置,等等。而且我仍然无法解决问题。
编辑:实际上,我并不希望在特定实例上拥有如此大的重量(在本例中为“结论”),我只是这样做以测试重复检查。实际上,权重更接近3、1、1、2、1等。 另一件事是,在我的实际代码中,action_tables的长度为43个值,而不是7个值。
#first list to draw random items from
action_table = ["conclusion", "none", "confrontation", "protector", "crescendo", "destroy the thing", "meta"]
#code to draw random items from first list
#first variable weighted to guarantee duplicates for testing
def turning_point():
turning_point = (random.choices(action_table, [500, 1, 1, 1, 1, 1, 1]))
return turning_point
#generate second list called 'plot_points'
plot_points = []
for x in range(int(input("How many Turning Points to generate? "))):
tp = turning_point()
#the code below is what I got from this site
#added tp != to allow duplicates of "none" result
if any(plot_points.count(tp) > 1 for tp in plot_points) and tp != "none":
continue
#results get added to the plot_points list:
plot_points.append(tp)
print(plot_points)
如果我删除检查重复的行,这就是我得到的:
[['conclusion'], ['conclusion'], ['meta'], ['conclusion'], ['conclusion']]
如果我不删除该行,这就是我得到的:
[['conclusion'], ['conclusion']]
我想要得到的是这样的:
[['conclusion'], ['none'], ['none'], ['meta'], ['protector']]
答案 0 :(得分:2)
错误在这里:
tp != "none"
tp
始终是一个元素的列表,因为random.choices()
默认情况下会返回一个元素的列表。来自documentation for random.choices()
:
random.choices(population, weights=None, *, cum_weights=None, k=1)
返回k
大小的元素列表,该列表从总体中选择并替换。
k
保留为1时,tp
每次将成为1元素列表,并且永远不能等于"none"
。它将等于["none"]
或["conclusion"]
,依此类推。这意味着`tp!=“ none” 始终为真。
接下来,您的any()
测试只会在包含一个当前选定值的嵌套列表比一个嵌套列表多个时才开始执行。此时,您至少要跳过2个。出现两次,因为tp != "none"
始终为真:
>>> plot_points = [["conclusion", "conclusion"]]
>>> tp = ["conclusion"]
>>> any(plot_points.count(tp) > 1 for tp in plot_points)
True
>>> tp != "none"
True
您对给定选择的权重使得极不可能选择"conclusion"
以外的任何东西。对于您的7个选项,在调用["conclusion"]
函数的506次操作中,turning_point()
会被选择500次,因此上述情况在大多数情况下都会发生(每1036579476493个实验中的976562500000个实验会出现{{ 1}}连续5次,或每35个测试中约33个)。因此,您极少数会看到其他任何选项产生两次,更不用说3次了(每64777108个测试中只有3个会重复其他选项三次或多次)。 / p>
如果您必须生成一个列表,其中除["conclusion"]
外没有重复,那么加权选择就没有意义了。一旦none
被选中,您就无法再再次选择它。如果目标是使"conclusion"
元素成为结果的一部分的可能性很大,则只需在末尾进行一个单独的交换,然后首先对其余选项列表进行随机排序。改组使您可以将结果缩小到一定的大小,并且前"conclusion"
个元素都是随机的,和唯一的:
N
您可以使用>>> import random
>>> action_table = ["confrontation", "protector", "crescendo", "destroy the thing", "meta"]
>>> random.shuffle(action_table) # changes the list order in-place
>>> action_table[:3]
['meta', 'crescendo', 'destroy the thing']
元素填充该列表,以使其足够长以满足长度要求,然后根据应该包含一个的机会在随机位置插入"none"
:
conclusion
请注意,这是一个伪加权选择; def plot_points(number):
action_table = ["none", "confrontation", "protector", "crescendo", "destroy the thing", "meta"]
if number > 6:
# add enough `"none"` elements
action_table += ["none"] * (number - 6)
random.shuffle(action_table)
action_table = action_table[:number]
if random.random() > 0.8:
# add in a random "conclusion"
action_table[random.randrange(len(number))] = "conclusion"
return action_table
被选中的时间占80%,唯一性得以保留,仅重复conclusion
即可填充结果。否则,您将无法对其他元素具有唯一性。
但是,如果您必须拥有
"none"
)然后您要一个weighted random sample selection without replacement。您可以使用标准的Python库来实现此目的:
"none"
如果您需要7个或更少的项目,请使用此选项来选择您的项目,否则,需要额外的import heapq
import math
import random
def weighted_random_sample(population, weights, k):
"""Chooses k unique random elements from a population sequence.
The probability of items being selected is based on their weight.
Implementation of the algorithm by Pavlos Efraimidis and Paul
Spirakis, "Weighted random sampling with a reservoir" in
Information Processing Letters 2006. Each element i is selected
by assigning ids with the formula R^(1/w_i), with w_i the weight
for that item, and the top k ids are selected.
"""
if not 0 <= k < len(population):
raise ValueError("Sample larger than population or negative")
if len(weights) != len(population):
raise ValueError("The number of weights does not match the population")
key = lambda iw: math.pow(random.random(), 1 / iw[1])
decorated = heapq.nlargest(k, zip(population, weights), key=key)
return [item for item, _ in decorated]
值并随机播放(因为全部7个项目最终都被选中了):
"none"
演示:
def plot_points(number):
action_table = ["conclusion", "none", "confrontation", "protector", "crescendo", "destroy the thing", "meta"]
if number > len(action_table):
# more items than are available
# pad out with `"none"` elements and shuffle
action_table += ["none"] * (number - len(action_table))
random.shuffle(action_table)
return action_table
weights = [3, 1, 1, 1, 2, 2, 1]
return weighted_random_sample(action_table, weights, number)
当然,如果实际的>>> plot_points(5)
['none', 'conclusion', 'meta', 'crescendo', 'destroy the thing']
>>> plot_points(5)
['conclusion', 'destroy the thing', 'crescendo', 'meta', 'confrontation']
>>> plot_points(10)
['none', 'crescendo', 'protector', 'confrontation', 'meta', 'destroy the thing', 'none', 'conclusion', 'none', 'none']
大得多,并且您不允许选择比动作多的绘图点,则完全不需要进行填充,而只需使用action_table
直接。
答案 1 :(得分:0)
我尽力减少了更改。
def turning_point():
turning_point = (random.choices(action_table))
return turning_point
for x in range(int(input("How many Turning Points to generate? "))):
tp = turning_point()
print(tp)
#the code below is what I got from this site
#added tp != to allow duplicates of "none" result
plot_points.append(tp)
if any(plot_points.count(tp) > 1 for tp in plot_points) and tp != ["none"]:
plot_points.pop()
#results get added to the plot_points list:
# else:
# plot_points.append(tp)
print(plot_points)
print(plot_points)