更好的整数随机数生成器
Better random number generator for Integers
目前,我正在使用随机数生成 JPanel 中组件的位置。我发现使用 Random.nextInt(6000);
返回了太多小于 100 的数字,太多了,我的意思是其中绝大多数小于 100。所以我调查了一下,发现了一个质量更高的随机数生成器使用 Long
所以我冒昧地正确格式化它(见下面的实现)。之后,我在 1000-2000 范围内得到了太多数字。
使用计数器变量进行测试后,当我生成 2000 个数字时,大约有 1350 个数字在该范围内。
我需要一个更随机的数字生成器,甚至是 真正随机的(我不希望得到,但如果我能得到就好了)。
随机Class:
public class HighQualityRandom extends Random {
private static final long serialVersionUID = 1L;
private Lock l = new ReentrantLock();
private long u;
private long v = 4101842887655102017L;
private long w = 1;
public HighQualityRandom() {
this(System.nanoTime());
}
public HighQualityRandom(long seed) {
l.lock();
u = seed ^ v;
nextLong();
v = u;
nextLong();
w = v;
nextLong();
l.unlock();
}
public long nextLong() {
l.lock();
try {
u = u * 2862933555777941757L + 7046029254386353087L;
v ^= v >>> 17;
v ^= v << 31;
v ^= v >>> 8;
w = 4294957665L * (w & 0xffffffff) + (w >>> 32);
long x = u ^ (u << 21);
x ^= x >>> 35;
x ^= x << 4;
long ret = (x + v) ^ w;
return ret;
} finally {
l.unlock();
}
}
public int next(int bits) {
return (int) (nextLong() >>> (64-bits));
}
}
实施:
public int getRandomX(){
HighQualityRandom ran = new HighQualityRandom();
int rand = (int) ran.nextLong();
String p = rand + "";
if(rand < 0){
rand = rand * -1;
}
p = rand + "";
p = p.substring(0, 4);
rand = Integer.parseInt(p);
while(rand > 6144){
rand = (int) ran.nextLong();
p = rand + "";
if(rand < 0){
rand = rand * -1;
}
p = rand + "";
p = p.substring(0,4);
rand = Integer.parseInt(p);
}
System.out.print("X is: " + rand + " Y is: ");
return rand;
}
public int getRandomY(){
HighQualityRandom ran = new HighQualityRandom();
int rand = (int) ran.nextLong();
String p = rand + "";
if(rand < 0){
rand = rand * -1;
}
p = rand + "";
p = p.substring(0, 4);
rand = Integer.parseInt(p);
while(rand > 4608){
rand = (int) ran.nextLong();
p = rand + "";
if(rand < 0){
rand = rand * -1;
}
p = rand + "";
p = p.substring(0,4);
rand = Integer.parseInt(p);
}
System.out.println(rand);
return rand;
}
关于我对 long
所做的操作以获取我的 4 位数号码的解释:
- 如果是负数,乘以负数1。
- 将其设为字符串。
- 前 4 位数字的子串
- 如果太大:再做一次。
附带说明一下,seed
参数应该在 Random
class 中是什么?我试着用谷歌搜索它,人们在谈论加密的东西......
编辑:
使用 java.util.random() 实现:
public int getRandomX(){
Random ran = new Random();
int rand = ran.nextInt(6144);
if(rand <= 100){
Game.between++;
}
return rand;
}
public int getRandomY(){
Random ran = new Random();
int rand = ran.nextInt(4608);
if(rand <= 100){
Game.between++;
}
System.out.println(Game.between);
return rand;
}
平均而言,其中约有 37-40 个少于 100 个(满分 1000 个)。所以我错了,绝对不是 50% 的人在所说的盒子上或外面。但是:在 28311552
种可能的解决方案(地图为 6144x4608)中可供选择,似乎太多了,不能停留在 <100。
我不知道你在做什么,但这是一个小示例程序:
Random r = new Random();
int count = 0;
for (int i = 0; i < 1000_000; i++)
if (r.nextInt(6000) < 100)
count++;
System.out.println(count);
调用 Random.nextInt(6000)
一百万次。结果是:
16655
理想情况下为:100/5999 * 1000000 = 16669
。我认为这非常接近。
你做错了什么。请 post 你的原始代码证明 Random.nextInt(6000)
在超过 50% 的情况下给出的结果小于 100。
出于在游戏中生成随机数的目的,java.util.Random
is perfectly fine. There is also java.security.SecureRandom
但游戏不需要它,而且它的速度也很慢。
编辑:
对您的编辑:您总是在创建一个新的 Random
实例!不要那样做。如果您总是创建一个新的 Random
实例并且您没有指定种子值,它将根据您计算机的当前时间播种。这本身会减少生成器的 "randomness"。
按照我的示例进行操作:创建一个 Random
实例,可选择将其播种到您想要的任何位置,并始终使用它!
编辑#2:
种子:类似于随机数生成器的"state"。引用自 Random
class javadoc:
If two instances of Random are created with the same seed, and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers.
实际上,如果你想重现相同的序列,你只需要设置种子。例如,您保存了一个游戏/重播,并且您想要重新玩游戏。在这种情况下,您可以将起始种子与 game/replay 一起保存,当再次玩游戏时,您将设置保存的种子,因此您将获得相同的随机序列。
您的数字剥离问题导致了您的问题。 XOR 移位生成器工作正常,但它的范围是 64 位——2 的幂,而不是 10 的幂,因此没有理由认为其输出的十进制数字是随机的。因此,取随机值并使用 mod(10000) 而不是挑选十进制数字。
99% 的程序员对数学的理解不够好,无法改进 RNG 或加密算法。不幸的是,大约 10% 的程序员认为他们属于那 1% 的人。寓意:不要乱用 RNG,按预期使用它并仔细阅读文档。
目前,我正在使用随机数生成 JPanel 中组件的位置。我发现使用 Random.nextInt(6000);
返回了太多小于 100 的数字,太多了,我的意思是其中绝大多数小于 100。所以我调查了一下,发现了一个质量更高的随机数生成器使用 Long
所以我冒昧地正确格式化它(见下面的实现)。之后,我在 1000-2000 范围内得到了太多数字。
使用计数器变量进行测试后,当我生成 2000 个数字时,大约有 1350 个数字在该范围内。
我需要一个更随机的数字生成器,甚至是 真正随机的(我不希望得到,但如果我能得到就好了)。
随机Class:
public class HighQualityRandom extends Random {
private static final long serialVersionUID = 1L;
private Lock l = new ReentrantLock();
private long u;
private long v = 4101842887655102017L;
private long w = 1;
public HighQualityRandom() {
this(System.nanoTime());
}
public HighQualityRandom(long seed) {
l.lock();
u = seed ^ v;
nextLong();
v = u;
nextLong();
w = v;
nextLong();
l.unlock();
}
public long nextLong() {
l.lock();
try {
u = u * 2862933555777941757L + 7046029254386353087L;
v ^= v >>> 17;
v ^= v << 31;
v ^= v >>> 8;
w = 4294957665L * (w & 0xffffffff) + (w >>> 32);
long x = u ^ (u << 21);
x ^= x >>> 35;
x ^= x << 4;
long ret = (x + v) ^ w;
return ret;
} finally {
l.unlock();
}
}
public int next(int bits) {
return (int) (nextLong() >>> (64-bits));
}
}
实施:
public int getRandomX(){
HighQualityRandom ran = new HighQualityRandom();
int rand = (int) ran.nextLong();
String p = rand + "";
if(rand < 0){
rand = rand * -1;
}
p = rand + "";
p = p.substring(0, 4);
rand = Integer.parseInt(p);
while(rand > 6144){
rand = (int) ran.nextLong();
p = rand + "";
if(rand < 0){
rand = rand * -1;
}
p = rand + "";
p = p.substring(0,4);
rand = Integer.parseInt(p);
}
System.out.print("X is: " + rand + " Y is: ");
return rand;
}
public int getRandomY(){
HighQualityRandom ran = new HighQualityRandom();
int rand = (int) ran.nextLong();
String p = rand + "";
if(rand < 0){
rand = rand * -1;
}
p = rand + "";
p = p.substring(0, 4);
rand = Integer.parseInt(p);
while(rand > 4608){
rand = (int) ran.nextLong();
p = rand + "";
if(rand < 0){
rand = rand * -1;
}
p = rand + "";
p = p.substring(0,4);
rand = Integer.parseInt(p);
}
System.out.println(rand);
return rand;
}
关于我对 long
所做的操作以获取我的 4 位数号码的解释:
- 如果是负数,乘以负数1。
- 将其设为字符串。
- 前 4 位数字的子串
- 如果太大:再做一次。
附带说明一下,seed
参数应该在 Random
class 中是什么?我试着用谷歌搜索它,人们在谈论加密的东西......
编辑:
使用 java.util.random() 实现:
public int getRandomX(){
Random ran = new Random();
int rand = ran.nextInt(6144);
if(rand <= 100){
Game.between++;
}
return rand;
}
public int getRandomY(){
Random ran = new Random();
int rand = ran.nextInt(4608);
if(rand <= 100){
Game.between++;
}
System.out.println(Game.between);
return rand;
}
平均而言,其中约有 37-40 个少于 100 个(满分 1000 个)。所以我错了,绝对不是 50% 的人在所说的盒子上或外面。但是:在 28311552
种可能的解决方案(地图为 6144x4608)中可供选择,似乎太多了,不能停留在 <100。
我不知道你在做什么,但这是一个小示例程序:
Random r = new Random();
int count = 0;
for (int i = 0; i < 1000_000; i++)
if (r.nextInt(6000) < 100)
count++;
System.out.println(count);
调用 Random.nextInt(6000)
一百万次。结果是:
16655
理想情况下为:100/5999 * 1000000 = 16669
。我认为这非常接近。
你做错了什么。请 post 你的原始代码证明 Random.nextInt(6000)
在超过 50% 的情况下给出的结果小于 100。
出于在游戏中生成随机数的目的,java.util.Random
is perfectly fine. There is also java.security.SecureRandom
但游戏不需要它,而且它的速度也很慢。
编辑:
对您的编辑:您总是在创建一个新的 Random
实例!不要那样做。如果您总是创建一个新的 Random
实例并且您没有指定种子值,它将根据您计算机的当前时间播种。这本身会减少生成器的 "randomness"。
按照我的示例进行操作:创建一个 Random
实例,可选择将其播种到您想要的任何位置,并始终使用它!
编辑#2:
种子:类似于随机数生成器的"state"。引用自 Random
class javadoc:
If two instances of Random are created with the same seed, and the same sequence of method calls is made for each, they will generate and return identical sequences of numbers.
实际上,如果你想重现相同的序列,你只需要设置种子。例如,您保存了一个游戏/重播,并且您想要重新玩游戏。在这种情况下,您可以将起始种子与 game/replay 一起保存,当再次玩游戏时,您将设置保存的种子,因此您将获得相同的随机序列。
您的数字剥离问题导致了您的问题。 XOR 移位生成器工作正常,但它的范围是 64 位——2 的幂,而不是 10 的幂,因此没有理由认为其输出的十进制数字是随机的。因此,取随机值并使用 mod(10000) 而不是挑选十进制数字。
99% 的程序员对数学的理解不够好,无法改进 RNG 或加密算法。不幸的是,大约 10% 的程序员认为他们属于那 1% 的人。寓意:不要乱用 RNG,按预期使用它并仔细阅读文档。