Python shuffle():种子数/ shuffle()的粒度结果多样性

Sea*_*ean 0 python random

我的任务是我想指定一个种子来混洗一个项目列表,这样我可以使用下次输入的相同种子重新创建混洗结果.然而,我尝试了一些" 接近但不同的数字 "(回顾起来一个相当无意义的陈述)作为种子,它们都产生了相同的结果.

这是我编写的一段代码,试图在Python的random.shuffle()中检查种子数的行为

from random import shuffle

seed_list = [   0.0,    0.05,   0.1,    0.15,   0.2,    0.25,   0.3,    0.35,
                0.4,    0.45,   0.5,    0.55,   0.6,    0.65,   0.7,    0.75,
                0.8,    0.85,   0.9,    0.95
]

last_list = list(range(0, 10))

for seed in seed_list:
    num_list = list(range(0, 10))
    shuffle(num_list, lambda:seed)
    print("Seed", str(seed)+":\t", num_list, num_list==last_list)
    last_list = num_list
Run Code Online (Sandbox Code Playgroud)

输出看起来像这样:

:~$ python3 test_shuffle.py 
Seed 0.0:    [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] False
Seed 0.05:   [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] True
Seed 0.1:    [9, 2, 3, 4, 5, 6, 7, 8, 0, 1] False
Seed 0.15:   [6, 2, 3, 4, 5, 0, 7, 8, 9, 1] False
Seed 0.2:    [4, 9, 3, 0, 5, 6, 7, 8, 1, 2] False
Seed 0.25:   [3, 7, 0, 4, 5, 6, 1, 8, 9, 2] False
Seed 0.3:    [9, 6, 0, 4, 5, 1, 7, 8, 2, 3] False
Seed 0.35:   [5, 0, 8, 4, 1, 6, 7, 2, 9, 3] False
Seed 0.4:    [9, 0, 7, 1, 5, 6, 2, 8, 3, 4] False
Seed 0.45:   [8, 0, 6, 1, 5, 2, 7, 3, 9, 4] False
Seed 0.5:    [0, 9, 1, 7, 2, 6, 3, 8, 4, 5] False
Seed 0.55:   [0, 9, 1, 7, 2, 6, 3, 8, 4, 5] True
Seed 0.6:    [0, 9, 1, 2, 8, 3, 7, 4, 5, 6] False
Seed 0.65:   [0, 9, 1, 2, 7, 3, 4, 8, 5, 6] False
Seed 0.7:    [0, 1, 9, 2, 3, 8, 4, 5, 6, 7] False
Seed 0.75:   [0, 1, 2, 9, 3, 4, 5, 8, 6, 7] False
Seed 0.8:    [0, 1, 2, 3, 9, 4, 5, 6, 7, 8] False
Seed 0.85:   [0, 1, 2, 3, 4, 9, 5, 6, 7, 8] False
Seed 0.9:    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] False
Seed 0.95:   [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] True
Run Code Online (Sandbox Code Playgroud)

根据我在间隔之间尝试的打印输出和一些更细粒度的种子,触发"将第一个元素移动到最后一个元素"的种子间隔至少为0.05-0.0 = 0.05(与[0.9,0.95]的"无所事事"相同).

关于行为有两个方面我觉得很麻烦:
1)总间隔的二十分之一是很大的比例.其余的shuffle行为如何在其余的间隔中公平分配?("随机性"或"随机播放"是否表达"任何顺序都可能?"如何在大间隔被某些行为占用时填补剩余的随机性.)
2) "从头到尾移动"和"做对于改组函数来说,似乎没什么"非常糟糕/无用的行为".我的实施有问题吗?

编辑:我故意挑选种子,所以我可以在我的任务中重现结果.

更多信息:我得到的印象是我使用的是这篇文章中的种子.由于random.random()在[0.,1.]中产生一些浮动,我假设我可以自己挑选一个,并通过在不同的相同距离间隔中挑选它们来进一步测试不同种子的行为.

如果我做出错误假设,逻辑错误或编码错误,请告诉我.谢谢.

Mar*_*ers 5

您正在传递一个返回固定数字的函数:

shuffle(num_list, lambda: seed)
Run Code Online (Sandbox Code Playgroud)

seed是您的浮点值之一.这与默认random()功能非常不同; 你将永远重复返回相同的数字.从文档:

可选参数random是一个0参数函数,返回随机浮点数[0.0, 1.0) ; 默认情况下,这是函数random().

你在这里制作了Dilbert会计部门随机数发生器:

在此输入图像描述

当你传递一个替代random()函数作为第二个参数时,它返回的值用于选择前面的索引来交换'当前'索引(从结尾开始); 运行源代码基本上是这样的:

x = list_to_shuffle
for i in reversed(range(1, len(x))):
    # pick an element in x[:i+1] with which to exchange x[i]
    j = int(random() * (i+1))
    x[i], x[j] = x[j], x[i]
Run Code Online (Sandbox Code Playgroud)

所以你的固定数字总是选择相同的相对指数来交换.对于该固定值的足够小的差异,向下舍入到最接近的整数将导致用于交换的完全相同的索引.

这是发生了什么0.50.55,例如:在这两种情况下,所选择的指数(5, 4, 4, 3, 3, 2, 2, 1, 1)都不是"随机"洗牌.同上,用于00.05,当你交换的一切与指数0,以及0.90.95,当你换各项指标与自身.

如果您想测试种子如何工作,random.Random()请使用种子创建该类的实例并调用shuffle()该对象:

from random import Random

seed_list = [   0.0,    0.05,   0.1,    0.15,   0.2,    0.25,   0.3,    0.35,
                0.4,    0.45,   0.5,    0.55,   0.6,    0.65,   0.7,    0.75,
                0.8,    0.85,   0.9,    0.95
]

last_list = ten_digits = list(range(10))

for seed in seed_list:
    num_list = ten_digits[:]
    Random(seed).shuffle(num_list)
    print("Seed {}:\t {} {}".format(seed, num_list, num_list==last_list))
    last_list = num_list
Run Code Online (Sandbox Code Playgroud)

哪个输出

Seed 0.0:    [7, 8, 1, 5, 3, 4, 2, 0, 9, 6] False
Seed 0.05:   [3, 8, 5, 4, 2, 1, 9, 7, 0, 6] False
Seed 0.1:    [0, 4, 8, 7, 1, 9, 5, 6, 2, 3] False
Seed 0.15:   [6, 1, 8, 7, 9, 5, 2, 4, 3, 0] False
Seed 0.2:    [9, 6, 8, 2, 7, 4, 5, 0, 1, 3] False
Seed 0.25:   [2, 8, 0, 3, 1, 6, 5, 9, 7, 4] False
Seed 0.3:    [7, 4, 5, 1, 2, 3, 8, 9, 6, 0] False
Seed 0.35:   [0, 7, 6, 2, 8, 3, 9, 5, 1, 4] False
Seed 0.4:    [3, 5, 7, 1, 9, 4, 6, 0, 8, 2] False
Seed 0.45:   [4, 3, 6, 8, 1, 7, 5, 2, 9, 0] False
Seed 0.5:    [8, 9, 3, 5, 0, 6, 1, 2, 7, 4] False
Seed 0.55:   [3, 0, 4, 6, 2, 8, 7, 1, 9, 5] False
Seed 0.6:    [3, 4, 7, 2, 9, 1, 6, 5, 8, 0] False
Seed 0.65:   [9, 1, 8, 2, 4, 0, 7, 3, 6, 5] False
Seed 0.7:    [1, 6, 2, 4, 8, 5, 7, 9, 3, 0] False
Seed 0.75:   [8, 3, 6, 1, 9, 0, 4, 5, 7, 2] False
Seed 0.8:    [4, 7, 5, 2, 0, 3, 8, 1, 9, 6] False
Seed 0.85:   [2, 4, 6, 5, 7, 8, 0, 3, 9, 1] False
Seed 0.9:    [3, 6, 5, 0, 8, 9, 1, 4, 7, 2] False
Seed 0.95:   [1, 5, 2, 6, 4, 9, 3, 8, 0, 7] False
Run Code Online (Sandbox Code Playgroud)

或者您可以调用random.seed()每个测试,传入seed值,但这会更改Random()影响其他模块的全局实例.

第二个论点random.seed()应该被遗忘,你永远不需要它.它仅在函数第一次修订中作为性能改进,以确保在紧密循环中使用本地名称而不是全局名称.但是因为它被添加到函数签名而没有前导下划线,所以它偶然成为永久性公共API的一部分.没有真正的用例需要使用它.