(點選上方公眾號,可快速關註)
來源:KAAAsS,
blog.kaaass.net/archives/42
其實寫這篇文章的原由是最近準備在Java上寫一個Perlin噪聲的外掛,所以對各種噪聲函式有了一丟丟的瞭解,若有問題還請大家指正。轉載的話希望能註明出處。
註意,本教程中的隨機函式均是形參為整形,傳回值為區間[0,1)內的單精浮點數的函式。測試均為1~10000的隨機數生成速度測試(1D – 輸入x、2D – 輸入x, y)。
更新記錄
2016.1.22 – 初稿。
2016.1.28
(1) – 更新了Wichman-Hill隨機數的演演算法,修改內容。增加了幾個隨機演演算法。
(2) – 統計出了各個方法比較後的分數。
隨機方法
1.Wichman-Hill 隨機數產生器
Excel的隨機函式曾用的方法,參考文獻:
-
Wichman, B.A. 和 I.D. Hill,Algorithm AS 183:An Efficient and Portable Pseudo-Random Number Generator,《Applied Statistics》,31,188-190,1982。
-
Wichman, B.A. 和 I.D. Hill,Building a Random-Number Generator,BYTE,第127-128 頁,1987 年 3 月。
-
Rotz, W. 和 E. Falk,D. Wood 和 J. Mulrow,A Comparison of Random Number Generators Used in Business,發表於 2001 年在佐治亞州亞特蘭大市舉行的“統計學聯合會議”上。
直接上原始碼(2D請前去Github上檢視):
/**
* This is a method of Wichman-Hill random number generator.
*
* @param x
* A seed for generator.
* @return A float random value between [0.0,1.0)
*/
public static float randomWH(java.lang.Integer x) {
int[] seed = new int[3];
seed[0] = (171 * x) % 30269;
seed[1] = (172 * (30000 – x)) % 30307;
seed[2] = (170 * x) % 30323;
return (x / Math.abs(x))
* (seed[0] / 30269.0F + seed[1] / 30307.0F + seed[2] / 30323.0F) % 1.0F;
}
以下是測試結果:
Start testing randomWH(), test: Generate 10000 numbers(1D).
Testing randomWH() completed, using time: 10 ms.
Start testing randomWH(), test: Generate 10000 numbers(2D).
Testing randomWH() completed, using time: 7 ms.
還蠻樂觀,但是影象就…
無論怎麼改,還是呈現了線性的趨勢,波動很小……Orz
2.RSA 隨機數產生器
RSA公鑰演演算法大家都不會不熟悉吧,公認很靠譜的金鑰演演算法。這裡就是用了RSA的隨機演演算法。參考:
Wikipedia – RSA problem
其公式:C = (x * exp P) mod N(P是質數,N是兩個質數之積)
這是Java程式碼:
/**
* This is a method of RSA.
*
* @param x
* A seed for generator.
* @return A float random value between [0.0,1.0)
*/
public static float randomRSA(java.lang.Integer x) {
return (float) (x * Math.exp(seedRSA[0]) % seedRSA[1] / seedRSA[1]);
}
測試結果:
Start testing randomRSA(), test: Generate 10000 numbers(1D).
Testing randomRSA() completed, using time: 10 ms.
Start testing randomRSA(), test: Generate 10000 numbers(2D).
Testing randomRSA() completed, using time: 9 ms.
從影象看出,這個演演算法的隨機性很贊。況且運算速度也不賴,適合使用。
3.Java 隨機數產生器
Java自帶的隨機數(就是java.util.Random類),用過的都知道吧。那就直接上程式碼:
/**
* This is a method of Java random number generator.
*
* @param x
* A seed for generator.
* @return A float random value between [0.0,1.0)
*/
public static float randomJava(java.lang.Integer x) {
return (float) (new java.util.Random(1000 * x).nextDouble()); //乘1000來讓種子間差距增大
}
這是測試資料:
Start testing randomJava(), test: Generate 10000 numbers(1D).
Testing randomJava() completed, using time: 11 ms.
Start testing randomJava(), test: Generate 10000 numbers(2D).
Testing randomJava() completed, using time: 8 ms.
非常優秀的隨機數演演算法,速度快而且基本看不出規律。
4.簡單的隨機數產生器
又是掃蕩Google的戰利品,很抱歉忘記出處惹……程式碼:
/**
* This is a method of basic random generator.
*
* @param x
* A seed for generator.
* @return A float random value between [0.0,1.0)
*/
public static float randomBasic(java.lang.Integer x) {
x = (x << 13) ^ x;
return (float) Math
.abs((1.0 – ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0));
}
測試結果:
Start testing randomBasic(), test: Generate 10000 numbers(1D).
Testing randomBasic() completed, using time: 9 ms.
Start testing randomBasic(), test: Generate 10000 numbers(2D).
Testing randomBasic() completed, using time: 8 ms.
也是非常優秀的演演算法,隨機的效果很棒,且用時也不長。
5.dotNet 隨機數產生器
寫了尼瑪整整一個類啊我擦,不過是原始碼轉寫進來的,也沒費什麼力氣。程式碼:
(DotNetRandom 類)
package kaaass.perlin2d.random;
/**
* This is a random generator which translated from dotNet.
*
* @author dotNet, KAAAsS(Translate)
*
*/
class DotNetRandom {
private final static int MBIG = Integer.MAX_VALUE;
private final static int MSEED = 161803398;
private int inext, inextp;
private int[] SeedArray = new int[56];
public DotNetRandom() {
this((int) System.currentTimeMillis());
}
public DotNetRandom(int Seed) {
int ii;
int mj, mk;
mj = MSEED – Math.abs(Seed);
SeedArray[55] = mj;
mk = 1;
for (int i = 1; i < 55; i++) {
/*
* Apparently the range [1..55] is special (Knuth) and so we’re
* wasting the 0’th position.
*/
ii = (21 * i) % 55;
SeedArray[ii] = mk;
mk = mj – mk;
if (mk < 0)
mk += MBIG;
mj = SeedArray[ii];
}
for (int k = 1; k < 5; k++) {
for (int i = 1; i < 56; i++) {
SeedArray[i] -= SeedArray[1 + (i + 30) % 55];
if (SeedArray[i] < 0) SeedArray[i] += MBIG; } } inext = 0; inextp = 21; Seed = 1; } /** * Return a new random number [0,1) and reSeed the Seed array. * @return A double [0,1) */ protected double rand() { int retVal; int locINext = inext; int locINextp = inextp; if (++locINext >= 56)
locINext = 1;
if (++locINextp >= 56)
locINextp = 1;
retVal = SeedArray[locINext] – SeedArray[locINextp];
if (retVal < 0)
retVal += MBIG;
SeedArray[locINext] = retVal;
inext = locINext;
inextp = locINextp;
/*
* Including this division at the end gives us significantly improved
* random number distribution.
*/
return (retVal * (1.0 / MBIG));
}
}
(呼叫)
/**
* This is a method of doNet random number generator.
*
* @param x
* A seed for generator.
* @return A float random value between [0.0,1.0)
*/
public static float randomDoNet(java.lang.Integer x) {
return (float) new DotNetRandom(1000 * x).rand();
}
測試:
Start testing randomDoNet(), test: Generate 10000 numbers(1D).
Testing randomDoNet() completed, using time: 61 ms.
Start testing randomDoNet(), test: Generate 10000 numbers(2D).
Testing randomDoNet() completed, using time: 109 ms.
目瞪口獃這速度,這質量……是我的鍋嗎……
統計
經過比對之後,終於得到了大致的評分表(已按高低名次排序):
看來Java的綜合實力不差。同樣可以在圖表中看到實際上Basic的效率是最好的,然而與Java的差距也只有1毫秒。但是Java的質量要好上不少。RSA質量不錯,基本和Basic持平,但是速度上還是差了一丟丟。
這裡是對比方法:
-
速度:1-D、2-D隨機數生成速度排序(上圖表格中給出的是ms成績),打出得分:1~5(各佔20%)
-
隨機程度:透過方差以及其他統計分析得出,打出得分:1~5(最好~最差)(佔40%)
-
重覆率:透過對插值影象的分析得出,打出得分:1~5(最好~最差)(佔20%)
-
結果按百分制處理後,求出與100的差為最終成績
當然本對比可能不是嚴謹科學,僅供參考。
附測試程式碼:
public static void testRandom(String method) {
RandomGenerator obj = new RandomGenerator();
Method m;
try {
long t;
// 1-D tests
m = RandomGenerator.class.getMethod(method, Integer.class);
t = System.currentTimeMillis();
System.out.println(“Start testing ” + method
+ “(), test: Generate 10000 numbers(1D).”);
for (int i = 1; i <= 10000; i++) {
m.invoke(obj, i);
}
System.out.println(“Testing ” + method
+ “() completed, using time: “
+ (System.currentTimeMillis() – t) + ” ms.\n”);
// 2-D tests
m = RandomGenerator.class.getMethod(method, Integer.class,
Integer.class);
t = System.currentTimeMillis();
System.out.println(“Start testing ” + method
+ “(), test: Generate 10000 numbers(2D).”);
for (int i = 1; i <= 100; i++) {
for (int ii = 1; ii <= 100; ii++) {
m.invoke(obj, i, ii);
}
}
System.out.println(“Testing ” + method
+ “() completed, using time: “
+ (System.currentTimeMillis() – t) + ” ms.\n”);
// Generate data
m = RandomGenerator.class.getMethod(method, Integer.class);
String s1 = “”;
String s2 = “”;
for (int i = 1; i <= 50; i++) {
if (s1.equals(“”)) {
s1 = String.valueOf(i);
} else {
s1 = s1 + “,” + String.valueOf(i);
}
if (s2.equals(“”)) {
s2 = “” + (float) m.invoke(obj, i);
} else {
s2 = s2 + “,” + (float) m.invoke(obj, i);
}
}
System.out.println(s1);
System.out.println(s2);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
return;
} catch (IllegalAccessException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO 自動生成的 catch 塊
e.printStackTrace();
}
}
詳情請看我的Github。
https://github.com/kaaass/JavaPerlin
看完本文有收穫?請轉發分享給更多人
關註「ImportNew」,提升Java技能