I am trying to sum up all the numbers between 2 given numbers excluding the boundary.. E.g. addNumbers("5", "8") should return 13 since 6+7=13. This is the function I currently have.
public static BigInteger addNumbers(String from, String to) {
BigInteger total = new BigInteger("0");
BigInteger startingBoundary = new BigInteger(from);
BigInteger finishingBoundary = new BigInteger(to);
if (startingBoundary.compareTo(finishingBoundary) < 0) {
startingBoundary = new BigInteger(from);
finishingBoundary = new BigInteger(to);
} else {
finishingBoundary = new BigInteger(from);
startingBoundary = new BigInteger(to);
}
while (startingBoundary.compareTo(finishingBoundary) != 0 ) {
System.out.println("Starting boundary:" + startingBoundary.intValue());
System.out.println("Finishing boundary: " + finishingBoundary.intValue());
total.add(startingBoundary);
System.out.println("total: "+total.intValue());
startingBoundary.add(new BigInteger("1"));
}
return total;
}
The problem is that the while condition seems to be running infinitely despite me changing the value of it. Also when printing out the total object in each loop, it always prints out 0. I know I initialised it to 0 but i was expecting it to change as I am adding to it..
Note that the use of BigInteger implies that the numbers, and thus also the difference between the two numbers, might be huge. Looping from lower to upper boundary might take ages, literally. Instead, you could use a variant of the closed form sum(1..N) = (N*(N+1))/2. Use it to sum the number from 1 to upper and from 1 to lower, then combine the two to get your desired result.
BigInteger lower = new BigInteger("5");
BigInteger upper = new BigInteger("8");
BigInteger one = BigInteger.ONE, two = BigInteger.TWO;
BigInteger oneToUpper = upper.multiply(upper.add(one)).divide(two);
BigInteger oneToLower = lower.multiply(lower.add(one)).divide(two);
BigInteger lowertoUpperInc = oneToUpper.subtract(oneToLower).add(lower);
System.out.println(lowertoUpperInc); // 5 + 6 + 7 + 8 = 26
BigInteger lowertoUpperExc = oneToUpper.subtract(oneToLower).subtract(upper);
System.out.println(lowertoUpperExc); // 6 + 7 = 13
(Note that your loop also seems to return 18 for this example, which seems to be 5+6+7 and thus not what you really wanted.)
Other than your loop, this will also work for truly BigInteger, e.g. the sums (inclusive and exclusive) for lower = 123456789123456789 and upper = 987654321987654321 are 480109740480109740075445815075445815 and 480109740480109738964334703964334705 respectively.
As already mentioned in another answer: Calls like
total.add(startingBoundary);
do not have any observable effect. The add method does not modify the total object. Instead, it returns a new BigInteger object. The reason for that is, more generally, that BigInteger is immutable. This means that the value of a BigInteger object cannot be changed after it has been created. For the reasons, have a look at Why BigInteger in java is designed to be immutable?
Changing the line to
total = total.add(startingBoundary);
will solve this (similarly, for the other lines - for a fixed implementation, see the example below).
A side note: Instead of new BigInteger("0") and new BigInteger("1"), you should usually use BigInteger.ZERO and BigInteger.ONE. There is no reason to create new objects for these frequently used values.
A possible improvement, though:
Unless the assignment explicitly says that this has to be solved with a loop, there is a far more efficient and elegant solution for this. You can use the Gauß'sche Summenformel (sorry, no English version of that one), which basically states that
The sum of the natural numbers from 1 to n is equal to (n*(n+1))/2
So you can compute these sums directly, for the limits of your range, and then just return the difference between the two.
The following contains a fixed version of your original code, and the alternative implementation, together with a (very basic) "microbenchmark":
import java.math.BigInteger;
import java.util.Locale;
public class SumFromRange
{
public static void main(String[] args)
{
simpleExample();
simpleBenchmark();
}
private static void simpleExample()
{
System.out.println(addNumbers("5", "8"));
System.out.println(addNumbersFast("5", "8"));
System.out.println(addNumbers("15", "78"));
System.out.println(addNumbersFast("15", "78"));
}
private static void simpleBenchmark()
{
int blackHole = 0;
for (long min = 10000; min <= 20000; min += 10000)
{
for (long max = 10000000; max <= 20000000; max += 10000000)
{
String from = String.valueOf(min);
String to = String.valueOf(max);
long before = 0;
long after = 0;
before = System.nanoTime();
BigInteger slow = addNumbers(from, to);
after = System.nanoTime();
blackHole += slow.hashCode();
System.out.printf("Compute %10d to %10d slow took %8.3f ms\n",
min, max, (after - before) / 1e6);
before = System.nanoTime();
BigInteger fast = addNumbersFast(from, to);
after = System.nanoTime();
blackHole += fast.hashCode();
System.out.printf(Locale.ENGLISH,
"Compute %10d to %10d fast took %8.3f ms\n", min, max,
(after - before) / 1e6);
}
}
System.out.println("blackHole " + blackHole);
}
public static BigInteger addNumbers(String from, String to)
{
BigInteger total = BigInteger.ZERO;
BigInteger startingBoundary = new BigInteger(from);
BigInteger finishingBoundary = new BigInteger(to);
if (startingBoundary.compareTo(finishingBoundary) < 0)
{
startingBoundary = new BigInteger(from);
finishingBoundary = new BigInteger(to);
}
else
{
finishingBoundary = new BigInteger(from);
startingBoundary = new BigInteger(to);
}
startingBoundary = startingBoundary.add(BigInteger.ONE);
while (startingBoundary.compareTo(finishingBoundary) != 0)
{
total = total.add(startingBoundary);
startingBoundary = startingBoundary.add(BigInteger.ONE);
}
return total;
}
public static BigInteger addNumbersFast(String from, String to)
{
BigInteger f = new BigInteger(from);
BigInteger t = new BigInteger(to);
BigInteger sf = computeSum(f);
BigInteger st = computeSum(t.subtract(BigInteger.ONE));
return st.subtract(sf);
}
// Compute the sum of 1...n
public static BigInteger computeSum(BigInteger n)
{
BigInteger n1 = n.add(BigInteger.ONE);
return n.multiply(n1).divide(BigInteger.valueOf(2));
}
}
The benchmark results for larger values are obvious:
Compute 10000 to 10000000 slow took 635,506 ms
Compute 10000 to 10000000 fast took 0.089 ms
Compute 10000 to 20000000 slow took 1016,381 ms
Compute 10000 to 20000000 fast took 0.037 ms
Compute 20000 to 10000000 slow took 477,258 ms
Compute 20000 to 10000000 fast took 0.038 ms
Compute 20000 to 20000000 slow took 987,400 ms
Compute 20000 to 20000000 fast took 0.040 ms
These aren't even in the same league...
Use
total = total.add(startingBoundary);
and
startingBoundary = startingBoundary.add(new BigInteger("1"));
Because add does not add to the first operand, but returns the sum.
Also, before starting the loop, do
startingBoundary = startingBoundary.add(new BigInteger("1"));
to satisfy your condition that the starting boundary has to be excluded.
As defensive coding, don't use equals to zero, but use
startingBoundary.compareTo(finishingBoundary) < 0
Related
For GA needed two methods:
BigInteger greyToBigInteger(boolean[]){...}
and
boolean[] bigIntegerToGrey(BigInteger){...}
For example:
15 ---> {true,false,false,false}
and
{true,false,false,false} --> 15
I don't know, how to make that very fast. Max number to convert is 10^1125, so it works more than 5 minutes for one number, if I do it, like in Wikipedia example.
I've writen this piece of code, and it run pretty fast without any special tricks - on my laptop machine it takes less than 2 milliseconds to convert the given number that is less than or equal 10^1125 to gray and back:
import java.math.BigInteger;
import java.util.Random;
public class GrayCode {
public static void main(String[] args) {
// actually 10^1125 is a number with max length of 3738 bits
int bitLength = new BigInteger("10").pow(1125).bitLength();
System.out.println("10^1125 is a number with max bit length = " + bitLength);
new GrayCode().test(bitLength);
}
private void test(int bitLength) {
long totalTime = 0;
long numberOfTests = 1000;
for (int n = 0; n < numberOfTests; n++) {
// We don't include time needed for generation of a number
BigInteger binary = generateBigInteger(bitLength);
// here we start measuring execution time of single conversion
long start = System.currentTimeMillis();
// The conversion to gray and back
boolean[] gray = bigIntegerToGrey(binary);
BigInteger back = grayToBigInteger(gray);
// we sum up the conversion times to totalTime per this test suite
totalTime += System.currentTimeMillis() - start;
}
System.out.println("We have run a "+numberOfTests+" tests, for a random numbers of "+bitLength+" bit length to convert to gray code and back to binary");
System.out.println("The total execution time of this test was "+totalTime+" milliseconds, giving average convesion time of " + totalTime/1000d + " ms per single test ");
}
private BigInteger grayToBigInteger(boolean[] booleanGray) {
BigInteger input = booleanToBigInteger(booleanGray);
return grayToBinary(input);
}
private boolean[] bigIntegerToGrey(BigInteger binary) {
BigInteger gray = binaryToGray(binary);
return bigIntegerToBooleanArray(gray);
}
public boolean[] bigIntegerToBooleanArray(BigInteger number) {
char[] binary = number.toString(2).toCharArray();
boolean[] binaryBoolean = new boolean[binary.length];
for (int n = 0; n < binary.length; n++) {
if (binary[n] == '1') {
binaryBoolean[n] = true;
}
}
return binaryBoolean;
}
private BigInteger generateBigInteger(int numBits) {
Random rnd = new Random();
BigInteger number = new BigInteger(numBits, rnd);
return number;
}
public BigInteger booleanToBigInteger(boolean[] binary) {
StringBuilder sb = new StringBuilder();
for (boolean b : binary) {
sb.append(b ? '1' : '0');
}
return new BigInteger(sb.toString(), 2);
}
public BigInteger binaryToGray(BigInteger num) {
return num.xor(num.shiftRight(1));
}
public BigInteger grayToBinary(BigInteger num) {
BigInteger mask;
for (mask = num.shiftRight(1); mask.compareTo(BigInteger.ZERO) != 0; mask = mask.shiftRight(1)) {
num = num.xor(mask);
}
return num;
}
}
the results:
10^1125 is a number with max bit length = 3738
We have run a 1000 tests, for a number of 3738 bit length to convert to gray code and back to binary
The total execution time of this test was 1828 milliseconds, giving average convesion time of 1.828 ms per single test
I am trying to calculate the square root of all the integers below 100 with A precision of up to 10000 digits. I already tried it using Newton's method with Big Decimal, where it eats a lot of time.
So now am using Jarvis method for finding the square root using BigInteger.(I think this method involves less number of calculations and gets rid of the maintenance of decimal digits). Even then my code takes a lot of time.The following piece of code depicts the calculations.
public class SquareRootHackerRankJarvis {
static BigInteger limit;
static BigInteger a;
static BigInteger b;
private static BigInteger squareroot(int n, int digits, BigInteger ten,
BigInteger hundred, BigInteger five) {
limit = ten.pow(digits + 1);
a = BigInteger.valueOf(n * 5);
b = BigInteger.valueOf(5);
while (b.compareTo(limit) == -1) {
if (a.compareTo(b) != -1) {
a = a.subtract(b);
b = b.add(ten);
} else {
a = a.multiply(hundred);
b = (b.divide(ten)).multiply(hundred).add(five);
}
}
return b.divide(hundred);
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int N = scanner.nextInt();
int P = scanner.nextInt();
int sum = 0;
int p = 1;
BigInteger ten = BigInteger.valueOf(10);
BigInteger hundred = BigInteger.valueOf(100);
BigInteger five = BigInteger.valueOf(5);
for (int i = 1; i <= N; i++) {
if (p * p == i) {
p++;
continue;
}
BigInteger x = squareroot(i, P, ten, hundred, five);
char[] digits = x.toString().toCharArray();
for (int j = 0; j <= P - 1; j++) {
sum += Character.getNumericValue(digits[j]);
}
}
System.out.println(sum);
scanner.close();
}}
Can anyone provided or suggestions about the proper usage of BigInteger for optimum performance?
Comments on improvement of the above algorithm are also welcomed.
BigInteger ten = BigInteger.valueOf(10);
BigInteger hundred = BigInteger.valueOf(100);
BigInteger five = BigInteger.valueOf(5);
Should be moved outside of the function squareroot so they are not created and initialized every time function is called. Make sure they are still accessible in this function.
BigInteger num;
BigInteger limit;
BigInteger a;
BigInteger b;
Should be created outside of the function and should be only initialized on every fucntion call.
Also following line
b = (b.divide(ten)).multiply(hundred).add(five);
can be optimized to
b = b.multiply(ten).add(five);
One observation beyond fast computation of numerous digits of roots of non-squares is that there are just 25 non-compound numbers from 2 to 100.
Next, in addition to introducing constants like Maciej suggested, reduce the "introduction of 0 before the trailing 5" to two operations:
static final BigInteger
ten = BigInteger.TEN,
oneHundred = BigInteger.valueOf(100),
five = BigInteger.valueOf( 5),
fourtyFive = BigInteger.valueOf( 45);
/** Computes <code>digits</code> decimal digits of <code>n</code>
* <em>ignoring</em> (decimal) scaling. */
private static BigInteger sqrtDigitsJarvis(int n, int digits) {
BigInteger
limit = ten.pow(digits + 1), // might be an instance data member
a = BigInteger.valueOf(n*5L), // la*100),
b = five; // BigInteger.valueOf(ib*10 - 45);
// flawed for limit < sqrt(5n)
while (b.compareTo(limit) < 0) {
if (0 <= a.compareTo(b)) { // each branch can be parallelised
a = a.subtract(b);
b = b.add(ten);
} else {
a = a.multiply(oneHundred);
b = b.multiply(ten).subtract(fourtyFive);
}
}
return b.divide(oneHundred);
}
Here are two codes for solving problem 6 in project euler: Why do they give similar answers until I make the number larger? (100,000)
The sum of the squares of the first ten natural numbers is,
12 + 22 + ... + 102 = 385
The square of the sum of the first ten
natural numbers is,
(1 + 2 + ... + 10)2 = 552 = 3025
Hence the difference between the sum
of the squares of the first ten natural numbers and the square of the
sum is 3025 − 385 = 2640.
Find the difference between the sum of the squares of the first one
hundred natural numbers and the square of the sum.
Code 1:
public class Problem_Six_V2 {
public static void main(String[] args) {
long limit = 100000;
long sum = (limit * (limit + 1)) / 2;
long sumOfSqr = (long)((((2*limit)*limit)+((2*limit)*1)+(1*limit)+(1*1))*limit)/6;
System.out.println(Math.pow(sum, 2) +" "+ sumOfSqr);
System.out.println(Math.pow(sum, 2) - sumOfSqr);
}
}
^^^ Outputs = 2.500016666416665E19
Here's code two:
public class Problem_Six {
public static void main(String[] args) {
long sum = 0;
long sumSqr = 0;
long sumOfSqr = 0;
for(long i = 1; i <= 100000; i++){
sum += i;
sumOfSqr += Math.pow(i,2);
}
sumSqr = (long) Math.pow(sum, 2);
System.out.println(sumSqr +" "+ sumOfSqr);
System.out.println(sumSqr - sumOfSqr);
}
}
^^ Outputs = 9223038698521425807
I guess it's something to two with the types being used, but they seem similar in both codes..hmm
Math.pow(i,2) accepts doubles as parameters. Doubles are not 100% precise,
you lose precision. Stick to operations on int/long only. The answer is pretty small
and fits even into an int.
Not sure why you use 100000 as your limit, problem 6 has 100 as a limit.
In Java when results of integer arithmetic don't fit into int variables,
you should use long, when they don't fit even into long variables, you
should use BigInteger.
But avoid doubles, they are not precise for such kinds of tasks.
Here is your program corrected.
import java.math.BigInteger;
public class Problem_Six {
public static void main(String[] args) {
BigInteger sum = BigInteger.ZERO;
BigInteger sumSqr = BigInteger.ZERO;
BigInteger sumOfSqr = BigInteger.ZERO;
for (long i = 1; i <= 100000; i++) {
sum = sum.add(BigInteger.valueOf(i));
sumOfSqr = sumOfSqr.add(BigInteger.valueOf(i * i));
}
sumSqr = sum.multiply(sum);
System.out.println(sumSqr + " " + sumOfSqr);
System.out.println(sumSqr.subtract(sumOfSqr).toString());
// System.out.println(Long.MAX_VALUE);
}
}
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
I'm trying to count trailing zeros of numbers that are resulted from factorials (meaning that the numbers get quite large). Following code takes a number, compute the factorial of the number, and count the trailing zeros. However, when the number is about as large as 25!, numZeros don't work.
public static void main(String[] args) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
double fact;
int answer;
try {
int number = Integer.parseInt(br.readLine());
fact = factorial(number);
answer = numZeros(fact);
}
catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static double factorial (int num) {
double total = 1;
for (int i = 1; i <= num; i++) {
total *= i;
}
return total;
}
public static int numZeros (double num) {
int count = 0;
int last = 0;
while (last == 0) {
last = (int) (num % 10);
num = num / 10;
count++;
}
return count-1;
}
I am not worrying about the efficiency of this code, and I know that there are multiple ways to make the efficiency of this code BETTER. What I'm trying to figure out is why the counting trailing zeros of numbers that are greater than 25! is not working.
Any ideas?
Your task is not to compute the factorial but the number of zeroes. A good solution uses the formula from http://en.wikipedia.org/wiki/Trailing_zeros (which you can try to prove)
def zeroes(n):
i = 1
result = 0
while n >= i:
i *= 5
result += n/i # (taking floor, just like Python or Java does)
return result
Hope you can translate this to Java. This simply computes [n / 5] + [n / 25] + [n / 125] + [n / 625] + ... and stops when the divisor gets larger than n.
DON'T use BigIntegers. This is a bozosort. Such solutions require seconds of time for large numbers.
You only really need to know how many 2s and 5s there are in the product. If you're counting trailing zeroes, then you're actually counting "How many times does ten divide this number?". if you represent n! as q*(2^a)*(5^b) where q is not divisible by 2 or 5. Then just taking the minimum of a and b in the second expression will give you how many times 10 divides the number. Actually doing the multiplication is overkill.
Edit: Counting the twos is also overkill, so you only really need the fives.
And for some python, I think this should work:
def countFives(n):
fives = 0
m = 5
while m <= n:
fives = fives + (n/m)
m = m*5
return fives
The double type has limited precision, so if the numbers you are working with get too big the double will be only an approximation. To work around this you can use something like BigInteger to make it work for arbitrarily large integers.
You can use a DecimalFormat to format big numbers. If you format your number this way you get the number in scientific notation then every number will be like 1.4567E7 this will make your work much easier. Because the number after the E - the number of characters behind the . are the number of trailing zeros I think.
I don't know if this is the exact pattern needed. You can see how to form the patterns here
DecimalFormat formater = new DecimalFormat("0.###E0");
My 2 cents: avoid to work with double since they are error-prone. A better datatype in this case is BigInteger, and here there is a small method that will help you:
public class CountTrailingZeroes {
public int countTrailingZeroes(double number) {
return countTrailingZeroes(String.format("%.0f", number));
}
public int countTrailingZeroes(String number) {
int c = 0;
int i = number.length() - 1;
while (number.charAt(i) == '0') {
i--;
c++;
}
return c;
}
#Test
public void $128() {
assertEquals(0, countTrailingZeroes("128"));
}
#Test
public void $120() {
assertEquals(1, countTrailingZeroes("120"));
}
#Test
public void $1200() {
assertEquals(2, countTrailingZeroes("1200"));
}
#Test
public void $12000() {
assertEquals(3, countTrailingZeroes("12000"));
}
#Test
public void $120000() {
assertEquals(4, countTrailingZeroes("120000"));
}
#Test
public void $102350000() {
assertEquals(4, countTrailingZeroes("102350000"));
}
#Test
public void $1023500000() {
assertEquals(5, countTrailingZeroes(1023500000.0));
}
}
This is how I made it, but with bigger > 25 factorial the long capacity is not enough and should be used the class Biginteger, with witch I am not familiar yet:)
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in = new Scanner(System.in);
System.out.print("Please enter a number : ");
long number = in.nextLong();
long numFactorial = 1;
for(long i = 1; i <= number; i++) {
numFactorial *= i;
}
long result = 0;
int divider = 5;
for( divider =5; (numFactorial % divider) == 0; divider*=5) {
result += 1;
}
System.out.println("Factorial of n is: " + numFactorial);
System.out.println("The number contains " + result + " zeroes at its end.");
in.close();
}
}
The best with logarithmic time complexity is the following:
public int trailingZeroes(int n) {
if (n < 0)
return -1;
int count = 0;
for (long i = 5; n / i >= 1; i *= 5) {
count += n / i;
}
return count;
}
shamelessly copied from http://www.programcreek.com/2014/04/leetcode-factorial-trailing-zeroes-java/
I had the same issue to solve in Javascript, and I solved it like:
var number = 1000010000;
var str = (number + '').split(''); //convert to string
var i = str.length - 1; // start from the right side of the array
var count = 0; //var where to leave result
for (;i>0 && str[i] === '0';i--){
count++;
}
console.log(count) // console shows 4
This solution gives you the number of trailing zeros.
var number = 1000010000;
var str = (number + '').split(''); //convert to string
var i = str.length - 1; // start from the right side of the array
var count = 0; //var where to leave result
for (;i>0 && str[i] === '0';i--){
count++;
}
console.log(count)
Java's doubles max out at a bit over 9 * 10 ^ 18 where as 25! is 1.5 * 10 ^ 25. If you want to be able to have factorials that high you might want to use BigInteger (similar to BigDecimal but doesn't do decimals).
I wrote this up real quick, I think it solves your problem accurately. I used the BigInteger class to avoid that cast from double to integer, which could be causing you problems. I tested it on several large numbers over 25, such as 101, which accurately returned 24 zeros.
The idea behind the method is that if you take 25! then the first calculation is 25 * 24 = 600, so you can knock two zeros off immediately and then do 6 * 23 = 138. So it calculates the factorial removing zeros as it goes.
public static int count(int number) {
final BigInteger zero = new BigInteger("0");
final BigInteger ten = new BigInteger("10");
int zeroCount = 0;
BigInteger mult = new BigInteger("1");
while (number > 0) {
mult = mult.multiply(new BigInteger(Integer.toString(number)));
while (mult.mod(ten).compareTo(zero) == 0){
mult = mult.divide(ten);
zeroCount += 1;
}
number -= 1;
}
return zeroCount;
}
Since you said you don't care about run time at all (not that my first was particularly efficient, just slightly more so) this one just does the factorial and then counts the zeros, so it's cenceptually simpler:
public static BigInteger factorial(int number) {
BigInteger ans = new BigInteger("1");
while (number > 0) {
ans = ans.multiply(new BigInteger(Integer.toString(number)));
number -= 1;
}
return ans;
}
public static int countZeros(int number) {
final BigInteger zero = new BigInteger("0");
final BigInteger ten = new BigInteger("10");
BigInteger fact = factorial(number);
int zeroCount = 0;
while (fact.mod(ten).compareTo(zero) == 0){
fact = fact.divide(ten);
zeroCount += 1;
}
}