如何在 Java 中创建随机 BigDecimal?

发布于 2024-10-15 22:26:34 字数 1144 浏览 4 评论 0原文

这个问题:如何生成随机 BigInteger 描述一种实现与 BigIntegers Random.nextInt(int n) 相同语义的方法。

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

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

我的直觉是,使用此方法需要将整数按诸如 n-log10(R) 的比例缩放,其中 n是输出中所需精度的位数,R 是随机 BigInteger。这应该允许出现正确的位数,以便(例如)1 -> 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;
}

This question: How to generate a random BigInteger describes a way to achieve the same semantics as Random.nextInt(int n) for BigIntegers.

I would like to do the same for BigDecimal and Random.nextDouble().

One answer in the above question suggests creating a random BigInteger and then creating a BigDouble from it with a random scale. A very quick experiment shows this to be a very bad idea :)

My intuition is that using this method would require the integer to be scaled by something like n-log10(R), where n is the number of digits of precision required in the output and R is the random BigInteger. This should allow the correct number of digits to be present so that (for example) 1 -> 10^-64 and 10^64 -> 1.

The scaling value also needs to be chosen correctly for the result to fall in the range [0,1].

Has anyone done this before, and do they know if the results are correctly distributed? Is there a better way to achieve this?

EDIT: Thanks to @biziclop for correcting my understanding of the scale argument. The above isn't necessary, a constant scale factor has the desired effect.

For later reference, my (apparently working code) is:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

似最初 2024-10-22 22:26:34

这肯定很容易...只要我知道你想要什么就好了。对于范围 [0, 1) 和精度 N 个十进制数字的均匀分布数字,生成小于 10*N 的统一 BigInteger,并将其缩小 10*N。

It's surely very easy... if I only knew what you want. For a uniformly distributed number in range [0, 1) and precision N decimal digits generate a uniform BigInteger less than 10*N and scale it down by 10*N.

心如狂蝶 2024-10-22 22:26:34

我发表了一篇关于生成随机 BigInteger Andy特纳关于生成随机 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;
}

I made a post about generating a random BigInteger Andy Turner's answer about generating a random BigInteger. I don't use this directly for generating a random BigDecimal. Essentially my concern is to use independent instances of Random to generate each digit in a number. One problem I noticed is that with Random there are only so many values of and particular number that you get in a row. Also the generation tries to maintain something of an even distribution of generated values. My solution depends on something storing an array or collection of Random instances and calling these. I think this is a good way of going about it and I am trying to find out, so am interested if anyone has any pointers or criticism of this approach.

/**
 *
 * @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;
}
太阳男子 2024-10-22 22:26:34

我可能在这里忽略了明显的内容,但是创建两个随机的 BigInteger 怎么样,一个是整数部分,另一个是小数部分?显然,“小数”bigint 的范围将由您想要允许的精度决定,而您无法摆脱固定。

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

I might be missing the obvious here but how about creating two random BigIntegers, one being the integer part, and the other the fractional? Obviously the range of the "fractional" bigint would be dictated by the precision you want to allow, which you can't get away from pinning down.

Update: This can be further simplified to work with just one random bigint. If you want a random number between 0 and n with k decimal precision (where k is a constant), you just generate a random number between 0 and n*10^k and divide it by 10^k.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文