2016-07-23 83 views
0

在Java中,相對於簡單的算術運算需要多長時間才能用Math.random()生成一個數字?我試圖隨機分配一個ArrayList中的對象,這個對象已經包含了一些值,這樣就可以創建一個平均的,但不是完全均勻的分佈,我不確定是否使用Math.random()爲每個插入點選擇一個隨機索引是最好的方法。Math.random()運行的時間與簡單算術運算的時間相比如何?

澄清:插入對象的分配,就是要甚至沒有足夠的值不都集中在一個區域,也夠不均衡的分佈是不可預測的(如果有人要經過值一個由一個,他們將無法通過檢測一個常量模式來確定下一個值是否將成爲新插入的值)。

+0

的Math.random()給你一個隨機位置。所以「有點」,甚至是不會發生的每一次......如果你想有一個一致的輸出(均勻分佈)\你應該創建自己的操作... – DarkV1

+2

只需基準!它的速度比添加的要慢,但速度足夠快,可以多次採樣。如果您需要一個以上的隨機樣本,則一次調用這些樣本會更快(例如,創建一個隨機池)。我不知道你在做什麼,但我懷疑這裏有更多的算法問題。例如:很難實現正確的隨機播放。 – sascha

+2

生成一個隨機值(通過Math.random)是一個相當常見的操作,如果沒有很好的理由,也不應該擔心。它看起來像不成熟的優化。你有什麼其他的方法? – zoom

回答

4

不要使用Math.random。它依賴於使用AtomicLong的全局實例java.util.Random。儘管java.util.Random中使用的PRNG算法是pretty simple,但性能主要受原子CAS和相關緩存一致性流量的影響。

對於多線程應用程序(如this example),這可能特別糟糕,但即使在單線程情況下也有懲罰。

ThreadLocalRandom總是優於Math.random。它不依賴於原子操作,也不會遭受爭用。它只更新thread-local state並使用一對arithmetic and bitwise operations

這是一個JMH基準,用於將Math.random()ThreadLocalRandom.current().nextDouble()的性能與簡單的算術運算進行比較。

package bench; 

import org.openjdk.jmh.annotations.*; 
import java.util.concurrent.ThreadLocalRandom; 

@State(Scope.Thread) 
public class RandomBench { 
    double x = 1; 

    @Benchmark 
    public double multiply() { 
     return x * Math.PI; 
    } 

    @Benchmark 
    public double mathRandom() { 
     return Math.random(); 
    } 

    @Benchmark 
    public double threadLocalRandom() { 
     return ThreadLocalRandom.current().nextDouble(); 
    } 
} 

結果表明,ThreadLocalRandom工作在短短的幾納秒,其性能相當於一個簡單的算術運算,並在不同的Math.random多線程環境完美比例。

Benchmark      Threads  Score  Error Units 
RandomBench.mathRandom   1  34.265 ± 1.709 ns/op 
RandomBench.multiply    1  4.531 ± 0.108 ns/op 
RandomBench.threadLocalRandom  1  8.322 ± 0.047 ns/op 

RandomBench.mathRandom   2  366.589 ± 63.899 ns/op 
RandomBench.multiply    2  4.627 ± 0.118 ns/op 
RandomBench.threadLocalRandom  2  8.342 ± 0.079 ns/op 

RandomBench.mathRandom   4  1328.472 ± 177.216 ns/op 
RandomBench.multiply    4  4.592 ± 0.091 ns/op 
RandomBench.threadLocalRandom  4  8.474 ± 0.157 ns/op 
2

Java文檔報告此爲Random.nextDouble()的實現,這是Math.random()最終調用的內容。

public double nextDouble() { 
    return (((long)next(26) << 27) + next(27)) 
    /(double)(1L << 53); 
} 

如果未來更新種子(seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)並返回(int)(seed >>> (48 - bits))

正如您所看到的,它使用簡單的算法來生成僞隨機值。它只需要幾個便宜的操作,所以我不會擔心使用它。

+1

儘管該算法非常簡單,但其性能受[原子CAS]影響(http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/a11ab21bb799/src/share/classes/java/util /Random.java#l200),即使在單線程應用程序中也會有性能損失,更不用說多線程情況下可能出現的爭用。 – apangin

1

創建一個隨機數是一個簡單的操作,你不應該擔心它。

但是你應該記住幾件事情

  • 這是更好地重用隨機實例,創建每次你需要一個隨機值,通常是一個錯誤的決定時間new Random()實例。

  • 但是不要在同一個線程中同時使用同一個隨機實例來避免爭用,您可以使用ThreadLocalRandom.current()代替。

  • 如果您使用圓頂密碼術,請改用SecureRandom