如何直接获得rand()的第n个数字而不必调用rand()n次?

时间:2014-02-22 02:58:57

标签: c++ objective-c c random srand

根据我的理解,使用特定种子设置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()。

  • 我无法预先计算和存储值

编辑:我在看这篇文章:

https://mathoverflow.net/questions/104915/pseudo-random-algorithm-allowing-o1-computation-of-nth-element

关于线性反馈移位寄存器的答案似乎是这样做的,但我宁愿使用可靠的实现,而不是自己实现它,因为这似乎是一个常见的问题。

编辑:我想“跳”到第n个词的原因是因为我在不同的班级中使用rand和不同的种子,并且我在每个班级之间来回跳跃。我希望每个类中的序列在它停止的地方继续,而不是每次从第一个数字开始。这是一个单线程应用程序。

编辑:在撰写帖子时,我使用的是PRNG这个词。但实际上我只是在寻找一个似乎产生随机数的函数。我正在将它用于图形,因此没有安全问题。我使用随机数来产生像素的轻微偏移。

  • 我只需要一个快速的功能。
  • 似乎产生随机数,但不一定是安全应用程序中使用的那种。
  • 必须能够在O(1)时间内计算第n个数字。

编辑:犯了一个错误 - 存储状态是不够的。我需要在O(1)时间内计算串联的第n个随机数。因为在同一个类中可能有多个调用相同的第n个术语,所以存储状态是不够的,我需要在O(1)中计算第n个术语

7 个答案:

答案 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';
    }
}

http://ideone.com/0zeRNq

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,这是你想要得到的,不是。