2

我创建了一个线性同余生成器 (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 类也使用这些值。但是使用具有相同种子的此类会给出不同的答案。我还检查了其他 lcg 计算器,我的答案也不匹配。我不知道出了什么问题。

4

1 回答 1

2

LCG 需要大模数

线性同余生成器的关键之一是它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 不完全相同

以我的经验,实现几乎总是带有启发式。nextInt这是使用 OpenJDK 15根据openjdk / jdk15生成 eger 所使用的启发式方法重新实现您的代码。特别是根据从 198 到 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 使用不同的启发式算法。

于 2021-01-27T16:27:34.617 回答