探索 Java 各種隨機函數

2021-03-02 ImportNew

(點擊上方公眾號,可快速關注)

來源: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的隨機算法。參考:

其公式: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技能

相關焦點

  • Java隨機數的幾種有趣用法
    最明顯的,也是直觀的方式,在Java中生成隨機數隻要簡單的調用:java.lang.Math.random()  在所有其他語言中,生成隨機數就像是使用Math工具類,如abs, pow, floor, sqrt和其他數學函數。大多數人通過書籍、教程和課程來了解這個類。一個簡單的例子:從0.0到1.0之間可以生成一個雙精度浮點數。
  • 淺談Java中的幾種隨機數
    眾所周知,隨機數是任何一種程式語言最基本的特徵之一。而生成隨機數的基本方式也是相同的:產生一個0到1之間的隨機數。看似簡單,但有時我們也會忽略了一些有趣的功能。我們從書本上學到什麼?最明顯的,也是直觀的方式,在Java中生成隨機數隻要簡單的調用:java.lang.Math.random() 在所有其他語言中,生成隨機數就像是使用Math工具類,如abs, pow, floor, sqrt和其他數學函數。
  • java生成隨機數的五種方法
    碰巧generator1 和 generator2 使用相同的種子,導致 generator1 以後產生的隨機數每次都和 generator2 以後產生的隨機數相同。:因為種子確定,隨機數算法也確定,因此輸出是確定的!
  • 【編程基礎】C語言產生隨機數需要了解的幾個函數
  • WPS Excel:巧用隨機函數rand和randbetween生成各種數據
    rand和randbetween函數的本職工作是產生隨機小數和隨機整數。然而,應用巧妙的話,還可以獲取隨機姓名、隨機時間,常見的抽獎神器、點菜神器、選擇困難終結器等都可以用它們來完成。基礎用法這兩個函數都非常簡單,「rand()」沒有參數,返回0到1之間的小數,因此「rand()*100」可以得到0到100之間的小數;randbetween函數有兩個參數,分別是最小值和最大值,使用它可以獲得這兩個值之間的整數。
  • 為什麼函數式編程在Java中很危險?
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space  at java.util.Arrays.copyOf(Arrays.java:2760)  at java.util.Arrays.copyOf(Arrays.java:2734)  at java.util.ArrayList.ensureCapacity
  • Java 生成隨機數的 5 種方式,你知道幾種?
    :因為種子確定,隨機數算法也確定,因此輸出是確定的! 結果: 是 JDK 7 之後提供,也是繼承至 java.util.Random。 每一個線程有一個獨立的隨機數生成器,用於並發產生隨機數,能夠解決多個線程發生的競爭爭奪。
  • 「Excel技巧」有了隨機函數rand和randbetween函數,想隨機就隨機
    今天要說的是Excel的兩個隨機函數RAND函數和RANDBETWEEN函數。別小看這兩個函數,它們雖是小函數,但有大能量。因為它們為我們隨機錄入批量數據提供了很大方便。一、Rand函數用途:用於生成0~1之間的隨機數。
  • 啟動一個沒有 main 函數的 java 程序
    為什麼 main 函數是 java 執行入口我們在通過一般的 IDE 去 debug 時,main 函數確實是在堆棧的最開始地方…這種引導啟動的方式,單從最普通 java 程序來看,我們來看下 main 函數作為入口的原因。
  • 乾貨 | Java必背英語單詞不會,別說你是Java程式設計師!
    OO: object-oriented ,面向對象OOP: object-oriented programming,面向對象編程JDK:Java development kit, java開發工具包JVM:java virtual machine ,java虛擬機Compile:編繹Run:運行Class
  • 使用Frida列印Java類函數調用關係
    通過對Android源碼進行閱讀,可以很快發現ART下類函數的四種調用:java調用java,java調用jni,jni調用java,jni調用jni;而java函數又有兩種運行模式:interpreter模式和quick模式。ART對不用的調用過程有著不同的處理邏輯。
  • Java讀取和寫入txt文件
    1 問題描述對於java的讀取和寫入txt一直心存疑惑,隨著知識的積累,又重新進行學習,對java的文件讀寫理解更加深刻,在這裡將自己的小小經驗總結分享給大家。下面是大家了解java流的一個基本框架。2 問題分析在java中,java的讀寫操作(輸入輸出)可以用「流」這個概念來表示,輸入和輸出功能是Java對程序處理數據能力的提高, java的讀寫操作又分為兩種:字符流和字節流。Java以流的形式處理數據。流是一組有序的數據序列,根據操作的類型,分為輸入流和輸出流。
  • mysql隨機函數的例子
    mysql隨機函數的例子,用過mysql的同學都知道rand()函數是最最常見的,要實現隨機數的功能,還非得藉助rand(),它的作用是產生0到1直接的隨機數,下面就列出幾個常見的用例。生成隨機的三位數SELECT ceiling(rand()*899+101)-1 as隨機三位數ceiling是向上取整,rand()為0時,生成的三位數是ceiling(0+101)-1=100
  • Excel:RAND隨機類函數
    Excel 中隨機類函數常用於產生隨機數。隨機數可廣泛用於各類抽獎、抽查及分類分組等活動中。
  • 函數的計算機處理8(13)_3javaJDK14
    計算機語言運用--數值計算8-函數的計算機處理8(13)_3javaJDK14……接上一講……函數有三種基本的表達形式
  • 在EXCEL中隨機函數的利用
    第一節 在EXCEL中隨機函數的利用隨機函數就是產生隨機數的函數,是EXCEL中很重要的函數,應該說Excel和VBA對隨機數的支持都是有限的。在Excel中,可以使用RAND工作表函數返回一個隨機數D,其中0<=D<1。
  • excel隨機數函數是什麼?excel怎樣生成隨機數?
    excel為數據的處理提供了很多函數,今天小編要介紹的是excel隨機數函數,以及隨機數函數的用法,希望對大家有所幫助!excel隨機數二、excel隨機數函數excel中常用的隨機數函數有兩個:RAND()函數和RANDBETWEEN()函數。
  • 自從學會Java中的lambda表達式和函數式編程技巧,再也不用加班了!
    本教程不會詳細介紹那些你之前沒有學過(也可能學過)的非lambda的語言特性,但是我會使用它們來進行演示,比如:java.lang.Math類。因此如果要學習那些非lambda之外的知識,建議先閱讀JDK 12 API文檔去了解更多信息。Lambda初識一個lambda表達式描述了一個可以傳遞給構造函數或方法以便後續執行的代碼塊(一個匿名函數)。
  • Excel中隨機函數的應用!
    我們工作中在講解案例或者分析數據,經常要錄入一些模擬數字,特別是一些Excel培訓師,若一個個的錄入,效率肯定會低,其實Excel給我們提供了很多函數可以幫助我們。的平均分布的隨機實數。每次計算工作表時都會返回一個新的隨機實數。語法RAND 函數語法沒有參數。
  • JAVA8——JAVA成長之路
    在這篇教程中,我們將一一探索這些變化,並用真實的例子說明它們適用的場景。同時,你也可以通過把參數類型與參數包括在括號中的形式直接給出參數的類型:在某些情況下lambda的函數體會更加複雜,這時可以把函數體放到在一對花括號中,就像在Java中定義普通函數一樣。