首页 > 解决方案 > 如何在 Java 中生成与 Numpy 相同的伪随机数(对于相同的种子)?

问题描述

当使用相同的种子(例如 12345)时,是否有任何选项可以像在Numpy random中一样在Java中生成相同的随机数。

Numpy中,我得到以下输出的代码:0.9296160928171479

from numpy.random import RandomState
rs = RandomState(12345)
rs.random()

Java中,我得到以下输出的代码:0.3618031071604718

import java.util.Random;
Random random = new Random(12345);
System.out.println(random.nextDouble());

我正在比较SciKit learn中一些方法的输出和我自己的 Java 库。为了生成相同的输出,我需要像 Numpy 那样生成相同的随机数(SciKit learn 使用Numpy random)。

标签: javanumpyrandom

解决方案


传统的NumPy 随机生成器使用Mersenne Twister (MT)算法生成随机位,并将它们转换或解释为所需的类型,而 Java 使用更简单的算法,称为LCG。要使用 Java 获得相同的原始位集,您需要使用来自 Apache 的 Mersenne Twister for Java。MT 生成无符号 32 位整数,然后可以将其转换为其他范围或分布。如果需要一个 64 位精度的数字,则该实现会隐式调用随机数生成器两次并组合结果。

但是,似乎 NumPy 和 Java 中的数字表示不同,因此获得相同的随机位是不够的。

让我们在 Java 和 python 中都使用 MT,并比较结果。首先看一下Java示例代码:

import org.apache.commons.math3.random.RandomGenerator;
import org.apache.commons.math3.random.MersenneTwister;

class QuickStart {
    public static void main(String[] args) {
        RandomGenerator prng = new MersenneTwister(0);
        for (int i = 0; i < 3; ++i) {
            long num = prng.nextLong();
            System.out.println(Long.toString(num) + "\t" + Long.toBinaryString(num));
        }
        System.out.println();
        for (int i = 0; i < 3; ++i) {
            int num = prng.nextInt();
            System.out.println(Integer.toString(num) + "\t" + Integer.toBinaryString(num));
        }
        System.out.println();
        for (int i = 0; i < 3; ++i) {
            double num = prng.nextDouble();
            System.out.println(Double.toString(num) + "\t" + Long.toBinaryString(Double.doubleToRawLongBits(num)));
        }
    }
}

这会产生输出

-8322921849960486353    1000110001111111000010101010110010010111110001001010101000101111
-5253828890213626688    1011011100010110101001100111010111011000001000011100110011000000
-7327722439656189189    1001101001001110101100110100001111011011101000100101001011111011

-1954711869     10001011011111010111011011000011
-656048793      11011000111001010111110101100111
1819583497      1101100011101001010010000001001

0.6235637015982585      11111111100011111101000011101111011101001010101100101010001000
0.38438170310239794     11111111011000100110011011010110110111000000000101101101110000
0.2975346131886989      11111111010011000010101100111010011110010001001011001111000100

而python示例是:

import numpy
import bitstring


numpy.random.seed(0)
for i in range(3):
    state = numpy.random.get_state()
    num = numpy.random.randint(0, 2**64, dtype=numpy.uint64)
    print(num, bin(num), sep="\t")
print()

for i in range(3):
    state = numpy.random.get_state()
    num = numpy.random.randint(0, 2**32, dtype=numpy.uint32)
    print(num, bin(num), sep="\t")
print()

for i in range(3):
    state = numpy.random.get_state()
    num = numpy.random.random()
    f1 = bitstring.BitArray(float=num, length=64)
    print(num, f1.bin, sep="\t")

产生输出:

10123822223749065263    0b1000110001111111000010101010110010010111110001001010101000101111
13192915183495924928    0b1011011100010110101001100111010111011000001000011100110011000000
11119021634053362427    0b1001101001001110101100110100001111011011101000100101001011111011

2340255427      0b10001011011111010111011011000011
3638918503      0b11011000111001010111110101100111
1819583497      0b1101100011101001010010000001001

0.6235636967859723      0011111111100011111101000011101111011010100101010110010101000100
0.3843817072926998      0011111111011000100110011011010110111011100000000010110110111000
0.2975346065444723      0011111111010011000010101100111010010111001000100101100111100010

结果比较

您可以看到原始数字是相同的。二进制值的打印格式略有不同:如果使用内置bin()函数,python 使用 0b,而 bitstring 将一些二进制值放在前面,即前导零。由于整数的有符号/无符号类型不同,表示的值完全不同,但对于双精度数,表示差异很小。通过查看该模式,可以用 Java 或 Python 编写二进制数解释器来模仿其他语言的行为。

初始状态

如果您使用 Mersenne Twister 的另一个实现,请注意初始状态是如何初始化的。我不知道是否记录了 NumPy 的遗留随机初始化,但它似乎使用了Wikipedia 所写的相同内容(如果提供了 32 位整数),这是Makoto Matsumoto 等人建议的方法的一个特例, 2007(检查 eq. 30)。还有其他语言中使用的其他初始化可能性,即使在给定的语言中,不同的组可能会提出不同的实现(MS VC++ 和 GCC 或 boost 的 C++ std Mersenne Twister 实现是不同的)。甚至 NumPy 的新接口也使用了涉及散列函数的不同技术。


推荐阅读