我如何计算这个按位公式的倒数?
How could I calculate the inverse of this bitwise formula?
我正在努力练习我的算法并研究一些我还不太精通的按位的东西。
所以我有这个功能:
def fn1(a):
return (a >> 1) ^ a
但是我需要为我正在处理的算法反转操作。因此,例如,如果函数 fn1(11)
returns 14,我需要创建一个函数 fn2(14)
即 returns 11。它只需要对正整数起作用。
我以为逆可能有不止一个答案,但是运行 fn1
循环了数千次都没有产生任何重复值,所以一定只有一个答案fn2
.
的任意值
位序列 11 和 00 转到 *0。位序列 10、01 转到 *1。因此图像 *1 表示 a 中的相同位和下一个更高位被翻转。
a 中的前导 1 位前面是 0,因此仍然是 1。
对于 fn1(a) = b,
的二进制表示
fn1(am am-1 。 ... a0) = bm bm-1 .... b0
是
bi = ai+1 ^ ai ⇔
ai = ai+1 ^ bi ⇔
ai-1 = ai^bi-1
通过这个递归和 am = bm = 1 你得到了数字am-1, m-2, ..., a0 .
编辑
一个不同的观察(尚未正式证明)是 fn1 对某些参数 a 的迭代应用将导致回到原始参数 a.
For a in argument range 22n...22n+1-1周期为p=2n+1,已解决 p=2floor( ld( ld (a) )+1
有了这个fn1-1(a) = fn1p-1(a) .
至于b=fn1(a),a和b属于同一个循环,p-公式同样适用于b.
最后fn1-1(b) = fn 1p-1(b) 与 p=2floor( ld( ld (b) ) +1
这是 C++ 中的实现
typedef unsigned long long N;
N fn1(N a)
{
return a ^ (a >> 1);
}
N floor_ld(N x);
N fn1_inv(N b)
{
if (b<2) return b;
N p = (N)1 << (floor_ld(floor_ld(b)) + 1);
N y = b;
for (int i = 1; i <= p - 1; i++)
{
y = fn1(y);
}
return y;
}
N floor_ld(N x)
{
return x == 1 ? 0 : 1 + floor_ld(x >> 1);
}
编辑 2
fn1 的进一步 属性 是,可以收缩迭代。
让更一般的fnk(a) := a ^(a>>k), 然后
(fnk ∘ fnk)(a) = fn2k(a ),通过简单的重新计算。
用二进制表示 e=p-1 = ∑ αi 2 i 普通迭代变为
fn1e(a) = ( ‖α i≠0 fn1{2 i} ) (一) =
( ∠αi≠0 fn{2 i}) (一)
渐近复杂度为 O(n log n) 与第一次尝试 digit-wise 求逆相反。
N fn1_invC(N b)
{
if (b < 2) return b;
N p = (N)1 << (floor_ld(floor_ld(b)) + 1);
N e = p - 1;
N y = b;
N k = 1;
while (e != 0)
{
if ((e & 1) != 0)
{
y = y ^ (y >> k);
}
k <<= 1;
e >>= 1;
}
return y;
}
我正在努力练习我的算法并研究一些我还不太精通的按位的东西。
所以我有这个功能:
def fn1(a):
return (a >> 1) ^ a
但是我需要为我正在处理的算法反转操作。因此,例如,如果函数 fn1(11)
returns 14,我需要创建一个函数 fn2(14)
即 returns 11。它只需要对正整数起作用。
我以为逆可能有不止一个答案,但是运行 fn1
循环了数千次都没有产生任何重复值,所以一定只有一个答案fn2
.
位序列 11 和 00 转到 *0。位序列 10、01 转到 *1。因此图像 *1 表示 a 中的相同位和下一个更高位被翻转。
a 中的前导 1 位前面是 0,因此仍然是 1。
对于 fn1(a) = b,
的二进制表示fn1(am am-1 。 ... a0) = bm bm-1 .... b0
是
bi = ai+1 ^ ai ⇔
ai = ai+1 ^ bi ⇔
ai-1 = ai^bi-1
通过这个递归和 am = bm = 1 你得到了数字am-1, m-2, ..., a0 .
编辑
一个不同的观察(尚未正式证明)是 fn1 对某些参数 a 的迭代应用将导致回到原始参数 a.For a in argument range 22n...22n+1-1周期为p=2n+1,已解决 p=2floor( ld( ld (a) )+1
有了这个fn1-1(a) = fn1p-1(a) .
至于b=fn1(a),a和b属于同一个循环,p-公式同样适用于b.
最后fn1-1(b) = fn 1p-1(b) 与 p=2floor( ld( ld (b) ) +1
这是 C++ 中的实现
typedef unsigned long long N;
N fn1(N a)
{
return a ^ (a >> 1);
}
N floor_ld(N x);
N fn1_inv(N b)
{
if (b<2) return b;
N p = (N)1 << (floor_ld(floor_ld(b)) + 1);
N y = b;
for (int i = 1; i <= p - 1; i++)
{
y = fn1(y);
}
return y;
}
N floor_ld(N x)
{
return x == 1 ? 0 : 1 + floor_ld(x >> 1);
}
编辑 2
fn1 的进一步 属性 是,可以收缩迭代。
让更一般的fnk(a) := a ^(a>>k), 然后 (fnk ∘ fnk)(a) = fn2k(a ),通过简单的重新计算。
用二进制表示 e=p-1 = ∑ αi 2 i 普通迭代变为
fn1e(a) = ( ‖α i≠0 fn1{2 i} ) (一) = ( ∠αi≠0 fn{2 i}) (一)
渐近复杂度为 O(n log n) 与第一次尝试 digit-wise 求逆相反。
N fn1_invC(N b)
{
if (b < 2) return b;
N p = (N)1 << (floor_ld(floor_ld(b)) + 1);
N e = p - 1;
N y = b;
N k = 1;
while (e != 0)
{
if ((e & 1) != 0)
{
y = y ^ (y >> k);
}
k <<= 1;
e >>= 1;
}
return y;
}