I need the count the number of decimal digits of a BigInteger. For example:
99 returns 2
1234 returns 4
9999 returns 4
12345678901234567890 returns 20
I need to do this for a BigInteger with 184948 decimal digits and more. How can I do this fast and scalable?
The convert-to-String approach is slow:
public String getWritableNumber(BigInteger number) {
// Takes over 30 seconds for 184948 decimal digits
return "10^" + (number.toString().length() - 1);
}
This loop-devide-by-ten approach is even slower:
public String getWritableNumber(BigInteger number) {
int digitSize = 0;
while (!number.equals(BigInteger.ZERO)) {
number = number.divide(BigInteger.TEN);
digitSize++;
}
return "10^" + (digitSize - 1);
}
Are there any faster methods?
Here's a fast method based on Dariusz's answer:
public static int getDigitCount(BigInteger number) {
double factor = Math.log(2) / Math.log(10);
int digitCount = (int) (factor * number.bitLength() + 1);
if (BigInteger.TEN.pow(digitCount - 1).compareTo(number) > 0) {
return digitCount - 1;
}
return digitCount;
}
The following code tests the numbers 1, 9, 10, 99, 100, 999, 1000, etc. all the way to ten-thousand digits:
public static void test() {
for (int i = 0; i < 10000; i++) {
BigInteger n = BigInteger.TEN.pow(i);
if (getDigitCount(n.subtract(BigInteger.ONE)) != i || getDigitCount(n) != i + 1) {
System.out.println("Failure: " + i);
}
}
System.out.println("Done");
}
This can check a BigInteger with 184,948 decimal digits and more in well under a second.
This looks like it is working. I haven't run exhaustive tests yet, n'or have I run any time tests but it seems to have a reasonable run time.
public class Test {
/**
* Optimised for huge numbers.
*
* http://en.wikipedia.org/wiki/Logarithm#Change_of_base
*
* States that log[b](x) = log[k](x)/log[k](b)
*
* We can get log[2](x) as the bitCount of the number so what we need is
* essentially bitCount/log[2](10). Sadly that will lead to inaccuracies so
* here I will attempt an iterative process that should achieve accuracy.
*
* log[2](10) = 3.32192809488736234787 so if I divide by 10^(bitCount/4) we
* should not go too far. In fact repeating that process while adding (bitCount/4)
* to the running count of the digits will end up with an accurate figure
* given some twiddling at the end.
*
* So here's the scheme:
*
* While there are more than 4 bits in the number
* Divide by 10^(bits/4)
* Increase digit count by (bits/4)
*
* Fiddle around to accommodate the remaining digit - if there is one.
*
* Essentially - each time around the loop we remove a number of decimal
* digits (by dividing by 10^n) keeping a count of how many we've removed.
*
* The number of digits we remove is estimated from the number of bits in the
* number (i.e. log[2](x) / 4). The perfect figure for the reduction would be
* log[2](x) / 3.3219... so dividing by 4 is a good under-estimate. We
* don't go too far but it does mean we have to repeat it just a few times.
*/
private int log10(BigInteger huge) {
int digits = 0;
int bits = huge.bitLength();
// Serious reductions.
while (bits > 4) {
// 4 > log[2](10) so we should not reduce it too far.
int reduce = bits / 4;
// Divide by 10^reduce
huge = huge.divide(BigInteger.TEN.pow(reduce));
// Removed that many decimal digits.
digits += reduce;
// Recalculate bitLength
bits = huge.bitLength();
}
// Now 4 bits or less - add 1 if necessary.
if ( huge.intValue() > 9 ) {
digits += 1;
}
return digits;
}
// Random tests.
Random rnd = new Random();
// Limit the bit length.
int maxBits = BigInteger.TEN.pow(200000).bitLength();
public void test() {
// 100 tests.
for (int i = 1; i <= 100; i++) {
BigInteger huge = new BigInteger((int)(Math.random() * maxBits), rnd);
// Note start time.
long start = System.currentTimeMillis();
// Do my method.
int myLength = log10(huge);
// Record my result.
System.out.println("Digits: " + myLength+ " Took: " + (System.currentTimeMillis() - start));
// Check the result.
int trueLength = huge.toString().length() - 1;
if (trueLength != myLength) {
System.out.println("WRONG!! " + (myLength - trueLength));
}
}
}
public static void main(String args[]) {
new Test().test();
}
}
Took about 3 seconds on my Celeron M laptop so it should hit sub 2 seconds on some decent kit.
I think that you could use bitLength() to get a log2 value, then change the base to 10.
The result may be wrong, however, by one digit, so this is just an approximation.
However, if that's acceptable, you could always add 1 to the result and bound it to be at most. Or, subtract 1, and get at least.
You can first convert the BigInteger to a BigDecimal and then use this answer to compute the number of digits. This seems more efficient than using BigInteger.toString() as that would allocate memory for String representation.
private static int numberOfDigits(BigInteger value) {
return significantDigits(new BigDecimal(value));
}
private static int significantDigits(BigDecimal value) {
return value.scale() < 0
? value.precision() - value.scale()
: value.precision();
}
This is an another way to do it faster than Convert-to-String method. Not the best run time, but still reasonable 0.65 seconds versus 2.46 seconds with Convert-to-String method (at 180000 digits).
This method computes the integer part of the base-10 logarithm from the given value. However, instead of using loop-divide, it uses a technique similar to Exponentiation by Squaring.
Here is a crude implementation that achieves the runtime mentioned earlier:
public static BigInteger log(BigInteger base,BigInteger num)
{
/* The technique tries to get the products among the squares of base
* close to the actual value as much as possible without exceeding it.
* */
BigInteger resultSet = BigInteger.ZERO;
BigInteger actMult = BigInteger.ONE;
BigInteger lastMult = BigInteger.ONE;
BigInteger actor = base;
BigInteger incrementor = BigInteger.ONE;
while(actMult.multiply(base).compareTo(num)<1)
{
int count = 0;
while(actMult.multiply(actor).compareTo(num)<1)
{
lastMult = actor; //Keep the old squares
actor = actor.multiply(actor); //Square the base repeatedly until the value exceeds
if(count>0) incrementor = incrementor.multiply(BigInteger.valueOf(2));
//Update the current exponent of the base
count++;
}
if(count == 0) break;
/* If there is no way to multiply the "actMult"
* with squares of the base (including the base itself)
* without keeping it below the actual value,
* it is the end of the computation
*/
actMult = actMult.multiply(lastMult);
resultSet = resultSet.add(incrementor);
/* Update the product and the exponent
* */
actor = base;
incrementor = BigInteger.ONE;
//Reset the values for another iteration
}
return resultSet;
}
public static int digits(BigInteger num)
{
if(num.equals(BigInteger.ZERO)) return 1;
if(num.compareTo(BigInteger.ZERO)<0) num = num.multiply(BigInteger.valueOf(-1));
return log(BigInteger.valueOf(10),num).intValue()+1;
}
Hope this will helps.
Related
I posted earlier trying to bruteforce it, but it didn't work. Here's my attempt #2 with recursion (first time using recursive methods). Please help!
Here's what happens: The code runs fine, with smaller numbers, but when we get up to one million, the code simply will run, and nothint at all happens. In Eclipse, it still gives me the option to end, but I've let it run for a very long time with nothing helping.
/**
* The following iterative sequence is defined for the set of positive
* integers:
*
* n → n/2 (n is even) n → 3n + 1 (n is odd)
*
* Using the rule above and starting with 13, we generate the following
* sequence: 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1
*
* It can be seen that this sequence (starting at 13 and finishing at 1)
* contains 10 terms. Although it has not been proved yet (Collatz Problem),
* it is thought that all starting numbers finish at 1.
*
* Which starting number, under one million, produces the longest chain?
*
* NOTE: Once the chain starts the terms are allowed to go above one
* million.
*/
public class Euler14 {
static int desiredMax = 1000000;
static int maxTerm = 0;
static int maxNumberOfTerms = 0;
static int currentNumber = 0;
static int numberOfTerms = 0;
public static void doMath(int startingNumber) {
if(startingNumber == 1) {
System.out.print( maxTerm + " " + maxNumberOfTerms);
}
else {
currentNumber = desiredMax;
while(currentNumber!= 1) {
if(currentNumber%2 == 0) {
currentNumber = currentNumber/2;
numberOfTerms++;
} else {
currentNumber = (3 * currentNumber) + 1;
numberOfTerms++;
}
}
numberOfTerms++;
if(numberOfTerms > maxNumberOfTerms) {
maxNumberOfTerms = numberOfTerms;
maxTerm = startingNumber;
}
desiredMax--;
doMath(desiredMax);
}
}
public static void main(String[] args) {
doMath(desiredMax);
}
}
There are many wrong things with your code :
use of a recursive method which is no more no less than a loop going downward
use of static variables
numberOfTerms never reinitialized
as pointed by azurefrog, you have an integer overflow which is causing an infinite loop.
I was rearranging your code with as few changes as possible when he came up with the answer, so all I can do now is to show you a working code very similar to yours. See how cleaner it is this way :
public class Euler14 {
public static void main(String[] args) {
int maxTerm = 1000000;
int maxNumberOfTerms = 1;
// this loop replaces your recursion, which is not needed here and quite costly even if it is tail-recursion
for (int i = maxTerm ; i >= 2; i--) {
int numberOfTerms = 0;
// declare as long to prevent the overflow
long currentNumber = i;
while (currentNumber != 1) {
if (currentNumber % 2 == 0)
currentNumber = currentNumber / 2;
else
currentNumber = (3 * currentNumber) + 1;
numberOfTerms++;
if (numberOfTerms > maxNumberOfTerms) {
maxNumberOfTerms = numberOfTerms;
maxTerm = i;
}
}
}
System.out.println(maxTerm);
}
}
The main problem is that you are trying to do math on large numbers with ints. When your program gets down to a desiredMax of 999167, you're going into an infinite loop.
In Java, the largest value an int can represent is 2,147,483,647.
When your algorithm gets to 999167, it quickly exceeds that limit.
If you print the value of currentNumber in your inner while-loop, you see this:
...
1330496806
665248403
1995745210
997872605
-1301349480 <-- oops
-650674740
-325337370
...
You are trying to set currentNumber to 2,993,617,816, so your value is going to overflow.
This causes your while-loop to never terminate, since you don't account for negative numbers. You quickly settle into a repeating sequence of
-25
-74
-37
-110
-55
-164
-82
-41
-122
-61
-182
-91
-272
-136
-68
-34
-17
-50
-25
... ad infinitum
You could try switching to a bigger numerical representation (long), but, even if you switch to using long values, the way you are trying to recurse will cause a stack overflow long before you ever finish trying to evaluate a desiredMax of 1000000. (On my box, I get a StackOverflowError when I get down to 997474).
You need to go back and rethink the structure of your program. Recursion can be a useful tool, but it's dangerous to use unless you know that you aren't going to go too deep.
This is a good example of where you can employ Memoization.
Below is a solution that uses recursion, but avoids the need to continually go over paths you've calculated already.
This also separates the chain-calculation code from the searching-for-the-maximum code.
public class Euler14 {
static long[] records = new long[1000000];
// //////////////////////////////////////////////
// Recursively calculates one chain length
//
static long getLength(long n) {
// Terminating condition
if (n == 1) {
return n;
}
// Have we already calculated this?
if ((n < records.length) && (records[(int) n] != 0)) {
return records[(int) n];
}
// Haven't calculated this yet, so calculate it now
long length = getLength(n % 2 == 0 ? n / 2 : 3 * n + 1) + 1;
// Record the result for later use
if (n < records.length) {
records[(int) n] = length;
}
return length;
}
static long calculateQuestionFourteen() {
long maxLength = 0;
long maxStart = 0;
for (long i = 1; i < 1000000; ++i) {
long thisLength = getLength(i);
if (thisLength > maxLength) {
maxLength = thisLength;
maxStart = i;
}
}
return maxStart;
}
public static void main(String[] args) {
long start = System.currentTimeMillis();
System.out.println(calculateQuestionFourteen());
System.out.println(System.currentTimeMillis() - start);
}
}
Suppose I have Long someLong = 1004L. What efficient method can I use to round this down to 1000L? Note that I do not actually know that someLong == 1004L so I can't simply do someLong -= 4L;. I need a generalizable method. I also want the ability to round down to each 5 instead of each 10, for example a function to round to 1005L (since if we're rounding by 5's then it'll round up instead of down).
More examples .. It could be that I have 1926L and I want to round to 5 meaning I need 1925L. Or I need to round to 10 meaning I need 1930L.
This is very simple.
If you want to round always down:
Your required formula is:
someLong-someLong%10
It is because someLong%10 is the remainder of someLong divided by 10. If you get this from the original number, you get the downrounded value, which you wanted.
The generalization is also simple: you can use 100, or even 13, if you want.
If you want to rounding in another direction (for example, rounding always up or always to the middle), then first to add something to this number, and then round always down.
If you want to round always up:
Then first you need to first add 9, then round always down.
someLong+9-(someLong+9)%10
If you want to round always to the middle:
...also you want to round to the nearest neightbor. Then you first add the half of the required interval, then round always down. For example, for 10 it is:
someLong+5-(someLong+5)%10
If you want to round a value towards the nearest multiple of step using the semantics of BigDecimal.ROUND_HALF_UP (if exactly halfway between two steps, round up), the necessary calculations are:
val += step/2;
val -= val%step;
Try this:
double a=1002l;
double b=a/10;
a=Math.round(b)*10;
System.out.println("Double round of value : "+a);
A generic function to round to the nearest multiple of k would be (works for positives only):
public static long round(long toRound, long k) {
long times = toRound / k;
long reminder = toRound % k;
if (reminder < k / 2) {
return k * times;
} else {
return k * (times + 1);
}
}
And a branchless variant (reminder < k / 2 => (2 * reminder / k) < 1:
public static long round(long toRound, long k) {
long times = toRound / k;
long reminder = toRound % k;
return k * (times + ((2 * reminder) / k));
}
The following example reachs what you need:
public static void main(String[] args) {
Long n = 1004L;
Long n2 = 1005L;
n = round(n);
n2 = round(n2);
System.out.println(n);
System.out.println(n2);
}
private static Long round(Long n) {
if (n%10 <=4) {
return n -=n%10;
} else {
return n += (10-n%10);
}
}
myFloor(long n, int m) {
return n - (n % m);
}
myRound(long n, int m) {
int i = (n % m) >= (m / 2) ? m : 0;
return n + i - (n % m);
}
so m could be 10 , 5 , ...
I was doing problem 14 on Project Euler (note: I'm not looking for a solution to the Project Euler problem) when I ran into an interesting stack overflow exception.
My non-probabilistic approach worked just fine but when I attempted the same problem with a probabilistic approach I ran into the stack overflow exception. The funny thing is that the exception only occurs about 17% of the times. A thousand run-throughs yielded 166 exceptions.
I know my probabilistic logic is flawed, but I'm more interested in the cause of the exceptions and ways to prevent them from occurring. Do I simply need to do some memory management, maybe set some variables to null after using them? If so where would the key-points be to do so?
The code is as follows:
public class Problem14_LongestCollatzSequence {
private static final int STARTING_CHAIN_LENGTH = 1;
private static final int PROBABLY_RIGHT = 100000;
/**
* Calculate and return the Collatz sequence of a given number.
*
* #param number The number for which the Collatz sequence is to be
* calculated.
* #param chainlength The length of the chain for the number. This should
* start with an initial value of 1.
* #return The Length of the Collatz sequence.
*/
private static int getChainLength(long number, int chainlength) {
// All chains should end with 1.
if (number != 1) {
// If the number is even, halve the number, otherwise multiply it by 3 and add 1.
if (number % 2 == 0) {
number = number / 2;
} else {
number = number * 3 + 1;
}
// Call this function again.
return getChainLength(number, ++chainlength);
}
// Return the length of the chain.
return chainlength;
}
/**
* Determine and return the number below a maximum value that will result in
* the longest Collatz chain.
*
* #param maxStartingNumber The maximum value (exclusive) of the numbers
* that will be tested.
* #return The number that will produce the longest Collatz sequence in the
* given range.
*/
private static int calculateLongestChain(int maxStartingNumber) {
Random random = new Random();
int probabilityCounter = 0;
int currentChainNumber = 0;
int longestChainNumber = 0;
int currentChainLength = 0;
int longestChainLength = 0;
// Get the chain length of random numbers until a certain number of unsuccsessful attempts have been made.
while (probabilityCounter <= PROBABLY_RIGHT) {
currentChainNumber = random.nextInt(maxStartingNumber);
currentChainLength = getChainLength(currentChainNumber, STARTING_CHAIN_LENGTH);
// If the current chain-length is bigger than the previously calculated one, reset the counter and update the chain number, otherwise increase the counter.
if (currentChainLength > longestChainLength) {
probabilityCounter = 0;
longestChainLength = currentChainLength;
longestChainNumber = currentChainNumber;
} else {
++probabilityCounter;
}
}
return longestChainNumber;
}
private static int calculateLongestChainNP(int maxStartingNumber) {
// Non-probabilistic way to calculate the longest Collatz sequence.
int currentChainLength = 0;
int longestChainLength = 0;
int longestChainNumber = 0;
// Simply loop through all the numbers in the range to calculate the one resulting in the longest sequence.
for (int i = 1; i < maxStartingNumber; i++) {
currentChainLength = getChainLength(i, STARTING_CHAIN_LENGTH);
if (currentChainLength > longestChainLength) {
longestChainLength = currentChainLength;
longestChainNumber = i;
}
}
return longestChainNumber;
}
public static void main(String[] args) {
int exceptionCount = 0;
for (int count = 0; count < 1000; count++) {
try {
int testNumber = 1000000;
System.out.println("Probabilistic answer: " + calculateLongestChain(testNumber));
System.out.println("Non-probabilistic answer: " + calculateLongestChainNP(testNumber) + "\n");
} catch (java.lang.StackOverflowError soe) {
exceptionCount++;
System.err.println(soe + "\n");
}
}
System.out.println("Exception count: " + exceptionCount);
}
}
I wanted to provide the full output as well, but that puts me over the character limit.
Your recursion is too deep. You can increase call stack on your JVM with -Xss 4096m, but this is brute force. Be more elegant and use a while loop instead of recursion in getChainLength():
private static int getChainLength(long number, int chainlength) {
// All chains should end with 1.
while (number != 1) {
// If the number is even, halve the number, otherwise multiply it by 3 and add 1.
if (number % 2 == 0) {
number = number / 2;
} else {
number = number * 3 + 1;
}
// Call this function again.
++chainlength;
}
// Return the length of the chain.
return chainlength;
}
You will see in your stackoverflow exception the cause of the exception. In this case it is too much recursion and you will see it by a repeating stackframes in the stacktrace.
Try to make your algorithm iterative instead of recursive and your problem is solved.
I am trying to solve Project Euler problem 2 in Java:
public class Euler2 {
public static long GenerateFibonacci(int term) {
long sum = 0;
long fib = 0;
long f1 = 0;
long f2 = 1;
if (term <=1) return term;
for (int i = 1; i <= term; i++) {
fib = f1 + f2;
f1 = f2;
f2 = fib;
if(fib %2 ==0)
sum += fib;
}
return sum;
}
/**
* #param args
*/
public static void main(String[] args) {
int n = 100;
long result = GenerateFibonacci(n);
System.out.println("The sum of the even Fibonacci numbers is: "+result);
}
}
When n is small I get the right answer but for bigger values I get the wrong result. What's the problem here?
int is limited to 32-bit accuracy, long to 64-bit.
When you exceed the limit by adding numbers whose result is larger then the bit limit, they "roll over" and you lose the most significant bits from the result of the addition - essentially, they are "rounded" to 32/64 bits.
Here's an example of rolling over:
int i = Integer.MAX_VALUE; // 2147483647
i++; // -2147483648
Roughly speaking, each fibonnacci number is double the previous one, so roughly speaking you can only handle in the order of 64 iterations using a long as the total.
The largest long value in Java is 9223372036854775807. Adding 1 to this value produces -9223372036854775807 because the integer values in most programming languages come from a finite set of values, and when you reach the highest value and add one the sequence "wraps around" to the beginning.
If you need to go outside this range, which you will to get the 100th Fibonacci number, use BigInteger.
The sum is greater than Long.MAX_VALUE. You're correct (in your comment to #Bohemian) that n is less than that limit, but it is rather surprising how quickly this simple series can grow. The 100th Fibonacci number, for example, is 354224848179261915075. The sum of the first 100 is a 20 digit number, just to give you a feeling for the scale you're dealing with.
You need to use BigInteger, you can also use the fact that every third Fibonacci number is even.
public static BigInteger sumOfEvenFibonacci(int term) {
BigInteger sum = BigInteger.ZERO;
BigInteger f1 = BigInteger.ONE;
BigInteger f2 = BigInteger.ONE;
for (int i = 1; i <= term; i+=3) {
BigInteger fib = f1.add(f2);
sum = sum.add(fib);
f1 = f2.add(fib);
f2 = fib.add(f1);
}
return sum;
}
System.out.println(sumOfEvenFibonacci(100));
prints
1213946614199987541226
You can improve efficiency of 'GenerateFibonacci' with following code. This should be a comment but I can not format the code in comment, I am doing this in answer,
public class FibUtil {
//Constants used in equation to calculate nth fib term
private static final double fibA=1/Math.sqrt(5);
private static final double fibB=(1+Math.sqrt(5))/2;
private static final double fibC=(1-Math.sqrt(5))/2;
public static double getNthFibTerm(long n){
return fibA*(Math.pow(fibB, n)-Math.pow(fibC, n));
}
}
Further, based on euler 2 problem statement, you can just add only nth terms which are multiples of 3. I leave 'why' to you.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package evenfibonaccisum;
import java.math.BigInteger;
/**
*
* #author blades of Aragon
*/
public class EvenFibonacciSum {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
long a=0;
long b=1;
long fib=1;
int i=10;
long sum=0;
while(fib<=4000000){
fib=a+b;
a=b;
b=fib;
if(fib>=4000000){
break ;
}
else{
if(fib%2==0){
sum=sum+fib;
}
}
}
System.out.println("sum of even Fibonacci "+sum);
}
}
I need to generate arbitrarily large random integers in the range 0 (inclusive) to n (exclusive). My initial thought was to call nextDouble and multiply by n, but once n gets to be larger than 253, the results would no longer be uniformly distributed.
BigInteger has the following constructor available:
public BigInteger(int numBits, Random rnd)
Constructs a randomly generated BigInteger, uniformly distributed over the range 0 to (2numBits - 1), inclusive.
How can this be used to get a random value in the range 0 - n, where n is not a power of 2?
Use a loop:
BigInteger randomNumber;
do {
randomNumber = new BigInteger(upperLimit.bitLength(), randomSource);
} while (randomNumber.compareTo(upperLimit) >= 0);
on average, this will require less than two iterations, and the selection will be uniform.
Edit: If your RNG is expensive, you can limit the number of iterations the following way:
int nlen = upperLimit.bitLength();
BigInteger nm1 = upperLimit.subtract(BigInteger.ONE);
BigInteger randomNumber, temp;
do {
temp = new BigInteger(nlen + 100, randomSource);
randomNumber = temp.mod(upperLimit);
} while (s.subtract(randomNumber).add(nm1).bitLength() >= nlen + 100);
// result is in 'randomNumber'
With this version, it is highly improbable that the loop is taken more than once (less than one chance in 2^100, i.e. much less than the probability that the host machine spontaneously catches fire in the next following second). On the other hand, the mod() operation is computationally expensive, so this version is probably slower than the previous, unless the randomSource instance is exceptionally slow.
The following method uses the BigInteger(int numBits, Random rnd) constructor and rejects the result if it's bigger than the specified n.
public BigInteger nextRandomBigInteger(BigInteger n) {
Random rand = new Random();
BigInteger result = new BigInteger(n.bitLength(), rand);
while( result.compareTo(n) >= 0 ) {
result = new BigInteger(n.bitLength(), rand);
}
return result;
}
The drawback to this is that the constructor is called an unspecified number of times, but in the worst case (n is just slightly greater than a power of 2) the expected number of calls to the constructor should be only about 2 times.
The simplest approach (by quite a long way) would be to use the specified constructor to generate a random number with the right number of bits (floor(log2 n) + 1), and then throw it away if it's greater than n. In the worst possible case (e.g. a number in the range [0, 2n + 1) you'll throw away just under half the values you create, on average.
Why not constructing a random BigInteger, then building a BigDecimal from it ?
There is a constructor in BigDecimal : public BigDecimal(BigInteger unscaledVal, int scale) that seems relevant here, no ? Give it a random BigInteger and a random scale int, and you'll have a random BigDecimal. No ?
Here is how I do it in a class called Generic_BigInteger available via:
Andy Turner's Generic Source Code Web Page
/**
* There are methods to get large random numbers. Indeed, there is a
* constructor for BigDecimal that allows for this, but only for uniform
* distributions over a binary power range.
* #param a_Random
* #param upperLimit
* #return a random integer as a BigInteger between 0 and upperLimit
* inclusive
*/
public static BigInteger getRandom(
Generic_Number a_Generic_Number,
BigInteger upperLimit) {
// Special cases
if (upperLimit.compareTo(BigInteger.ZERO) == 0) {
return BigInteger.ZERO;
}
String upperLimit_String = upperLimit.toString();
int upperLimitStringLength = upperLimit_String.length();
Random[] random = a_Generic_Number.get_RandomArrayMinLength(
upperLimitStringLength);
if (upperLimit.compareTo(BigInteger.ONE) == 0) {
if (random[0].nextBoolean()) {
return BigInteger.ONE;
} else {
return BigInteger.ZERO;
}
}
int startIndex = 0;
int endIndex = 1;
String result_String = "";
int digit;
int upperLimitDigit;
int i;
// Take care not to assign any digit that will result in a number larger
// upperLimit
for (i = 0; i < upperLimitStringLength; i ++){
upperLimitDigit = new Integer(
upperLimit_String.substring(startIndex,endIndex));
startIndex ++;
endIndex ++;
digit = random[i].nextInt(upperLimitDigit + 1);
if (digit != upperLimitDigit){
break;
}
result_String += digit;
}
// Once something smaller than upperLimit guaranteed, assign any digit
// between zero and nine inclusive
for (i = i + 1; i < upperLimitStringLength; i ++) {
digit = random[i].nextInt(10);
result_String += digit;
}
// Tidy values starting with zero(s)
while (result_String.startsWith("0")) {
if (result_String.length() > 1) {
result_String = result_String.substring(1);
} else {
break;
}
}
BigInteger result = new BigInteger(result_String);
return result;
}
For those who are still asking this question and are looking for a way to generate arbitrarily large random BigIntegers within a positive integer range, this is what I came up with. This random generator works without trying bunch of numbers until one fits the range. Instead it will generate a random number directly that will fit the given range.
private static BigInteger RandomBigInteger(BigInteger rangeStart, BigInteger rangeEnd){
Random rand = new Random();
int scale = rangeEnd.toString().length();
String generated = "";
for(int i = 0; i < rangeEnd.toString().length(); i++){
generated += rand.nextInt(10);
}
BigDecimal inputRangeStart = new BigDecimal("0").setScale(scale, RoundingMode.FLOOR);
BigDecimal inputRangeEnd = new BigDecimal(String.format("%0" + (rangeEnd.toString().length()) + "d", 0).replace('0', '9')).setScale(scale, RoundingMode.FLOOR);
BigDecimal outputRangeStart = new BigDecimal(rangeStart).setScale(scale, RoundingMode.FLOOR);
BigDecimal outputRangeEnd = new BigDecimal(rangeEnd).add(new BigDecimal("1")).setScale(scale, RoundingMode.FLOOR); //Adds one to the output range to correct rounding
//Calculates: (generated - inputRangeStart) / (inputRangeEnd - inputRangeStart) * (outputRangeEnd - outputRangeStart) + outputRangeStart
BigDecimal bd1 = new BigDecimal(new BigInteger(generated)).setScale(scale, RoundingMode.FLOOR).subtract(inputRangeStart);
BigDecimal bd2 = inputRangeEnd.subtract(inputRangeStart);
BigDecimal bd3 = bd1.divide(bd2, RoundingMode.FLOOR);
BigDecimal bd4 = outputRangeEnd.subtract(outputRangeStart);
BigDecimal bd5 = bd3.multiply(bd4);
BigDecimal bd6 = bd5.add(outputRangeStart);
BigInteger returnInteger = bd6.setScale(0, RoundingMode.FLOOR).toBigInteger();
returnInteger = (returnInteger.compareTo(rangeEnd) > 0 ? rangeEnd : returnInteger); //Converts number to the end of output range if it's over it. This is to correct rounding.
return returnInteger;
}
How does it work?
First it generates a String with random numbers with the same length as the maximum range. For example: with given range of 10-1000 it will generate some number between 0000 and 9999 as a String.
Then it creates BigDecimals to represent the maximum possible value (9999 in previous example) and minimum value (0) and converts the range parameter BigIntegers to BigDecimals. Also in this step to the given range maximum value is added 1 in order to correct rounding errors in the next step.
Then using this formula the generated random number is mapped to the given range:
(generated - inputRangeStart) / (inputRangeEnd - inputRangeStart) * (outputRangeEnd - outputRangeStart) + outputRangeStart
After that it will do a last check whether or not the mapped number fits the given range and sets it to the given range maximum if it doesn't. This is done in order to correct rounding errors.
Just use modular reduction
new BigInteger(n.bitLength(), new SecureRandom()).mod(n)
Compile this F# code into a DLL and you can also reference it in your C# / VB.NET programs
type BigIntegerRandom() =
static let internalRandom = new Random()
/// Returns a BigInteger random number of the specified number of bytes.
static member RandomBigInteger(numBytes:int, rand:Random) =
let r = if rand=null then internalRandom else rand
let bytes : byte[] = Array.zeroCreate (numBytes+1)
r.NextBytes(bytes)
bytes.[numBytes] <- 0uy
bigint bytes
/// Returns a BigInteger random number from 0 (inclusive) to max (exclusive).
static member RandomBigInteger(max:bigint, rand:Random) =
let rec getNumBytesInRange num bytes = if max < num then bytes else getNumBytesInRange (num * 256I) bytes+1
let bytesNeeded = getNumBytesInRange 256I 1
BigIntegerRandom.RandomBigInteger(bytesNeeded, rand) % max
/// Returns a BigInteger random number from min (inclusive) to max (exclusive).
static member RandomBigInteger(min:bigint, max:bigint, rand:Random) =
BigIntegerRandom.RandomBigInteger(max - min, rand) + min