27

我正在尝试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;
    });
}

这是一个无耻地从这里偷来的编译时随机数生成器示例,但认为它可能对查找此内容的任何人有用:

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;
    };
};
4

7 回答 7

27

只有constexpr函数和常量表达式可以在编译时求值。这排除了<chrono><random>

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

于 2012-07-16T05:02:11.717 回答
8

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

于 2014-04-14T10:21:51.623 回答
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;
}
于 2017-09-01T13:58:33.920 回答
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

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

于 2015-12-06T03:43:07.313 回答
3

不仅system_clock::now()编译时不可知,而且您的函数被标记为返回布尔值,但在任何地方都没有返回语句。

于 2012-07-16T05:04:06.553 回答
3

这个问题现在可能已经有九年了,但我仍然没有找到任何令人满意的解决方案,所以我自己实现了一个可靠的编译时随机生成器作为单个头库。它使用 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 to the github project containing the project.

于 2021-02-12T09:36:39.503 回答
1

根据错误消息:

cpp11tmprands.cpp:22:15: 错误: 'rands' 未在此范围内声明

该变量rands未在 的范围内声明main。将其设为全局变量而不是局部变量generate_random,该错误就会消失。

于 2012-07-16T05:10:40.137 回答