2011-02-04 244 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; 
}