c ++中的统一随机数生成器

时间:2012-04-06 17:52:33

标签: c++ random tr1

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

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

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

5 个答案:

答案 0 :(得分:10)

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

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

然而, true 随机性是在没有额外输入的情况下在确定性机器上无法实现的。每个伪随机数生成器在某种程度上都是周期性的,这是你不会从非确定性数字中得到的。例如,std::mt19937的周期为2 19937 -1次迭代。真正的随机性很难实现,因为您必须监控看似不确定的东西(用户输入,大气噪声)。请参阅Jerry'sHandprint's answer

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

答案 1 :(得分:7)

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

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

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

然而,后者往往相当慢,所以你通常希望将它们用作种子,而不只是从它们中检索所有随机数。

答案 2 :(得分:3)

这个答案是一个wiki。我正在研究.NET中的库和示例,随时可以用任何语言添加自己的...

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

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

不幸的是,我不太了解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();
            }
        }
    }
}

只要在生成期间存在用户输入/交互,该技术产生不可克隆的,非重复的“随机”数字序列。在这种情况下,了解机器的初始状态将不足以预测结果。

答案 3 :(得分: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实用程序。

答案 4 :(得分: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中得到的任何东西都不会满足你对“真随机数”的要求,我怀疑你并不需要真正的随机性。