我接触编程时,就知道有随机函数,在我学过的Dos和Windows编程语言中都的。应该是在系统中调用的。例如在TC2.0 ,在BASIC;到VC6.0,VB,Delphi;VS中的C#。一些久远年代的数据库语言,例如 FoxPor,也都有随机函数。
SQL这货比较专业,应该用不着随机函数。我没有细查,我想是因为用不着所以没有的。略。
但是在单片机和FC/NES里面就会时常用到随机数,在游戏编程还会大量的使用随机函数,但它们没有底层OS系统。有人会反驳:单片机有COS等。我的意思是,在未曾编程前,是没有系统的。单片机的系统也等于是程序本身的一部分,与用户程序生成一个HEX再烧入单片机。即使COS里面也是没有提供随机函数。
结论是,有关单片机和FC\NES编程时的随机函数要各位自己解决了。
(一)关于算法
既然Dos和Windows系统底层存在随机函数,那么我们挖出来反汇编呀。这个想法我立马废掉。我这种非专业人士可没有这么高超的技能。其实在我初学编程时就用过某某学习机,里面的G-BASIC和F-BASIC都有随机函数。要从学习机的ROM里面挖随机函数的本身,相对简单。但是我自认那比重新探讨一个算法更花时间和精力。
旦凡系统(或学习机)提供的随机函数,都是要有一个种子数,种子数本身要求是一个多变的数值,我一般用秒数搭上分钟和日期,混合成一个数。不同日子和时间启动程序,那么种子数就有不同。后面关于“运用”再重新细说。
通过种子数作为第一个迭代,调用随面函数。所得结果,作为下一个迭代,调用随面函数。
(1.1)错误的算法思路
很多人认为,只要写一个混乱的公式,调用之后得到什么结果连自己都不知道。这样就是好用的随机算法了。大家都曾有过这种想法,也去试过。但结果呢。真正使用时,这结果让人感到很不理想。但后大家有没有试过,继续将公式改得更乱。这些我都曾经做过呀。
如果细心分析,不管怎么改,只要是四则运算(包括整除,求余),都会得以下的不良结果:
A)多次迭代后会得到唯一的结果。数学上叫收敛。一个失败的算法。
B)会得到少量几个数字的循环。数学上叫环。例如求一个100以内的随机数,来来回回迭代只得出某几个数的结果。不论迭代多少回结果都一样。这实在令人崩溃。
C)产生的结果足够多,但不知全不全,因为发现某些数的产生机率比较高。例如编游戏时,用随机数产生一个骰子值,但是有几个骰子值没发被随机产生出来。我们用蒙特卡罗法做一个检测,发现某些数的产生机随较高,某些较低甚至不会产生出来。神啊,拿出你的骰子来打救我吧。
最终结论:混乱的公式是错误的随机算法。
(1.2)随机函数的数学性质
困绕我多年的随机函数问题,直到我在大学学习了概率论和数学分析,才有了一些明白。在编写随机函数前,要对随机数有一个数学上的分析。
首先,历遍性;随机数要求规定范围内每一个数都要出现。例如玩飞行棋游戏,不能没有6呀。
第二,机率平均律;随机数要求规定范围内每一个数的出现机率是均等的。每个数的概率
只有这么两个简单的要求。
(1.3)实现算法
计算机电路里面,有一种叫循环移位寄存器。寄存器中的某些位做逻辑运算,再传回输入端。这种算法只有某些公式达到历遍性的,单次迭代不符合机率平均律,而多次迭代统计结果则符合机率平均律。
虽说勉强符合要求,但是求一个历遍性的循环移位寄存器公式比较烧脑。(下次更新再将循环移位寄存器的知识详细补全。)
因为是函数,纯软件方式,则每次迭代的输入与输出必定是唯一对应的(数学上说映射的)。有随机数硬件支持又有不同,例如接收一个频道电台(或一个被干扰的电台信号)的音频采样,每次调用都得到一个未知值呀,不取在映射。这样就不需要算法了。
既然算法的结果是映射的,那么直接用查表法,一个输入就查一个随机表,得到另一个值好了。
马上有人会说,这是伪随机数算法。对,Dos,Windows和学习机上调用的随机函数都是伪随机数。我们的目的,也是达到这一点就O.K.。
查表法的实现过程,用一个格子本,先定义格子的ID,ID要求是规定范围内的值。例如,规定范围1~100,那么ID也是1~100
在第一个格子写上(规定范围内)随意一个数N,再在ID=N的格子写上另一个随意的没有被占格子的(ID值)数M,再到ID=M的格子写上另一个随意的没有被占格子的(ID值)数,如此类推。
也可以用我的工具产生这个表。
我们可以多做几个不同的随机表。
有人会说,这杯具,伪随机数,这跟本就是假的。对于追求真理的人来说,这是绝对不能接受。
我先不管,随机函数就这样,用查随机数表的方式就行。从数学上而言,两个要求都可以勉强达到了。
(二)关于运用
运用的键关在于种子数。上面提到“用秒数搭上分钟和日期,混合成一个数”。对于没有时间系统RTC的单片机和FC/NES而言,行不通。对于单片机,可以用ADC模块,读取一个红外元件经放大后的模拟器,或者一个光敏电阻与固定电阻的分压。对于FC/NES可以用“手柄上启动按扭按下时间长度”作为种子数。
对于将伪随机函数发挥到最极致,可以用多个随机表。而读取随机表也有学问。
可以用迭代式,但是每次都一样,那么可以连续迭代多次。那么是多少次呢?要示求是随机表的元素个数Num的互质数。例如范围是1~100的随机表,元素100个。那么与100互质的数都可以。例如,7,19都行。
迭代比较慢,另一种方法用一个静态变量做指针,随机函数,要输入一个步长,步长值要求也是一个与随机表的元素个数Num互质的数。查表时直接从上次静态变量指针位置向后一个步长,超出范围则取余。这样做也能历遍。
最后,种子数,最好能取3个,用3个随机表互相交错运用,能达到很好的效果。