根据我的理解,使用特定种子设置srand会导致对rand()的调用序列每次为该特定种子生成相同的数字序列:
例如:
srand(seed1);
rand() // firstnumber (e.g.: 42)
rand() // second number (e.g: 17)
srand(seed1)
rand() // first number (same as above (42))
rand() // second number (same as above (17))
有没有办法直接获取序列中的第n个数字而不必调用rand()n次?
例如,如果我想要系列中的第17个随机数,我想在一次调用中得到数字,而不是17次调用rand()。
我无法预先计算和存储值
关于线性反馈移位寄存器的答案似乎是这样做的,但我宁愿使用可靠的实现,而不是自己实现它,因为这似乎是一个常见的问题。
编辑:我想“跳”到第n个词的原因是因为我在不同的班级中使用rand和不同的种子,并且我在每个班级之间来回跳跃。我希望每个类中的序列在它停止的地方继续,而不是每次从第一个数字开始。这是一个单线程应用程序。
编辑:在撰写帖子时,我使用的是PRNG这个词。但实际上我只是在寻找一个似乎产生随机数的函数。我正在将它用于图形,因此没有安全问题。我使用随机数来产生像素的轻微偏移。编辑:犯了一个错误 - 存储状态是不够的。我需要在O(1)时间内计算串联的第n个随机数。因为在同一个类中可能有多个调用相同的第n个术语,所以存储状态是不够的,我需要在O(1)中计算第n个术语
答案 0 :(得分:2)
制作自己的兰特并在每个班级存储一个。 当然这是最弱的PRNG。 关键是你可以同时激活多个PRNG。
class Rand {
int seed;
const int a = 1103515245;
const int c = 12345;
public:
Rand();
void srand( int );
int rand();
};
Rand::Rand() : seed(123456789) {}
void Rand::srand( int s ) { seed = s; }
int Rand::rand()
{
seed = a * seed + c;
return seed;
}
OP要求“我在不同的班级使用不同种子的兰特”。 兰德的每个实例都有自己的种子。 因此,在每个需要自己种子的对象中放置一个Rand实例。
答案 1 :(得分:2)
使用rand_r()
。使用该函数,种子不是全局的和隐式的。您传递种子以显式使用,并且函数在计算下一个随机数时更新它。这样,每个班级的随机数流都独立于其他人。
每个对象或每个类(取决于您的设计需求)都会将种子值存储在unsigned int
变量中。它会初始化它;对于对象,在init方法中;对于课程,+initialize
。您可以使用时间或/dev/random
作为初始值。如果你连续初始化几个这样的对象或类,那么使用时间是一个坏主意,因为它们可能都发生在“相同”的时间(在你使用的时钟分辨率内)。
之后,每当您想要一个随机数时,请致电rand_r(&yourSeedVariable)
。这将返回仅从传入的种子计算的伪随机值,而不使用任何隐式或全局状态。它使用与rand()
相同的算法。它还会更新种子变量,以便下一个调用将生成该序列中的下一个随机数。
使用相同技术的任何其他对象或类都将具有独立的随机序列。他们对rand_r()
的调用不会影响这个对象或类,这个对象或类的调用不会影响它们。任何rand()
的来电者都一样。
进一步澄清。你在对你的问题的一个编辑中说:
我想“跳”到第n个词的原因是因为我使用兰德 不同种类的不同种子,我不断跳回去 每节课之间。我想要每个类中的序列 从它停止的地方继续,而不是从第一个数字开始 每一次。
我正在根据我的建议解决这个问题。我的建议并未解决您最初提出的问题。它不会让你以伪随机序列得到* n *数。它允许您在代码的不同部分使用单独的序列,以便它们不会相互干扰。
答案 2 :(得分:2)
所有C++11 PRNG都有“discard”功能,例如
#include <random>
#include <iostream>
int main() {
std::mt19937 rng;
static const size_t distance = 5;
rng.seed(0);
rng.discard(distance);
std::cout << "after discard 5: " << rng() << '\n';
rng.seed(0);
for (size_t i = 0; i <= distance; ++i) {
std::cout << i << ": " << rng() << '\n';
}
}
after discard 5: 3684848379
0: 2357136044
1: 2546248239
2: 3071714933
3: 3626093760
4: 2588848963
5: 3684848379
答案 3 :(得分:0)
PRNG的主要内容是(通常,快速实施)下一个值取决于之前的值。所以不,你不能在不计算所有以前的N-1值的情况下得到第N个值。
答案 4 :(得分:0)
您希望随机访问一组伪随机流。您可以通过从std::rand()
切换到counter mode(CTR)中的分组密码作为伪随机数生成器来获取它。要读取连续的伪随机数,请加密连续的明文数字。要读取其他顺序,请按某种顺序加密相同范围的数字。然后每个类都有自己的种子,由一个键和初始值组成。
例如,一个类的种子可能是8675309,初始值是8008135.要读取连续的随机数,请用该密钥加密8008136,8008137,8008138,8008139,8008140 ......中的每一个。要读取此序列中的第17个数字,请加密(8008135 + 17)= 8008152。
答案 5 :(得分:0)
您可以在32位或64位计数器上使用1:1 hash函数。对于你的哈希,你可以调整PRNG用作反馈和/或调节函数的任何方法,比如维基百科的xorshift页面中的这个:
uint64_t state;
void srand(uint64_t seed) {
state = seed;
}
uint64_t hash(uint64_t x) {
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
return x * 2685821657736338717ull;
}
uint32_t rand(void) {
return hash(state++) >> 32;
}
uint32_t rand(uint32_t n) {
return hash(n) >> 32;
}
答案 6 :(得分:-1)
简答:不。
更长的答案:伪随机系列是“随机的”,因为计算机无法在不知道先前预先计算的项目(或种子)的情况下预先计算系列,但是“伪”是因为系列可以使用相同的方式再现种子。
使用Google-fu,LSFR需要有限数量的状态。 PRNG,这是你想要得到的,不是。