-->
当前位置:首页 > DayDayUp > 正文内容

python实现 LCG算法 生成与java.util.Random相同的随机数序列

Luz1年前 (2023-11-28)DayDayUp1376

java.util.Random中随机数生成方法

在Java中,java.util.Random类的内部实现主要基于伪随机数生成器
(Pseudorandom Number Generator,简称PRNG)。
具体来说,Random类使用了一个48位的随机数种子(seed),
并通过线性同余法(Linear Congruential Generator,简称LCG)生成伪随机数序列。

下面是Random类的简化版本的部分源码:

package java.util;
public class Random implements java.io.Serializable {
    private static final long serialVersionUID = 3905348978240129619L;
    private static final long multiplier = 0x5DEECE66DL;
    private static final long addend = 0xBL;
    private static final long mask = (1L << 48) - 1;
    private long seed;
    public Random(long seed) {
        this.seed = (seed ^ multiplier) & mask;
    }
    protected int next(int bits) {
        seed = (seed * multiplier + addend) & mask;
        return (int) (seed >>> (48 - bits));
    }
    public int nextInt() {
        return next(32);
    }
    public int nextInt(int bound) {
        if (bound <= 0)
            throw new IllegalArgumentException("bound must be positive");
        if ((bound & -bound) == bound)  // i.e., bound is a power of 2
            return (int) ((bound * (long) next(31)) >> 31);
        int bits, val;
        do {
            bits = next(31);
            val = bits % bound;
        } while (bits - val + (bound-1) < 0);
        return val;
    }
    public long nextLong() {
        return ((long) next(32) << 32) + next(32);
    }
    public double nextDouble() {
        return (((long) next(26) << 27) + next(27)) * DOUBLE_UNIT;
    }
    private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
    public float nextFloat() {
        return next(24) / ((float)(1 << 24));
    }
    public boolean nextBoolean() {
        return next(1) != 0;
    }
    public double nextGaussian() {
        if (haveNextNextGaussian) {
            haveNextNextGaussian = false;
            return nextNextGaussian;
        } else {
            double v1, v2, s;
            do {
                v1 = 2 * nextDouble() - 1; // between -1 and 1
                v2 = 2 * nextDouble() - 1; // between -1 and 1
                s = v1 * v1 + v2 * v2;
            } while (s >= 1 || s == 0);
            double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s) / s);
            nextNextGaussian = v2 * multiplier;
            haveNextNextGaussian = true;
            return v1 * multiplier;
        }
    }
    private boolean haveNextNextGaussian = false;
    private double nextNextGaussian;
    // 其他方法...
}

在构造函数中,传递给构造函数的种子经过一系列位运算和算术运算后被存储在seed变量中。

next方法使用线性同余法更新seed并返回指定位数的伪随机数。

python实现与java对照

class JavaRandomCompat:
    def __init__(self, seed=None):
        if seed is None:
            seed = (int((id(self) + id(seed)) * 997) & ((1 << 48) - 1))
        self.seed = (seed ^ 0x5DEECE66D) & ((1 << 48) - 1)

    def next(self, bits):
        self.seed = (self.seed * 0x5DEECE66D + 0xB) & ((1 << 48) - 1)
        value = self.seed >> (48 - bits)
        return value if value < (1 << (bits - 1)) else value - (1 << bits)

    def next_int(self):
        return self.next(32)

    def next_long(self):
        return (self.next(32) << 32) + self.next(32)

    def next_float(self):
        return self.next(24) / (1 << 24)

    def next_double(self):
        return ((self.next(26) << 27) + self.next(27)) * (1.0 / (1 << 53))
# 使用示例
java_random_compat = JavaRandomCompat(seed=1)
for i in range(5):
    print("随机整数:", java_random_compat.next_int())

python随机数种子2测试

image.png

java对照代码

import java.util.Random;
public class RandomExample {
    public static void main(String[] args) {
        Random random = new Random(1);
        for (int i = 0; i < 5; i++) {
            int randomNumber = random.nextInt();
            System.out.println("Random number: " + randomNumber);
        }
    }
}

java随机数种子2测试

image.png

可以看到,生成的序列完全相同

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。