从集合中随机选择? Python
Posted
技术标签:
【中文标题】从集合中随机选择? Python【英文标题】:random.choice from set? python 【发布时间】:2013-03-28 02:13:46 【问题描述】:我正在研究猜谜游戏的人工智能部分。我想让 AI 从这个列表中随机选择一个字母。我将其作为一组进行,因此我可以轻松地从列表中删除字母,因为它们在游戏中被猜到,因此不再可以被再次猜到。
它说set
对象不可索引。我该如何解决这个问题?
import random
aiTurn=True
while aiTurn == True:
allLetters = set(list('abcdefghijklmnopqrstuvwxyz'))
aiGuess=random.choice(allLetters)
print (aiGuess)
【问题讨论】:
顺便说一句,您不需要使用 set(list('string')) 来获取一组字母,因为字符串本身是可迭代的—— set('abc') 会做你想做的. 对于其他遇到这个问题的人,值得看看这个关于如何创建一个允许有效随机选择的类集合对象的问题。这里给出的选项都是 O(N)。 ***.com/q/15993447/2966723 【参考方案1】:注意(2020 年 10 月): 从 v3.9 开始,Python 有 officially deprecated random.sample()
处理集合,官方指导是将集合显式转换为列表或元组在传入之前,虽然这并不能解决效率问题。
>>> random.sample(set('abcdefghijklmnopqrstuvwxyz'), 1)
['f']
文档:https://docs.python.org/3/library/random.html#random.sample
请注意,无论您如何从集合中选择随机元素效率极低 - 它所花费的时间与集合的大小成正比,或者如果集合的底层哈希表由于稀疏而变得更糟删除元素。
相反,您可能应该使用有效支持此操作的different data structure。
【讨论】:
在末尾添加[0]
,因此它与random.choice
基本相同(它不会以列表的形式返回它的值)
random.sample
在内部执行tuple(population)
,所以random.choice(tuple(allLetters))
可能会更好。
需要强调的是这个过程是O(N)。
我认为它确实效率低下...正如您所看到的github.com/python/cpython/blob/2.7/Lib/random.py#L332-L339,每次您进行上述调用时,示例函数都会从集合中创建一个列表并从中获取一个随机元素。假设你有一个很大的集合并且你想要制作很多样本。如果集合没有改变,最好将其转换为列表并使用random.choice
。如果在您对其进行采样时集合也发生了变化,那么您可能根本不应该使用集合。如果您知道集合中占用的哈希值和存储桶大小,那么编写采样函数会很容易......
请注意,从 3.9 开始,Python 有 officially deprecated random.sample()
处理集合,官方指导是在传入之前将集合显式转换为列表或元组。【参考方案2】:
您应该使用random.choice(tuple(myset))
,因为它比random.sample
更快且看起来更简洁。我写了以下内容进行测试:
import random
import timeit
bigset = set(random.uniform(0,10000) for x in range(10000))
def choose():
random.choice(tuple(bigset))
def sample():
random.sample(bigset,1)[0]
print("random.choice:", timeit.timeit(choose, setup="global bigset", number=10000)) # 1.1082136780023575
print("random.sample:", timeit.timeit(sample, setup="global bigset", number=10000)) # 1.1889629259821959
从数字看来,random.sample
需要的时间增加了 7%。
【讨论】:
在我的机器上,random.choice 快了 7 倍。 没有办法直接从set中选择,不用复制到tuple中? 我得到的样本比在一组 5000 个元素上选择的速度慢大约 12%(250 毫秒)。 在我的机器上,random.sample
随着设置大小的增长从比random.choice
慢变成比它快(交叉点在设置大小100k-500k 之间)。也就是说,集合越大,random.sample
越有可能更快。【参考方案3】:
您可以使用list
而不是set
来解决此问题。您仍然可以从列表中“轻松”删除字母。试试这个,例如:
allLetters = list('abcdefghijklmnopqrstuvwxyz')
aiGuess = random.choice(allLetters)
allLetters.remove(aiGuess)
另一种选择是随机选择索引而不是字母,这可能会稍微快一些,因为我们不需要搜索要删除的元素(但我想在这里速度是否真的很重要?):
allLetters = list('abcdefghijklmnopqrstuvwxyz')
index = random.randint(0, len(allLetters)-1) # Top is inclusive, unlike slices
aiGuess = allLetters[index]
del allLetters[index]
【讨论】:
【参考方案4】:由于选择列表不是很长,你可以先使用 random.shuffle 列表。然后迭代列表中的每个元素。这样可以避免从列表中一个一个地删除元素,并使您的代码更清晰。
【讨论】:
该死,这很聪明 但是你不能两次获得相同的元素(一般来说,不是上下文 OP 的问题)【参考方案5】:如果你想从集合中获取一个随机元素。
a = set()
for i in range(10):
a.add(i)
a.pop() // gives a random element from a set
【讨论】:
你需要把它加回来,sample = a.pop()
然后a.add(sample)
以上是关于从集合中随机选择? Python的主要内容,如果未能解决你的问题,请参考以下文章