随机数(random)
一、随机数概述
随机数是专门的随机试验的结果. 在统计学的不同技术中需要使用随机数,比如在从统计总体中抽取有代表性的样本的时候,或者在将实验动物分配到不同的试验组的过程中,或者在进行蒙特卡罗模拟法计算的时候等等. 产生随机数有多种不同的方法.这些方法被称为随机数生成器.随机数最重要的特性是它在产生时后面的那个数与前面的那个数毫无关系.
根据密码学原理,随机数的随机性检验可以分为三个标准:
1.统计学伪随机性.统计学伪随机性指的是在给定的随机比特流样本中,1 的数量大致等于 0 的数量,同理,“10”“01”“00”“11”四者数量大致相等.类似的标准被称为统计学随机性.满足这类要求的数字在人类“一眼看上去”是随机的.
2.密码学安全伪随机性.其定义为,给定随机样本的一部分和随机算法,不能有效的演算出随机样本的剩余部分.
3.真随机性.其定义为随机样本不可重现.实际上只要给定边界条件,真随机数并不存在,可是如果产生一个真随机数样本的边界条件十分复杂且难以捕捉(比如计算机当地的本底辐射波动值),可以认为用这个方法演算出来了真随机数.
相应的,随机数也分为三类:
1.伪随机数:满足第一个条件的随机数.
2.密码学安全的伪随机数:同时满足前两个条件的随机数.可以通过密码学安全伪随机数生成器计算得出.
3.真随机数:同时满足三个条件的随机数.
我们知道,随机数是通过一些复杂的数学算法得到的,那么 随机种子就是这些随机数的初始值.
一般计算机里面产生的随机数都是伪随机数. 伪随机数,也是就一个一直不变的数,所以我们可以通过输入随机种子得到一个初始固定的随机数.
例如,随机数的种子设置为 x,产生的随机数序列为: [123, 456,789,...],那么只要输入相同的随机种子 x,就能得到相同的随机数序列:[123, 456,789,...],否则就无法还原出随机数序列,也就无法通过密码学安全的随机数生成器计算出随机数序列.
二、演示功能概述
本文章通过 Air8101 的核心板烧录底层固件(LuatOS-SoC_V10001_Air8101_20241224_153939.soc)和修改 LuatOS 示例代码
PROJECT = "cryptodemo"
VERSION = "1.0.0"
-- sys库是标配
_G.sys = require("sys")
sys.taskInit(function()
sys.wait(1000)
-- ---------------------------------------
log.info("随机数测试")
math.randomseed(os.time())
for i=1, 10 do
sys.wait(100)
log.info("crypto", "真随机数",string.unpack("I",crypto.trng(4)))
log.info("crypto", "伪随机数",math.random()) -- 输出的是浮点数,不推荐
log.info("crypto", "伪随机数",math.random(100)) --输出1-100之间随机数
log.info("crypto", "伪随机数",math.random(1, 65525)) -- 不推荐
end
log.info("crypto", "ALL Done")
sys.wait(100000)
end)
sys.run()
三、硬件准备
“古人云:‘工欲善其事,必先利其器。’在深入介绍本功能示例之前,我们首先需要确保以下硬件环境的准备工作已经完成。”
参考:硬件环境清单,准备以及组装好硬件环境。
四、软件环境
“凡事预则立,不预则废。”在详细阐述本功能示例之前,我们需先精心筹备好以下软件环境。
1. Luatools 工具;
2. 内核固件文件(底层 core 固件文件):LuatOS-SoC_V10001_Air8101.soc;参考项目使用的内核固件;
3. luatos 需要的脚本和资源文件
脚本和资源文件:https://gitee.com/openLuat/LuatOS-Air8101/tree/master/demo/RANDOM
lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;
准备好软件环境之后,接下来查看如何烧录项目文件到 Air8101 开发板,将本篇文章中演示使用的项目文件烧录到 Air8101 开发板中。
五、API 说明
5.1 crypto.trng(len)
生成真随机数
参数
返回值
例子
-- 生成 32 位随机数 ir
local r = crypto.trng(4)
local _, ir = pack.unpack(r, "I")
5.2 math.random([n [,m]])
生成伪随机数
参数
返回值
例子
--无参调用,产生[0, 1)之间的浮点随机数.
local num = math.random()
--一个参数 n,产生[1, n]之间的整数
local num = math.random(n)
--两个参数,产生[n, m]之间的整数.
local num = math.random(n,m)
5.3 math.randomseed(n)
用法:设置一个整数 n 作为随机序列的种子.
参数
返回值
无
例子
--得到了正常的随机数
math.randomseed(os.time())
for i=0, 10 do
local n = math.random(10)
end
--一系列相同的数,这种在一个 for 循环内设置随机数的写法 seed 几乎是一样导致.
for i=0, 10 do
math.randomseed(os.time())
local n = math.random(10)
end
六、功能演示
6.1 示例
示例代码
PROJECT = "cryptodemo"
VERSION = "1.0.0"
-- sys库是标配
_G.sys = require("sys")
sys.taskInit(function()
sys.wait(1000)
-- ---------------------------------------
log.info("随机数测试")
math.randomseed(os.time())
for i=1, 10 do
sys.wait(100)
log.info("crypto", "真随机数",string.unpack("I",crypto.trng(4)))
log.info("crypto", "伪随机数",math.random()) -- 输出的是浮点数,不推荐
log.info("crypto", "伪随机数",math.random(100)) --输出1-100之间随机数
log.info("crypto", "伪随机数",math.random(1, 65525)) -- 不推荐
end
log.info("crypto", "ALL Done")
sys.wait(100000)
end)
sys.run()
对应 Log
[2024-12-31 14:56:59.362] I/pm poweron: Power/Reset
[2024-12-31 14:56:59.362] D/main poweron reason 0
[2024-12-31 14:56:59.362] D/main UID: 4C4D190C41
[2024-12-31 14:56:59.374] cal:W(10):temp in otp is:576
[2024-12-31 14:56:59.470] D/main STA MAC: C8478CDCA752
[2024-12-31 14:56:59.470] D/main AP MAC: C8478CDCA753
[2024-12-31 14:56:59.470] D/main BLE MAC: C8478CDCA754
[2024-12-31 14:56:59.470] D/main ETH MAC: C8478CDCA755
[2024-12-31 14:56:59.470] D/fota 没有OTA参数区数据,正常启动
[2024-12-31 14:56:59.470] I/main LuatOS@Air8101 base 24.10 bsp V10001 32bit
[2024-12-31 14:56:59.470] I/main ROM Build: Dec 24 2024 15:39:19
[2024-12-31 14:56:59.470] D/main loadlibs luavm 204792 14256 14336
[2024-12-31 14:56:59.486] D/main loadlibs sys 297944 95512 95512
[2024-12-31 14:57:00.467] I/user.随机数测试
[2024-12-31 14:57:00.576] I/user.crypto 真随机数 863054682 5
[2024-12-31 14:57:00.576] I/user.crypto 伪随机数 0.1461691
[2024-12-31 14:57:00.576] I/user.crypto 伪随机数 71
[2024-12-31 14:57:00.576] I/user.crypto 伪随机数 35596
[2024-12-31 14:57:00.685] I/user.crypto 真随机数 -1914540365 5
[2024-12-31 14:57:00.685] I/user.crypto 伪随机数 0.3040537
[2024-12-31 14:57:00.685] I/user.crypto 伪随机数 41
[2024-12-31 14:57:00.685] I/user.crypto 伪随机数 36802
[2024-12-31 14:57:00.793] I/user.crypto 真随机数 984246320 5
[2024-12-31 14:57:00.793] I/user.crypto 伪随机数 0.3008735
[2024-12-31 14:57:00.793] I/user.crypto 伪随机数 38
[2024-12-31 14:57:00.793] I/user.crypto 伪随机数 36487
[2024-12-31 14:57:00.885] I/user.crypto 真随机数 1723438864 5
[2024-12-31 14:57:00.885] I/user.crypto 伪随机数 0.1987624
[2024-12-31 14:57:00.885] I/user.crypto 伪随机数 14
[2024-12-31 14:57:00.885] I/user.crypto 伪随机数 61993
[2024-12-31 14:57:00.994] I/user.crypto 真随机数 -1719318510 5
[2024-12-31 14:57:00.994] I/user.crypto 伪随机数 0.6601917
[2024-12-31 14:57:00.994] I/user.crypto 伪随机数 15
[2024-12-31 14:57:00.994] I/user.crypto 伪随机数 29535
[2024-12-31 14:57:01.086] I/user.crypto 真随机数 497496824 5
[2024-12-31 14:57:01.086] I/user.crypto 伪随机数 0.8998879
[2024-12-31 14:57:01.086] I/user.crypto 伪随机数 95
[2024-12-31 14:57:01.086] I/user.crypto 伪随机数 31944
[2024-12-31 14:57:01.196] I/user.crypto 真随机数 1709671847 5
[2024-12-31 14:57:01.196] I/user.crypto 伪随机数 0.9402610
[2024-12-31 14:57:01.196] I/user.crypto 伪随机数 31
[2024-12-31 14:57:01.196] I/user.crypto 伪随机数 4228
[2024-12-31 14:57:01.289] I/user.crypto 真随机数 503134195 5
[2024-12-31 14:57:01.289] I/user.crypto 伪随机数 0.6523194
[2024-12-31 14:57:01.289] I/user.crypto 伪随机数 91
[2024-12-31 14:57:01.289] I/user.crypto 伪随机数 896
[2024-12-31 14:57:01.396] I/user.crypto 真随机数 -49190906 5
[2024-12-31 14:57:01.396] I/user.crypto 伪随机数 0.1757935
[2024-12-31 14:57:01.396] I/user.crypto 伪随机数 6
[2024-12-31 14:57:01.396] I/user.crypto 伪随机数 31565
[2024-12-31 14:57:01.490] I/user.crypto 真随机数 -1866827370 5
[2024-12-31 14:57:01.490] I/user.crypto 伪随机数 0.3093695
[2024-12-31 14:57:01.490] I/user.crypto 伪随机数 18
[2024-12-31 14:57:01.490] I/user.crypto 伪随机数 42802
[2024-12-31 14:57:01.490] I/user.crypto ALL Done
七、总结
通过本章内容的学习,你可以学习到有关随机数的函数,例如:crypto.trng(len)、math.random([n [,m]])、math.randomseed(n) 等函数.
八、扩展
8.1 注意事项
需要注意的是,Lua 中的随机数算法可能存在一些问题.例如,math.random(1, 3276700)返回的值最后两位必为 0,这是由于 Lua 本身的随机函数算法决定的.因此,在使用 Lua 生成随机数时,应当注意这些潜在的限制,并尽可能通过设置合适的随机种子来避免生成可预测的随机数序列.
通过以上方法,可以在 Lua 中有效地生成随机数,并确保每次运行程序时都能得到不同的随机数序列,从而满足各种需要随机性的应用场景.
8.2 设置随机数种子
为了确保每次运行程序时都能生成不同的随机数序列,需要使用 math.randomseed 来设置一个随机种子.通常,使用系统时间 os.time()作为种子是一个简单而有效的方法:
math.randomseed(os.time())
然而,如果程序在很短的时间内多次运行,可能会发现生成的随机数序列几乎不变.这是因为 os.time()返回的是秒级的时间戳,不够精确.为了解决这个问题,可以将时间戳转换为字符串,然后反转并截取高位数字作为种子:
math.randomseed(tonumber(tostring(os.time()):reverse():sub(1, 7)))
这种方法通过提高种子数值的变化量,使得即使在短时间内多次运行程序,也能产生更好的伪随机序列.