Java 调整XORShift生成器以返回最大值内的数字

Java 调整XORShift生成器以返回最大值内的数字,java,performance,random,Java,Performance,Random,我需要在最大值内生成随机整数。由于性能至关重要,我决定使用XORShift生成器而不是Java的Random类 long seed = System.nanoTime(); seed ^= (seed << 21); seed ^= (seed >>> 35); seed ^= (seed << 4); 实现此方法最有效的方法是什么?我对您的代码很感兴趣,并提出了以下建议: public class XORShiftRandom { private

我需要在最大值内生成随机整数。由于性能至关重要,我决定使用XORShift生成器而不是Java的Random类

long seed = System.nanoTime();
seed ^= (seed << 21);
seed ^= (seed >>> 35);
seed ^= (seed << 4);

实现此方法最有效的方法是什么?

我对您的代码很感兴趣,并提出了以下建议:

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;
}

}
公共类XORShiftRandom{
私人长袍;
私人朗公司;
公共XORShiftRandom(){
这个(System.currentTimeMillis());
}
公共XORShiftRandom(长种子){
this.last=种子| 1;
inc=种子;
}
公共整数nextInt(整数最大值){
最后^=(最后>>35);
最后^=(最后一次播种)
这里有很多问题。如果你不止一次使用
nanoTime
,你肯定会做错,因为
nanoTime
很慢(数百纳秒)。此外,这样做可能会导致质量下降

让我们假设,你只给你的发电机播种一次

一致性 如果你关心均匀性,那么至少有两个问题:

异或移位 它永远不会生成零(除非你在播种时运气不好,然后你只能得到零)

这很容易通过以下简单的方法解决

private long nextLong() {
    x ^= x << 21;
    x ^= x >>> 35;
    x ^= x << 4;
    y += 123456789123456789L;
    return x + y;
}
nextInt(整数限制) 由于我在a中提到的原因,接受的答案提供了非均匀分布的值。正确操作有点复杂,您可以从
Random#nextInt
复制代码,也可以尝试类似的操作(未经测试):

public int nextInt(int limit){
检查参数(限制>0);
整数掩码=-1>>>整数。前导零的数目(限制);
while(true){
int result=(int)nextLong()&掩码;
if(结果<限制)返回结果;
}
}
上面,
掩码
以二进制形式显示,类似于
0…01…1
,其中最高的一个对应于
限制
中的最高一个。使用它,可以生成范围
0.掩码
内的均匀分布的数字(统一性很容易,因为
掩码+1
是二的幂)。条件拒绝数不低于
限制
。正如
限制>掩码/2
,发生这种情况的概率低于50%,因此预期迭代次数低于2

推荐
这很有趣,但测试起来很难,我建议使用
ThreadLocalRandom
,除非你需要再现性。

你不能将长模化为最大值吗?@SajitKunnumkal这会对一些整数产生(轻微)偏差(当然,如果最大值足够小,可能可以忽略不计)。如果性能至关重要,则应避免使用
nanoTime()
。如果性能至关重要,则可能应使用简单的LCG。如果max为常量且提前知道,则甚至可以构建一个临时LCG,该LCG将直接生成[0,max]范围内的数字没有偏见。使用XORShift时不应考虑性能;使用它的事实意味着它适用于非关键应用程序,这意味着System.nanoTime()非常合适。对移位值21、35、4有什么解释吗?也就是说,你有没有用其他值测试速度和/或随机性?这些值来自OP,我没有用它们。也许可以问他一个问题。这里可以快速讨论为什么这些值有用:注意(与
java.util.Random
不同)您的结果不是完全均匀分布的。它们不能是
2**32
(整数的数量)不能被
max
整除,除了二的幂。对于真正大的
max
3e9
,比率是1:2,对于小的值,它要好得多。@maaartinus我认为这是一个非常重要的点,不应该被低估。我将要求解释为找到一种性能高效的方法来扩展result在不影响原始算法均匀分布特性的情况下达到所需范围。取模不能满足此要求。很好。感谢您花时间响应坚实方法的基础!
private long nextLong() {
    x ^= x << 21;
    x ^= x >>> 35;
    x ^= x << 4;
    y += 123456789123456789L;
    return x + y;
}
seed = System.nanoTime();
x = seed | 1;
y = seed;
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;
    }
}