Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating random BigDecimal value from given range

I need to generate random BigDecimal value from given range. How to do it in Java?

like image 238
Marcin Sanecki Avatar asked Feb 16 '11 23:02

Marcin Sanecki


3 Answers

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();
    }
}
like image 132
corsiKa Avatar answered Sep 21 '22 19:09

corsiKa


I do this that way

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

And the way I run it:

BigDecimal random = Application.generateRandomBigDecimalFromRange(
    new BigDecimal(-1.21).setScale(2, BigDecimal.ROUND_HALF_UP),
    new BigDecimal(21.28).setScale(2, BigDecimal.ROUND_HALF_UP)
);
like image 35
masterdany88 Avatar answered Sep 18 '22 19:09

masterdany88


Previous answers don't address the loss of precision that results from scaling values with arbitrarily large numbers of digits by double precision floating point values with a relatively small number of digits. The following implementation of BigRandom can generate random BigInteger and BigDecimal values at specified precision:

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

    }
}

The following example attempts to clarify the reason why previous answers omit very large intervals of potentially valid random values.

Please consider an arbitrarily small epsilon value:

e = x 10^(-infinity)

and an interval:

[0 + e, 1 + e].

As we try to approximate this conceptual range with larger and larger numeric values substituted in place of infinity, the scale of the range's endpoints grows well beyond the scale of Math.random() which returns double precision floating point numbers.

Conceptually:

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())

If Math.random() returns 0, randBigDecimal equals: e; If Math.random() returns Double.MIN_VALUE, randBigDecimal equals: Double.MIN_VALUE + e.

We can innumerate all possible values of randBigDecimal like so:

double d = 0.0;

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

The more that the scale of e exceeds the scale of Java's double precision floating point numeric type, i.e. the primary motivation for using BigDecimal, the larger the gaps this algorithm leaves between e + d and e + Math.nextUp(d).

In any case, this algorithm always leaves out min + 2e, min + 3e, ..., min + (N-1)e, min + Ne. For all integers [2, N] such that (new BigDecimal(N).times(new BigDecimal(Double.MIN_VALUE))).scale() > e.scale().

Of course, many infinities of numbers exist between e and 2e, but we might like our random BigDecimal algorithm to cover at least all values with the same scale as Math.max(min.scale(), MAX.scale()).

like image 36
Ben McKenneby Avatar answered Sep 19 '22 19:09

Ben McKenneby