【问题标题】:Random number generation with next and previous support?具有下一个和上一个支持的随机数生成?
【发布时间】:2016-08-28 15:44:09
【问题描述】:

如何编写两个函数来生成支持next和previous的随机数?

我的意思是如何写两个函数:next_number()previous_number()next_number()函数生成一个新的随机数,previous_number()函数生成之前生成的随机数。

例如:

int next_number()
{
   // ...?
}

int previous_number()
{
   // ...?
}

int num;

// Forward random number generating.
// ---> 54, 86, 32, 46, 17
num = next_number(); // num = 54
num = next_number(); // num = 86
num = next_number(); // num = 32
num = next_number(); // num = 46
num = next_number(); // num = 17

// Backward random number generating.
// <--- 17, 46, 32, 86, 54
num = previous_number(); // num = 46
num = previous_number(); // num = 32
num = previous_number(); // num = 86
num = previous_number(); // num = 54

【问题讨论】:

  • 您不能将目前所有生成的数字存储在一个列表中吗?
  • 如果你只需要N个数字的有限序列,那么随机打乱整数1..N,并存储打乱后的集合。
  • 在你的例子中,如果你第五次调用previous_number()会发生什么?
  • @mbeckish: 一个新的随机数 :)
  • 我认为您需要定义“随机”对您意味着什么。它的意思是“不连续”还是“尽可能少的确定性”(如en.wikipedia.org/wiki/Random_number_generation

标签: algorithm random


【解决方案1】:

您可以使用伪随机函数 (PRF) 轻松做到这一点。

这样的函数接受一个键和一个值,并根据它们输出一个伪随机数。您将从 /dev/random 中选择一个键,该键在程序运行时保持不变,然后为函数提供一个整数,您递增以向前或递减以返回。

这是一个伪代码示例:

initialize():
    Key = sufficiently many bytes from /dev/random
    N = 0

next_number():
    N = N + 1
    return my_prf(Key, N)

previous_number():
    N = N - 1
    return my_prf(Key, N)

在大多数密码库中都可以找到强伪随机函数。正如 rici 指出的那样,您还可以使用任何加密函数(加密函数是伪随机排列,PRF 的一个子集,而且周期是如此之大,以至于差异并不重要)。

【讨论】:

  • 我称之为伪随机。那是 PRF 中的 P。
  • 我很确定你正在有效地杀死 R 部分 :) stackoverflow.com/a/23083105/3142427
  • @harold 伪随机算法在每次调用之前播种,如果不经常重新播种,它们通过的测试可能会失败。
  • @thatotherguy 可能用于“安全”伪随机算法,但我知道微软的rand() 存在一个问题,即种子与生成的下一个随机数之间的高度相关性.用递增的数字重新播种根本不是随机的。
  • @ErkiA 如果您只播种一次,情况也是如此。 PRNG 的输入始终是确定性的。
【解决方案2】:

一些linear congruential generators(一种常见但不是很好的PRNG)是可逆的。

他们通过next = (a * previous + c) mod m 工作。如果a 有一个modular multiplicative inverse mod m,这是可逆的。经常出现这种情况,因为m 通常是 2 的幂,而a 通常是奇数。

例如对于来自第一个链接的表中的“MSVC”参数:

  • m = 232
  • a = 214013
  • c = 2531011

反过来是:

previous = (current - 2531011) * 0xb9b33155;

选择类型使其工作模 232

【讨论】:

    【解决方案3】:

    假设你有一个线性同余序列S定义为

    S[0] = seed
    S[i] = (p * S[i-1] + k) % m
    

    对于某些pmk,例如gcd(p, m) == 1。然后你可以找到q 这样(p * q) % m == 1 并计算:

    S[i-1] = (q * (S[i] - k)) % m
    

    换句话说:如果您选择合适的p 并预计算q,您可以在O(1) 时间内以任一顺序遍历您的序列。

    【讨论】:

      【解决方案4】:

      生成可索引伪随机序列的一种相当简单的方法 - 即看起来随机但可以在任一方向遍历的序列 - 是选择一些(相当好的)加密算法和固定的加密密钥,然后定义:

      sequence(i): encrypt(i, known_key)
      

      你不需要知道i的值,因为你可以从数字中解密:

      next(r): encrypt(decrypt(r, known_key) + 1)
      
      prev(r): encrypt(decrypt(r, known_key) - 1)
      

      因此,i 不必是一个小整数;因为你需要做的唯一算术是一个小整数的加法和减法,所以一个 bignum 实现是微不足道的。因此,如果您想要 128 位伪随机数,您可以将第一个 i 设置为从 /dev/random 中提取的 128 位随机数。

      你必须将i的整个值保存在静态存储中,并且伪随机数的周期不能大于i的范围。但是,对于这个问题的任何解决方案都是如此:因为 next()prev() 运算符必须是函数,所以每个值都有唯一的后继和前驱,因此在值的循环中只能出现一次。这与 Mersenne twister 完全不同,例如,其周期远大于 232

      【讨论】:

        【解决方案5】:

        我认为您要的是确定性的随机数生成器。这是没有意义的,因为如果它是确定性的,它就不是随机的。唯一的解决办法是生成一个随机数列表,然后在这个列表中后退一步。

        PS!我知道本质上所有软件 PRNG-s 都是确定性的。您当然可以使用它来创建您需要的功能,但不要自欺欺人,它与随机性无关。如果您的软件设计需要确定性 PRNG,那么您可能完全可以跳过 PRNG 部分。

        【讨论】:

        • 所有 PRNG 都是确定性的。 OP 要求一个伪随机序列,其逆也是确定性的,这将其循环周期限制为可能的随机值的数量。
        猜你喜欢
        • 1970-01-01
        • 2018-04-21
        • 2023-01-03
        • 2011-09-18
        • 1970-01-01
        • 2018-04-03
        • 2015-05-25
        • 2013-05-20
        • 2014-12-27
        相关资源
        最近更新 更多