3

我正在尝试使用 C++ TR1 在 C++ 中生成真正的随机数。但是,当再次运行我的程序时,它会产生相同的随机数。代码如下。

我需要尽可能随机的每次运行的真实随机数。

std::tr1::mt19937 eng;  
std::tr1::uniform_real<double> unif(0, 1);
unif(eng);
4

5 回答 5

10

您必须使用种子初始化引擎,否则将使用默认种子:

eng.seed(static_cast<unsigned int >(time(NULL)));

然而,真正的随机性是在没有额外输入的情况下在确定性机器上无法实现的。每个伪随机数生成器在某种程度上都是周期性的,这是您对非确定性数字所不期望的。例如std::mt19937,有 2次 19937 -1 次迭代。真正的随机性很难实现,因为您必须监控似乎不确定的事物(用户输入、大气噪声)。请参阅JerryHandprint 的回答

如果您不想要基于时间的种子,您可以使用std::random_deviceemsr's answer中所见。您甚至可以将std::random_device其用作生成器,这是仅使用标准库方法最接近真正随机性的方法。

于 2012-04-06T17:58:03.780 回答
7

这些是伪随机数生成器。他们永远无法产生真正的随机数。为此,您通常需要特殊的硬件(例如,通常需要测量热二极管中的噪声或放射源的辐射)。

要在不同的运行中从伪随机生成器中获取不同的序列,您通常会根据当前时间为生成器播种。

但是,这会产生相当可预测的结果(即,其他人可以很容易地找出您使用的种子。如果您需要防止这种情况发生,大多数系统确实提供了一些至少相当随机数的来源。在 Linux 上,/dev/random 和在 Windows 上,CryptGenRandom.

但是,后者往往相当慢,因此您通常希望将它们用作种子,而不仅仅是从中检索所有随机数。

于 2012-04-06T18:01:05.550 回答
3

这个答案是一个维基。我正在.NET 中开发一个库和示例,请随时以任何语言添加您自己的...

没有外部“随机”输入(例如监控街道噪音),作为确定性机器,计算机无法生成真正的随机数:随机数生成

由于我们大多数人没有资金和专业知识来使用特殊设备来提供混乱的输入,因此有一些方法可以利用您的操作系统、任务调度程序、进程管理器和用户输入(例如鼠标移动)的一些不可预测的性质,生成改进的伪随机性。

不幸的是,我对 C++ TR1 知之甚少,不知道它是否有能力做到这一点。

编辑

正如其他人指出的那样,通过使用不同的输入为 RNG 播种,您会得到不同的数字序列(最终会重复,因此它们不是真正随机的)。因此,您有两种选择来改进您的一代:

定期用某种混乱的输入为您的 RNG 重新播种,或者根据您的系统运行方式使您的 RNG 的输出不可靠

前者可以通过创建算法来完成,这些算法通过检查系统环境来明确产生种子。这可能需要设置一些事件处理程序、委托函数等。

后者可以通过较差的并行计算实践来实现:即设置许多 RNG 线程/进程以“不安全的方式”竞争以创建每个后续随机数(或数字序列)。这隐含地从系统上的活动总和中增加了混乱,因为每分钟的事件都会影响到哪个线程的输出最终被写入并最终在调用“GetNext()”类型方法时被读取。下面是 .NET 3.5 中的粗略概念证明。注意两点:1)即使RNG每次都以相同的数字作为种子,但不会创建24个相同的行;2) 性能受到明显影响,资源消耗明显增加,这是改进随机数生成时给定的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace RandomParallel
{
    class RandomParallel
    {
        static int[] _randomRepository;
        static Queue<int> _randomSource = new Queue<int>();
        static void Main(string[] args)
        {
            InitializeRepository(0, 1, 40);
            FillSource();
            for (int i = 0; i < 24; i++)
            {
                for (int j = 0; j < 40; j++)
                    Console.Write(GetNext() + " ");
                Console.WriteLine();
            }
            Console.ReadLine();
        }
        static void InitializeRepository(int min, int max, int size)
        {
            _randomRepository = new int[size];
            var rand = new Random(1024);
            for (int i = 0; i < size; i++)
                _randomRepository[i] = rand.Next(min, max + 1);
        }
        static void FillSource()
        {
            Thread[] threads = new Thread[Environment.ProcessorCount * 8];
            for (int j = 0; j < threads.Length; j++)
            {
                threads[j] = new Thread((myNum) =>
                    {
                        int i = (int)myNum * _randomRepository.Length / threads.Length;
                        int max = (((int)myNum + 1) * _randomRepository.Length /     threads.Length) - 1;
                        for (int k = i; k <= max; k++)
                        {
                            _randomSource.Enqueue(_randomRepository[k]);
                        }
                    });
                threads[j].Priority = ThreadPriority.Highest;
            }
            for (int k = 0; k < threads.Length; k++)
                threads[k].Start(k);
        }
        static int GetNext()
        {
            if (_randomSource.Count > 0)
                return _randomSource.Dequeue();
            else
            {
                FillSource();
                return _randomSource.Dequeue();
            }
        }
    }
}

只要在生成过程中有用户输入/交互,这种技术就会产生一个不可破解的、非重复的“随机”数字序列。在这种情况下,知道机器的初始状态不足以预测结果。

于 2012-04-06T18:08:13.483 回答
3

如果您想要真正的硬件随机数,则标准库通过 random_device 类提供对此的访问:

我用它来播种另一个生成器:

#include <random>
...
  std::mt19937_64 re;
  std::random_device rd;
  re.seed(rd());
...
  std::cout << re();

如果您的硬件有 /dev/urandom 或 /dev/random 则将使用它。否则,实现可以免费使用它的伪随机生成器之一。在 G++ 上,mt19937 用作后备。

我很确定 tr1 也有这个,正如其他人指出的那样,我认为此时最好使用 std C++11 实用程序。

埃德

于 2012-04-06T19:43:15.140 回答
2

这是播种引擎的示例(使用 C++11 而不是 TR1)

#include <chrono>
#include <random>
#include <iostream>

int main() {
    std::mt19937 eng(std::chrono::high_resolution_clock::now()
                                          .time_since_epoch().count());
    std::uniform_real_distribution<> unif;
    std::cout << unif(eng) << '\n';
}

用当前时间播种是相对可预测的,可能不是应该做的事情。以上至少不会将您限制为每秒一个可能的种子,这是非常可预测的。

如果你想从 /dev/random 之类的东西而不是当前时间播种,你可以这样做:

std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 eng(seed);

(这取决于你的标准库实现。例如,libc++ 默认使用 /dev/urandom,但在 VS11 中 random_device 是确定性的)

当然,您从 mt19937 中得到的任何东西都无法满足您对“真正的随机数”的要求,我怀疑您并不真的需要真正的随机性。

于 2012-04-06T18:05:23.090 回答