我创建了一个名为QuickRandom
的类,它的任务是快速生成随机数。它非常简单:只需取旧值,乘以一个双精度
,然后取小数部分。
以下是我的QuickRandom
课程的全部内容:
public class QuickRandom {
private double prevNum;
private double magicNumber;
public QuickRandom(double seed1, double seed2) {
if (seed1 >= 1 || seed1 < 0) throw new IllegalArgumentException("Seed 1 must be >= 0 and < 1, not " + seed1);
prevNum = seed1;
if (seed2 <= 1 || seed2 > 10) throw new IllegalArgumentException("Seed 2 must be > 1 and <= 10, not " + seed2);
magicNumber = seed2;
}
public QuickRandom() {
this(Math.random(), Math.random() * 10);
}
public double random() {
return prevNum = (prevNum*magicNumber)%1;
}
}
下面是我为测试它而编写的代码:
public static void main(String[] args) {
QuickRandom qr = new QuickRandom();
/*for (int i = 0; i < 20; i ++) {
System.out.println(qr.random());
}*/
//Warm up
for (int i = 0; i < 10000000; i ++) {
Math.random();
qr.random();
System.nanoTime();
}
long oldTime;
oldTime = System.nanoTime();
for (int i = 0; i < 100000000; i ++) {
Math.random();
}
System.out.println(System.nanoTime() - oldTime);
oldTime = System.nanoTime();
for (int i = 0; i < 100000000; i ++) {
qr.random();
}
System.out.println(System.nanoTime() - oldTime);
}
这是一个非常简单的算法,只需将前一个两倍乘以一个“神奇的数字”两倍。我很快就把它组合在一起,所以我可能会做得更好,但奇怪的是,它似乎运行良好。
这是main
方法中注释掉的行的示例输出:
0.612201846732229
0.5823974655091941
0.31062451498865684
0.8324473610354004
0.5907187526770246
0.38650264675748947
0.5243464344127049
0.7812828761272188
0.12417247811074805
0.1322738256858378
0.20614642573072284
0.8797579436677381
0.022122999476108518
0.2017298328387873
0.8394849894162446
0.6548917685640614
0.971667953190428
0.8602096647696964
0.8438709031160894
0.694884972852229
嗯,很随意。事实上,这对游戏中的随机数生成器是有效的。
下面是未注释输出部分的示例输出:
5456313909
1427223941
哇!它的执行速度几乎是数学的4倍。随机
。
我记得在某个地方读到过关于数学的书。随机使用系统。nanoTime()
以及大量疯狂的模和除法。真的有必要吗?我的算法执行得更快,而且看起来很随机。
Math。random(随机)
当看起来只是简单的乘法和去掉小数就足够了的时候,你会这么做吗
您的随机数函数很差,因为它的内部状态太少——函数在任何给定步骤中输出的数字完全依赖于前一个数字。例如,如果我们假设magicNumber
是2(作为示例),那么序列:
0.10 -> 0.20
类似的序列强烈反映了这一点:
0.09 -> 0.18
0.11 -> 0.22
在许多情况下,这将在游戏中产生明显的相关性——例如,如果连续调用函数来生成对象的X和Y坐标,对象将形成清晰的对角线模式。
除非您有充分的理由相信随机数生成器正在减慢您的应用程序(这是非常不可能的),否则没有充分的理由尝试编写自己的。
你所描述的是一种叫做线性同余发生器的随机发生器。发电机的工作原理如下:
这个生成器有很多很好的特性,但是作为一个好的随机源,它有很多重要的问题。上面链接的维基百科文章描述了一些优势和劣势。简而言之,如果你需要好的随机值,这可能不是一个很好的方法。
希望这有帮助!
您的QuickRandom
实现并没有真正的统一分布。当Math时,较低值的频率通常较高。random()
的分布更加均匀。以下是一个SSCCE,它显示:
package com.stackoverflow.q14491966;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws Exception {
QuickRandom qr = new QuickRandom();
int[] frequencies = new int[10];
for (int i = 0; i < 100000; i++) {
frequencies[(int) (qr.random() * 10)]++;
}
printDistribution("QR", frequencies);
frequencies = new int[10];
for (int i = 0; i < 100000; i++) {
frequencies[(int) (Math.random() * 10)]++;
}
printDistribution("MR", frequencies);
}
public static void printDistribution(String name, int[] frequencies) {
System.out.printf("%n%s distribution |8000 |9000 |10000 |11000 |12000%n", name);
for (int i = 0; i < 10; i++) {
char[] bar = " ".toCharArray(); // 50 chars.
Arrays.fill(bar, 0, Math.max(0, Math.min(50, frequencies[i] / 100 - 80)), '#');
System.out.printf("0.%dxxx: %6d :%s%n", i, frequencies[i], new String(bar));
}
}
}
平均结果如下所示:
QR distribution |8000 |9000 |10000 |11000 |12000
0.0xxx: 11376 :#################################
0.1xxx: 11178 :###############################
0.2xxx: 11312 :#################################
0.3xxx: 10809 :############################
0.4xxx: 10242 :######################
0.5xxx: 8860 :########
0.6xxx: 9004 :##########
0.7xxx: 8987 :#########
0.8xxx: 9075 :##########
0.9xxx: 9157 :###########
MR distribution |8000 |9000 |10000 |11000 |12000
0.0xxx: 10097 :####################
0.1xxx: 9901 :###################
0.2xxx: 10018 :####################
0.3xxx: 9956 :###################
0.4xxx: 9974 :###################
0.5xxx: 10007 :####################
0.6xxx: 10136 :#####################
0.7xxx: 9937 :###################
0.8xxx: 10029 :####################
0.9xxx: 9945 :###################
如果你重复测试,你会发现QR分布变化很大,这取决于初始种子,而MR分布是稳定的。有时,它会达到所需的均匀分布,但通常不会。下面是一个更极端的例子,它甚至超出了图的边界:
QR distribution |8000 |9000 |10000 |11000 |12000
0.0xxx: 41788 :##################################################
0.1xxx: 17495 :##################################################
0.2xxx: 10285 :######################
0.3xxx: 7273 :
0.4xxx: 5643 :
0.5xxx: 4608 :
0.6xxx: 3907 :
0.7xxx: 3350 :
0.8xxx: 2999 :
0.9xxx: 2652 :
问题内容: 使用cPickle读取一个1 GB的NetworkX图形数据结构花了我一个小时的时间(将它作为二进制pickle文件存储在磁盘上时为1 GB)。 请注意,文件会快速加载到内存中。换句话说,如果我运行: 如何加快上一个操作的速度? 请注意,我已经尝试使用两种二进制协议(1和2)对数据进行腌制,并且似乎对使用哪种协议并没有太大的影响。还要注意,尽管我在上面使用“ loads”(意思是“ l
问题内容: 方法链接 是对象方法返回对象本身以使结果被另一个方法调用的实践。像这样: 这似乎被认为是一种好习惯,因为它会产生可读的代码或“流畅的界面”。但是,对我而言,它似乎打破了面向对象本身所隐含的对象调用表示法-生成的代码不代表对先前方法的结果执行的动作,通常这是面向对象的代码的工作方式: 这种差异设法为“调用结果对象”的点标记创建了两种不同的含义:在链接的上下文中,以上示例将被视为保存参与者
问题内容: 我可以依靠地图的随机迭代顺序在Web应用程序中实现客户端的随机“配对”吗?我试过环顾四周,但似乎找不到这种随机性有多大的细分。 该算法将类似于: 当连接的客户端> 1000个时,这是否足够?还是应该维护一个单独的客户端片段并从中随机选择? 问题答案: 尽管它说是随机的(随机的)(spec,blog,hashmap源,另一个blog,SO),但分布远非完美。 为什么?因为我们喜欢地图 快
在求树的直径时,我们考虑以下最大值: 1:左子树的直径 2:右子树的直径 3:左子树高度右子树高度1. 为什么这三个是必要的?为什么是3。光靠自己是不够的。让我们看一个简单的3节点树和2节点树的例子。在前一个例子中,仅第3点就给出了1=3。而在后一种情况下,仅第3点就给出0 1=2。 在这种情况下,为什么我们需要找到三的最大值。请解释
我读到种子是用来初始化随机数生成器的。但似乎种子的随机性对于从生成器获得良好的随机性并不重要。所以我想明白什么是种子?为什么这么叫?最后,为什么计算机系统中的时间被用来产生这样的种子?
问题内容: 为了在工作中进行演示,我想比较NodeJS和C的性能。这是我写的: Node.js(for.js): 我使用GCC编译for.c并运行它: 结果: 然后我在NodeJS中尝试了它: 结果: 在运行了无数次之后,我发现无论如何它都是成立的。如果我将for.c切换double为long在循环中使用a而不是a ,则C花费的时间甚至更长! 不是试图发动火焰战争,但是为什么执行相同操作的Node