如何手动生成随机数

16

我想手动生成随机数。我知道每种编程语言都有rand或random函数,但我很好奇它的工作原理。 有人有相关代码吗?

14个回答

13

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;
}

11

9
    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只是使用当前日期和时间,大多数编程语言都有这样的功能。
hour、minute、second和milisecond变量将日期时间值分解成其组成部分。我们只对这些部分感兴趣,因此可以忽略day。同样,在大多数语言中,日期和时间通常表示为字符串。在 .Net 中,我们有一些工具可以轻松解析此信息。但在大多数其他语言中,要解析所需的部分并将其转换为数字并不过于困难。即使在最古老的语言中也通常提供这些工具。
种子实质上给我们一个始终变化的起始数。传统上,您只需要将此数字乘以介于0和1之间的十进制值即可省略此步骤。 upperRange定义了最大值。因此生成的数字永远不会超过这个值。它也永远不会低于0。如果您想要负数,可以手动取反(将其乘以-1)。
randNum变量实际上保存了您感兴趣的随机值。
关键是得到除以上限范围后的余数(模数)。在本例中,余数将始终小于除数,即100。简单的数学告诉您,您不能有比除数更大的余数。因此,如果您除以10,则不能有大于10的余数。正是这个简单的法则让我们在这种情况下得到了介于0和100之间的随机数字。
console.writeline只是将其输出到屏幕上。
console.readline只是暂停程序,以便您可以看到它。
这是一个生成随机数的非常简单的代码。如果您每天以完全相同的间隔运行此程序(但必须在相同的小时、分钟、秒和毫秒运行),超过1天,您将开始再次生成相同的一组数字。这是因为它与时间有关。这是生成器的分辨率。因此,如果您知道该程序的代码和运行时间,您可以预测生成的数字,但并不容易。这就是我使用毫秒的原因。只使用秒或分钟即可了解我的意思。因此,您可以编写一个表格,显示1进入时,0出现,2进入时,0出现,等等。然后,您可以预测每秒的输出和生成的数字范围。您增加分辨率的越多(通过增加更改的数字),预测模式就越难以及需要更长的时间。这种方法对大多数人的使用足够好。
那是基本游戏中生成随机数的老式方法。它需要快速和简单。它确实如此。这也凸显了为什么随机数生成器不是真正的随机而是伪随机。我希望这是对你问题的合理回答。

6
我猜你是指伪随机数。我知道的最简单的方法(从以前在旧机器上编写电子游戏时)是这样的:
seed = seed * 5 + 1;
每次调用随机数时都要执行此操作,然后使用任意数量的低位。*5+1有一个好的特性(如果我没记错的话),无论你查看多少位,它都会在重复之前命中每个可能性。
当然,缺点是它的可预测性。但在游戏中这并不重要。我们为各种事情疯狂地抓取随机数,你永远不知道下一个数字是什么。
同时执行几个类似于此的操作,并将结果合并。这就是线性同余生成器。

这些选择的 a、c 和 m 真是糟糕透了! - derobert
1
哈哈。是的,但我第一次看到那个例程是在一台1 MHz的机器上。 :-) - Nosredna

5

5

你所说的手动是指“不使用计算机”还是“编写自己的代码”?

如果你不使用计算机,可以使用像骰子、袋中数字以及电视上选择团队、赢得Bingo系列等方法。拉斯维加斯充斥着这些用于给你带来糟糕赔率和ROI的过程(游戏)中使用的方法。你也可以获取伟大的RAND书,并翻到随机选择的页面:

http://www.amazon.com/Million-Random-Digits-Normal-Deviates/dp/0833030477

(此外,为了一些娱乐,阅读评论)

如果您要编写自己的代码,则需要考虑为什么不使用系统提供的RNG不够好。如果您正在使用现代操作系统,则用户程序将有一个RNG可用,应该足以满足您的应用程序。

如果您确实需要实现自己的RNG,则有大量的生成器可供选择。对于非安全使用,您可以查看LFSR链、同余生成器等。无论您需要哪种分布(均匀分布、正态分布、指数分布等),都应该能够找到算法描述和实现库。

对于安全使用,您应该查看像Yarrow/Fortuna、NIST SP 800-89指定的PRNG和RFC 4086这样需要提供良好熵源来馈送PRNG的内容。或者更好的是,使用操作系统中的那个应该满足安全RNG要求。

RNG的实现可能是一项有趣的练习,但很少需要。不要发明自己的算法,除非它是为玩具应用程序而设计的。不要为安全应用(例如生成加密密钥)发明RNG,除非您做了一些认真的阅读和调查。你会感谢我的(我希望)。


一个小问题:“你也可以获取伟大的RAND书籍并翻到随机选择的一页。”这将会对书中位于页面顶部的数字产生强烈偏见。人类是可怕的随机数生成器。 - Michael Lorton
翻到真随机数书的特定页面不会影响结果,只要您不重复使用数字,因为书中间的数字和前面的数字一样随机。无论从哪一页选择数字,重复使用数字都会导致结果偏差。 - Dour High Arch

3

梅森旋转算法的周期非常长(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;
    }
};

3
希望我不会重复,因为我还没有阅读所有链接,但我相信您可以接近真正的随机生成器。现在的系统通常非常复杂,即使是最好的极客也需要很长时间才能理解内部发生了什么 :)

打开你的头脑,想想你是否可以监视一些全局系统属性,用它来种子到...选择一个网络数据包(不是为您意图?)并计算其内容的“某些事物”,然后用它来种子到...等等。

您可以根据所有这些提示设计最适合您需求的最佳解决方案 ;)

3
获取随机数的一种好方法是监控通过计算机麦克风传入的环境噪声水平。如果您可以获得一个驱动程序(或支持麦克风输入的语言)并将其转换为数字,则已经成功了一半!同时,人们还研究了如何获得“真正的随机性”-因为计算机只不过是二进制机器,它们无法给我们“真正的随机性”。一段时间后,序列将开始重复。更好的随机数生成的探索仍在继续,但他们说监测房间中的环境噪声水平是防止随机生成中出现模式的一种好方法。您可以查看此维基百科文章以获取有关随机数生成背后科学的更多信息。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接