[记录点滴] 一个解决Lua 随机数生成问题的办法

摘要:
[记录]解决Lua随机数生成问题的方法0x00摘要这是一个开发中的简短记录,具体涉及知识点:Lua,随机数。0x01背景Lua语言需要两个函数来生成随机数:数学。randomseed:用法是接收整数n作为随机序列的种子。0x02问题2.1 Lua随机数函数Lua的随机数函数有一个问题:第一个随机数总是固定的,并且通常是最小值。如果种子很小或者种子变化很小,那么产生的随机序列仍然非常相似。线性同余法是一种生成伪随机数的方法。
[记录点滴] 一个解决Lua 随机数生成问题的办法

0x00 摘要

本文是开发中的简略记录,具体涉及知识点有:Lua,随机数。

0x01 背景

Lua语言生成随机数需要用到两个函数:

  • math.randomseed(n) : 用法是 接收一个整数n作为随即序列的种子。
  • math.random([n [,m]]) : 用法有三种:
    • random(),产生[0, 1)之间的浮点随机数。
    • random(n),产生[1, n]之间的整数。
    • .random(m, n),产生[n, m]之间的整数。

0x02 问题

2.1 Lua随机数函数问题

Lua语言的随机数函数存在问题:

  1. 第一个随机数总是固定,而且常常是最小的那个值
  2. 如果 seed 很小或者seed 变化很小,产生的随机序列仍然很相似。
  3. 如果很短的时间内多次运行这个程序,那么你得到的随机序列会是几乎不变的。

原因是LUA的random只是封装了C的rand函数,使得random函数有一定的缺陷,

2.2 C语言随机数函数问题

其实计算机产生的随机数都是依照事先写好的算法执行出来的,行为是可以预测的,所以计算机产生的随机数都不是真正意义上的随机数,只是伪随机数,是以一个真值(也称为种子)为初始条件,然后用一定的算法不停迭代产生随机数。

C语言 rand的内部是用线性同余法做的,因为其周期特别长,所以在一定范围内可以看成是随机的。

线性同余方法(LCG)是一种产生伪随机数的方法。它是根据递归公式实现:

RandSeed = (A * RandSeed + B) % M

线性同余法最重要的是定义了三个整数,乘数 A、增量 B和模数 M,其中A, B, M是产生器设定的常数。 LCG的周期最大为 M,但大部分情况都会少于M。要令LCG达到最大周期,应符合以下条件:

  • B,M互质;
  • M的所有质因数都能整除A-1;
  • 若M是4的倍数,A-1也是;
  • A,B,N[0]都比M小;
  • A,B是正整数。

0x03 解决方案

问题的解决方案就是:让用户使用randomseed先设一个随机种子。比如在服务器启动的时候设置一个随机种子,让系统产生的随机序列不相同。

3.1 移位轮转 + 线性同余

一种常见的办法是以 time 函数返回的秒数为基准。但是因为如果需要短期内频繁使用随机数,这个方法不可行,因为容易产生类似数字,所以就把 time返回的数值字串倒过来(低位变高位), 再取高位几位。这样即使 time变化很小, 但是因为低位变了高位, 种子数值变化却很大,就可以使伪随机序列生成的更好一些。

这其实是一种 “移位轮转“ 的思想

math.randomseed(tostring(os.time()):reverse():sub(1, 6))

3.2 Linux随机种子

在linux下,我们可以使用 /dev/random以及/dev/urandom产生随机种子。

其原理是利用当前系统的熵池来计算出一定数量的随机比特,其中熵池是根据当前系统的“环境噪音”,它是由很多参数共同评估的,如内存的使用,文件使用量等等,环境噪音直接影响着所产生的随机种子的有效性。

/dev/random与/dev/urandom之间存在区别:

  • urandom即”unlocked random”,,每次打开并读取/dev/urandom时,会从熵池中随机返回所需要的字节数。/dev/urandom的读取操作不会阻塞,因为它会重复使用熵池中的数据以产生随机数;
  • /dev/random则是每次读之前去检查熵池是否为空,若为空,则需要阻塞并去更新熵池。

对于我们来说,需要阻塞总是不好的,因此 urandom 更加理想

3.3 移位轮转 + 线性同余 + Linux随机种子

我们可以采用的是在之前办法上,加入Linux随机种子,代码如下:

local _M = {}

function _M.random_seed()
    local in_file = io.open("/dev/urandom", "r")
    if in_file ~= nil then
    	  local d= in_file:read(4)
        math.randomseed(os.time() + d:byte(1) + (d:byte(2) * 256) + (d:byte(3) * 65536) + (d:byte(4) * 4294967296))
    else
        math.randomseed(tostring(os.time()):reverse():sub(1, 7))            
    end   
end

return _M

当然也可以再结合起来设置

math.randomseed(tostring(os.time()):reverse():sub(1, 6)+ d:byte(1) + (d:byte(2) * 256) + (d:byte(3) * 65536) + (d:byte(4) * 4294967296))

0xFF 参考

线性同余方法(LCG)产生随机数

Linux下随机数生成的函数与常见方法

免责声明:文章转载自《[记录点滴] 一个解决Lua 随机数生成问题的办法》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【基础组件2】Flume入门(二)Agentlinux(centos 7) 下安装nodejs下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

CentOS5.5挂载本地ISO镜像

操作步骤: 一、挂载iso文件到挂载点 [root@server ~ ]# mount -o loop /mnt/iso/CentOS5.iso /mnt/cdrom 二、查看挂载状态 [root@server ~ ]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGrou...

Linux_LVM Couldn't find device with uuid

Linux LVM commands result in Couldn't find device with uuid Couldn't find all physical volumes for volume group Help!  Commands like 'lvs', 'lvdisplay', 'vgdisplay', and 'pvsca...

Linux下smi/mdio总线驱动

Linux下smi/mdio总线驱动 韩大卫@吉林师范大学 MII(媒体独立接口), 是IEEE802.3定义的以太网行业标准接口, smi是mii中的标准管理接口, 有两跟管脚, mdio 和mdc ,用来现实双向的数据输入/输出和时钟同步。mdio主要作用用来配置/读取phy的寄存器, 实现监控作用。 Smi总线也就是mdio总线。...

c#dev操作读取excel方法

一:使用spreadsheetControl1 方法 1:打开excel; private void barButtonItem1_ItemClick(objectsender, DevExpress.XtraBars.ItemClickEventArgs e) { //if (Convert.ToInt32...

V4L2驱动的移植与应用(三)

三、V4L2的demo capture.c是官方示例程序。 capture.c 程序中的 process_image 函数: capture.c 程序主要是用来演示怎样使用 v4l2 接口,并没有对采集到的视频帧数据做任何实际的处理,仅仅用 process_image 函数表示了处理图像的代码位置。 process_image 函数只有一个参数,就是存储...

CentOS 7上的主机名设置和基本网络管理

主机名 CentOS 6 查看。 # hostname 设置。 # hostname NEW_NAME 设置完成后,xshell的会话中不会显示NEW_NAME,可通过重新登录会话来显示。不过实际上我们在设置完成后立即再次查看的话,是已经设置为NEW_NAME了。 这样子设置,是临时有效,系统重启之后,就恢复为之前的主机名了。想要永久有效的话,应该将NEW...