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