在编译时用C++生成随机数

pyC*_*hon 24 c++ random template-meta-programming c++11

我正在尝试random在编译时使用C++ 11的库预先计算随机值.我主要是按照例子.我在这做错了什么?

using namespace std;
#include <iostream>
#include <vector>
#include <random>

vector<double> rands;
typedef std::mt19937_64 RNG;
uint64_t seed_val;
RNG rng; 

void initialize() {
     rng.seed(seed_val);
}

constexpr vector<double> generate_random( )                 //size_t numbers)
{   
    int numbers = 1000;
    std::uniform_real_distribution<double> zero_one(0.0, 1.0);
        for (unsigned int i = 0; i < numbers; i++) { 
             double rand_num = zero_one(rng);
             rands.push_back( rand_num );
    }
    return rands;
}

int main()
{
    cout << "TMP rands";
    for_each( rands.begin(), rands.end(), [] (double value)
    {
        cout<<value<<endl;
    });
}
Run Code Online (Sandbox Code Playgroud)

这是一个从这里无耻地窃取的编译时随机数生成器的示例,但认为它对于查看此内容的任何人都有用:

template<u32 S, u32 A = 16807UL, u32 C = 0UL, u32 M = (1UL<<31)-1>
struct LinearGenerator {
    static const u32 state = ((u64)S * A + C) % M;
    static const u32 value = state;
    typedef LinearGenerator<state> next;
    struct Split { // Leapfrog
        typedef LinearGenerator< state, A*A, 0, M> Gen1;
        typedef LinearGenerator<next::state, A*A, 0, M> Gen2;
    };
};
Run Code Online (Sandbox Code Playgroud)

Pot*_*ter 24

只有constexpr功能和常量表达式可以在编译的时候进行评估.这排除了<chrono><random>.

您可以做的是访问__TIME__预处理器宏并定义由单行constexpr函数组成的自己的PRNG .

  • 或使用繁琐但已知的*template-meta-programming*方法来实现*编译时*SRNG. (2认同)
  • @pyCthon这个Q&A与播种有什么关系?您可以使用任何可用的数据播种,但需要播种,并且`__TIME__`是唯一可行的候选者.这里不需要模板,只需要`constexpr`,你可能也想要`__COUNTER__`或http://stackoverflow.com/questions/6166337/does-c-support-compile-time-counters.但我不知道预制解决方案.这是一个有点模糊的问题. (2认同)

jmi*_*cza 5

有一篇关于这个主题的研究论文:用于C++模板元程序的随机数生成器, 包含用于__TIME__技巧的代码片段.它还谈到支持不同的随机数引擎和分布作为正交选择.


小智 5

我会尝试从外部来源获取它。一个非常简单的例子是使用编译命令中定义的宏变量来编译程序。这$RANDOM是unix/linux系统中一个特殊的内置变量,它自动返回一个随机的16位数字。

g++ -D__RANDOM__=$RANDOM yourprog.cpp -o yourprog

//yourprog.cpp
#include <iostream>
int main() {
  std::cout << "Random variable " << __RANDOM__ << std::endl;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

您还可以编写自己的脚本或可执行文件来分配给宏变量。

//DevRandomGenerator.cpp
#include <iostream>
#include <fstream>

class DevRandom {
private:
    std::ifstream stream;
public:

    DevRandom() {
        stream.open("/dev/urandom",std::ios::in|std::ios::binary);
    }

    unsigned int unsignedInt() {
        unsigned int u = 0;
        stream.read((char*)&u, sizeof(unsigned int));
        return u;
    }
};

int main() {
  DevRandom rand;
  std::cout << rand.unsignedInt() << std::endl;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

然后编译为:

g++ DevRandomGenerator.cpp -o DevRandomGenerator
g++ -D__RANDOM__="$(./DevRandomGenerator)" yourprog.cpp -o yourprog
Run Code Online (Sandbox Code Playgroud)

更好的随机生成器是编写一个使用音频和视觉输入的程序。

  • 以“_”开头并后跟大写字母的全局名称保留给实现,因此您的解决方案具有未定义的行为。 (3认同)

Max*_*ert 5

我知道这个问题已经有五年了,并且已经有了公认的答案。即便如此,我想补充一点,在编译时生成随机数当然是可能的,前提是每次运行程序时都会得到相同的随机数序列。简单来说,如果在编译时种子已知,那么编译器就可以计算出将输出什么随机数,然后将程序变成“输出这个数字序列”。

编译器对其优化的积极程度有限制,所以我不能保证它们总是会进行这种替换,并且我怀疑任何编译器都能够对像 Mersenne Twister 这样复杂的东西进行替换,但是像这样更简单的linear_congruential_engine东西有机会(而且,确保它发生的唯一方法是让编译器输出汇编代码,然后查看汇编代码)。

我知道这是可能的,因为我实现了一个随机生成器,random_device该生成器是使用Marsaglia 的 Xorshift 算法建模的。由于 Marsaglia 的论文实际上包含了多种相关算法,因此我让该类采用模板参数来选择要使用的轮班模式。我想知道编译器是否会优化switch我使用的语句。我忘记传递种子,因此编译器使用默认值,即种子在编译时已知。当我查看汇编代码时,不仅没有了switch,而且GCC已经将程序优化为“输出这三个数字”。

问题中列出的程序的最终版本从未实际调用函数来生成数字序列,也从未调用函数来为生成器提供种子。这个版本会做到这一点,但我怀疑它会变成“打印这个随机数序列”。

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <random>

int get_seed()
{
    int hour = std::atoi(__TIME__);
    int min = std::atoi(__TIME__ + 3);
    int sec = std::atoi(__TIME__ + 6);
    return 10000 * hour + 100 * min + sec;
}

int main()
{
    // get_seed() returns an int based on __TIME__ (a string literal
    // set by the preprocessor), which is known at compile time.
    //
    // Also, w/r/t the engines in <random>: not setting a seed explicitly
    // will use a default seed, which is known at compile time.  So if
    // you're OK getting the same sequence of numbers for any compilation,
    // then "std::mt19937_64 rng;" may be all you need.
    std::mt19937_64 rng(get_seed());
    std::uniform_real_distribution<double> zero_one(0.0, 1.0);
    const int COUNT = 1000;
    std::generate_n(std::ostream_iterator<double>(std::cout, "\n"), COUNT,
        [&rng, &zero_one]() { return zero_one(rng); });
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 尽管您的答案通常是有效的,但您编写的代码在编译时没有任何作用。 (4认同)
  • 问题是询问编译时函数,而不是运行时函数。种子生成是最不用担心的。 (2认同)