Lua获取随机数Lua获取随机数函数:math.random ([m, n]) 该函数有三种⽤法
1.random():返回0到1之间的⼀个伪随机数
2.random(n):返回1到n之间的伪随机整数
3.random(m, n):返回m到n之间的伪随机整数
⽐如计算100次1到10的随机数:
电解离子接地棒local res = {}
for var=1, 100 do
local num = math.random(10)
读日志if(res[num] == nil) then
res[num] = 0
end
res[num] = res[num] + 1
水力模块end
for var=1, 100 do
if(res[var] ~= nil) then
print("num=" .. var .. ", num=" .. res[var])
end
end
运⾏结果:
num=1, qty=11
num=2, qty=10
num=3, qty=8
num=4, qty=11
num=5, qty=9
num=6, qty=12
num=7, qty=10
num=8, qty=10
num=9, qty=11
num=10, qty=8
但是执⾏⼏次后发现,每次的结果都是⼀样的,看起来这种随机数计算的结果都是⼀样的。
也许很多⼈会奇怪为什么使⽤LUA的时候,第⼀个随机数总是固定,⽽且常常是最⼩的那个值,下⾯我就简要的说明⼀下吧,说得不好,还请谅解。我现在使⽤的4.0版本的LUA,看的代码是5.0的,呵呵 LUA4.0版本中的⾃带函数库中有两个关于随机数的函数,⼀个是random,⼀个是randomseed。random有两个参数,⽤来设置随机数的范围,⽐如random(1,100)设置随机数的范围为1⾄100之间。由于C中所产⽣的随机序列是固定的,并且第⼀个随机数⽐较⼩,只有41。LUA重新设计了random函数,使得它可以产⽣范围固定的随机数,但由于LUA的random只是封装了C的rand函数,使得random函数也有⼀定的缺陷,那就是如果random的两个输⼊参数的值相差很⼩的时候,那么随机序列的第⼀个随机数就会和第⼀个输⼊参数很接近,⽐如第⼀次调⽤random(1,100)的时候,返回值肯定是1,只有相差⼤于799时,如random(1,800)第⼀次调⽤才会返回2,也是很接近1。 由于这个原因,为了实现真正的随机,那么第⼀次就不能让玩家调⽤random函数,不然玩家就可以获得⼀些低概率的东西了。⽐如if random(1,100) == 1 then ...... do,看起来是1%的的概率,但是第⼀次执⾏的时候是100%成⽴的,存在⼀定的隐患。解决这个问题的⽅法有两个,⼀就是第⼀次random函数不能让玩家执⾏,⼆就是使⽤randomseed先设⼀个随机种⼦。对于第⼀种⽅法,可能还是有⼀定的风险,毕竟随机序列还是固定的,玩家第⼀次调⽤random的时候还是得到有规律的返回值。第⼆种⽅法⽐较安全,在服务器启动的时候设置⼀个随机种⼦,让系统产⽣的随机序列不相同,但使⽤randomseed的时候也还要注意⼀个问题,那就是做种⼦的数要⾜够的⼤,⼤于10000就⾏了。不然randomseed所产⽣的随机序列的第⼀个值还是很⼩。原因是randomseed是直接封装了C的srand,如果种⼦的值太⼩,那么srand所产⽣的序列和默认序列(srand(1)所产⽣的序列)是相差不⼤的,序列的第⼀个值还是很⼩。 因此,只要在服务器启动的时候调⽤⼀下randomseed(GetTime())就可以解决这个问题了。
还要补充⼀下,LUA中产⽣随机数的算法还是有⼀些问题,⽐如执⾏random(1,3276700),它返回的值最后两位必为0。这是由LUA本⾝的随机函数算法决定的。
还是简要介绍⼀下LUA中random函数的实现⽅法吧,主要由源码中的下⾯两⾏实现:
lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
lua_pushnumber(L, (int)floor(r*(u-m+1))+m);
其中m为random函数的第⼀个参数,u为第⼆个参数。由上⾯的代码可以看出,如果u-l太⼩,那么当r也很⼩的时候,r*(u-m+1)就会很⼩(⼩于1),那么再经过floor运算,最经结果就是m。这就可以解释为什么random 产⽣的第⼀个随机数常常会很接近m。再来看看当m为0,u为327670的时候会怎样。在上⾯的代码⾥,RAND_MAX是⼀个宏,它的值是32767,也就是C语⾔中rand函数可以返回的最⼤值(不同的操作系统可能会有不⼀样的最⼤值)。当m为0,u为327670的时候,那么返回值就是floor(r*(327671)+0),我们再假设LUA与平台⽆关并且rand不会返回32767(上⾯⽤%避免了这个问题),那么r就可以简化为rand()/RAND_MAX,代⼊上式为floor(rand()*327671/32767)+0,就算rand()的返回值是32766,最终的结果也只有,经过floor运算后,最后那位数必为0。
呵呵,我叫这样的随机数就伪随机数中的伪随机数。实际上⾯的公式是不允许化简的,即不能简单地把r代⼊r*(u-m+1),⾄于为什么,呵呵,因为r的值并不是rand()/RAND_MAX的值,r是double类型的,所以它只是⼀个和rand()/RAND_MAX很接近的数。
Lua ⽣成随机数需要⽤到两个函数:
math.randomseed(xx), math.random([n [, m]])
1. math.randomseed(n) 接收⼀个整数 n 作为随机序列种⼦。
2. math.random([n [, m]]) 有三种⽤法: ⽆参调⽤, 产⽣ (0,1) 之间的浮点随机数; 只有参数 n, 产⽣ 1-n 之间的整数; 有两个参数 n, m, 产⽣ n-m 之间的随机整数
对于相同的随机种⼦, ⽣成的随即序列⼀定是相同的。所以程序每次运⾏, 赋予不同的种⼦很重要。很⾃然想到使⽤系统时间作为随机种⼦,即: math.randomseed(os.time())
----然后不断产⽣随机数
for i=1, 5 do
print(math.random())
end
ct二次过电压保护器但是问题出来了, 如果程序运⾏时间, 你⼜在很短的时间内多次运⾏这个程序,那么你得到的随机序列会是⼏乎不变的。 像这样:
>lua -e "io.stdout:setvbuf 'no'" "test.lua"
0.71141697439497
0.060121463667714
循环水旁滤器0.067506942960906
0.8607745597705
0.60652485732597
>Exit code: 0
>lua -e "io.stdout:setvbuf 'no'" "test.lua"
0.71141697439497
0.060121463667714
0.067506942960906
0.8607745597705
0.60652485732597
>Exit code: 0
>lua -e "io.stdout:setvbuf 'no'" "test.lua"
流媒体播放0.7115085299234
0.38813440351573
0.6127201147496
0.59511093478195
0.9212927640614
>Exit code: 0
可以看到前两次运⾏的随机数都是⼀样的。究其原因,就是 os.time() 返回的时间是秒级的, 不够精确, ⽽ random() 还有个⽑病就是如果 seed 很⼩或者seed 变化很⼩,产⽣的随机序列仍然很相似。⽐如:
math.randomseed(100)
print(math.random(1000))
math.randomseed(102)
print(math.random(1000))
两次赋予的 seed 分别是 100, 102 但是random ⽣成的第⼀个随机数却是⼀样的。因此“短时间内多次运⾏程序” 这样的需求下
os.time 还真不⼤好。可是⼜没有⽐ time 函数更⽅便的种⼦⽣成器, 怎么办呢?
可以这样:
math.randomseed(tostring(os.time()):reverse():sub(1, 6))
就是把 time返回的数值字串倒过来(低位变⾼位), 再取⾼位6位。 这样, 即使 time变化很⼩, 但是因为低位变了⾼位, 种⼦数值变化却很⼤,就可以使伪随机序列⽣成的更好⼀些