我需要在最大值内生成随机整数。由于性能至关重要,我决定使用XORShift生成器而不是Java的Random类。
long seed = System.nanoTime();
seed ^= (seed << 21);
seed ^= (seed >>> 35);
seed ^= (seed << 4);
这个实现 (source) 给了我一个长整数,但我真正想要的是一个介于0和最大值之间的整数。
public int random(int max){ /*...*/}
实施此方法的最有效方法是什么?
答案 0 :(得分:7)
我对您的代码感兴趣并想出了这个:
public class XORShiftRandom {
private long last;
private long inc;
public XORShiftRandom() {
this(System.currentTimeMillis());
}
public XORShiftRandom(long seed) {
this.last = seed | 1;
inc = seed;
}
public int nextInt(int max) {
last ^= (last << 21);
last ^= (last >>> 35);
last ^= (last << 4);
inc += 123456789123456789L;
int out = (int) ((last+inc) % max);
return (out < 0) ? -out : out;
}
}
我做了一个简单的测试,它与java.util.Random
如果您对它的工作方式感兴趣,可以阅读paper:
Disclamer:
上面的代码仅用于研究,而不是用于研究 替换库存Random或SecureRandom。
答案 1 :(得分:2)
这里有很多问题。如果你不止一次使用nanoTime
,你肯定做错了,因为nanoTime
很慢(几百纳秒)。此外,这样做可能会导致质量下降。
因此,我们假设您只为种子生成一次。
如果关心一致性,那么至少有两个问题:
它永远不会产生零(除非你不熟悉播种,然后零就是你得到的)。
这很容易通过像
这样简单的东西来解决private long nextLong() {
x ^= x << 21;
x ^= x >>> 35;
x ^= x << 4;
y += 123456789123456789L;
return x + y;
}
使用的常量非常随意,除非它必须是奇数。为了获得最佳结果,它应该很大(以便所有位经常更改),它应该有很多位转换(在二进制表示中出现10
和01
)并且它不应该太常规(0x55...55
很糟糕)。
但是,使用x!=0
和任何奇数常量,均匀性得到保证,生成器的周期为2**64 * (2*64-1)
。
我建议播种
seed = System.nanoTime();
x = seed | 1;
y = seed;
由于我在comment中提到的原因,接受的答案提供了非均匀分布的值。做得对,有点复杂,你可以从Random#nextInt
复制代码,或者你可以尝试这样的事情(未经测试):
public int nextInt(int limit) {
checkArgument(limit > 0);
int mask = -1 >>> Integer.numberOfLeadingZeros(limit);
while (true) {
int result = (int) nextLong() & mask;
if (result < limit) return result;
}
}
在上面,mask
看起来像二进制文件0...01...1
,其中最高的一个对应limit
中的最高一个。使用它,可以生成0..mask
范围内的均匀分布数(由于mask+1
是2的幂),因此一致性很容易。条件拒绝不低于limit
的数字。如limit > mask/2
,这种情况发生的概率低于50%,因此预期的迭代次数低于2。
这很有趣,但测试很难,我建议改为使用ThreadLocalRandom
,除非你需要重复性。