【发布时间】:2010-12-15 12:52:59
【问题描述】:
随机数生成器是如何工作的? (例如在 C/C++ Java 中)
如何编写自己的随机数生成器? (例如在 C/C++ Java 中)
【问题讨论】:
-
Donald Knuth 在其经典著作《计算机编程的艺术》的第一卷中讨论了随机数生成。我会从那里开始。好吧,我确实从那里开始,很久以前。 :)
标签: random
随机数生成器是如何工作的? (例如在 C/C++ Java 中)
如何编写自己的随机数生成器? (例如在 C/C++ Java 中)
【问题讨论】:
标签: random
还有这个算法:
哦,更严重的是:
随机数生成器使用数学公式将一组数字转移到另一组。例如,如果你取一个常数 N 和另一个数字 n_0,然后取 n mod N 的值(取模运算符),你将得到一个新数字 n_1,看起来像与n_0 无关。现在,使用n_1 重复相同的过程,您将获得另一个号码。您在这里拥有的是看似随机数的(非常糟糕的)生成器。
请记住,我在这里描述的方法是一种玩具方法,不应该用于任何严重的事情。但是,它确实说明了一般原则。
另请注意:
如果所有因
rands 错误而导致结果受到质疑的科学论文都从图书馆的书架上消失,那么每个书架上都会有一个和你的拳头一样大的空隙。
(转述自Numerical recipes 的第 7 章)。对于任何使用随机数生成器进行任何严肃工作的人来说,这是一本必读的文本。
【讨论】:
要记住的一件事是没有“真正的”随机数生成器。它们只是生成对我们这些凡人来说看起来随机的数字。
Linear congruential generator 是最简单的示例之一(也可以实现)。当然,这些数字在你我看来是不可预测的,但它们实际上是在一个有限域内均匀分布的。
当然,有些生成器,例如Blum Blum Shub,即使外行人运用了严格的数学技能和计算能力,也无法预测他的任务,但从根本上说,随机数生成器不是随机的;它们是有规律的和可预测的。
【讨论】:
在过去,我制作它们的方法是从变化非常迅速的系统中获取一些价值,例如系统毫秒计时器。
接下来您要做的就是应用一些公式,该公式将从此“输入”数字生成一个新数字,并将其剪辑到您需要的范围内,例如 0..255:
随机数 = 整数(公式(计时器值))MOD 255
这样,每次调用函数时都会有一个新的“随机”数字。
一个示例公式函数可以是:
公式(x) = ((x XOR 常数) + 常数2) MOD 范围
XOR 曾经是我的最爱之一。
更新:我意识到这个公式是一个非常糟糕的公式,它会生成一组非常可预测的数字。此外,系统计时器作为来源太可预测了。因此,对于大多数应用程序来说,这还不够。如果您需要更好的随机性,请使用比系统计时器和better formulas 更多的源来组合它们。
【讨论】:
我为 Java 找到了这个:
http://www.javamex.com/tutorials/random_numbers/java_util_random_algorithm.shtml
通过谷歌搜索how random functions work java
我确定答案是特定于语言的,但您可以尝试将我的 Google 查询更改为您选择的语言。
【讨论】:
有很多关于它们如何工作的信息......请参阅 Konamiman 的回答并使用 google。
您为什么要编写一个新的随机生成器?你可能不应该尝试这样做......直到你需要一些非常特别的东西。例如,在游戏中,您可以使用产生“公平”随机值的shuffle bag - 看看this interesting question on SO。
我在这里发布这个,因为当我第一次读到它时,我真的很喜欢这个想法和实现:)
【讨论】:
通过使用当前时钟时间的小数点后五位之类的东西来播种这些类型的生成器也很常见,这对我们来说也是随机的。将来自一些混沌系统的输入加在一起,例如天气数据,股票市场数据,再次使用模数,你有很好的种子数。
【讨论】:
这是一些掷骰子的代码,它使用我自己开发的随机数生成器 这个 RNG 中的 pad 总共包含 15 个十六进制值
DIM pad(15) AS INTEGER
CLS
egg$ = "EFCDBA01457FA968"
ghh$ = egg$
nom% = 0
zen% = LEN(ghh$)
WHILE zen% > 0
opp$ = LEFT$(ghh$, 1)
eff% = ASC(opp$)
IF eff% >= 48 AND eff% <= 57 THEN eff% = eff% - 48 ELSE eff% = (eff% - 65) + 10
IF eff% > 15 THEN eff% = eff% - 32
pad(nom%) = eff%
nom% = nom% + 1
zen% = LEN(ghh$) - 1
ypp$ = RIGHT$(ghh$, zen%)
ghh$ = ypp$
WEND
sol& = 0
FOR zyx% = 0 TO 3
sol& = sol& * 16
sol& = sol& + pad(zyx%)
NEXT zyx%
sat% = sol& - 32768
RANDOMIZE sat%
FOR zyx% = 0 TO 15
PRINT HEX$(pad(zyx%));
NEXT zyx%
RANDOMIZE TIMER
respawn:
INPUT "sides per die"; die%
INPUT " number of dice"; dice%
INPUT "number to add to dice roll can be negative"; num%
INPUT "multiplier use 1 if so desired single precision floating point number"; g!
PRINT " hit any key to roll again with these values hit n for new values and q to quit"
PRINT " the number will be added or subtracted first before the multiplier takes effect"
reroll:
sum! = 0
FOR x% = 1 TO dice%
GOSUB rndmz
GOSUB demf
GOSUB drand
k% = INT(dr# * die%) + 1
sum! = sum! + k%
NEXT x%
sum! = (sum! + num) * g!
PRINT "you rolled a :"; sum!
i$ = ""
WHILE i$ = "": i$ = INKEY$: WEND
IF i$ = "n" THEN GOTO respawn
IF i$ = "q" THEN GOTO theend
GOTO reroll
theend:
SYSTEM
END
rndmz: rhet$ = ""
zum% = 0
FOR yxz% = 0 TO 15
FOR zyx% = 0 TO 15
IF zyx% MOD 3 = 0 THEN zum% = (zum% + pad(zyx%)) MOD 16
IF zyx% MOD 3 = 1 THEN zum% = (zum% + 16 - pad(zyx%)) MOD 16
IF zyx% MOD 3 = 2 THEN zum% = (zum% + INT(RND * 16)) MOD 16
NEXT zyx%
rhet$ = rhet$ + HEX$(zum%)
NEXT yxz%
ghh$ = rhet$
RETURN
demf: nom% = 0
zen% = LEN(ghh$)
WHILE zen% > 0
opp$ = LEFT$(ghh$, 1)
eff% = ASC(opp$)
IF eff% >= 48 AND eff% <= 57 THEN eff% = eff% - 48 ELSE eff% = (eff% - 65) + 10
IF eff% > 15 THEN eff% = eff% - 32
pad(nom%) = eff%
nom% = nom% + 1
zen% = LEN(ghh$) - 1
ypp$ = RIGHT$(ghh$, zen%)
ghh$ = ypp$
WEND
FOR zyx% = 0 TO 15
'PRINT HEX$(pad(zyx%));
NEXT zyx%
RETURN
drand: dr# = pad(0)
FOR eff% = 1 TO 15
dr# = dr# / 16
dr# = dr# + pad(eff%)
NEXT eff%
dr# = dr# / 16
RETURN
derf: a# = 1
x# = 1
b# = 1 / (2 ^ .5)
c# = .2500000000000011#
FOR u% = 1 TO 3
y# = a#
a# = (a# + b#) / 2
b# = (b# * y#) ^ .5
c# = c# - x# * (a# - y#) ^ 2
x# = 2 * x#
pi# = ((a# + b#) ^ 2) / (4 * c#)
PRINT pi#
NEXT u%
pi# = pi# + .000000000000015#
PRINT pi#
这里最后通过算法在 3 个循环中将 pi 计算到几乎尽可能多的地方,抱歉它是用古老的基本语言编写的,它是我所知道的唯一编程语言,有可能将它移植到c++ 或 Java
只需仔细查看其逻辑,尤其要密切注意必须使用的启动或播种/初始化程序,否则它将作为一个好的 rng 失败......这是我为具有巨大安全性的游戏目的而开发的不像速度那么令人担忧...
【讨论】:
取决于目标:速度 vs 质量 vs 安全性。
速度和安全性:线性一致性是直截了当的,不依赖于公共来源,密钥的隐私对于加密系统至关重要。
质量:random.org 是真正随机的来源,您每天可以免费获得大约 10000 个 16 位值。
组合几个随机源(最简单的异或)组合它们的属性。
因此,如果您还维护一个真正的随机数池,那么您将拥有所有的速度、质量和安全性。
【讨论】:
https://github.com/fsssosei/Pure_PRNG/tree/main/src/prng_algorithms_package下面是下面算法的实现: 逆同余生成器,LCG64_32_ext,XSM64,Ran64
【讨论】: