【发布时间】:2017-12-12 02:20:02
【问题描述】:
摘要
您好,假设您有 128 位自动机(由四个 32 位字 X、Y、Z、W 表示)根据以下规则更改其状态:
X = ...
Y = ...
Z = ...
W = ...
void next()
{
var t = X ^ (X << 11);
X = Y;
Y = Z;
Z = W;
W = W ^ (W >> 19) ^ (t ^ (t >> 8));
}
^ - 表示二进制XOR 操作
<< - 表示二进制左移操作
>> - 表示二进制右移操作
保证上述自动机不会产生冲突,即每个状态都是一个(并且只有一个)前一个状态的结果。也保证上述状态机产生2^128个唯一状态。
问题
对于任何给定的状态(X,Y,Z,W) 产生与next 相反的操作(即prev)将状态恢复到前一个状态。
也就是说,如果你有以下状态(X=1, Y=2, Z=3, W=4),并且将调用next,状态将变为(X=2, Y=3, Z=4, W=2061),假设调用prev后状态应该再次等于@987654338 @。
附言
next 操作是 George Marsaglia 发现的 XorShift 伪随机数生成器的实现之一
https://en.wikipedia.org/wiki/Xorshift
这个操作的逆操作通常非常有用,考虑 Guid.Next(...)、Guid.Prev(...) 可用性的含义
编辑
我对 Niklas B. 的原始答案有所改进,并将结果移植到 C#,所以这是最后一段代码,希望有人能从 Random.Next() 和 Random.Prev() 操作中受益:
public class Xor128
{
public UInt32 X { get; set; }
public UInt32 Y { get; set; }
public UInt32 Z { get; set; }
public UInt32 W { get; set; }
public Xor128()
{
}
public Xor128(UInt32 x, UInt32 y, UInt32 z, UInt32 w)
{
X = x;
Y = y;
Z = z;
W = w;
}
//private UInt32 UnXorShl(UInt32 x, Int32 shift)
//{
// for (var i = shift; i < 32; i <<= 1) {
// x ^= x << i;
// }
// return x;
//}
//private UInt32 UnXorShr(UInt32 x, Int32 shift)
//{
// for (var i = shift; i < 32; i <<= 1) {
// x ^= x >> i;
// }
// return x;
//}
//public UInt32 Prev()
//{
// var t = UnXorShr(W ^ Z ^ (Z >> 19), 8);
// W = Z;
// Z = Y;
// Y = X;
// X = UnXorShl(t, 11);
// return W;
//}
public UInt32 Prev()
{
var t = W ^ Z ^ (Z >> 19);
t ^= t >> 8;
t ^= t >> 16;
W = Z;
Z = Y;
Y = X;
t ^= t << 11;
t ^= t << 22;
X = t;
return W;
}
public UInt32 Curr()
{
return W;
}
public UInt32 Next()
{
UInt32 t = X ^ (X << 11);
X = Y;
Y = Z;
Z = W;
return W = W ^ (W >> 19) ^ (t ^ (t >> 8));
}
}
顺便说一句。这是一个快速版本:
public class Xor128 {
public var X: UInt32
public var Y: UInt32
public var Z: UInt32
public var W: UInt32
public convenience init(uuid: uuid_t) {
let xa = (UInt32(uuid.0 ) << 24)
let xb = (UInt32(uuid.1 ) << 16)
let xc = (UInt32(uuid.2 ) << 8 )
let xd = (UInt32(uuid.3 ) << 0 )
let ya = (UInt32(uuid.4 ) << 24)
let yb = (UInt32(uuid.5 ) << 16)
let yc = (UInt32(uuid.6 ) << 8 )
let yd = (UInt32(uuid.7 ) << 0 )
let za = (UInt32(uuid.8 ) << 24)
let zb = (UInt32(uuid.9 ) << 16)
let zc = (UInt32(uuid.10) << 8 )
let zd = (UInt32(uuid.11) << 0 )
let wa = (UInt32(uuid.12) << 24)
let wb = (UInt32(uuid.13) << 16)
let wc = (UInt32(uuid.14) << 8 )
let wd = (UInt32(uuid.15) << 0)
self.init(
x: xa + xb + xc + xd,
y: ya + yb + yc + yd,
z: za + zb + zc + zd,
w: wa + wb + wc + wd
)
}
public convenience init(uuid: UUID) {
self.init(uuid: uuid.uuid)
}
public init(x: UInt32, y: UInt32, z: uint32, w: UInt32) {
X = x
Y = y
Z = z
W = w
}
@discardableResult
public func next() -> UInt32 {
let t = X ^ (X << 11);
X = Y;
Y = Z;
Z = W;
W = W ^ (W >> 19) ^ (t ^ (t >> 8))
return W;
}
public var curr: UInt32 {
return W
}
@discardableResult
public func prev() -> UInt32 {
var t = W ^ Z ^ (Z >> 19);
t ^= t >> 8;
t ^= t >> 16;
W = Z;
Z = Y;
Y = X;
t ^= t << 11;
t ^= t << 22;
X = t;
return W;
}
}
【问题讨论】:
-
有趣的问题。你试过什么?你离答案有多近?在寻求答案之前,您应该尝试自己解决它!
-
我认为这是不可能的,因为位移操作会删除移出变量范围的位 => 不可逆
-
在纯粹的理论基础上,您可以进行详尽的搜索,找到所有 2^128 个状态,然后将其全部映射出来。需要一些(难以置信的长和大)时间和空间,但理论上它是有效的。
-
@tucuxi - 我认为您只需要
2^32存储空间 - 我们知道W的先前值(以及Y和Z)。我们可以用它用当前的W值和公式计算(t ^ (t >> 8))。t只依赖于X,所以如果我们有一个X值到(t ^ (t >>8 ))值的映射,我们应该能够反转它。 -
它不能有 2^128 个可达状态,因为 X=Y=Z=W=0 将永远循环
标签: algorithm