制作可前后移动的可定制 LCG
Making a customizable LCG that travels backward and forward
我将如何让 LCG(一种伪随机数生成器)双向运行?
我知道向前行驶是 (a*x+c)%m
但我怎样才能扭转它呢?
我正在使用它,所以我可以将种子存储在地图中玩家的位置,并能够通过在 LCG 中前后传播(就像某种随机数字线)来生成周围的东西。
所有LCG循环。在达到最大循环长度的 LCG 中,每个值 x 都有唯一的前导和唯一的后继(对于未达到最大循环长度的 LCG 或具有子循环行为的其他算法(例如 von诺伊曼 middle-square method).
假设我们的 LCG 的循环长度为 L。由于行为是循环的,这意味着在 L 次迭代后我们回到了起始值。向后退一步求前驱值在数学上等同于向前 (L-1) 步。
最大的问题是是否可以将其转换为一步。如果您使用素模乘法 LCG(其中加法常数为零),结果证明它很容易做到。如果xi+1 = a * xi % m,则xi+n = a n * xi % m。作为具体示例,考虑 a = 16807 和 m = 231-1 的 PMMLCG。这具有 m-1 的最大循环长度(由于显而易见的原因,它永远不会产生 0),因此我们的目标是迭代 m-2 次。我们可以使用现成的 exponentiation/mod 库预先计算 am-2 % m = 1407677000。因此,发现前向步骤为 xi+1 = 16807 * xi % 231 -1,而后退步骤为 xi-1 = 1407677000 * xi % 231-1.
额外
相同的概念可以扩展到通用的全周期 LCG,方法是将转换转换为矩阵形式并进行快速矩阵求幂以得出等效的单级变换。 xi+1 = (a * xi + c) % m 的矩阵公式是 Xi+1 = T · Xi % m,其中T是矩阵[[a c],[0 1]]
,X是列向量(x, 1)转置。通过使用平方和减半幂的快速求幂技术将 T 提高到任何所需的幂,可以快速计算 LCG 的多次迭代。在注意到矩阵 T 的幂永远不会改变第二行之后,我能够专注于第一行计算并在 Ruby:
中生成以下实现
def power_mod(ary, mod, power)
return ary.map { |x| x % mod } if power < 2
square = [ary[0] * ary[0] % mod, (ary[0] + 1) * ary[1] % mod]
square = power_mod(square, mod, power / 2)
return square if power.even?
return [square[0] * ary[0] % mod, (square[0] * ary[1] + square[1]) % mod]
end
其中 ary
是包含乘法系数和加法系数 a 和 c 的向量。
通过将 power
设置为循环长度 - 1,我能够确定产生 various LCGs listed in Wikipedia 前身的系数。例如,对于"reverse" a = 1664525,c = 1013904223,m = 232的LCG,使用a = 4276115653和c = 634785765。您可以轻松确认后一组系数反转使用原始系数产生的序列。
我将如何让 LCG(一种伪随机数生成器)双向运行?
我知道向前行驶是 (a*x+c)%m
但我怎样才能扭转它呢?
我正在使用它,所以我可以将种子存储在地图中玩家的位置,并能够通过在 LCG 中前后传播(就像某种随机数字线)来生成周围的东西。
所有LCG循环。在达到最大循环长度的 LCG 中,每个值 x 都有唯一的前导和唯一的后继(对于未达到最大循环长度的 LCG 或具有子循环行为的其他算法(例如 von诺伊曼 middle-square method).
假设我们的 LCG 的循环长度为 L。由于行为是循环的,这意味着在 L 次迭代后我们回到了起始值。向后退一步求前驱值在数学上等同于向前 (L-1) 步。
最大的问题是是否可以将其转换为一步。如果您使用素模乘法 LCG(其中加法常数为零),结果证明它很容易做到。如果xi+1 = a * xi % m,则xi+n = a n * xi % m。作为具体示例,考虑 a = 16807 和 m = 231-1 的 PMMLCG。这具有 m-1 的最大循环长度(由于显而易见的原因,它永远不会产生 0),因此我们的目标是迭代 m-2 次。我们可以使用现成的 exponentiation/mod 库预先计算 am-2 % m = 1407677000。因此,发现前向步骤为 xi+1 = 16807 * xi % 231 -1,而后退步骤为 xi-1 = 1407677000 * xi % 231-1.
额外
相同的概念可以扩展到通用的全周期 LCG,方法是将转换转换为矩阵形式并进行快速矩阵求幂以得出等效的单级变换。 xi+1 = (a * xi + c) % m 的矩阵公式是 Xi+1 = T · Xi % m,其中T是矩阵[[a c],[0 1]]
,X是列向量(x, 1)转置。通过使用平方和减半幂的快速求幂技术将 T 提高到任何所需的幂,可以快速计算 LCG 的多次迭代。在注意到矩阵 T 的幂永远不会改变第二行之后,我能够专注于第一行计算并在 Ruby:
def power_mod(ary, mod, power)
return ary.map { |x| x % mod } if power < 2
square = [ary[0] * ary[0] % mod, (ary[0] + 1) * ary[1] % mod]
square = power_mod(square, mod, power / 2)
return square if power.even?
return [square[0] * ary[0] % mod, (square[0] * ary[1] + square[1]) % mod]
end
其中 ary
是包含乘法系数和加法系数 a 和 c 的向量。
通过将 power
设置为循环长度 - 1,我能够确定产生 various LCGs listed in Wikipedia 前身的系数。例如,对于"reverse" a = 1664525,c = 1013904223,m = 232的LCG,使用a = 4276115653和c = 634785765。您可以轻松确认后一组系数反转使用原始系数产生的序列。