我试图在编译时使用C ++ 11的random
库预先计算随机值。我主要是按照例子说的。我在这里做错了什么?
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;
});
}
这是一个编译时随机数生成器的示例,它是从here无耻地窃取的,但是认为它可能对任何看起来有用的人都有用:
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;
};
};
答案 0 :(得分:23)
在编译时只能评估constexpr
个函数和常量表达式。这排除了<chrono>
和<random>
。
您可以做的是访问__TIME__
预处理器宏并定义由单行constexpr
函数组成的自己的PRNG。
答案 1 :(得分:5)
有一篇关于这个主题的研究论文:Random number generator for C++ template metaprograms
包含__TIME__
{{1}}技巧的code snippet。它还谈到支持不同的随机数引擎和分布作为正交选择。
答案 2 :(得分:4)
不仅system_clock::now()
编译时不可知,而且你的函数被标记为返回bool,但在任何地方都没有return语句。
答案 3 :(得分:4)
我会尝试从外部来源拉入它。一个非常简单的示例是在编译命令中使用已定义的宏变量编译程序。这里$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;
}
您也可以编写自己的脚本或可执行文件以分配给您的宏变量。
//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;
}
然后编译为:
g++ DevRandomGenerator.cpp -o DevRandomGenerator
g++ -D__RANDOM__="$(./DevRandomGenerator)" yourprog.cpp -o yourprog
更好的随机生成器是编写一个使用音频和视觉输入的程序。
答案 4 :(得分:3)
我知道这个问题已经有五年了,已经有了一个公认的答案。即便如此,我还想补充一点,它确实可能在编译时生成随机数,并且每次运行程序时都会得到相同的随机数序列。简单地说,如果种子在编译时已知,编译器允许来确定将输出哪些随机数,只需将程序变为“输出这个数字序列。” / p>
编译器将限制他们优化的积极程度,所以我不能保证他们总会进行这种替换,我怀疑任何编译器都能够替换像Mersenne Twister这样复杂的东西,但是有些东西更简单,如linear_congruential_engine
有机会(同样,确保它发生的唯一方法是使用编译器输出汇编代码,然后查看汇编代码)。
我知道这是可能的,因为我实现了一个使用Marsaglia's Xorshift algorithm的random_device
后建模的随机生成器。由于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;
}
答案 5 :(得分:1)
根据错误消息:
cpp11tmprands.cpp:22:15: error: ‘rands’ was not declared in this scope
变量rands
未在main
范围内声明。 <{1}}使其成为全局变量而不是本地变量,并且该错误将消失。
答案 6 :(得分:1)
这个问题现在可能已经九年了,但我仍然没有找到任何令人满意的解决方案,所以我自己实现了一个可靠的编译时随机生成器作为单个头库。它使用 KISS 随机生成器并将其内部状态表示为参数化类型。使用计数器宏伪记住状态。
CTRandomTimeSeed
尝试在多次编译之间实现可变种子。此外,可以使用 DYNLEC_CUSTOM_RANDOM_SEED
定义自定义种子。
#pragma once
#include <cstdint>
// all numbers are generated randomly at compile time. the internal state is pseudo
// remembered using the counter macro. the seed is based on time using the timestamp
// and time macro. additionally a custom random seed can be specified to fully rely
#ifndef DYNLEC_CUSTOM_RANDOM_SEED
#define DYNLEC_CUSTOM_RANDOM_SEED 0xbdacf99b3f7a1bb4ULL
#endif
// just iterating over the macros will always result in same
// number because the internal state is only updated for each occurance
// of the following macros
// generates a random number seeded with time and the custom seed
#define DYC_RAND_NEXT (::Dynlec::CTRandomGeneratorValueSeeded<__COUNTER__>)
// generates a random number seeded with time and the custom seed between min and max ( [min, max[ )
#define DYC_RAND_NEXT_BETWEEN(min, max) (min + (::Dynlec::CTRandomGeneratorValueSeeded<__COUNTER__> % (max - min)))
// generates a random number seeded with time and the custom seed with a limit ( [0, limit[ )
#define DYC_RAND_NEXT_LIMIT(limit) DYC_RAND_NEXT_BETWEEN(0, limit)
// helper macro for non repetetive indexed values
#define DYC_RAND_INDEXED(index) (::Dynlec::CTRandomGeneratorValue<index, ::Dynlec::CTRandomSeed ^ DYC_RAND_NEXT>)
// helper macro for non repetetive random streams
#define DYC_RAND_STREAM(n, callback) (::Dynlec::CTRandomStream<n, ::Dynlec::CTRandomSeed ^ DYC_RAND_NEXT>)
namespace Dynlec
{
// the random generator internal state is represented by
// the CTRandomGeneratorRaw type with each of its values
// x, y, z and c
template <
uint64_t x,
uint64_t y,
uint64_t z,
uint64_t c>
class CTRandomGeneratorRaw
{
static_assert(y != 0,
"CompileTimeRandom can not be used with 'y' equals 0");
static_assert(z != 0 || c != 0,
"CompileTimeRandom can not be used with 'z' and 'c' equals 0");
public:
typedef CTRandomGeneratorRaw<
6906969069ULL * x + 1234567ULL,
((y ^ (y << 13)) ^ ((y ^ (y << 13)) >> 17)) ^ (((y ^ (y << 13)) ^ ((y ^ (y << 13)) >> 17)) << 43),
z + ((z << 58) + c),
((z + ((z << 58) + c)) >> 6) + (z + ((z << 58) + c) < ((z << 58) + c))> Next;
constexpr static uint64_t Value = x + y + z;
};
// to prevent any accidental selection of invalid parameters
// these values are omitted
template <
uint64_t x,
uint64_t y,
uint64_t z,
uint64_t c>
class CTRandomGeneratorRawSafe
:
public CTRandomGeneratorRaw<
x, (y == 0) ? 1 : y, (z == 0 && c == 0) ? 1 : z, c>
{
};
// CTRandomGenerator is used to quickly compute the nth iteration
// of CTRandomGeneratorSafeRaw based on a single uint64_t seed
template <uint64_t iterations, uint64_t seed>
class CTRandomGenerator
{
friend CTRandomGenerator<iterations + 1, seed>;
typedef typename CTRandomGenerator<iterations - 1, seed>::Current::Next Current;
public:
constexpr static uint64_t Value = Current::Value;
};
template <uint64_t seed>
class CTRandomGenerator<0ULL, seed>
{
friend CTRandomGenerator<1ULL, seed>;
typedef typename CTRandomGeneratorRawSafe<
seed ^ 1066149217761810ULL,
seed ^ 362436362436362436ULL,
seed ^ 1234567890987654321ULL,
seed ^ 123456123456123456ULL>::Next Current;
public:
constexpr static uint64_t Value = Current::Value;
};
template <uint64_t iteration, uint64_t seed>
constexpr static uint64_t CTRandomGeneratorValue = CTRandomGenerator<iteration, seed>::Value;
const uint64_t CTRandomTimeSeed =
CTRandomGeneratorValue<0, (__TIME__[0]) ^
CTRandomGeneratorValue<0, (__TIME__[1]) ^
CTRandomGeneratorValue<0, (__TIME__[3]) ^
CTRandomGeneratorValue<0, (__TIME__[4]) ^
CTRandomGeneratorValue<0, (__TIME__[6]) ^
CTRandomGeneratorValue<0, (__TIME__[7])>>>>>> ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[0]) ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[1]) ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[2]) ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[4]) ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[5]) ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[6])>>>>>> ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[8]) ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[9]) ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[20]) ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[21]) ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[22]) ^
CTRandomGeneratorValue<0, (__TIMESTAMP__[23])>>>>>>;
const uint64_t CTRandomSeed = (DYNLEC_CUSTOM_RANDOM_SEED ^ CTRandomTimeSeed);
template <uint64_t iteration>
constexpr static uint64_t CTRandomGeneratorValueSeeded = CTRandomGeneratorValue<iteration, CTRandomSeed>;
template <uint64_t n, uint64_t seed = ::Dynlec::CTRandomSeed>
struct CTRandomStream
{
// callback(uint64_t index [0;n[, uint64_t random_number)
template <typename T>
static void Call(T callback)
{
CTRandomStream<n - 1, seed>::Call(callback);
callback(n - 1, CTRandomGeneratorValue<n, seed>);
}
};
template <uint64_t seed>
struct CTRandomStream<0, seed>
{
template <typename T>
static void Call(T callback) { }
};
}
用法示例:
// random 64 bit number
std::cout << DYC_RAND_NEXT << std::endl;
// random 64 bit number between [0 and 10[
std::cout << DYC_RAND_NEXT_LIMIT(10) << std::endl;
// random 64 bit number between [1 and 10]
std::cout << DYC_RAND_NEXT_BETWEEN(1, 11) << std::endl;
// initialize array with random numbers
int array[50];
Dynlec::CTRandomStream<50>::Call([&array](uint64_t index, uint64_t n)
{
array[index] = n;
});
Link 到包含该项目的 github 项目。