2011-02-04 249 views
5

此問題:How to generate a random BigInteger描述了一種爲BigIntegers實現與Random.nextInt(int n)相同的語義的方法。如何在Java中創建隨機BigDecimal?

我想爲BigDecimal和Random.nextDouble()做同樣的事情。

上述問題中的一個答案建議創建一個隨機BigInteger,然後用隨機比例創建一個BigDouble。一個非常快的實驗表明這是一個非常糟糕的主意:)

我的直覺是,使用這種方法需要整數通過像 n-log10(R),其中n是精度數字在所要求的數量進行縮放輸出,R是隨機BigInteger。這應該允許存在正確的數字位數,以便(例如)1 - > 10^-64和10^64 - > 1.

縮放值也需要正確選擇以使結果下降在[0,1]的範圍內。

有沒有人做過這件事,他們知道結果是否正確分發?有沒有更好的方法來實現這一目標?

編輯:感謝@biziclop糾正我對尺度參數的理解。以上是不必要的,恆定比例因子具有期望的效果。

爲便於以後參考,我的(顯然是工作的代碼)是:

private static BigDecimal newRandomBigDecimal(Random r, int precision) { 
    BigInteger n = BigInteger.TEN.pow(precision); 
    return new BigDecimal(newRandomBigInteger(n, r), precision); 
} 

private static BigInteger newRandomBigInteger(BigInteger n, Random rnd) { 
    BigInteger r; 
    do { 
     r = new BigInteger(n.bitLength(), rnd); 
    } while (r.compareTo(n) >= 0); 

    return r; 
} 

回答

3

這當然很容易...如果我只知道你想要什麼。對於範圍[0,1]中的均勻分佈數字和精度爲N的十進制數字,將生成一個小於10 * N的統一BigInteger,並將其縮小10 * N。

1

我可能會在這裏缺少明顯的,但有關創建兩個隨機BigInteger S,一個是整數部分,其他的分數怎麼樣?很明顯,「小數」bigint的範圍將取決於您想要允許的精度,您無法擺脫這種精度。

更新:這可以進一步簡化,只用一個隨機bigint。如果你想得到一個0到n之間的隨機數,並且具有k十進制精度(其中k是一個常數),你只需要生成一個介於0到n * 10^k之間的隨機數併除以10^k。

+0

這樣做的結果是不均勻分佈的。我試過了,結果是均勻分佈在小數部分的,這意味着10^-27與0.01到0.1之間的數字出現在結果中的可能性相同。 10^-27應該比範圍在0.1-0.01之間的數字出現的可能性要小26個數量級 – 2011-02-04 16:25:16

+0

@Mike Houston我錯過了顯而易見的,因爲我仍然不明白。你希望它是否均勻分佈? – biziclop 2011-02-04 16:28:24

+0

@Mike Houston Nope,依然不明白。如果採用一個最多n位數的均勻分佈的變量,並將其除以10^n,它仍然是均勻分佈的。 – biziclop 2011-02-04 16:31:32

2

我發了一篇關於生成隨機BigInteger Andy Turner's answer about generating a random BigInteger的文章。我不直接使用它來生成一個隨機BigDecimal。基本上我的擔心是使用Random的獨立實例來生成數字中的每個數字。我注意到的一個問題是,隨着隨機,你只能獲得如此多的數值和特定的數字。此外,這一代人試圖保持生成值的均勻分佈。我的解決方案取決於存儲一個數組或隨機實例集合並調用它們的東西。我認爲這是一個很好的解決方法,我試圖找出答案,所以如果有人對這種方法有任何指點或批評,我會感興趣。

/** 
* 
* @param a_Random 
* @param decimalPlaces 
* @param lowerLimit 
* @param upperLimit 
* @return a pseudo randomly constructed BigDecimal in the range from 
* lowerLimit to upperLimit inclusive and that has up to decimalPlaces 
* number of decimal places 
*/ 
public static BigDecimal getRandom(
     Generic_Number a_Generic_Number, 
     int decimalPlaces, 
     BigDecimal lowerLimit, 
     BigDecimal upperLimit) { 
    BigDecimal result; 
    BigDecimal range = upperLimit.subtract(lowerLimit); 
    BigDecimal[] rangeDivideAndRemainder = 
      range.divideAndRemainder(BigDecimal.ONE); 
    BigInteger rangeInt = rangeDivideAndRemainder[0].toBigIntegerExact(); 
    BigInteger intComponent_BigInteger = Generic_BigInteger.getRandom(
      a_Generic_Number, 
      rangeInt); 
    BigDecimal intComponent_BigDecimal = 
      new BigDecimal(intComponent_BigInteger); 
    BigDecimal fractionalComponent; 
    if (intComponent_BigInteger.compareTo(rangeInt) == 0) { 
     BigInteger rangeRemainder = 
       rangeDivideAndRemainder[1].toBigIntegerExact(); 
     BigInteger fractionalComponent_BigInteger = 
       Generic_BigInteger.getRandom(a_Generic_Number, rangeRemainder); 
     String fractionalComponent_String = "0."; 
     fractionalComponent_String += fractionalComponent_BigInteger.toString(); 
     fractionalComponent = new BigDecimal(fractionalComponent_String); 
    } else { 
     fractionalComponent = getRandom(
       a_Generic_Number, decimalPlaces); 
    } 
    result = intComponent_BigDecimal.add(fractionalComponent); 
    result.add(lowerLimit); 
    return result; 
} 

/** 
* Provided for convenience. 
* @param a_Generic_BigDecimal 
* @param decimalPlaces 
* @return a random BigDecimal between 0 and 1 inclusive which can have up 
* to decimalPlaces number of decimal places 
*/ 
public static BigDecimal getRandom(
     Generic_Number a_Generic_Number, 
     int decimalPlaces) { 
    //Generic_BigDecimal a_Generic_BigDecimal = new Generic_BigDecimal(); 
    Random[] random = a_Generic_Number.get_RandomArrayMinLength(
      decimalPlaces); 
    //System.out.println("Got Random[] size " + random.length); 
    String value = "0."; 
    int digit; 
    int ten_int = 10; 
    for (int i = 0; i < decimalPlaces; i++) { 
     digit = random[i].nextInt(ten_int); 
     value += digit; 
    } 
    int length = value.length(); 
    // Tidy values ending with zero's 
    while (value.endsWith("0")) { 
     length--; 
     value = value.substring(0, length); 
    } 
    if (value.endsWith(".")) { 
     value = "0"; 
    } 
    BigDecimal result = new BigDecimal(value); 
    //result.stripTrailingZeros(); 
    return result; 
}