伪随机分布,保证值序列的所有可能的排列 - C ++

时间:2015-04-10 20:12:34

标签: c++ algorithm random permutation lcg

随机问题。

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

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 multiprecision。

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

---- ---- UPDATE

由于多个评论者关注的是相关数字的大小,我只是想明确表示我认识到使用这些数据集的实际问题,但在提出这个问题时,我只对理论感兴趣或问题的概念方法 - 例如,假设使用更小的整数集(如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

如果你使用下面的等式= ax + c%m,使用下面的代码和a和m的值将打印出你需要找到的反欧几何方程式,以及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;}

我希望将来能帮助那些冒险走上这条道路的人。

3 个答案:

答案 0 :(得分: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计算

好吧,如果它看起来不错,你可以尝试在你的15546bits空间中实现LCG

更新

快速搜索显示可逆LCG讨论/代码

Reversible pseudo-random sequence generator

答案 1 :(得分:1)

在您的更新中,&#34;随机出现(对于人眼)&#34;是你使用的措辞。 &#34;的定义似乎是随机的&#34;不是一个很好的议题。对随机性进行了不同程度的测试。&#34;

但是,如果你只是想让人眼看起来随机,你就可以使用环形倍增。

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

那就是诀窍。现在,困难的部分证明你所做的事情值得你花时间。考虑到仅仅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年,恒星会永久地眨眼,所以可能会有一段时间。

答案 2 :(得分:1)

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

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个数字的每个序列在总周期中均等地表示)。