Java: avoiding factorial overflow - java

I'm supposed to optimize the following code so that it calculates the central binomial coefficient up to the max value of integer (up to n = 16).
public static int factorial(int n)
{
int result= 1;
for(int i = 2; i <= n; i++) result *= i;
return result;
}
public static int centralbinom(int n)
{
return factorial(2*n) / (factorial(n) * factorial(n));
}
Naturally I get an overflow for every n > 6.
How do I 'break down' the factorial function so that doesn't has to deal with big numbers such as 2n = 2*16 = 32 ?
Or is there a better way to calculate the central binomial coefficient?

Here are several optimizations that you can do in addition to using BigIntegers that may reduce your calculations, in most of you cases overflow that you may be having in your program.
Since you need factorial(n) at least two time. Calculate it once and store it in a variable.
Factorial(2*n) has factorial(n) in it. Since you have already calculated factorial(n) before all you need to do is calculate till factorial(2n....n) and then multiply factorial(n) to it. Here's one way how that can be done.
//Pseudocode
//find factorial of n given I know already till k
int findFactorial(n, k) {
int result = 1
for i n to 1
if(i==k)
break;
result = result * n;
return result
}
//factorial(2*n) = facorial(n, k) * factorial(k)
This will reduce your calculations a lot and in case if you expect your program not to have an overflow, you can go away with BigIntegers.

If you need a factorial of big number, you have to use BigInteger class to calculate result:
public static BigInteger factorial(int n) {
BigInteger result = BigInteger.ONE;
for (int i = 2; i <= n; ++i) {
result = result.multiply(BigInteger.valueOf(i));
}
return result;
}

If the central binomial coefficient of 17 is greater than the integer max, and you only need to compute it for 17 numbers, then the obvious solution is a lookup table. Create a 17-element array containing the central binomial coefficient for n = 0 to 16. I think you'll find this solution is extremely efficient.
You can find a list of them here. http://oeis.org/A000984

Just compress your factorial by gamma.
set gamma to 10 is good enough
public static double factorial(int n, double gamma)
{
double result= 1;
double gammaInv = 1.0/gamma;
for(int i = 2; i <= n; i++) result *= pow(i,gammaInv);
return result;
}
public static int centralbinom(int n, double gamma)
{
return pow(factorial(2*n,gamma) /
(factorial(n,gamma) * factorial(n),gamma),
gamma);
}

Related

Rounding integers to one significant digit

I want to round down an int in Java, what i mean is, if I have an int 45678, i want to convert that int into 40000
this is how im calling it
int den = placeValue(startCode,length);
and this is the code
static int placeValue(int N, int num)
{
int total = 1, value = 0, rem = 0;
while (true) {
rem = N % 10;
N = N / 10;
if (rem == num) {
value = total * rem;
break;
}
total = total * 10;
}
return value;
}
so if i have 89765, i would want 80000,
but instead it return the place value of whatever length is.
So,
for 89765, the length would be 5, so the return value is 5 i.e. the value in the ones place.
but if the number was 85760
then it would return 5000.
I hope that makes sense.
Any suggestions would be much appreicated.
In my opinions, if I can avoid 'calculating' I will compute the answer from other concept since I am not confidence on my math (haha).
Here is my answer. (only work in positive numbers)
I think the length of the inputted number is not necessary.
static int placeValue2(int N) {
String tar = N+"";
String rtn = tar.substring(0,1); // take first digital
for (int i=0;i<tar.length()-1;i++) // pad following digitals
rtn+="0";
return Integer.parseInt(rtn);
}
I appreciate you asked the question here.
Here is my solution. I don't know why you are taking two parameters, but I tried it from one param.
class PlaceValue{
int placeValue(int num){
int length = 0; int temp2=1;
boolean result=false;
long temp1=1;
if (num<0){
result=true;
num=num*(-1);
}
if (num==0){
System.out.println("Value 0 not allowed");
return 0;
}
while (temp1 <= num){ //This loop checks for the length, multiplying temp1 with 10
//untill its <= number. length++ counts the length.
length++;
temp1*=10;
}
for (int i=1; i<length; i++){//this loop multiplies temp2 with 10 length number times.
// like if length 2 then 100. if 5 then 10000
temp2=temp2*10;
}
temp2=(num/temp2)*temp2;
/* Let's say number is 2345. This would divide it over 1000, giving us 2;
in the same line multiplying it with the temp2 which is same 1000 resulting 2000.
now 2345 became 2000;
*/
if (result==true){
temp2=temp2*(-1);
}
return temp2;
}
}
Here is the code above. You can try this. If you are dealing with the long numbers, go for long in function type as well as the variable being returned and in the main function. I hope you understand. otherwise, ask me.
Do you want something like this?
public static int roundDown(int number, int magnitude) {
int mag = (int) Math.pow(10, magnitude);
return (number / mag) * mag;
}
roundDown(53278,4) -> 50000
roundDown(46287,3) -> 46000
roundDown(65478,2) -> 65400
roundDown(43298,1) -> 43290
roundDown(43278,0) -> 43278
So the equivalent that will only use the most significant digit is:
public static int roundDown(int number) {
int zeros = (int) Math.log10(number);
int mag = (int) Math.pow(10, zeros);
return (number / mag) * mag;
}

How to calculate the probability of getting the sum X using N six-sided dice

The Challenge:
For example, what is the probability of getting the sum of 15 when using 3 six-sided dice. This can be for example by getting 5-5-5 or 6-6-3 or 3-6-6 or many more options.
A brute force solution for 2 dice - with complexity of 6^2:
Assuming we had only 2 six-sided dice, we can write a very basic code like that:
public static void main(String[] args) {
System.out.println(whatAreTheOdds(7));
}
public static double whatAreTheOdds(int wantedSum){
if (wantedSum < 2 || wantedSum > 12){
return 0;
}
int wantedFound = 0;
int totalOptions = 36;
for (int i = 1; i <= 6; i++) {
for (int j = 1; j <= 6; j++) {
int sum = i+j;
if (sum == wantedSum){
System.out.println("match: " + i + " " + j );
wantedFound +=1;
}
}
}
System.out.println("combinations count:" + wantedFound);
return (double)wantedFound / totalOptions;
}
And the output for 7 will be:
match: 1 6
match: 2 5
match: 3 4
match: 4 3
match: 5 2
match: 6 1
combination count:6
0.16666666666666666
The question is how to generalize the algorithm to support N dice:
public static double whatAreTheOdds(int wantedSum, int numberOfDices)
Because we can't dynamically create nested for loops, we must come with a different approach.
I thought of something like that:
public static double whatAreTheOdds(int sum, int numberOfDices){
int sum;
for (int i = 0; i < numberOfDices; i++) {
for (int j = 1; j <= 6; j++) {
}
}
}
but failed to come up with the right algorithm.
Another challenge here is - is there a way to do it efficiently, and not in a complexity of 6^N?
Here is a recursive solution with memoization to count the combinations.
import java.util.Arrays;
import java.lang.Math;
class Dices {
public static final int DICE_FACES = 6;
public static void main(String[] args) {
System.out.println(whatAreTheOdds(40, 10));
}
public static double whatAreTheOdds(int sum, int dices) {
if (dices < 1 || sum < dices || sum > DICE_FACES * dices) return 0;
long[][] mem = new long[dices][sum];
for (long[] mi : mem) {
Arrays.fill(mi, 0L);
}
long n = whatAreTheOddsRec(sum, dices, mem);
return n / Math.pow(DICE_FACES, dices);
}
private static long whatAreTheOddsRec(int sum, int dices, long[][] mem) {
if (dices <= 1) {
return 1;
}
long n = 0;
int dicesRem = dices - 1;
int minFace = Math.max(sum - DICE_FACES * dicesRem, 1);
int maxFace = Math.min(sum - dicesRem, DICE_FACES);
for (int i = minFace; i <= maxFace; i++) {
int sumRem = sum - i;
long ni = mem[dicesRem][sumRem];
if (ni <= 0) {
ni = whatAreTheOddsRec(sumRem, dicesRem, mem);
mem[dicesRem][sumRem] = ni;
}
n += ni;
}
return n;
}
}
Output:
0.048464367913724195
EDIT: For the record, the complexity of this algorithm is still O(6^n), this answer just aims to give a possible implementation for the general case that is better than the simplest implementation, using memoization and search space prunning (exploring only feasible solutions).
As Alex's answer notes, there is a combinatorial formula for this:
In this formula, p is the sum of the numbers rolled (X in your question), n is the number of dice, and s is the number of sides each dice has (6 in your question). Whether the binomial coefficients are evaluated using loops, or precomputed using Pascal's triangle, either way the time complexity is O(n2) if we take s = 6 to be a constant and X - n to be O(n).
Here is an alternative algorithm, which computes all of the probabilities at once. The idea is to use discrete convolution to compute the distribution of the sum of two random variables given their distributions. By using a divide and conquer approach as in the exponentiation by squaring algorithm, we only have to do O(log n) convolutions.
The pseudocode is below; sum_distribution(v, n) returns an array where the value at index X - n is the number of combinations where the sum of n dice rolls is X.
// for exact results using integers, let v = [1, 1, 1, 1, 1, 1]
// and divide the result through by 6^n afterwards
let v = [1/6.0, 1/6.0, 1/6.0, 1/6.0, 1/6.0, 1/6.0]
sum_distribution(distribution, n)
if n == 0
return [1]
else if n == 1
return v
else
let r = convolve(distribution, distribution)
// the division here rounds down
let d = sum_distribution(r, n / 2)
if n is even
return d
else
return convolve(d, v)
Convolution cannot be done in linear time, so the running time is dominated by the last convolution on two arrays of length 3n, since the other convolutions are on sufficiently shorter arrays.
This means if you use a simple convolution algorithm, it should take O(n2) time to compute all of the probabilities, and if you use a fast Fourier transform then it should take O(n log n) time.
You might want to take a look at Wolfram article for a completely different approach, which calculates the desired probability with a single loop.
The idea is to have an array storing the current "state" of each dice, starting will every dice at one, and count upwards. For example, with three dice you would generate the combinations:
111
112
...
116
121
122
...
126
...
665
666
Once you have a state, you can easily find if the sum is the one you are looking for.
I leave the details to you, as it seems a useful learning exercise :)

Java Compute C(n,k) and factorial with biginteger

I want to compute the answer of C(n,k),such as C(10,2)=10*9/2*1 = 45
If I test my code by small numbers like 10, the code works.
However, when I try to compute C(1000,900), it compiles
Exception in thread "main" java.lang.ArithmeticException: / by zero
I've seen someone says it should use BigInteger,But after I tried, it still has errors.
For example: I change int factorial into BigInteger factorial,
while the for loop in cSelect, I can not change int i into BigInteger type,
As result, the answer up/factorial(y) has errors.
Please help me to fix this problem. Thanks!!
public class Test {
// Write a factorial function
static int factorial(int m) {
int result =1;
for (int i=2; i<=m; i++) {
result = result*i;
}
return result;
}
// Caculate C(x,y)
static int cSelect(int x, int y) {
int up=1;
for(int i=x; i>=(x-y+1); i--) {
up = up*i;
}
return up/factorial(y);
}
public static void main(String[] args) {
System.out.println(cSelect(1000,900));
}
}
Your code is fairly easy to translate in factorial. Start with ONE, multiply by the BigInteger.valueOf(long) for each i in your loop. Like,
// Write a factorial function
static BigInteger factorial(int m) {
BigInteger result = BigInteger.ONE;
for (int i = 2; i <= m; i++) {
result = result.multiply(BigInteger.valueOf(i));
}
return result;
}
Your other function does exactly the same, plus a division by the result of factorial(y). Like,
// Caculate C(x,y)
static BigInteger cSelect(int x, int y) {
BigInteger up = BigInteger.ONE;
for (int i = x; i >= (x - y + 1); i--) {
up = up.multiply(BigInteger.valueOf(i));
}
return up.divide(factorial(y));
}
With no other changes I get
63850511926305130236698511142022274281262900693853331776286816221524376994750901948920974351797699894319420811933446197797592213357065053890
Which I assume is correct.
First, return value must be BigInteger, because result of C(1000,900) far exceeds the range on an int.
Second, you don't need separate factorial() method. Doing the division as you iterate will improve memory footprint by not creating excessively large intermediate values (at cost of doing multiple divisions, but even so it might actually be faster).
Like this:
static BigInteger cSelect(int x, int y) {
BigInteger v = BigInteger.ONE;
for (int i = x, j = 1; j <= y; i--, j++)
v = v.multiply(BigInteger.valueOf(i)).divide(BigInteger.valueOf(j));
return v;
}
By counting i down and j up, there will never be a fraction from the division.
Test
System.out.println(cSelect(10, 2));
System.out.println(cSelect(1000, 900));
Output
45
63850511926305130236698511142022274281262900693853331776286816221524376994750901948920974351797699894319420811933446197797592213357065053890
You have to use BigInteger to do the calculation.
The value you are trying to compute is approximately 6.385051192630516e+139 and it is not representable as a Java primitive integer value.
Even if the result was representable, the reason you are getting divide by zero errors is that the divisor expression 900! ∗ 100! is overflowing to zero. You then divide by that zero.
The reason that it overflows to zero is that it is divisible by 2^32 and 2^64. That can be proven by using some simple algebra to compute the number of factors of 2 there are in 900! and 100!

How to calculate and display large numbers in java

How could I calculate and display all digits of large numbers like 9999! (factorial of 9999) in java?
Take a look at this url which calculates 9999! and dislays all digits.
Use BigInteger, his limit is your memory
public static BigInteger factorial(BigInteger num) {
if (num.compareTo(new BigInteger("1")) < 0) {
return new BigInteger("1");
} else {
return factorial(num.subtract(new BigInteger("1"))).multiply(num) ;
}
}
Use BigInteger; 9999! took 120 ms with Java 8. Here is a version that uses longs, and halves that time:
public static BigInteger factorial(int n) {
// Try first to use longs in calculating the factorial.
BigInteger result = BigInteger.ONE;
long factor = 1;
for (int i = n; i > 1; --i) {
if Long.MAX_VALUE / factor < i) { // Overflow?
result = result.multiply(BigInteger.valueOf(factor));
factor = i;
} else {
factor *= i;
}
}
return result.multiply(BigInteger.valueOf(factor));
}
The Java standard library provide a BigInteger class, which can represent unlimited integer values (actually, they are limited, but only by available memory).
Not the fastest, but not really slow either.
public static BigInteger factorial(int n) {
BigInteger result = BigInteger.ONE;
for (int i = 2; i <= n; i++) {
result = result.multiply(BigInteger.valueOf(i));
}
return result;
}
You can use Strings (yes don't get astonished, you can!). A program can be created in strings to multiply two very large numbers (here i am talking about numbers say 5000 digits in length, each!)
I have already created them for addition and subtraction and it's not that hard to create it for Multiplication and i assure you that, though you will think that using BigInteger will be faster, but using Strings would be Ultrafast as compared to BigInt.
And the thing that slipped my mid, i used StringBuilder class to make the program more efficient.

Why java.Math.BigInteger bugs out after a certain limit?

Am trying to print the sum of digits in 2^n for n = 1 to 1000.
Here's what I've done.
public static void main(String[] args) {
int n = 1000;
for (int i = 1; i < n; i++) {
BigInteger power = BigInteger.valueOf((int)Math.pow(2, i));
int sum = 0;
while (power.intValue() > 0) {
sum += power.intValue() % 10;
power = power.divide(BigInteger.valueOf(10));
}
System.out.print(sum + " ");
}
}
It works only till about 2^30 or so and then prints the same result, 46, for the rest.
I tried a similar thing using "long long" in C and that printed 0's after a similar limit.
According to the answers, I changed
BigInteger power = BigInteger.valueOf((int)Math.pow(2, i));
to
BigInteger power = BigInteger.valueOf(2).pow(i);
and 46 changed to 0. Just like C.
Still not working...
You're using Math.pow to generate the value you should use the BigInteger functions to do so instead.
The sum should be stored in a BigInteger as well not an int.
You are doing integer arithmetic and then putting it into a biginteger. Use a biginteger's pow method instead.
Because you aren't using BigInteger.
Computing numbers using BigInteger doesn't let you magically store their sum in an int.
Similarly, passing an int to BigInteger.valueOf() doesn't magically make that int bigger.
You're calling Math.pow() with regular integers, not BigIntegers. You're calling it on integer literals.
You want this:
int i = 7; //or whatever power
BigInteger k = BigInteger.valueOf(2);
k = k.pow(i);

Categories

Resources