Python Set.pop()不是';不是随机的吗?
从“set.pop()删除并返回s中的任意元素”。在生成一些随机数据来测试程序时,我注意到这个pop()函数的奇怪行为。下面是我的代码(python 2.7.3): 我在这里做的是Python Set.pop()不是';不是随机的吗?,python,random,Python,Random,从“set.pop()删除并返回s中的任意元素”。在生成一些随机数据来测试程序时,我注意到这个pop()函数的奇怪行为。下面是我的代码(python 2.7.3): 我在这里做的是 在集合s中插入一些随机值,其中每个元素都在范围[0,numberRange) 从集合中弹出一个元素(根据文档,应该是随机的) 计算集合中有多少元素小于弹出值 我希望弹出的值应该是一个随机值,集合中大约50%的数字将大于弹出的值几乎总是返回集合中的最低数字。以下是numberRange=500的结果。第一行表示弹出元素
s
中插入一些随机值,其中每个元素都在范围[0,numberRange
)numberRange=500
的结果。第一行表示弹出元素的值。第二行是小于弹出值的元素百分比
9 0 3 1 409 0 1 2 4 0
0 % 0 % 0 % 0 % 87 % 0 % 0 % 0 % 0 % 0 %
我使用不同的numberRange
值进行了此测试。似乎对于集合元素的较低值,pop()
几乎总是返回最低的元素。但是对于较高的值,它返回一个随机元素。对于numberRange=1000
,结果是:
518 3586 3594 4103 2560 3087 4095 3079 3076 1622
7 % 72 % 73 % 84 % 54 % 51 % 79 % 63 % 67 % 32 %
我觉得这很随机。为什么会有这种奇怪的行为?我做错什么了吗
编辑:感谢大家的回答和评论,似乎“任意”并不保证它是“随机的”。当医生说:
从s中删除并返回任意元素;如果为空,则引发KeyError
这意味着行为没有定义,实现可以做任何可能的事情。在这种情况下,实现的行为似乎是删除最小的值。仅此而已。事实上,
set.pop()
基于HashMap
,并删除其中的第一个元素(较小的hashcode)。对于int的set
,它是最小的int
Python的其他实现可能会返回一个随机值或第一次推送的值…您不知道。这是一个实现细节-
set
被实现为一个HashMap(类似于dict
,但没有一个值槽),set.pop
删除HashMap中的第一个条目,并且int
s哈希值是相同的int
结合起来,这意味着您的
集合
(按散列值排序)实际上也按以散列表大小为模的条目排序;在您的情况下,这应该接近自然排序,因为您只插入一个小范围内的数字-如果您从随机范围(10**10)中获取随机数
而不是randrange(500)
您应该看到不同的行为。此外,根据您的插入顺序,由于散列冲突,您可以从原始散列顺序中获取一些值。它不是随机的,而是无序的。文档的“任意”并不意味着“随机”“不要依赖于任何特定的值,实现细节可能会在没有警告的情况下更改”Random并不意味着它分布得很好,甚至不可预测。它意味着你不能依赖任何观察结果在将来是真实的。是的,我想这是多么糟糕的Random。选择
对集合不起作用。此外,random.choice
对dicts来说是完全错误的。@wim:同意,如果有人刚刚阅读了random
文档,不理解Python/C++行话“序列“,集合
或dict
不是一个集合并不明显。我对“序列”、“iterable”、“迭代器”这样的术语没有强烈的感觉”在Python文档的各个部分中都假定有词汇表,但毫无疑问,它至少吸引了大多数人一次。另外,如果random.choice
处理任意iterable(在O(n)
time和O(1)
memory中)会更好在集合和DICT上,无论时间复杂度如何都是可行的。这是由于将set
作为HashMap实现而导致的实现细节,intsYour第二段的哈希值是不正确的。只有当插入的值相对于集合大小很小时,才是正确的。所有赌注都将以较大的值结束。@interjay是的,你是没错,编辑atm——它当然是按哈希表大小的模排序的;但由于OP是从一个小范围插入数字,所以对他的情况来说应该是正确的。
518 3586 3594 4103 2560 3087 4095 3079 3076 1622
7 % 72 % 73 % 84 % 54 % 51 % 79 % 63 % 67 % 32 %