所有 LCG 循环。在实现最大循环长度的 LCG 中,每个值 x 都有一个唯一的前导和唯一的后继(这对于未实现最大循环长度的 LCG 或具有子循环行为的其他算法(例如 von)不一定成立)诺伊曼的中平方法 https://en.wikipedia.org/wiki/Middle-square_method).
假设我们的 LCG 的周期长度为 L。由于行为是循环的,这意味着在 L 次迭代之后我们回到了起始值。通过向后一步查找前驱值在数学上相当于向前迈 (L-1) 步。
The big question is whether that can be converted into a single step. If you're using a Prime Modulus Multiplicative LCG (where the additive constant is zero), it turns out to be pretty easy to do. If xi+1 = a * xi % m, then xi+n = an * xi % m. As a concrete example, consider the PMMLCG with a = 16807 and m = 231-1. This has a maximal cycle length of m-1 (it can never yield 0 for obvious reasons), so our goal is to iterate m-2 times. We can precalculate am-2 % m = 1407677000 using readily available exponentiation/mod libraries. Consequently, a forward step is found as xi+1 = 16807 * xi % 231-1, while a backwards step is found as xi-1 = 1407677000 * xi % 231-1.
额外的
The same concept can be extended to generic full-cycle LCGs by casting the transition in matrix form and doing fast matrix exponentiation to come up with the equivalent one-stage transform. The matrix formulation for xi+1 = (a * xi + c) % m is Xi+1 = T · Xi % m, where T is the matrix [[a c],[0 1]]
and X is the column vector (x, 1) transposed. Multiple iterations of the LCG can be quickly calculated by raising T to any desired power through fast exponentiation techniques using squaring and halving the power. After noticing that powers of matrix T never alter the second row, I was able to focus on just the first row calculations and produced the following implementation in 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
where ary
是包含 a 和 c(乘法系数和加法系数)的向量。
Using this with power
set to the cycle length - 1, I was able to determine coefficients which yield the predecessor for various LCGs listed in Wikipedia https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use. For example, to "reverse" the LCG with a = 1664525, c = 1013904223, and m = 232, use a = 4276115653 and c = 634785765. You can easily confirm that the latter set of coefficients reverses the sequence produced by using the original coefficients.