【问题标题】:Uniform random number generator in c++C ++中的统一随机数生成器
【发布时间】:2012-04-06 17:52:32
【问题描述】:

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

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

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

【问题讨论】:

  • 如果可以的话,您应该放弃使用 TR1 并开始使用 C++11 标准库。

标签: c++ random tr1


【解决方案1】:

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

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

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

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

【讨论】:

  • 它解决了为每次运行产生不同的随机数。事实上,我想产生真正的随机。
  • True 随机性是在没有额外输入的情况下在确定性机器上无法实现的。每个伪随机数生成器在某种程度上都是周期性的,这是您对非确定性数字所不期望的。例如 mt19937 的周期为 219937-1 次迭代。真正的随机性很难实现,因为您必须监控似乎不确定的事物(用户输入、大气噪声)。请参阅 Jerry 和 Handprint 的答案。
【解决方案2】:

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

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

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

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

【讨论】:

    【解决方案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】:

      这个答案是一个 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();
                  }
              }
          }
      }
      

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

      【讨论】:

      • 您能解释一下如何利用您的操作系统、任务调度程序、进程管理器和用户输入的不可预测性吗?我该如何使用它们?
      • 虽然这确实是“有点”随机的,但考虑到您当时操作系统的初始状态,它仍然完全可以重现。它不是不可破解的。
      • @Blindy 当涉及到用户交互时,初始状态是不充分的:您必须在整个长期序列生成过程中了解有关机器状态的所有信息才能破解它。但我想挑战已经被扔掉了——让一些超级计算机和研究人员来这里!
      【解决方案5】:

      这是一个播种引擎的示例(使用 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 中得到的任何东西都无法满足您对“真正的随机数”的要求,我怀疑您并不真的需要真正的随机性。

      【讨论】:

      • 编译器找不到chrono。它是在linux还是windows中?我用的是windows vs2010
      • 它在 VS11 中。 VS10 没有。
      猜你喜欢
      • 2011-09-18
      • 1970-01-01
      • 2013-04-15
      • 2020-03-09
      • 1970-01-01
      • 2013-12-07
      • 1970-01-01
      • 2011-01-24
      相关资源
      最近更新 更多