这是一个函数,用 C 表示为:
uint32_t f(uint32_t x) {
return (x * 0x156) ^ 0xfca802c7;
}
然后我遇到了一个挑战:如何找到它的所有不动点?
我知道我们可以测试每个uint32_t
值来解决这个问题,但我仍然想知道是否有另一种更优雅的方法 - 特别是当uint32_t
成为uint64_t
并且(0x156, 0xfca802c7)
是任意一对值时。
这是一个函数,用 C 表示为:
uint32_t f(uint32_t x) {
return (x * 0x156) ^ 0xfca802c7;
}
然后我遇到了一个挑战:如何找到它的所有不动点?
我知道我们可以测试每个uint32_t
值来解决这个问题,但我仍然想知道是否有另一种更优雅的方法 - 特别是当uint32_t
成为uint64_t
并且(0x156, 0xfca802c7)
是任意一对值时。
Python代码:
def f(x, n):
return ((x*0x156)^0xfca802c7) % n
solns = [1] # The one solution modulo 2, see text for explanation
n = 1
while n < 2**32:
prev_n = n
n = n * 2
lifted_solns = []
for soln in solns:
if f(soln, n) == soln:
lifted_solns.append(soln)
if f(soln + prev_n, n) == soln + prev_n:
lifted_solns.append(soln + prev_n)
solns = lifted_solns
for soln in solns:
print soln, "evaluates to ", f(soln, 2**32)
输出:150129329 计算结果为 150129329
算法背后的想法:我们试图找到x XOR 0xfca802c7 = x*0x156 modulo n
,在我们的案例中n=2^32
。我这样写是因为右边是一个简单的模乘法,它与左边的表现很好。
我们将要使用的主要属性是将解决方案归x XOR 0xfca802c7 = x*0x156 modulo 2^(i+1)
约为 的解决方案x XOR 0xfca802c7 = x*0x156 modulo 2^i
。另一种说法是,一个解决方案x XOR 0xfca802c7 = x*0x156 modulo 2^i
转换为一个或两个解决方案模数2^(i+1)
:这些可能性是x
和/或x+2^i
(如果我们想要更精确,我们只查看 0,...,模数大小之间的整数 - 1 当我们说“解决方案”时)。
我们可以很容易地解决这个问题,因为i=1
:x XOR 0xfca802c7 = x*0x156 modulo 2^1
与 相同x XOR 1 = x*0 mod 2
,这意味着x=1
是唯一的解决方案。从那里我们知道只有 1 和 3 是可能的解模2^2 = 4
。所以我们只有两个可以尝试。事实证明,只有一个有效。这就是我们当前的模 4 解决方案。然后我们可以将该解决方案提升到模 8 的可能性。等等。最终我们得到了所有这样的解决方案。
备注1:此代码找到所有解决方案。在这种情况下,只有一个,但对于更一般的参数,可能不止一个。
Remark2:假设我没有出错,运行时间为 O(max[解决方案数,模数大小])。所以它很快,除非有很多很多的固定点。在这种情况下,似乎只有一个。
让我们使用Z3 求解器:
(declare-const x (_ BitVec 32))
(assert (= x (bvxor (bvmul x #x00000156) #xfca802c7)))
(check-sat)
(get-model)
结果是'#x08f2cab1' = 150129329.
由于位置处的输入位n
仅影响位置处的输出位,≥ n
因此您知道可以通过选择第一位,然后是第二位等来找到解决方案。
以下是如何在 C++ 中为 64 位整数解决它(当然它也适用于 32 位整数):
#include <cstdint>
#include <cstdio>
uint64_t f(uint64_t x) {
return (x * 0x7ef93a76ULL) ^ 0x3550e08f8a9c89c7ULL;
}
static void search(uint64_t x, uint64_t bit)
{
if (bit == 0)
{
printf("Fixed point: 0x%llx\n", (long long unsigned)x);
return;
}
if (f(x + bit) & bit) search(x + bit, bit << 1);
if ((f(x) & bit) == 0) search(x, bit << 1);
}
int main()
{
search(0x0, 1);
}
有了这个输出:
Fixed point: 0xb9642f1d99863811