好的,感谢@Jim Mischel 的指导。我阅读了所有相关页面并对此有了更多了解。
http://blog.mischel.com/2017/05/30/how-not-to-generate-unique-codes/ http://blog.mischel.com/2017/05/30/how-not-to-generate-unique-codes/
http://blog.mischel.com/2017/06/02/a-broken-unique-key-generator/ http://blog.mischel.com/2017/06/02/a-broken-unique-key-generator/
http://blog.mischel.com/2017/06/10/how-did-this-happen/ http://blog.mischel.com/2017/06/10/how-did-this-happen/
http://blog.mischel.com/2017/06/20/how-to-generate-random-looking-keys/ http://blog.mischel.com/2017/06/20/how-to-generate-random-looking-keys/
https://ericlippert.com/2013/11/12/math-from-scratch-part-thirteen-multiplicative-inverses/ https://ericlippert.com/2013/11/12/math-from-scratch-part-thirteen-multiplicative-inverses/
https://ericlippert.com/2013/11/14/a-practical-use-of-multiplicative-inverses/ https://ericlippert.com/2013/11/14/a-practical-use-of-multiplicative-inverses/
https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
简而言之,首先我应该使用序列号。那是 1,2,3,4,... 非常可预测,但它可能会变成随机且难以猜测的东西。
(请注意,在我的情况下,这并不完全可能,因为每个用户都将在本地生成自己的 ID,因此我无法运行全局序列号,因此我使用 GUID。但我将制定自己的解决方法以使 GUID 适合此解决方案,可能对 GUID 进行简单的取模以使其符合我想要的范围。)
具有连续整数n
我可以通过乘法然后取模得到另一个看似无关的整数。这可能看起来像(n * x)% m
with x
and m
我的选择。当然m
必须大于我想要使用的最大数字,因为它在乘法时环绕模数。
仅此一点就是一个良好的开端,因为数字接近n
不提供类似的输出。但我们对此不能那么确定。例如,如果我的x
是 4 并且m
是 16 那么输入只能产生 0,4,8,12。为了避免这种情况,我们选择x
and m
这是彼此的互质。 (最大公约数为 1)有许多明显的候选数,例如 100000m
(将我的输出限制定义为 99999)和 2429 作为x
。如果我们像这样选择2个互质,不仅可以尽可能减少结果冲突,还可以保证每个输入在该范围内产生唯一的输出。
我们可以从这个例子中学到:
(n * 5) % 16
由于 5 和 16 是互质数,因此我们可以在回绕之前获得唯一数字序列的最大长度。 (长度 = 16)如果我们按顺序输入从 0 到 16 的数字:
Input : 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
Output : 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11, 0
我们可以看到输出的顺序不太可预测,而且除了最后一个输出之外,没有一个输出是相同的。它会传送到所有可能的可用号码。
现在,我非常可预测的连续运行数字将产生足够不同的数字,并且只要它在范围内,也保证不会与任何其他输入发生冲突m
。剩下的就是通过基数转换将此数字转换为我选择的字符串。如果我有 5 个字符“ABCDE”,那么我将使用 base-5。
仅此对于我的用例就足够了。但是利用乘法逆元的概念我还可以找到另一个整数y
它可以将乘法模变换反转为原始数字。目前我还没有完全理解这部分,但它使用扩展欧几里得算法来查找y
.
由于我的应用程序不需要恢复,但我暂时不理解它也没关系。我一定会尝试理解那部分。