4

随机问题。

我正在尝试创建一个会生成伪随机分布的程序。我正在尝试为我的需要找到正确的伪随机算法。这些是我的担忧:

1)每次使用时我都需要一个输入来生成相同的输出。

2)它需要足够随机,以使查看输入 1 的输出的人看不到它与输入 2 的输出(等等)之间没有任何联系,但不需要它是加密安全的或真正随机的。

3)它的输出应该是一个介于 0 和 (29^3200)-1 之间的数字,该范围内的每个可能的整数都是一个可能且同样(或接近)可能的输出。

4)我希望能够保证 410 个输出序列的每个可能排列也是连续输入的潜在输出。换句话说,0 到 (29^3200)-1 之间的 410 个整数的所有可能分组应该是顺序输入的潜在输出。

5)我希望函数是可逆的,这样我就可以取一个整数或一系列整数,并说出哪个输入或一系列输入会产生该结果。

到目前为止我开发的方法是通过一个简单的 halson 序列运行输入:

boost::multiprecision::mpz_int denominator = 1;
boost::multiprecision::mpz_int numerator = 0;

while (input>0) {
    denominator *=3;
    numerator = numerator * 3 + (input%3);
    input = input/3;
}

并将结果乘以 29^3200。它满足要求 1-3,但不满足要求 4。而且它只对单个整数可逆,而不是对数列可逆(因为不是所有数列都可以由它产生)。我在 C++ 中工作,使用 boost 多精度。

任何人可以给我的关于生成满足这些要求的随机分布的方法的任何建议,或者只是为此目的值得研究的一类算法,将不胜感激。提前感谢您考虑我的问题。

- - 更新 - -

由于多个评论者都关注相关数字的大小,我只是想明确表示,我认识到使用这些集合会带来的实际问题,但在提出这个问题时,我只对理论或概念方法感兴趣问题 - 例如,想象使用更小的整数集(如 0 到 99)以及 10 个输出序列集的排列。您将如何设计一种算法来满足这五个条件 - 1)输入是确定性的,2)出现随机(至少对人眼而言),3)范围内的每个整数都是可能的输出,4)不仅是所有值,而且值序列的所有排列都是可能的输出,5)函数是可逆的。

---第二次更新---

非常感谢@Severin Pappadeux,我能够反转 lcg。我想我会添加一些关于我所做的事情,以希望将来让任何人更容易看到这一点。首先,这些是反模函数的极好资源:

https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/modular-inverses

https://www.khanacademy.org/computer-programming/discrete-reciprocal-mod-m/6253215254052864

如果你采用等式 next=ax+c%m,使用下面的代码和你的 a 和 m 值将打印出你需要找到 ainverse 的欧几里得方程,以及 ainverse 的值:

    int qarray[12];
    qarray[0]=0;
    qarray[1]=1;
    int i =2;
    int reset = m;
    while (m % a >0) {
      int remainder=m%a;
      int quotient=m/a;
      std::cout << m << " = " << quotient << "*" << a << " + " << remainder << "\n";
      qarray[i] =qarray[i-2]-(qarray[i-1]*quotient);
      m=a;
      a=remainder;
      i++;
  }
if (qarray[i-1]<0) {qarray[i-1]+=reset;}
std::cout << qarray[i-1] << "\n";

我花了一段时间才弄清楚的另一件事是,如果你得到一个否定的结果,你应该给它加上 m。您应该在新方程中添加一个类似的项:

prev = (ainverse(next-c))%m;
if (prev<0) {prev+=m;}

我希望这对将来在这条路上冒险的人有所帮助。

4

3 回答 3

1

在您的更新中,“出现随机(人眼)”是您使用的措辞。“出现随机”的定义不是一个很好的话题。“随机性”有不同程度的测试。

但是,如果您只是想让它在人眼看来是随机的,则可以使用环乘法。

  • 从生成 N 的想法开始!0 和 M 之间的值 (N>=410, M>=29^3200)
  • 将其组合成一个大数字。我们将生成一个从 0 到 *M^N! 的数字。如果我们可以证明伪随机数生成器生成从 0 到 M^N! 的每个值,我们就可以保证您的置换规则。
  • 现在我们需要让它“显得随机”。对人眼来说,线性全等生成器就足够了。选择一个周期大于等于410!*M^N满足规则的LCG,保证周期完整。确保公平的最简单方法是选择 x' = (ax+c) mod M^N 形式的 LCG!

这样就行了。现在,困难的部分是证明你所做的事情是值得的。考虑到只有 29^3200 长序列的周期超出了物理现实的范围。你永远不会真正使用它。曾经。考虑一个由约瑟芬结制成的超导体(10^-12kg 处理 10^11bits/s),重量为整个宇宙的质量 3*10^52kg)可以处理大约 10^75bits/s。一个可以数到 29^3200 的数字大约有 15545 位长,因此超级计算机可以处理大约 6.5x10^71 个数字/秒。这意味着仅计算这么高大约需要 10 ^ 4600 秒,或者大约 10 ^ 4592 年。从现在起大约 10^12 年后的某个地方,星星预计会永久消失,因此可能需要一段时间。

于 2015-04-12T05:52:10.987 回答
1

和之间有数M**N列。您可以想象以(伪随机)序列一个接一个地编写所有这些,并将您的读取指针随机放置在生成的数字循环中和...N0M-1N*(M**N)0M-1

def output(input):
    total_length = N*(M**N)
    index = input % total_length
    permutation_index = shuffle(index / N, M**N)
    element = input % N
    return (permutation_index / (N**element)) % M

当然,对于在 0 和 M-1 之间的 N 个元素的每个排列,都有一个 N 个连续输入的序列产生它(只是对排列索引进行 unshuffle)。我还要说(仅使用对称推理)给定任何起始输入,下一个 N 元素的输出是同样可能的(每个数字和每个 N 数字序列在总周期中均等表示)。

于 2015-04-12T06:24:56.140 回答
1

好的,我不确定是否有一个普遍的答案,所以我会专注于具有 64 位内部状态/种子、产生 64 位输出和 2^64-1 周期的随机数生成器。特别是,我会以以下形式查看线性同余生成器(又名 LCG)

next = (a * prev + c) mod m

在哪里am是彼此的质数

所以:

1) 检查

2) 检查

3)检查(好吧,当然是64位空间)

4)检查(再次,除了0我相信,但64位的每一个排列都是LCG的输出,从一些种子开始)

5) 检查。众所周知,LCG 是可逆的,即可以得到

prev = (next - c) * a_inv mod m

其中 a_inv 可以从 计算am使用欧几里得算法

好吧,如果你觉得没问题,你可以尝试在你的 15546 位空间中实现 LCG

更新

快速搜索在此处显示可逆的 LCG 讨论/代码

可逆伪随机序列发生器

于 2015-04-12T04:03:17.207 回答