现有答案在处理问题的具体问题方面做得很好,但我认为值得一提的是一个侧面问题:为什么你特别想要通过替代“随机生成器”到shuffle
而不是其他的函数random
模块。引述the docs:
注意,对于即使相当小 LEN(x)中,的x的 排列的总数大于所述 期间最随机数 发生器较大;这意味着永远不会产生长序列的大部分排列 。
短语“随机数生成器”在这里指的是什么,可以更迂腐称为伪 -random数发生器 - 发电机,让随机性好模仿,但完全是算法,因此被称为不是是“真正的随机”。任何这样的算法都会有一个“时期” - 最终会开始重演。
Python的random
模块采用了特别好的和充分研究的伪随机数发生器,该Mersenne Twister,用了一段2**19937-1
- 数字时在十进制数字写出有超过6000位数字,如len(str(2**19937-1))
将确认;-)。在我的笔记本电脑,我可以每秒产生大约500万这样的数字:
$ python -mtimeit -s'import random' 'random.random()'
1000000 loops, best of 3: 0.214 usec per loop
假设更快的机器,能产生每秒十亿这样的数字,该周期将需要约10 年到重复 - 并且宇宙年龄的最佳当前估计值比1.5 * 10 年少。因此,宇宙生命周期几乎不可思议的达到重复的地步;-)。平行计算并没有多大帮助;估计宇宙中有大约10个原子,所以即使你能够在宇宙中的每个原子上运行这样一个每秒十亿次的发生器,它仍然需要超过10个宇宙 - 生活时间开始重复。
所以,你可能有理由怀疑这种担心重复是理论而非实际问题的一小部分;-)。尽管如此,因式分解(计算长度序列N的排列)也增长得相当快。 Mersenne Twister,例如,可能能够产生长度为2080的序列的所有排列,但绝对不是长度为2081或更高的排列。如果不是“宇宙的一生”问题,那么文档担心“甚至小len(x)”是合理的 - 我们知道,许多可能的置换永远不可能通过用这样的伪-RNG,只要我们有一个相当长的序列,因此人们可能会担心什么样的偏见我们实际上甚至几洗牌!:介绍 - )
os.urandom介导访问的物理任何来源操作系统提供的随机性 - Windows上的CryptGenRandom,Linux上的/dev/urandom等。os.urandom
给出了字节序列,但借助struct很容易使它们变成随机数字:
>>> n = struct.calcsize('I')
>>> def s2i(s): return struct.unpack('I', s)[0]
...
>>> maxi = s2i(b'\xff'*n) + 1
>>> maxi = float(s2i(b'\xff'*n) + 1)
>>> def rnd(): return s2i(os.urandom(n))/maxi
现在,我们可以调用random.shuffle(somelist, rnd)
和少担心偏见;-)。
不幸的是,测量表明,这种方法对于RNG比调用random.random()
要慢50倍 - 如果我们需要很多随机数(这可能是一个重要的实际考虑因素(如果我们不需要,担心可能的偏见可能会错位;-)。 os.urandom
方法也很难以可预测的,可重复的方式使用(例如,用于测试目的),而random.random()
只需在测试开始时提供一个固定的初始random.seed
以保证可重复的行为。
因此,在实践中,os.urandom
仅用于需要“密码质量”随机数字 - 确定的攻击者无法预测的数字 - 因此愿意支付使用该数字的实际价格而不是random.random
。
嗨,我只是用一个额外的声明更新我的文章。所以我想这个额外的参数正好是1和0之间的概率(不包括1) – CppLearner 2009-12-25 15:41:24
否,附加参数是返回伪随机数的**函数**,它本身不是数字。 Python允许函数作为参数传递到其他函数,这是一个很好的显示这样的能力 – 2009-12-25 15:44:02