从给定范围生成随机BigDecimal值

Mar*_*cki 7 java bigdecimal

我需要从给定范围生成随机BigDecimal值.怎么用Java做?

mas*_*y88 8

我这样做

public static BigDecimal generateRandomBigDecimalFromRange(BigDecimal min, BigDecimal max) {
    BigDecimal randomBigDecimal = min.add(new BigDecimal(Math.random()).multiply(max.subtract(min)));
    return randomBigDecimal.setScale(2,BigDecimal.ROUND_HALF_UP);
}
Run Code Online (Sandbox Code Playgroud)

以及我运行它的方式:

BigDecimal random = Application.generateRandomBigDecimalFromRange(
    new BigDecimal(-1.21).setScale(2, BigDecimal.ROUND_HALF_UP),
    new BigDecimal(21.28).setScale(2, BigDecimal.ROUND_HALF_UP)
);
Run Code Online (Sandbox Code Playgroud)


cor*_*iKa 5

class BigDecRand {
    public static void main(String[] args) {
        String range = args[0];
        BigDecimal max = new BigDecimal(range + ".0");
        BigDecimal randFromDouble = new BigDecimal(Math.random());
        BigDecimal actualRandomDec = randFromDouble.divide(max,BigDecimal.ROUND_DOWN);

        BigInteger actualRandom = actualRandomDec.toBigInteger();
    }
}
Run Code Online (Sandbox Code Playgroud)


Ben*_*eby 5

以前的答案没有解决由于将具有任意大量数字的值缩放为具有相对较少数字的双精度浮点值而导致的精度损失。BigRandom 的以下实现可以生成指定精度的随机 BigInteger 和 BigDecimal 值:

// The short version
public static BigDecimal between(BigDecimal min, BigDecimal MAX) {
  int digitCount = Math.max(min.precision(), MAX.precision());
  int bitCount = (int)(digitCount / Math.log10(2.0));

  // convert Random BigInteger to a BigDecimal between 0 and 1
  BigDecimal alpha = new BigDecimal(
    new BigInteger( bitCount, new Random() )
  ).movePointLeft(digitCount);

  return min.add(MAX.subtract(min).multiply(alpha, new MathContext(digitCount)));
}
Run Code Online (Sandbox Code Playgroud)
// Full Implementation
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.Random;

public class BigRandom {

    private static Random defaultRandom = new Random();

    // Constants:
    private static double log2 = Math.log10(2.0);

    // Computes number of bits needed to represent an n digit positive integer.

    private static int bitCount(int n) {
        return (int)( n / log2 );
    }

    // Static Methods for generating Random BigInteger values:

    public static BigInteger nextBigInteger(int precision) {
        return nextBigInteger(precision, defaultRandom);
    }

    public static BigInteger nextBigInteger(int precision, Random r) {
        return new BigInteger(bitCount(precision), r);
    }

    public static BigInteger nextBigInteger(BigInteger norm) {
        return nextBigInteger(norm, defaultRandom);
    }

    public static BigInteger nextBigInteger(BigInteger norm, Random r) {
        BigDecimal bdNorm = new BigDecimal(norm);
        int precision = bdNorm.precision() - bdNorm.scale();
        return bdNorm.multiply(nextBigDecimal(precision, r), new MathContext(precision + 1)).toBigInteger();
    }

    public static BigInteger between(BigInteger min, BigInteger MAX) {
        return between(min, MAX, defaultRandom);
    }

    public static BigInteger between(BigInteger min, BigInteger MAX, Random r) {
        return min.add( nextBigInteger( MAX.subtract(min), r ) );
    }

    // Static Methods for generating Random BigDecimal values:

    public static BigDecimal nextBigDecimal(int scale) {
        return nextBigDecimal(scale, defaultRandom);
    }

    public static BigDecimal nextBigDecimal(int scale, Random r) {
        BigInteger bi = nextBigInteger(scale, r);  // generate random BigInteger with a number of digits equal to scale.
        BigDecimal bd = new BigDecimal(bi);  // convert BigInteger to a BigDecimal
        return bd.movePointLeft(bd.precision());  // move the decimal point all the way to the left
    }

    public static BigDecimal nextBigDecimal(BigDecimal norm, int scale) {
        return nextBigDecimal(norm, scale, defaultRandom);
    }

    public static BigDecimal nextBigDecimal(BigDecimal norm, int scale, Random r) {
        return norm.multiply( nextBigDecimal( scale, r ), new MathContext( (norm.precision() - norm.scale()) + scale) );
    }

    public static BigDecimal between(BigDecimal min, BigDecimal MAX) {
        return between(min, MAX, defaultRandom);
    }

    public static BigDecimal between(BigDecimal min, BigDecimal MAX, Random r) {
        return min.add(
            nextBigDecimal(
                MAX.subtract(min),
                Math.max( min.precision(), MAX.precision() ),
                r
            )
        );
    }


    public static void main(String[] args) {
        // Make a BigInteger independently from this implementation.
        int bc = ((150 - defaultRandom.nextInt(50)) * 8) - defaultRandom.nextInt(8);
        BigInteger bi = new BigInteger(bc, defaultRandom);
        String bistr = bi.toString();
        int precision = bistr.length();

        System.out.println("Independently generated random BigInteger:\n" + bistr);
        System.out.println("\tprecision: " + bistr.length());

        System.out.println("\n\n------------------------\n\n");

        // demonstrate nextBigInteger(precision)
        System.out.println("demonstrate nextBigInteger(precision = " + precision + "):\n");
        for (int i = 0; i < 5; i++) {
            BigInteger bii = nextBigInteger(precision);
            String biistr = bii.toString();
            System.out.println("iteration " + i + " nextBigInteger(precision = " + precision + "):\n\t" + biistr);
            System.out.println("\tprecision: " + biistr.length() + " == " + precision + " : " + ( biistr.length() == precision ));
        }

        System.out.println("\n\n------------------------\n\n");

        // demonstrate nextBigInteger(norm)
        System.out.println("demonstrate nextBigInteger(\n\tnorm = " + bi + "\n):\n");
        for (int i = 0; i < 5; i++) {
            BigInteger bii = nextBigInteger(bi);
            String biistr = bii.toString();
            System.out.println("iteration " + i + " nextBigInteger(norm = ... ):\n\t" + biistr);
            System.out.println("\tprecision: " + biistr.length() + " <= " + precision + " : " + ( biistr.length() <= precision ));
            System.out.println("\t( bii <= bi ) = " + (bii.compareTo(bi) <= 0));
        }

        BigInteger bin = bi.negate();

        System.out.println("\n\n------------------------\n\n");

        // demonstrate between(min, MAX)
        System.out.println("demonstrate between(\n\tmin = " + bin + ",\n\tMAX = " + bi + "\n):\n");
        for (int i = 0; i < 5; i++) {
            BigInteger bii = between(bin, bi);
            String biistr = bii.toString();
            System.out.println("iteration " + i + " between(norm = ... ):\n\t" + biistr);
            System.out.println("\tprecision: " + biistr.length() + " <= " + precision + " : " + ( biistr.length() <= precision ));
            System.out.println("\t( bii >= -bi ) = " + (bii.compareTo(bin) >= 0));
            System.out.println("\t( bii < bi ) = " + (bii.compareTo(bi) < 0));
        }

        System.out.println("\n\n------------------------\n\n");

        // Make a BigDecimal independently from this implementation.
        BigDecimal bd = new BigDecimal(Double.MAX_VALUE);
        for (int i = 10; i < 50; i = i + 10) {
            bd = bd.add( new BigDecimal(defaultRandom.nextDouble()).pow(i) );
        }

        System.out.println("Independently generated random BigDecimal:\n" + bd);
        System.out.println("\tprecision: " + bd.precision() + " scale: " + bd.scale());

        System.out.println("\n\n------------------------\n\n");

        // demonstrate nextBigDecimal(scale)
        System.out.println("demonstrate nextBigDecimal(scale = " + bd.scale() + "):\n");
        for (int i = 0; i < 5; i++) {
            BigDecimal bdi = nextBigDecimal(bd.scale());
            System.out.println("iteration " + i + " nextBigDecimal(scale = " + bd.scale() + "):\n\t" + bdi);
            System.out.println("\tprecision: " + bdi.precision() + " scale: " + bdi.scale());
        }

        System.out.println("\n\n------------------------\n\n");

        // demonstrate nextBigDecimal(norm, scale)
        System.out.println("demonstrate nextBigDecimal(\n\tnorm = " + bd + ",\n\tscale = " + bd.scale() + "\n):\n");
        for (int i = 0; i < 5; i++) {
            BigDecimal bdi = nextBigDecimal(bd, bd.scale());
            System.out.println("iteration " + i + " nextBigDecimal(norm = ..., scale = " + bd.scale() + "):\n\t" + bdi);
            System.out.println("\tprecision: " + bdi.precision() + " scale: " + bdi.scale());
            System.out.println("\t( bdi <= bd ) = " + (bdi.compareTo(bd) <= 0));
        }

        System.out.println("\n\n------------------------\n\n");

        // demonstrate between(min, MAX)
        BigDecimal bdn = bd.negate();
        System.out.println("demonstrate between(\n\tmin = " + bdn + ",\n\tMAX = " + bd + "\n):\n");
        for (int i = 0; i < 5; i++) {
            BigDecimal bdi = between(bdn, bd);
            System.out.println("iteration " + i + " between(-bd, bd):\n\t" + bdi);
            System.out.println("\tprecision: " + bdi.precision() + " scale: " + bdi.scale());
            System.out.println("\t( bdi >= -bd ) = " + (bdi.compareTo(bdn) >= 0));
            System.out.println("\t( bdi < bd ) = " + (bdi.compareTo(bd) < 0));
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

以下示例试图阐明为什么先前的答案省略了非常大的潜在有效随机值间隔的原因。

请考虑任意小的 epsilon 值:

e = x 10^(-infinity)

和一个区间:

[0 + e, 1 + e]

当我们尝试用越来越大的数值代替无穷大来近似这个概念范围时,范围端点的范围远远超出Math.random()返回双精度浮点数的范围。

从概念上讲:

BigDecimal e = ...  // arbitrarily small value.
BigDecimal min = new BigDecimal(0.0).add(e);
BigDecimal MAX = new BigDecimal(1.0).add(e);

BigDecimal norm = MAX.subtract(min);  // 1.0

BigDecimal randBigDecimal = min.add(
  norm.multiply(
    new BigDecimal(
      Math.random()
    )
  )
); // equivalent to e + (1.0 * Math.random())

Run Code Online (Sandbox Code Playgroud)

如果Math.random()返回0,则randBigDecimal等于:e;如果Math.random()返回 Double.MIN_VALUE,则 randBigDecimal 等于:Double.MIN_VALUE + e

我们可以像这样计算 randBigDecimal 的所有可能值:

double d = 0.0;

// Don't actually run this loop!  :)
while (d < 1.0) {
  System.out.println(e + d);
  d = Math.nextUp(d);
}
Run Code Online (Sandbox Code Playgroud)

e 的范围超出 Java 双精度浮点数值类型的范围(即使用 BigDecimal 的主要动机)越多,该算法在e + d和 之间留下的差距就越大e + Math.nextUp(d)

无论如何,该算法总是忽略 min + 2e、min + 3e、...、min + (N-1)e、min + Ne。对于所有整数 [2, N] 使得(new BigDecimal(N).times(new BigDecimal(Double.MIN_VALUE))).scale() > e.scale().

当然,e 和 2e 之间存在许多无穷大的数字,但我们可能希望我们的随机 BigDecimal 算法至少覆盖与 具有相同规模的所有值Math.max(min.scale(), MAX.scale())