【问题标题】:pseudo random distribution which guarantees all possible permutations of value sequence - C++伪随机分布,保证值序列的所有可能排列 - C++
【发布时间】:2015-06-02 09:40:40
【问题描述】:

随机问题。

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

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

2) 它需要足够随机,以使查看输入 1 输出的人看不到输入 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;}

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

【问题讨论】:

  • 为什么是 29^3200-1?那是巨大的;远远超出任何 C++ 类型,需要大约 15546 位来存储它。
  • 这叫散列函数,可以试试%225什么的
  • 我的建议是进行文献检索,找出最近在 PRNG 上工作的人,然后在合适的情况下使用他们已发表的工作,或者联系他们并支付费用来开发你想要的东西。在您指定的范围内,状态空间的隐含大小是巨大的。这类东西非常难——即使是像冯诺依曼这样杰出的数学家也有过惨痛的失败。
  • @JoshuaByer 1. md5 也是一个哈希函数 2. 已弃用
  • @JonathanBasile:我将比特数与权力混合在一起,所以我会重新开始。数字29^3200~2^15546 可以存储在 15546 位中。具有15546~2^13.924 位的状态机最多可以产生一个排列。 410~2^8.679 输出的每个排列都将singlePermSize*410=2^(13.924+8.679)~2^22.603 状态位作为理论最小值,~778 KB(您写了 6.5MB,但忘记了位->字节转换)。所以你大部分都在那儿,我离得很远

标签: c++ algorithm random permutation lcg


【解决方案1】:

好的,我不确定是否有一个普遍的答案,所以我会专注于具有 64 位内部状态/种子、产生 64 位输出和 2^64-1 周期的随机数生成器。特别是,我会以

的形式查看线性同余生成器(又名 LCG)
next = (a * prev + c) mod m

其中am 互为质数

所以:

1) 检查

2) 检查

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

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

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

prev = (next - c) * a_inv mod m

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

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

更新

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

Reversible pseudo-random sequence generator

【讨论】:

  • 我喜欢这种可能性。如果我理解正确 - 要获得 0 到 28 之间的 3200 个整数的所有可能排列,我可以使用 a=(2^15545)+1、c=3 和 m=2^15546 - 那么我的公式将是下一个 = ( (a*prev+c)%m)%28 - 这是实现这个的正确方法吗?
  • 我认为这是一个错误。我看到当我使用 %28 时,我得到了简短的重复模式。我想我可以将上面提到的值与公式 (a*prev+c)%m 一起使用,并对结果进行基本转换。
  • @JonathanBasile 我建议查看http://en.wikipedia.org/wiki/Linear_congruential_generator 中的表格以了解关于 a、m、c 的信息。比如说,对于 NumRecipes m=2^32,a=1664525,c=1013904223。对你来说,它可能是 m=2^15545,a= 一些大素数,其 1 和 0 的数量大致相等,长度略低于 15545 位,c=1 开始,也许是另一个大素数
  • 哦,伙计,我花了整整一天的时间才弄清楚如何反转模函数,这更多地说明了我的数学技能,而不是任务的难度。我将用我所做的来编辑我的问题,以便(希望)对将来看到它的任何人有用。感谢您提出这个建议。
  • @JonathanBasile 很高兴听到。想听听您为您的多位 LCG 使用的 a,m,c 是什么
【解决方案2】:

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

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

  • 从生成 N 的想法开始! 0 到 M 之间的值 (N>=410, M>=29^3200)
  • 将这些组合成一个大数字。我们将生成一个从 0 到 *M^N! 的数字。如果我们可以证明伪随机数生成器生成从 0 到 M^N! 的每个值,我们就可以保证您的置换规则。
  • 现在我们需要让它“显得随机”。对人眼来说,线性全等生成器就足够了。选择一个周期大于等于410!*M^N满足the rules的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 年后的某个地方,星星预计会永久消失,因此可能需要一段时间。

【讨论】:

    【解决方案3】:

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

    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 个连续输入的序列产生它(只是对排列索引进行非混洗)。我还要说(仅使用对称推理)给定任何起始输入,下一个 N 个元素的输出是同样可能的(每个数字和 N 个数字的每个序列在总周期中均等表示)。

    【讨论】:

      猜你喜欢
      • 2014-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-28
      • 2023-04-05
      • 1970-01-01
      • 2011-11-03
      相关资源
      最近更新 更多