【发布时间】:2012-11-10 11:20:34
【问题描述】:
我想手动生成随机数。我知道每种语言都有 rand 或 random 函数,但我很想知道它是如何工作的。 有人有代码吗?
【问题讨论】:
标签: random
我想手动生成随机数。我知道每种语言都有 rand 或 random 函数,但我很想知道它是如何工作的。 有人有代码吗?
【问题讨论】:
标签: random
POSIX.1-2001 给出了 rand() 和 srand() 的以下实现示例,当需要在两台不同的机器上使用相同的序列时可能很有用。
static unsigned long next = 1;
/* RAND_MAX assumed to be 32767 */
int myrand(void) {
next = next * 1103515245 + 12345;
return((unsigned)(next/65536) % 32768);
}
void mysrand(unsigned seed) {
next = seed;
}
【讨论】:
请看以下内容:
Linear Congruential Generator - Java 中也使用的一种流行方法
List of Random Number Generators
这里还有一个link,详细说明了 LCG 在 Java 的 Random 类中的使用
【讨论】:
static void Main()
{
DateTime currentTime = DateTime.Now;
int maxValue = 100;
int hour = currentTime.Hour;
int minute = currentTime.Minute;
int second = currentTime.Second;
int milisecond = currentTime.Millisecond;
int randNum = (((hour + 1) * (minute + 1) * (second + 1) * milisecond) % maxValue);
Console.WriteLine(randNum);
Console.ReadLine();
}
上面显示了一段非常简单的代码来生成随机数。它是一个用 C# 编写的控制台程序。如果您了解任何类型的基本编程,这应该是可以理解的,并且很容易转换为所需的任何其他语言。
DateTime 只接受当前日期和时间,大多数编程语言都有这样做的功能。
小时、分钟、秒和毫秒变量将日期时间值分解为其组成部分。我们只对这些部分感兴趣,所以可以忽略一天。同样,在大多数语言中,日期和时间通常显示为字符串。在 .Net 中,我们有一些工具可以让我们轻松解析这些信息。但是在大多数其他时间以字符串形式表示的语言中,将字符串解析为您想要的部分并将它们转换为它们的数字并不是太困难。即使是最古老的语言,通常也会提供这些设施。
种子本质上给了我们一个总是变化的起始数字。传统上,您只需将此数字乘以 0 到 1 之间的十进制值即可。
upperRange 定义最大值。所以生成的数字永远不会超过这个值。它也永远不会低于 0。所以没有 ngatives。但如果你想要否定,你可以手动否定它。 (乘以 -1)
实际变量 randNumis 保存你感兴趣的随机值。
诀窍是在将种子除以上限之后得到余数(模数)。余数总是小于除数,在这种情况下为 100。简单的数学告诉你,余数不能大于除数。因此,如果除以 10,余数就不能大于 10。在这种情况下,正是这个简单的定律让我们得到了介于 0 和 100 之间的随机数。
console.writeline 只是将它输出到屏幕上。
console.readline 只是暂停程序以便您可以看到它。
这是一段非常简单的生成随机数的代码。如果你每天在完全相同的间隔运行这个程序(但你必须在相同的小时、分钟、秒和毫秒)运行超过 1 天,你将开始一次又一次地生成相同的数字集额外的一天。这是因为它与时间有关。那就是生成器的分辨率。所以如果你知道这个程序的代码,以及它运行的时间,你就可以预测生成的数字,但这并不容易。这就是我使用毫秒的原因。仅使用秒或分钟来了解我的意思。所以你可以写一个表格,显示1什么时候进去,0出来,2什么时候进去,0出来等等。然后,您可以预测每秒的输出以及生成的数字范围。增加分辨率(通过增加变化的数字)越多,获得可预测模式所需的时间就越长。这种方法对于大多数人来说已经足够好了。
这是为基本游戏生成随机数的老式方法。它需要快速且简单。这是。这也突出了为什么随机数生成器不是真正随机而是伪随机的原因。
我希望这是对您问题的合理回答。
【讨论】:
我假设您的意思是伪随机数。我所知道的最简单的一个(通过在旧机器上编写电子游戏游戏)是这样工作的:
种子=种子*5+1;
每次调用 random 时都这样做,然后使用任意数量的低位。 *5+1 有一个很好的属性 (IIRC),可以在重复之前点击所有可能性,无论您查看多少位。
当然,缺点是它的可预测性。但这在游戏中并不重要。我们为各种各样的事情疯狂地抓取随机数字,而你永远不知道下一个数字是什么。
并行执行几项类似的操作,然后合并结果。这是一个线性同余生成器。
【讨论】:
http://en.wikipedia.org/wiki/Random_number_generator
描述不同类型的随机数生成器以及它们是如何创建的。
【讨论】:
你好!
手动是指“不使用计算机”还是“编写自己的代码”?
如果不使用计算机,您可以使用骰子、袋子里的数字以及电视上看到的所有这些方法,当他们选择球队、赢得宾果系列等时。拉斯维加斯充满了流程中使用的这些方法(游戏)旨在为您提供糟糕的赔率和投资回报率。您还可以获得很棒的 RAND 书籍并转到随机选择的页面:
http://www.amazon.com/Million-Random-Digits-Normal-Deviates/dp/0833030477
(另外,为了消遣,请阅读评论)
要编写自己的代码,您需要考虑为什么不使用系统提供的 RNG 还不够好。如果您使用的是现代操作系统,它将有一个可用于用户程序的 RNG,对于您的应用程序来说应该足够好。
如果您真的需要实现自己的,可以使用大量生成器。对于非安全用途,您可以查看 LFSR 链、同余生成器等。无论您需要什么分布(均匀、正态、指数等),您都应该能够找到算法描述和带有实现的库。
对于安全性使用,您应该查看诸如 Yarrow/Fortuna、NIST SP 800-89 指定的 PRNG 和 RFC 4086 之类的东西,以获得提供 PRNG 所需的良好熵源。或者更好的是,使用操作系统中应该满足安全 RNG 要求的那个。
实现 RNG 可能是一个有趣的练习,但很少需要。除非用于玩具应用,否则不要发明自己的算法。不要,重复不要为安全应用程序发明 RNG(例如生成加密密钥),至少除非您进行一些 seripus 阅读和调查。你会感谢我的(我希望)。
【讨论】:
希望我不是多余的,因为我没有阅读所有链接,但我相信你可以非常接近 true 随机生成器。如今的系统通常非常复杂,以至于即使是最优秀的极客也需要大量时间来了解内部发生的事情 :)
只要敞开心扉,想想是否可以监控某个全球系统属性,用它来播种...选择一个网络数据包(不适合您?)并从其内容中计算“某些东西”并使用它来播种...等等。
您可以设计最适合您需要的所有提示;)
【讨论】:
Mersenne twister 的周期很长 (2^19937-1)。
这是一个非常基本的 C++ 实现:
struct MT{
unsigned int *mt, k, g;
~MT(){ delete mt; }
MT(unsigned int seed) : mt(new unsigned int[624]), k(0), g(0){
for (int i=0; i<624; i++)
mt[i]=!i?seed:(1812433253U*(mt[i-1]^(mt[i-1]>>30))+i);
}
unsigned int operator()(){
unsigned int q=(mt[k]&0x80000000U)|(mt[(k+1)%624]&0x7fffffffU);
mt[k]=mt[(k+397)%624]^(q>>1)^((q&1)?0x9908b0dfU:0);
unsigned int y = mt[k];
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680U;
y ^= (y << 15) & 0xefc60000U;
y ^= (y >> 18);
k = (k+1)%624;
return y;
}
};
【讨论】:
获取随机数的一个好方法是监控通过计算机麦克风传来的环境噪音水平。如果您能获得驱动程序(或支持麦克风输入的语言)并将其转换为数字,那么您就成功了!
还研究了如何获得“真正的随机性”——由于计算机只不过是二进制机器,它们不能给我们“真正的随机性”。片刻之后,序列将开始重复。对更好的随机数生成的追求仍在继续,但他们说监控房间内的环境噪声水平是防止随机数生成模式形成的一种好方法。
您可以查找this wiki article 以获取有关随机数生成背后科学的更多信息。
【讨论】:
如果您正在寻找随机数的理论处理方法,您可能可以查看计算机编程艺术的第 2 卷。它有一章专门讨论随机数。看看能不能帮到你。
【讨论】:
如果您想要手动、硬编码、您自己的随机生成器,我无法为您提供效率,但是,我可以为您提供可靠性。实际上,我决定使用时间编写一些代码,通过及时计数来测试计算机的处理速度,这让我使用模数的计数算法编写了自己的随机数生成器(计数是随机的)。请自己尝试并在大型测试集中测试数字分布。顺便说一下,这是用python写的。
def count_in_time(n):
import time
count = 0
start_time = time.clock()
end_time = start_time + n
while start_time < end_time:
count += 1
start_time += (time.clock() - start_time)
return count
def generate_random(time_to_count, range_nums, rand_lst_size):
randoms = []
iterables = range(range_nums)
count = 0
for i in range(rand_lst_size):
count += count_in_time(time_to_count)
randoms.append(iterables[count%len(iterables)])
return randoms
【讨论】:
阅读随机数的兰兹书(随机数的蒙特卡洛书)里面的数字是为你随机生成的!!!我的祖父为 rand 工作。
【讨论】:
大多数 RNG(随机数生成器)都需要少量初始化。这通常是为了执行播种操作并存储播种值的结果以供以后使用。以下是我为游戏引擎编写的随机化器的播种方法示例:
/// <summary>
/// Initializes the number array from a seed provided by <paramref name="seed">seed</paramref>.
/// </summary>
/// <param name="seed">Unsigned integer value used to seed the number array.</param>
private void Initialize(uint seed)
{
this.randBuf[0] = seed;
for (uint i = 1; i < 100; i++)
{
this.randBuf[i] = (uint)(this.randBuf[i - 1] >> 1) + i;
}
}
这是从随机类的构造函数中调用的。现在可以使用上述种子值滚动/计算真实随机数。这通常是应用实际随机化算法的地方。这是另一个例子:
/// <summary>
/// Refreshes the list of values in the random number array.
/// </summary>
private void Roll()
{
for (uint i = 0; i < 99; i++)
{
uint y = this.randBuf[i + 1] * 3794U;
this.randBuf[i] = (((y >> 10) + this.randBuf[i]) ^ this.randBuf[(i + 399) % 100]) + i;
if ((this.randBuf[i] % 2) == 1)
{
this.randBuf[i] = (this.randBuf[i + 1] << 21) ^ (this.randBuf[i + 1] * (this.randBuf[i + 1] & 30));
}
}
}
现在存储滚动值以供本示例稍后使用,但这些数字也可以即时计算。预先计算的好处是性能略有提高。根据所使用的算法,滚动值可以直接返回或在代码请求时进行最后一分钟的计算。下面是一个示例,它取自预滚值并吐出一个非常漂亮的伪随机数:
/// <summary>
/// Retrieves a value from the random number array.
/// </summary>
/// <returns>A randomly generated unsigned integer</returns>
private uint Random()
{
if (this.index == 0)
{
this.Roll();
}
uint y = this.randBuf[this.index];
y = y ^ (y >> 11);
y = y ^ ((y << 7) + 3794);
y = y ^ ((y << 15) + 815);
y = y ^ (y >> 18);
this.index = (this.index + 1) % 100;
return y;
}
【讨论】: