zoukankan      html  css  js  c++  java
  • [记录点滴] 一个解决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下随机数生成的函数与常见方法

  • 相关阅读:
    Django的cmdb探索与开发(二)
    Django的cmdb探索与开发(一)
    通过itchat调用zabbixAPI实现微信确认zabbix告警
    zabbix自定义脚本监控docker容器学习及记录
    HTML5的三种存储方式以及区别
    Less基本语法的使用方法
    css/less画三角形
    前端性能优化
    前端实时可视化开发工具
    Sublime txt 安装、查看、升级、删除插件
  • 原文地址:https://www.cnblogs.com/rossiXYZ/p/13172155.html
Copyright © 2011-2022 走看看