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

pseudo random distribution which guarantees all possible permutations of value sequence - C++

本文关键字:-C++ 排列 有可能 分布 随机      更新时间:2023-10-16

随机问题。

我正在尝试创建一个程序,它将生成一个伪随机分布。我正在努力寻找适合我需要的伪随机算法。这些是我的担忧:

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;}

我希望这能帮助任何未来在这条路上冒险的人。

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

next = (a * prev + c) mod m

其中CCD_ 1和CCD_

因此:

1) 检查

2) 检查

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

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

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

prev = (next - c) * a_inv mod m

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

好吧,如果你觉得可以的话,你可以试着在你的15546位空间中实现LCG

更新

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

可逆伪随机序列发生器

在您的更新中,"随机出现(对人眼)"是您使用的措辞。"随机出现"的定义并不是一个公认的话题。有不同程度的"随机性"测试

然而,如果你只是想让它在人眼中看起来随机,你可以使用环乘法。

  • 从生成N的想法开始!0和M之间的值(N>=410,M>=29^3200)
  • 将这些数据组合成一个大数字。我们将生成一个范围从0到*M^N!的单个数字!。如果我们可以证明伪随机数生成器生成从0到M^N!的每个值!,我们保证你的排列规则
  • 现在我们需要让它"看起来随机"。对人眼来说,线性同余生成器就足够了。选择一个周期大于或等于410的LCG*M^N满足规则以确保一个完整的周期。确保公平性的最简单方法是选择一个形式为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个数字/s。这意味着仅仅计算这么高的时间大约需要10 ^4600,或者大约需要10 ^ 4592年。大约10^12年后的某个地方,恒星预计会永久消失,所以这可能需要一段时间。

0M-1之间存在N数的M**N序列。您可以想象将它们一个接一个地写入(伪随机)序列,并将读取指针随机放置在a0和M-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个数字的每个序列在总周期中都是相等的)。