简体   繁体   English

如何在Java中创建随机的BigDecimal?

[英]How can I create a random BigDecimal in Java?

This question: How to generate a random BigInteger describes a way to achieve the same semantics as Random.nextInt(int n) for BigIntegers. 这个问题: 如何生成随机BigInteger描述了一种与BigIntegers实现与Random.nextInt(int n)相同的语义的方法。

I would like to do the same for BigDecimal and Random.nextDouble(). 我想对BigDecimal和Random.nextDouble()做同样的事情。

One answer in the above question suggests creating a random BigInteger and then creating a BigDouble from it with a random scale. 上述问题的一个答案建议创建一个随机的BigInteger,然后从中以随机比例创建一个BigDouble。 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. 我的直觉是,使用此方法将需要对整数进行 n-log10(R)类的缩放,其中n是输出中所需精度的位数,R是随机BigInteger。 This should allow the correct number of digits to be present so that (for example) 1 -> 10^-64 and 10^64 -> 1. 这应该允许显示正确的数字位数,以便(例如)1-> 10 ^ -64和10 ^ 64-> 1。

\n

The scaling value also needs to be chosen correctly for the result to fall in the range [0,1]. 还需要正确选择缩放值,以使结果落在[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. 编辑:感谢@biziclop纠正了我对scale参数的理解。 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;
}

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. 对于范围为[0,1)的均匀分布的数字和精度为N的十进制数字,生成的统一BigInteger小于10 * N,并将其按比例缩小10 * N。

I made a post about generating a random BigInteger Andy Turner's answer about generating a random BigInteger . 我发表了一篇有关生成随机BigInteger的帖子, 安迪·特纳(Andy Turner)关于生成随机BigInteger的答案 I don't use this directly for generating a random BigDecimal. 我不会直接将其用于生成随机的BigDecimal。 Essentially my concern is to use independent instances of Random to generate each digit in a number. 本质上,我关心的是使用Random的独立实例来生成数字中的每个数字。 One problem I noticed is that with Random there are only so many values of and particular number that you get in a row. 我注意到的一个问题是,在Random中,连续出现的数值和特定数字如此之多。 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;
}

I might be missing the obvious here but how about creating two random BigInteger s, one being the integer part, and the other the fractional? 我可能在这里找不到明显的地方,但是如何创建两个随机的BigInteger ,一个是整数部分,另一个是小数部分呢? 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. 显然,“小数” bigint的范围将由您要允许的精度所决定,而您无法摆脱这种局限。

Update: This can be further simplified to work with just one random bigint. 更新:可以进一步简化为仅使用一个随机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. 如果您想要一个0到n之间的随机数,精度为k,十进制精度为k(其中k是一个常数),则只需生成一个介于0到n * 10 ^ k之间的随机数并将其除以10 ^ k。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM