6

这个问题:How to generate a random BigInteger描述了一种实现与 BigIntegers 的 Random.nextInt(int n) 相同语义的方法。

我想对 BigDecimal 和 Random.nextDouble() 做同样的事情。

上述问题中的一个答案建议创建一个随机的 BigInteger,然后从中创建一个具有随机比例的 BigDouble。一个非常快速的实验表明这是一个非常糟糕的主意:)

我的直觉是,使用这种方法需要将整数缩放为n-log10(R),其中 n 是输出中所需的精度位数,R 是随机 BigInteger。这应该允许出现正确的位数,以便(例如)1 -> 10^-64 和 10^64 -> 1。

还需要正确选择缩放值,以使结果落在 [0,1] 范围内。

有没有人这样做过,他们知道结果是否正确分布?有没有更好的方法来实现这一目标?

编辑:感谢@biziclop 纠正了我对 scale 参数的理解。以上不是必需的,恒定的比例因子具有预期的效果。

供以后参考,我的(显然是工作代码)是:

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;
}
4

3 回答 3

3

这当然很容易...如果我只知道你想要什么。对于 [0, 1) 范围内的均匀分布数和精度为 N 的十进制数字,生成一个小于 10* N 的统一 BigInteger,并将其按比例缩小 10 *N。

于 2011-02-04T16:34:06.353 回答
2

我发表了一篇关于生成随机 BigInteger 的帖子Andy Turner 关于生成随机 BigInteger 的答案。我不直接使用它来生成随机 BigDecimal。本质上,我关心的是使用 Random 的独立实例来生成数字中的每个数字。我注意到的一个问题是,使用 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;
}
于 2011-02-17T11:05:17.213 回答
1

我可能在这里遗漏了显而易见的东西,但是如何创建两个随机BigIntegers,一个是整数部分,另一个是小数部分?显然,“分数” bigint 的范围将由您想要允许的精度决定,您无法摆脱固定。

更新:这可以进一步简化为只使用一个随机 bigint。如果您想要一个介于 0 和 n 之间且具有 k 小数精度的随机数(其中 k 是一个常数),您只需生成一个介于 0 和 n*10^k 之间的随机数并将其除以 10^k。

于 2011-02-04T16:22:03.667 回答