关于随机过程的一些记录
语言本身提供的简单的伪随机 一般是 均匀分布的 随机结果,在这样的基础上,我们通过一点简单的封装可以实现几个常见的随机要求:(以lua实现)
一,随机固定个数的不重复结果
在这之中,我们需要一个随机池来辅助随机过程,调用一次随机过程之后将其从随机池中删除,继续进行下次随机过程,最后得到想要的最终集合。理论上,这是选项们的一种排列组合结果。
i. 准备随机池:是一个数组,将你要随机的选项作为元素值,而数组下标是连续的,所以自然能够调用math.random(1,num)这样的函数:[5,8,6,1,7,4,9,5,8,4,6,3,1] 共13个选项。
math.random(1,13)这样就在 1,13之间随机一个数字,然后将其在随机池中对应的选项摘取出来加入不重复结果组。
local rand_poll = {5,8,6,1,7,4,9,5,8,4,6,3,1} -- 随机池
local ret_array = {} -- 作为结果组
local index = 0 -- 一次随机将返回随机池中的下标
for i=1, count , 1 do
-- 调用真正随机规则,得到一个元素 的下标。
index = get_random_one( rand_poll )
-- 将元素加入到返回数组中
table.insert( ret_array , rand_poll[index] )
-- 将元素从随机池中删除
table.remove( rand_poll , index )
end
-- ret_array 是最后的结果组
ii. 随机过程函数:将在随机池的下标中随机一个,以此返回随机池中的元素。
function get_random_one( array )
if #array <=0 then
return false
end
local rand_value = math.random( 1, #array )
return rand_value
end
二,具有概率判断的结果
想要实现的功能是,传入一个概率参数,根据概率判断本次随机是否成功。比如,本次随机一个结果,正确的概率是80%,非正确的概率是20%。
function get_one_by_probability( probability )
-- probability is 浮点数
local boundary = probability * 100
--百分比,随机点数在[1,100]>均匀分布
local rand_value = math.random( 1 , 100 )
if rand_value < boundary then
return true -- 随机到的点数在 范围内,结果算正确
else
return false -- 随机到的点数 超出范围,本次结果非正确
end
end
三,具有随机库权重的实现过程
在随机过程一开始,需要通过随机池中的全部元素的权重累积得到权重的分配额,最终,权重总和作为随机上限。随机出结果之后,比对权重分配额,得到随机池中的对应id。
i. 准备条件:
-- array = {5,8,6,1,7,4,9,5,8,4,6,3,1}
-- obj_weight = {
{id=1, weight=2} ,
{id=2, weight=2} ,
{id=3, weight=1} ,
{id=4, weight=2} ,
{id=5, weight=10} ,
{id=6, weight=9} ,
{id=7, weight=23}
}
ii. 随机过程主函数
function get_random_one( array )
--整理得到权重的数组,及权重总和,weights不是数组。
local weights = {}
local total_weight = 0
for i=1, #array , 1 do
-- 取表中权重,并加和总权重
-- the same can be : ( before_open , after_close ]
weights[ array[i] ] = {}
weights[ array[i] ].before_open = total_weight
total_weight += get_obj_weight( array[i] ) -- 得到本元素的出现权重
weights[ array[i] ].after_close = total_weight
weights[ array[i] ].index = i
end
-- 得到随机值
-- local rand_value = math.random( 1, total_weight )
-- 换算随机值代表的object_id, 每个区间是不重合无交集的。
for key , value in pairs( weights ) do
if value.before_open < rand_value and rand_value <= value.after_close then
return value.index
end
end
end
iii. 取权重子函数
function get_obj_weight( id )
return obj_weight[id].weight
end
四,另一方面 随机种子:
(这个不算是 随机函数的再封装,应该是随机过程的另一种需求)
就是,系统中随机的结果能够形成一个固定的序列,即,输入一个种子,能够,得到一个肯定的结果。这在调试和时候能够使得随机结果固定,方便调试。当然还有另一种用途:当每次的随机结果作为某种数据的依据,而这些数据是需要记录下来的,那么为了节省空间,我们只需记录随机时的种子,每次需要随机时,我们取得相应的随机种子即可得到当时的随机结果,即可得到一些想要的数据。
比如,要求每次从所有玩家中,随机10个不同玩家id。得到结果之后,去数据库中返回这个玩家的所有属性。需要记录下这10个玩家,等待下一个命令。
这样,我们需要记录一个开始的种子,即随机出第一个玩家的种子。这样顺着再随机10次,得到的结果序列是一样的。
i. 那么这个利用种子得到随机数的实现过程如下:
local next_rand = randint(cur_rand)
local rand_value = ( next_rand )%( ceiling ) + 1
-- 上面一句相当于math.random( 1, ceiling )
-- 上面的过程相当于 在区间 [1, ceiling] 中得到一个数字
ii. 而其中的randint( rand_seed )函数是由C语言写成的库函数。
-- 这是函数的具体内容,函数返回的是下一个种子。
int next_rand = abs(cur_rand * 16807) % 2147483647;
-- 函数内部 的 2个数组:16807 ;2147483674是为什么。
--(我也不太清楚,大概的估计是有什么统计学的意义吧。如果有高手知道,烦请指点一二。)