线性同余生成器给出错误的输出
Linear congruential generator gives wrong output
我创建了一个线性同余生成器 (LCG),但它似乎给出了错误的输出。
// Instance variables
private long currentRandomNumber;
private long a;
private long c;
private long m;
public static void main(String[] args) {
// perform calculations and tests here
final long seed = 99L;
// Java's java.util.Random class values (according to Wikipedia):
long a = 25214903917L;
long c = 11L;
long m = 2^48L;
LCG lcg = new LCG(a, c, m, seed);
System.out.println("Sequence of LCG class: " + lcg.nextRandom() + ", " + lcg.nextRandom() + ", " + lcg.nextRandom() + ", " + lcg.nextRandom() + ", " + lcg.nextRandom());
}
public LCG(long seed, long a, long c, long m) {
currentRandomNumber = seed;
this.a = a;
this.c = c;
this.m = m;
}
// Implementation of the recurrence relation of the generator
public long nextRandom() {
currentRandomNumber = (a * currentRandomNumber + c) % m;
return currentRandomNumber;
}
我得到的输出是:
Sequence of LCG class: 28, 61, 28, 61, 28
我使用了 a、c 和 m 的这些值,因为我读到 java.util.Random class 也使用了这些值。但是使用这个 class 和相同的种子会给出不同的答案。我还检查了其他 lcg 计算器,我的答案也不符合这些计算器。我不知道哪里出了问题。
LCG需要大模数
Linear congruential generator的关键之一是m
要足够大。或者,您可以快速找到重复的子序列,因为模运算总是会为任何算术级数生成重复的子序列。但是,如果足够大,重复的子序列本身会很长,因此看起来不会重复。
你的
long m = 2^48L;
是 50。^
不符合您的预期。它是 2 XOR 48
而不是 2 的 48 次方。所以使用
long m = 1L << 48; // or (long) Math.pow(2, 48)
相反。然后你会得到
Sequence of LCG class: 2496275487794, 103243855293781, 72264694917948, -37076138618729, -26695784318378
为什么与 java.util.Random
不完全相同
根据我的经验,实现几乎总是带有启发式方法。这是使用 OpenJDK 15 根据 openjdk / 生成 nextInt
eger 使用的启发式方法重新实现您的代码 /
jdk15.特别是根据 lines from 198 to 206.
import java.lang.Math;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
class LCG {
private AtomicLong currentRandomNumber;
//private long a;
//private long c;
//private long m;
private int bits = 32;
private long addend = 0xBL; // your `c` is here!
private long mask = (1L << 48) - 1; // your `m` is here!
private long multiplier = 0x5DEECE66DL; // your `a` is here!
public LCG(long seed, long a, long c, long m) {
currentRandomNumber = new AtomicLong((seed ^ multiplier) & mask);
//this.a = a;
//this.c = c;
//this.m = m;
}
public long nextRandom() {
long oldseed, nextseed;
AtomicLong seed = this.currentRandomNumber;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits)); // your `m` is here again
}
}
public class main {
public static void main(String[] args) {
long seed = 99L;
long a = 25214903917L;
long c = 11L;
long m = (long) Math.pow(2, 48);
LCG lcg = new LCG(seed, a, c, m);
Random random = new Random(seed);
System.out.println(lcg.nextRandom());
System.out.println(random.nextInt());
}
}
如果您使用 OpenJDK 15 编译代码,您将看到 lcg.nextRandom()
和 random.nextInt()
生成相同的整数。在重新实现时,我发现较旧的 OpenJDK 使用不同的启发式方法。
我创建了一个线性同余生成器 (LCG),但它似乎给出了错误的输出。
// Instance variables
private long currentRandomNumber;
private long a;
private long c;
private long m;
public static void main(String[] args) {
// perform calculations and tests here
final long seed = 99L;
// Java's java.util.Random class values (according to Wikipedia):
long a = 25214903917L;
long c = 11L;
long m = 2^48L;
LCG lcg = new LCG(a, c, m, seed);
System.out.println("Sequence of LCG class: " + lcg.nextRandom() + ", " + lcg.nextRandom() + ", " + lcg.nextRandom() + ", " + lcg.nextRandom() + ", " + lcg.nextRandom());
}
public LCG(long seed, long a, long c, long m) {
currentRandomNumber = seed;
this.a = a;
this.c = c;
this.m = m;
}
// Implementation of the recurrence relation of the generator
public long nextRandom() {
currentRandomNumber = (a * currentRandomNumber + c) % m;
return currentRandomNumber;
}
我得到的输出是:
Sequence of LCG class: 28, 61, 28, 61, 28
我使用了 a、c 和 m 的这些值,因为我读到 java.util.Random class 也使用了这些值。但是使用这个 class 和相同的种子会给出不同的答案。我还检查了其他 lcg 计算器,我的答案也不符合这些计算器。我不知道哪里出了问题。
LCG需要大模数
Linear congruential generator的关键之一是m
要足够大。或者,您可以快速找到重复的子序列,因为模运算总是会为任何算术级数生成重复的子序列。但是,如果足够大,重复的子序列本身会很长,因此看起来不会重复。
你的
long m = 2^48L;
是 50。^
不符合您的预期。它是 2 XOR 48
而不是 2 的 48 次方。所以使用
long m = 1L << 48; // or (long) Math.pow(2, 48)
相反。然后你会得到
Sequence of LCG class: 2496275487794, 103243855293781, 72264694917948, -37076138618729, -26695784318378
为什么与 java.util.Random
不完全相同根据我的经验,实现几乎总是带有启发式方法。这是使用 OpenJDK 15 根据 openjdk / 生成 nextInt
eger 使用的启发式方法重新实现您的代码 /
jdk15.特别是根据 lines from 198 to 206.
import java.lang.Math;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
class LCG {
private AtomicLong currentRandomNumber;
//private long a;
//private long c;
//private long m;
private int bits = 32;
private long addend = 0xBL; // your `c` is here!
private long mask = (1L << 48) - 1; // your `m` is here!
private long multiplier = 0x5DEECE66DL; // your `a` is here!
public LCG(long seed, long a, long c, long m) {
currentRandomNumber = new AtomicLong((seed ^ multiplier) & mask);
//this.a = a;
//this.c = c;
//this.m = m;
}
public long nextRandom() {
long oldseed, nextseed;
AtomicLong seed = this.currentRandomNumber;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits)); // your `m` is here again
}
}
public class main {
public static void main(String[] args) {
long seed = 99L;
long a = 25214903917L;
long c = 11L;
long m = (long) Math.pow(2, 48);
LCG lcg = new LCG(seed, a, c, m);
Random random = new Random(seed);
System.out.println(lcg.nextRandom());
System.out.println(random.nextInt());
}
}
如果您使用 OpenJDK 15 编译代码,您将看到 lcg.nextRandom()
和 random.nextInt()
生成相同的整数。在重新实现时,我发现较旧的 OpenJDK 使用不同的启发式方法。