欢迎关注我的专栏( つ•̀ω•́)つ【人工智能通识】

均匀
理想化的随机应该是均匀的。
向一个靶盘随机扔飞镖,靶盘上的飞镖越多,分布的应该越均匀。但现实与理想化不同,我们的飞镖总是更加聚集到中心,实际上更加接近正态分布和不是均匀分布。

我们可以用一个程序测试自己的随机情况,比如下面这个代码:
nums='987......38266859' #随便打一些随机数
counts=[0 for i in range(10)]
for i in nums:
counts[int(i)]+=1
##绘制分布图
import plotly.offline as py
import plotly.graph_objs as go
py.init_notebook_mode()
go.FigureWidget(
data=[go.Bar(x=[i for i in range(10)], y=counts)],
layout=dict(
title='Total number:{}'.format(len(nums)),
width=500,
xaxis=dict(dtick=1),
),
)
我得到类似下图,我总共随机打了110个数字,横轴是数字,纵轴是每个数字出现的次数:

在这里0和1出现的次数比较少,尽管我打字的时候还有意识的照顾了它们,但实际由于我使用双手在大键盘上面的敲打数字,导致了两端的1和0比较少,5在双手中间最容易被忽略,6789比较高说明当时我的右手比较灵活打字更多些。
看到了吧,人的随机受到很多因素影响,人并不能真的做到均匀随机。
算术随机
怎么用算法实现一个均匀的随机?让0~9每个数字出现的平均?

这是冯诺依曼最早给出的中间平方法生成伪随机的思路。
这东西靠谱吗?
首先,如果看一下0~9十个数字个位数相乘得到的结果的个位数是不是均匀的,比如3乘3得9,个位数是9;而6乘7得42,个位数是2;如果我们只截取相乘结果的最后一位,如果是均匀的0~9分布,那么这个简化版的随机算法应该就是均匀的。
修改一下代码:
nums = ''
for a in range(10):
nums = nums + str(a * a)[-1]
counts = [0 for i in range(10)]
for i in nums:
counts[int(i)] += 1
import plotly.offline as py
import plotly.graph_objs as go
py.init_notebook_mode()
go.FigureWidget(
data=[go.Bar(x=[i for i in range(10)], y=counts)],
layout=dict(
title='Total number:{}'.format(len(nums)),
width=500,
xaxis=dict(dtick=1),
),
)
得到10个乘方结果个位数的分布图如下:

结果显示非常的不均匀。
如我们之前文章所说,冯诺依曼的中间平方算法不仅无法得到均匀随机,甚至数百次之后还会出现循环的情况。——总之是挺糟糕的。
均匀的随机
我们需要均匀的0~9数字分布,不用乘方,用乘法行不行?
修改一下代码,得到:
nums=''
for a in range(10):
for b in range(10):
nums=nums+str(a*b)[-1]
counts=[0 for i in range(10)]
for i in nums:
counts[int(i)]+=1
#以下绘图部分不再重复
得到100个数字分布图:

共100个数字的个位数中,0异常多,因为0乘以任何数都是0,5乘以任何偶数的个位都是0。总之也不均匀。
但是,我们改为加法的时候却是均匀的。
nums=''
for a in range(10):
for b in range(10):
nums=nums+str(a+b)[-1]
counts=[0 for i in range(10)]
for i in nums:
counts[int(i)]+=1
个位数分布图如下:

可行的算法
首先我们做一个假设,就是每次我们需要随机数的情况都是不确定的,随机的,十分钟前需要一个随机数,可能3分钟后又需要另一个,10秒后又需要下一个,既然这种需要是随机的,那么我们可以利用它来生成随机数。
下面的代码可以有效的生成0~9的随机数字,它利用计算机for循环中每次计算都会消耗随机时间的原理来实现的。
因为在微秒级别,就是毫秒的千分之一级别,上一次for循环用了8微秒,下一次用了3微秒,下一次用了18微秒,这就导致最后一位数字的均匀随机性。
import time
nums=''
for i in range(100000):
nums=nums+'{:.6f}'.format(time.time()).split('.')[1][-1]
counts=[0 for i in range(10)]
for i in nums:
counts[int(i)]+=1
这个代码运行1000次就可以看到比较均匀的状态。

其实这个算法的瑕疵也很明显,尤其是在计算速度很快的计算机上面。为什么呢?想想看。
这张图是最后100个0~9数字的分布图,虽然数字不是完全的循环重复,但规律性还是比较明显的。

这是由于芯片计算每次循环虽然耗时有一定的随机性,但整体上还是有周期的,这导致了微秒数最后一位的循环波动。
上面的时间戳算法借助芯片计算速度的随机性才实现了看似均匀的随机算法,但无可避免的产生循环波动,所以这个算法仅在每次取随机数的时间都是不确定的情况下才有效,即把真实世界的真随机性带入到计算机中才可能产生较为理想化的随机。
正如冯诺依曼所说,依靠纯算法来获取真正理想的随机数,是根本不可行。
欢迎关注我的专栏( つ•̀ω•́)つ【人工智能通识】
每个人的智能新时代
如果您发现文章错误,请不吝留言指正;
如果您觉得有用,请点喜欢;
如果您觉得很有用,欢迎转载~
END
网友评论