java - 如何在 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)。
解决方案
传统的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 的新接口也使用了涉及散列函数的不同技术。
推荐阅读
- batch-file - 尝试创建一个 Windows 批处理脚本,该脚本仅循环通过文件夹中特定级别的目录
- flutter - 使用 BLoC 作为全局状态管理的最佳方式
- java - 未使用局部变量类别的值
- python - 如何使 pd.MultiIndex 垂直合并列索引?
- kubernetes - 使用客户端 Postico 2 连接到运行 Postgres 的 GKE POD
- qt - QSvgRenderer 使用的是旧版本的 SVG
- python - selenium python 3.7(未解决的导入:webdriver)
- java - 哪种设计模式最适合用于将模型与行为分开?
- linux - 如何挂载Linux系统时间文件?
- python - python说缺少参数,但实际上有一个参数