在 python 中每次都从 0-9 对集合进行排序?!不是无序的
Sets are sorted from 0-9 every single time in python?! Not unordered
起初我认为这是巧合,所以我写了一个测试来尝试它,这是真的,我 运行 它 100 万次,每次返回时集合都是有序和排序的。只有当您使用 0-9 的整数时才会发生这种情况,只要插入大于 9 的整数,之后插入的任何整数都不会被排序。为什么是这样?同样对于花车,它有点排序,但并不总是正确的,所以很奇怪我认为它们是完全无序的。任何关于为什么每次都对 0-9 进行排序的建议都将不胜感激,起初我也不相信所以这是我使用的代码你可以轻松地 运行 自己看看它是真的。
import random
def check_set():
constructing = True
s = set()
while constructing:
x = random.randint(0, 9)
if x not in s: s.add(x)
if len(s) == 10: constructing = False
return s
def main():
for x in range(10000):
l = list(check_set())
if l != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:
print('wow')
if __name__ == '__main__':
main()
这些整数散列到自己:
>>> [*map(hash, range(10))]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
当您将数字 0 到 9 添加到一个集合中时,该集合至少可以容纳 10 个数字(我认为实际上是 32 个)。所以它的内部数组至少有索引 0 到 9。并且因为这些数字散列到它们自己,所以它们存储在集合的内部数组中它们自己的索引处(值 i
存储在索引 hash(i)
=i
)。所以当你迭代它时,你会对它们进行排序。
用更小的例子进一步说明:
集合从内部大小 8 开始,值 i
想要转到索引 hash(i) % 8
。因此,如果您添加 0
和 8
,两者都想转到索引 0
。第一个实际上到达索引 0
,另一个必须转到其他(更大的)索引。因此:
>>> {0, 8}, {8, 0}
({0, 8}, {8, 0})
如果您改为添加 1
和 8
,则 1
想要转到索引 1
而 8
想要转到索引 0
,因此无论插入顺序如何,8
始终排在第一位:
>>> {1, 8}, {8, 1}
({8, 1}, {8, 1})
0 到 9 的例子:
>>> s = set()
>>> for i in 8, 9, 0, 1, 2, 3, 4, 5, 6, 7:
s.add(i)
print(s)
{8} # the only element (stored at index 0)
{8, 9} # 9 gets stored at index 1, so after 8
{8, 9, 0} # indices 0 and 1 are already taken, so 0 goes to some higher index
{8, 9, 0, 1} # similar
{0, 1, 2, 8, 9} # the set internally resized and re-added all values, each
# value ends up at its own index (e.g., 8 goes to index 8)
{0, 1, 2, 3, 8, 9} # 3 goes to index 3
{0, 1, 2, 3, 4, 8, 9} # same for the rest, all go to their own index...
{0, 1, 2, 3, 4, 5, 8, 9}
{0, 1, 2, 3, 4, 5, 6, 8, 9}
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
起初我认为这是巧合,所以我写了一个测试来尝试它,这是真的,我 运行 它 100 万次,每次返回时集合都是有序和排序的。只有当您使用 0-9 的整数时才会发生这种情况,只要插入大于 9 的整数,之后插入的任何整数都不会被排序。为什么是这样?同样对于花车,它有点排序,但并不总是正确的,所以很奇怪我认为它们是完全无序的。任何关于为什么每次都对 0-9 进行排序的建议都将不胜感激,起初我也不相信所以这是我使用的代码你可以轻松地 运行 自己看看它是真的。
import random
def check_set():
constructing = True
s = set()
while constructing:
x = random.randint(0, 9)
if x not in s: s.add(x)
if len(s) == 10: constructing = False
return s
def main():
for x in range(10000):
l = list(check_set())
if l != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:
print('wow')
if __name__ == '__main__':
main()
这些整数散列到自己:
>>> [*map(hash, range(10))]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
当您将数字 0 到 9 添加到一个集合中时,该集合至少可以容纳 10 个数字(我认为实际上是 32 个)。所以它的内部数组至少有索引 0 到 9。并且因为这些数字散列到它们自己,所以它们存储在集合的内部数组中它们自己的索引处(值 i
存储在索引 hash(i)
=i
)。所以当你迭代它时,你会对它们进行排序。
用更小的例子进一步说明:
集合从内部大小 8 开始,值 i
想要转到索引 hash(i) % 8
。因此,如果您添加 0
和 8
,两者都想转到索引 0
。第一个实际上到达索引 0
,另一个必须转到其他(更大的)索引。因此:
>>> {0, 8}, {8, 0}
({0, 8}, {8, 0})
如果您改为添加 1
和 8
,则 1
想要转到索引 1
而 8
想要转到索引 0
,因此无论插入顺序如何,8
始终排在第一位:
>>> {1, 8}, {8, 1}
({8, 1}, {8, 1})
0 到 9 的例子:
>>> s = set()
>>> for i in 8, 9, 0, 1, 2, 3, 4, 5, 6, 7:
s.add(i)
print(s)
{8} # the only element (stored at index 0)
{8, 9} # 9 gets stored at index 1, so after 8
{8, 9, 0} # indices 0 and 1 are already taken, so 0 goes to some higher index
{8, 9, 0, 1} # similar
{0, 1, 2, 8, 9} # the set internally resized and re-added all values, each
# value ends up at its own index (e.g., 8 goes to index 8)
{0, 1, 2, 3, 8, 9} # 3 goes to index 3
{0, 1, 2, 3, 4, 8, 9} # same for the rest, all go to their own index...
{0, 1, 2, 3, 4, 5, 8, 9}
{0, 1, 2, 3, 4, 5, 6, 8, 9}
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}