C++17:g++ 版本 8.5 似乎无法正确生成无符号 64 位伪随机整数

问题描述 投票:0回答:1

我正在使用以下 g++ 版本在 Rocky Linux 环境中编译 C++ 程序:

gcc version 8.5.0 20210514 (Red Hat 8.5.0-3) (GCC)

我按如下方式调用 g++:

g++ -std=c++17 -Wall -Wpedantic -Wextra -Ofast main.cpp

我的程序使用 C++ 标准库测试无符号 64 位伪随机整数的生成。我遇到了问题,我正在尝试确定是否发现了编译器/库错误或者我是否做错了什么。问题是伪随机生成器永远不会生成小于 27,917,522,695(大约 2^34.7)的数字。当然,考虑到 0..2^64-1 的大小,这可能需要一些时间,但我已经生成了超过 11 万亿个伪随机数,以确保在一切正常的情况下对成功有极高的期望。

下面的源代码是独立的,符合标准(C++17),并且编写是为了尽可能清楚地演示这个问题。这是我的源代码:

#include <array>
#include <cmath>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <limits>
#include <random>
#include <type_traits>

using namespace std;

// This singleton is a uniform pseudorandom number generator.
// This singleton is lazily initialized.
template<typename T>
class prng_s
{
   public:
      prng_s(const prng_s &) = delete;
      void operator=(const prng_s &) = delete;

      prng_s(prng_s &&) = delete;
      void operator=(prng_s &&) = delete;

      prng_s *operator&() = delete;

      T rand()
      {
         return di(dre);
      }

      static prng_s<T> &get_instance()
      {
         // This is the instance of this singleton.
         // Putting this inside get_instance() makes this a lazily-initialized
         // singleton.
         static prng_s<T> instance;

         return instance;
      }

   private:
      prng_s() {}
      ~prng_s() {}

      random_device rd;
      default_random_engine dre{rd()};

      // Prepare a uniform random number generator. This should be used in
      // conjunction with the engine as follows:
      // T prn{di(dre)};
      uniform_int_distribution<T> di{
                                       0,
                                       numeric_limits<T>::max()
                                    };
};

using my_uint_t = uint64_t;

static_assert(
                sizeof(my_uint_t) == 8 &&
                is_integral_v<my_uint_t> &&
                is_unsigned_v<my_uint_t>,
                "my_uint_t must be a 64-bit unsigned integer type"
             );

int main()
{
   prng_s<my_uint_t> &prng{prng_s<my_uint_t>::get_instance()};

   my_uint_t total_count{0};
   my_uint_t update_count{0};

   my_uint_t min_n{numeric_limits<my_uint_t>::max()};
   long double min_n_lg{log2(static_cast<long double>(min_n))};

   my_uint_t max_n{0};
   long double max_n_lg{log2(static_cast<long double>(max_n))};


   cout << "update_count = " << setw(3) << setfill(' ') << right << update_count
        << "    total_count = " << setw(15) << setfill(' ') << right << total_count
        << "    min_n = " << setw(20) << setfill(' ') << right << min_n
        << "    min_n_lg = " << fixed << setprecision(5) << setw(8) << setfill(' ') << right << min_n_lg
        << "    max_n = " << setw(20) << setfill(' ') << right << max_n
        << "    max_n_lg = " << fixed << setprecision(10) << setw(13) << setfill(' ') << right << max_n_lg;

   while (true)
   {
      bool update_needed{false};
      bool periodic_update_needed{false};

      my_uint_t one_num{prng.rand()};
      ++total_count;

      if (one_num > max_n)
      {
         max_n = one_num;
         max_n_lg = log2(static_cast<long double>(max_n));
         update_needed = true;
      }

      if (one_num < min_n)
      {
         min_n = one_num;
         min_n_lg = log2(static_cast<long double>(min_n));
         update_needed = true;
      }

      if (update_needed)
         ++update_count;

      if (! (total_count & 0x0000000000FFFFFFULL) && ! update_needed)
      {
         periodic_update_needed = true;
         update_needed = true;
      }

      if (update_needed)
      {
         if (periodic_update_needed)
            cout << "\r" << flush;
         else
            cout << endl;

         cout << "update_count = " << setw(3) << setfill(' ') << right << update_count
              << "    total_count = " << setw(15) << setfill(' ') << right << total_count
              << "    min_n = " << setw(20) << setfill(' ') << right << min_n
              << "    min_n_lg = " << fixed << setprecision(5) << setw(8) << setfill(' ') << right << min_n_lg
              << "    max_n = " << setw(20) << setfill(' ') << right << max_n
              << "    max_n_lg = " << fixed << setprecision(10) << setw(13) << setfill(' ') << right << max_n_lg;
      }
   }
}

以下是该程序的一些当前输出:

update_count =   0    total_count =               0    min_n = 18446744073709551615    min_n_lg = 64.00000    max_n =                    0    max_n_lg =          -inf
update_count =   1    total_count =               1    min_n =  7002304003565306067    min_n_lg = 62.60254    max_n =  7002304003565306067    max_n_lg = 62.6025354054
update_count =   2    total_count =               2    min_n =  3783328042247973794    min_n_lg = 61.71436    max_n =  7002304003565306067    max_n_lg = 62.6025354054
update_count =   3    total_count =               3    min_n =  3783328042247973794    min_n_lg = 61.71436    max_n = 12978918679643380525    max_n_lg = 63.4928039951
update_count =   4    total_count =               4    min_n =  3783328042247973794    min_n_lg = 61.71436    max_n = 14336044164615022123    max_n_lg = 63.6362807898
update_count =   5    total_count =               5    min_n =  3783328042247973794    min_n_lg = 61.71436    max_n = 16226104930620721283    max_n_lg = 63.8149505260
update_count =   6    total_count =              12    min_n =  1656254512494108391    min_n_lg = 60.52263    max_n = 16226104930620721283    max_n_lg = 63.8149505260
update_count =   7    total_count =              15    min_n =   113413242023177263    min_n_lg = 56.65437    max_n = 16226104930620721283    max_n_lg = 63.8149505260
update_count =   8    total_count =              19    min_n =   113413242023177263    min_n_lg = 56.65437    max_n = 17611098941292711456    max_n_lg = 63.9331187397
update_count =   9    total_count =              20    min_n =   113413242023177263    min_n_lg = 56.65437    max_n = 17655211259205598036    max_n_lg = 63.9367278871
update_count =  10    total_count =              46    min_n =   113413242023177263    min_n_lg = 56.65437    max_n = 17873875422597147885    max_n_lg = 63.9544862770
update_count =  11    total_count =              83    min_n =   113413242023177263    min_n_lg = 56.65437    max_n = 17963711407470407736    max_n_lg = 63.9617192529
update_count =  12    total_count =             150    min_n =   113413242023177263    min_n_lg = 56.65437    max_n = 18015384043678543638    max_n_lg = 63.9658632097
update_count =  13    total_count =             201    min_n =   113413242023177263    min_n_lg = 56.65437    max_n = 18323125361920749911    max_n_lg = 63.9902994067
update_count =  14    total_count =             300    min_n =   113413242023177263    min_n_lg = 56.65437    max_n = 18328438497086837431    max_n_lg = 63.9907176827
update_count =  15    total_count =             390    min_n =    61992352998145645    min_n_lg = 55.78294    max_n = 18328438497086837431    max_n_lg = 63.9907176827
update_count =  16    total_count =             437    min_n =    28110783079001471    min_n_lg = 54.64197    max_n = 18328438497086837431    max_n_lg = 63.9907176827
update_count =  17    total_count =             470    min_n =    15530575099522685    min_n_lg = 53.78596    max_n = 18328438497086837431    max_n_lg = 63.9907176827
update_count =  18    total_count =             548    min_n =    15530575099522685    min_n_lg = 53.78596    max_n = 18348594481069843039    max_n_lg = 63.9923033584
update_count =  19    total_count =             655    min_n =    15530575099522685    min_n_lg = 53.78596    max_n = 18421702955696027995    max_n_lg = 63.9980402374
update_count =  20    total_count =            2738    min_n =    15530575099522685    min_n_lg = 53.78596    max_n = 18432143699169067459    max_n_lg = 63.9988576722
update_count =  21    total_count =            3187    min_n =    15353715552198611    min_n_lg = 53.76944    max_n = 18432143699169067459    max_n_lg = 63.9988576722
update_count =  22    total_count =            3307    min_n =    15353715552198611    min_n_lg = 53.76944    max_n = 18442503997275137543    max_n_lg = 63.9996683512
update_count =  23    total_count =            4793    min_n =     7449623236915790    min_n_lg = 52.72609    max_n = 18442503997275137543    max_n_lg = 63.9996683512
update_count =  24    total_count =            7456    min_n =     7449623236915790    min_n_lg = 52.72609    max_n = 18446639273540196852    max_n_lg = 63.9999918037
update_count =  25    total_count =           12849    min_n =     2162335386263772    min_n_lg = 50.94151    max_n = 18446639273540196852    max_n_lg = 63.9999918037
update_count =  26    total_count =           13094    min_n =      942673241417463    min_n_lg = 49.74375    max_n = 18446639273540196852    max_n_lg = 63.9999918037
update_count =  27    total_count =           43816    min_n =       57864399722891    min_n_lg = 45.71774    max_n = 18446639273540196852    max_n_lg = 63.9999918037
update_count =  28    total_count =           60784    min_n =        7726706646700    min_n_lg = 42.81299    max_n = 18446639273540196852    max_n_lg = 63.9999918037
update_count =  29    total_count =           78943    min_n =        5239901122126    min_n_lg = 42.25268    max_n = 18446639273540196852    max_n_lg = 63.9999918037
update_count =  30    total_count =          130162    min_n =        5239901122126    min_n_lg = 42.25268    max_n = 18446686842822731255    max_n_lg = 63.9999955240
update_count =  31    total_count =          643439    min_n =        2465330536850    min_n_lg = 41.16492    max_n = 18446686842822731255    max_n_lg = 63.9999955240
update_count =  32    total_count =         1808778    min_n =        2465330536850    min_n_lg = 41.16492    max_n = 18446698312622650728    max_n_lg = 63.9999964211
update_count =  33    total_count =         2139847    min_n =        2465330536850    min_n_lg = 41.16492    max_n = 18446699753595454691    max_n_lg = 63.9999965338
update_count =  34    total_count =         2510203    min_n =        2465330536850    min_n_lg = 41.16492    max_n = 18446734162995213110    max_n_lg = 63.9999992249
update_count =  35    total_count =        16777216    min_n =        2465330536850    min_n_lg = 41.16492    max_n = 18446743463819675053    max_n_lg = 63.9999999523
update_count =  36    total_count =        20323037    min_n =        1629952860633    min_n_lg = 40.56797    max_n = 18446743463819675053    max_n_lg = 63.9999999523
update_count =  37    total_count =        21727248    min_n =        1629952860633    min_n_lg = 40.56797    max_n = 18446743506769684113    max_n_lg = 63.9999999557
update_count =  38    total_count =        33554432    min_n =         957785218844    min_n_lg = 39.80091    max_n = 18446743506769684113    max_n_lg = 63.9999999557
update_count =  39    total_count =        33605538    min_n =         938457714767    min_n_lg = 39.77150    max_n = 18446743506769684113    max_n_lg = 63.9999999557
update_count =  40    total_count =        50331648    min_n =         152472548969    min_n_lg = 37.14976    max_n = 18446743506769684113    max_n_lg = 63.9999999557
update_count =  41    total_count =        83886080    min_n =         103080038550    min_n_lg = 36.58497    max_n = 18446743506769684113    max_n_lg = 63.9999999557
update_count =  42    total_count =       150994944    min_n =         103080038550    min_n_lg = 36.58497    max_n = 18446743519654686831    max_n_lg = 63.9999999567
update_count =  43    total_count =       201326592    min_n =         103080038550    min_n_lg = 36.58497    max_n = 18446743781649742097    max_n_lg = 63.9999999772
update_count =  44    total_count =       352321536    min_n =         103080038550    min_n_lg = 36.58497    max_n = 18446743987809785585    max_n_lg = 63.9999999933
update_count =  45    total_count =       369098752    min_n =          53687528131    min_n_lg = 35.64387    max_n = 18446743987809785585    max_n_lg = 63.9999999933
update_count =  46    total_count =       553648128    min_n =          53687528131    min_n_lg = 35.64387    max_n = 18446744017874791927    max_n_lg = 63.9999999956
update_count =  47    total_count =       587202560    min_n =          27917522695    min_n_lg = 34.70045    max_n = 18446744017874791927    max_n_lg = 63.9999999956
update_count =  48    total_count =     60800630784    min_n =          27917522695    min_n_lg = 34.70045    max_n = 18446744050087332335    max_n_lg = 63.9999999982

有人知道为什么这个伪随机生成器似乎永远不会生成小于27,917,522,695的数字吗?

c++ linux random c++17 g++
1个回答
0
投票

直到更新号 47,如果您假设完美的均匀随机分布,则最小值才有意义。

在第 47 号更新中,您大约有 2^25 次迭代。

在第 48 号更新中,您大约有 2^35 次迭代。

std::default_random_engine
是一些实现定义的随机数生成器,没有任何特定的保证。例如,在 libstdc++ 和 libc++ 上,它都是模数为
2^32-1
的线性同余生成器。因此,它的周期最多为
2^32-1
并且您的迭代次数显着超过,以至于您看不到新的随机值。

使用 中您明确知道的特定随机数生成器之一,它具有足够好的属性来满足您的目的。例如,应该知道它的周期远大于

2^35

© www.soinside.com 2019 - 2024. All rights reserved.