ichirin2501's diary

いっちりーん。

XORShiftの逆算

XORShiftされた値を逆算する機会がありましたのでメモ書きです。

y = x ^ (x << t)
のとき、y ,t が分かってるとして、xを求めたい。
x << t の値は下位t桁は必ず0になっているから、
y & ((1 << t) - 1) と x & ((1 << t) - 1) は等しい。
また、x << tの t桁 ~ 2*t - 1桁目も y & ((1 << t) - 1) と等しい。
今回は x = 22, t = 3 を例として下記に示す。
x                    = 000010110
x << 3               = 010110000
y                    = 010100110
y & ((1 << 3) - 1)   = ______110
x & ((1 << 3) - 1)   = ______110
x << 3 の 3桁 ~ 5桁目 = ___110___
この時点で判明してるのは、y と x << t の 0桁 ~ 2*t - 1桁目までである。
x << t の判明してる部分をzと置く。XORの性質を利用すると、
z                    = ___110000
y                    = 010100110
x' = z ^ y           = ___010110
よって、x の t桁 ~ 2*t - 1桁目が新たに得られた。
x' << 3              = 010110000
z'                   = 010110000 ; x << tの判明している部分
y                    = 010100110
x'' = z' ^ y         = 000010110
これでxの全ての桁が復元できた。

右シフトのXORShiftもほぼ同様の処理で逆算可能である。
ただし、右シフトは算術シフトか論理シフトで値が異なる点に注意。
ちなみに、C言語の右シフトは算術シフトか論理シフトかは処理系依存。
gccではsignedでは算術シフト、unsignedでは論理シフトになる。


sample code(左シフト)

#include <stdio.h>

// 1 <= t <= 31
unsigned int decodeXORLeftShift(unsigned int y, int t){
    unsigned int x;
    unsigned int z = (y & ((1U << t) - 1)) << t;
    unsigned int mask = ((1U << t) - 1) << t;

    while( mask ){
        x = z ^ y;
        z = z | ((x & mask) << t);
        mask <<= t;
    }
    return x;
}

unsigned int encodeXORLeftShift(unsigned int x, int t){
    return x ^ (x << t);
}

int check(){
    int i;
    unsigned int j;

    for(i=1; i<32; i++){
        for(j=0; j<10000; j++){
            if( j != decodeXORLeftShift(encodeXORLeftShift(j,i), i) ){
                return 0;
            }
        }
    }
    return 1;
}

int main(){
    puts(check() ? "OK" : "NO");
    return 0;
}
実行結果
OK