Alternating factorial terms using Java - java

I'm trying to write a loop in Java that can output the sum of a series which has this form... 1! -3! + 5! – 7! + ... up to n (user gives n as a positive odd number). For example, if the user inputs 5 for n, then the series should calculate the sum of 1! -3! + 5! (hard part) & display it to the user with a basic print statement (easy part). If the user gives 9, then the sum calculated would come from 1! -3! + 5! - 7! + 9!.
For ease, just assume the user always puts in a positive odd number at any time. I'm just concerned about trying to make a sum using a loop for now.
The closest code I've come up with to do this...
int counter = 1;
int prod = 1;
n = console.nextInt();
while (counter <= n)
{
prod = prod * counter;
counter++;
}
System.out.println(prod);
This does n!, but I'm finding it hard to get it do as specified. Any pointers would be great.

As you calculate the factorials, keep a running total of the series so far. Whenever counter % 4 == 1, add the factorial to the running total. Whenever counter % 4 == 3, subtract the factorial from the running total.
You said "any pointers" - I assume that means you don't want me to write the code for you.
Update
This is closely based on your original code, so that it would be as easy as possible for you to understand. I have changed the bare minimum that I needed to change, to get this working.
int counter = 1;
long prod = 1;
long total = 0;
n = console.nextInt();
while (counter <= n)
{
prod = prod * counter;
if( counter % 4 == 1 ) {
total += prod;
} else if (counter % 4 == 3) {
total -= prod;
}
counter++;
}
System.out.println(total);
First up, notice that I have changed prod to a long. That's because factorials get very big very fast. It would be even better to use a BigInteger, but I'm guessing you haven't learnt about these yet.
Now, there are those two conditions in there, for when to add prod to the total, and when to subtract prod from the total. These both work by checking the remainder when counter is divided by 4 - in other words, checking which factorial we're up to, and doing the right operation accordingly.

First of all, you need to introduce a variable int sum = 0; to store the value of the alternate series.
To only sum every second value, you should skip every second value. You can check that using the modulo operation, e.g. if( counter % 2 == 1 ).
If that is true, you can add/subtract the current value of prod to the sum.
To get the alternating part, you can use a boolean positive = true; like this:
if( positive ) {
sum += prod;
} else {
sum -= prod;
}
positive = !positive;
Based on the boolean, the prod is either added or subtracted. The value is altered afterwards.
Because factorials become very large very fast, it would be better to use variables of type long.

Related

How to find the 5th perfect number (which is 33550336)? The problem is taking forever to run

I am trying to write a Java method that checks whether a number is a perfect number or not.
A perfect number is a number that is equal to the sum of all its divisor (excluding itself).
For example, 6 is a perfect number because 1+2+3=6. Then, I have to write a Java program to use the method to display the first 5 perfect numbers.
I have no problem with this EXCEPT that it is taking forever to get the 5th perfect number which is 33550336.
I am aware that this is because of the for loop in my isPerfectNumber() method. However, I am very new to coding and I do not know how to come up with a better code.
public class Labreport2q1 {
public static void main(String[] args) {
//Display the 5 first perfect numbers
int counter = 0,
i = 0;
while (counter != 5) {
i++;
isPerfectNumber(i);
if (isPerfectNumber(i)) {
counter++;
System.out.println(i + " ");
}
}
}
public static boolean isPerfectNumber(int a) {
int divisor = 0;
int sum = 0;
for (int i = 1; i < a; i++) {
if (a % i == 0) {
divisor = i;
sum += divisor;
}
}
return sum == a;
}
}
This is the output that is missing the 5th perfect number
Let's check the properties of a perfect number. This Math Overflow question tells us two very interesting things:
A perfect number is never a perfect square.
A perfect number is of the form (2k-1)×(2k-1).
The 2nd point is very interesting because it reduces our search field to barely nothing. An int in Java is 32 bits. And here we see a direct correlation between powers and bit positions. Thanks to this, instead of making millions and millions of calls to isPerfectNumber, we will be making less than 32 to find the 5th perfect number.
So we can already change the search field, that's your main loop.
int count = 0;
for (int k = 1; count < 5; k++) {
// Compute candidates based on the formula.
int candidate = (1L << (k - 1)) * ((1L << k) - 1);
// Only test candidates, not all the numbers.
if (isPerfectNumber(candidate)) {
count++;
System.out.println(candidate);
}
}
This here is our big win. No other optimization will beat this: why test for 33 million numbers, when you can test less than 100?
But even though we have a tremendous improvement, your application as a whole can still be improved, namely your method isPerfectNumber(int).
Currently, you are still testing way too many numbers. A perfect number is the sum of all proper divisors. So if d divides n, n/d also divides n. And you can add both divisors at once. But the beauty is that you can stop at sqrt(n), because sqrt(n)*sqrt(n) = n, mathematically speaking. So instead of testing n divisors, you will only test sqrt(n) divisors.
Also, this means that you have to start thinking about corner cases. The corner cases are 1 and sqrt(n):
1 is a corner case because you if you divide n by 1, you get n but you don't add n to check if n is a perfect number. You only add 1. So we'll probably start our sum with 1 just to avoid too many ifs.
sqrt(n) is a corner case because we'd have to check whether sqrt(n) is an integer or not and it's tedious. BUT the Math Overflow question I referenced says that no perfect number is a perfect square, so that eases our loop condition.
Then, if at some point sum becomes greater than n, we can stop. The sum of proper divisors being greater than n indicates that n is abundant, and therefore not perfect. It's a small improvement, but a lot of candidates are actually abundant. So you'll probably save a few cycles if you keep it.
Finally, we have to take care of a slight issue: the number 1 as candidate. 1 is the first candidate, and will pass all our tests, so we have to make a special case for it. We'll add that test at the start of the method.
We can now write the method as follow:
static boolean isPerfectNumber(int n) {
// 1 would pass the rest because it has everything of a perfect number
// except that its only divisor is itself, and we need at least 2 divisors.
if (n < 2) return false;
// divisor 1 is such a corner case that it's very easy to handle:
// just start the sum with it already.
int sum = 1;
// We can stop the divisors at sqrt(n), but this is floored.
int sqrt = (int)Math.sqrt(n);
// A perfect number is never a square.
// It's useful to make this test here if we take the function
// without the context of the sparse candidates, because we
// might get some weird results if this method is simply
// copy-pasted and tested on all numbers.
// This condition can be removed in the final program because we
// know that no numbers of the form indicated above is a square.
if (sqrt * sqrt == n) {
return false;
}
// Since sqrt is floored, some values can still be interesting.
// For instance if you take n = 6, floor(sqrt(n)) = 2, and
// 2 is a proper divisor of 6, so we must keep it, we do it by
// using the <= operator.
// Also, sqrt * sqrt != n, so we can safely loop to sqrt
for (int div = 2; div <= sqrt; div++) {
if (n % div == 0) {
// Add both the divisor and n / divisor.
sum += div + n / div;
// Early fail if the number is abundant.
if (sum > n) return false;
}
}
return n == sum;
}
These are such optimizations that you can even find the 7th perfect number under a second, on the condition that you adapt the code for longs instead of ints. And you could still find the 8th within 30 seconds.
So here's that program (test it online). I removed the comments as the explanations are here above.
public class Main {
public static void main(String[] args) {
int count = 0;
for (int k = 1; count < 8; k++) {
long candidate = (1L << (k - 1)) * ((1L << k) - 1);
if (isPerfectNumber(candidate)) {
count++;
System.out.println(candidate);
}
}
}
static boolean isPerfectNumber(long n) {
if (n < 2) return false;
long sum = 1;
long sqrt = (long)Math.sqrt(n);
for (long div = 2; div <= sqrt; div++) {
if (n % div == 0) {
sum += div + n / div;
if (sum > n) return false;
}
}
return n == sum;
}
}
The result of the above program is the list of the first 8 perfect numbers:
6
28
496
8128
33550336
8589869056
137438691328
2305843008139952128
You can find further optimization, notably in the search if you check whether 2k-1 is prime or not as Eran says in their answer, but given that we have less than 100 candidates for longs, I don't find it useful to potentially gain a few milliseconds because computing primes can also be expensive in this program. If you want to check for bigger perfect primes, it makes sense, but here? No: it adds complexity and I tried to keep these optimization rather simple and straight to the point.
There are some heuristics to break early from the loops, but finding the 5th perfect number still took me several minutes (I tried similar heuristics to those suggested in the other answers).
However, you can rely on Euler's proof that all even perfect numbers (and it is still unknown if there are any odd perfect numbers) are of the form:
2i-1(2i-1)
where both i and 2i-1 must be prime.
Therefore, you can write the following loop to find the first 5 perfect numbers very quickly:
int counter = 0,
i = 0;
while (counter != 5) {
i++;
if (isPrime (i)) {
if (isPrime ((int) (Math.pow (2, i) - 1))) {
System.out.println ((int) (Math.pow (2, i -1) * (Math.pow (2, i) - 1)));
counter++;
}
}
}
Output:
6
28
496
8128
33550336
You can read more about it here.
If you switch from int to long, you can use this loop to find the first 7 perfect numbers very quickly:
6
28
496
8128
33550336
8589869056
137438691328
The isPrime method I'm using is:
public static boolean isPrime (int a)
{
if (a == 1)
return false;
else if (a < 3)
return true;
else {
for (int i = 2; i * i <= a; i++) {
if (a % i == 0)
return false;
}
}
return true;
}

project euler #10, java, correct for small numbers

*disclaimer, when I say "I have verified this is the correct result", please interpret this as I have checked my solution against the answer according to WolframAlpha, which I consider to be pretty darn accurate.
*goal, to find the sum of all the prime numbers less than or equal to 2,000,000 (two million)
*issue, my code will output the correct result whenever my range of tested values is approximately less than or equal to
I do not output correct result once test input becomes larger than approximately 1,300,000; my output will be off...
test input: ----199,999
test output: ---1,709,600,813
correct result: 1,709,600,813
test input: ----799,999
test output: ---24,465,663,438
correct result: 24,465,663,438
test input: ----1,249,999
test output: ---57,759,511,224
correct result: 57,759,511,224
test input: ----1,499,999
test output:--- 82,075,943,263
correct result: 82,074,443,256
test input: ----1,999,999
test output:--- 142,915,828,925
correct result: 142,913,828,925
test input: ----49,999,999
test output:--- 72,619,598,630,294
correct result: 72,619,548,630,277
*my code, what's going on, why does it work for smaller inputs? I even used long, rather than int...
long n = 3;
long i = 2;
long prime = 0;
long sum = 0;
while (n <= 1999999) {
while (i <= Math.sqrt(n)) { // since a number can only be divisible by all
// numbers
// less than or equal to its square roots, we only
// check from i up through n's square root!
if (n % i != 0) { // saves computation time
i += 2; // if there's a remainder, increment i and check again
} else {
i = 3; // i doesn't need to go back to 2, because n+=2 means we'll
// only ever be checking odd numbers
n += 2; // makes it so we only check odd numbers
}
} // if there's not a remainder before i = n (meaning all numbers from 0
// to n were relatively prime) then move on
prime = n; // set the current prime to what that number n was
sum = sum + prime;
i = 3; // re-initialize i to 3
n += 2; // increment n by 2 so that we can check the next odd number
}
System.out.println(sum+2); // adding 2 because we skip it at beginning
help please :)
The problem is that you don't properly check whether the latest prime to be added to the sum is less than the limit. You have two nested loops, but you only check the limit on the outer loop:
while (n <= 1999999) {
But you don't check in the inner loop:
while (i <= Math.sqrt(n)) {
Yet you repeatedly advance to the next candidate prime (n += 2;) inside that loop. This allows the candidate prime to exceed the limit, since the limit is only checked for the very first candidate prime in each iteration of the outer loop and not for any subsequent candidate primes visited by the inner loop.
To take an example, in the case with the limit value of 1,999,999, this lets in the next prime after 1,999,999, which is 2,000,003. You'll note that the correct value, 142,913,828,922, is exactly 2,000,003 less than your result of 142,915,828,925.
A simpler structure
Here's one way the code could be structured, using a label and continue with that label to simplify structure:
public static final long primeSum(final long maximum) {
if (maximum < 2L) return 0L;
long sum = 2L;
// Put a label here so that we can skip to the next outer loop iteration.
outerloop:
for (long possPrime = 3L; possPrime <= maximum; possPrime += 2L) {
for (long possDivisor = 3L; possDivisor*possDivisor <= possPrime; possDivisor += 2L) {
// If we find a divisor, continue with the next outer loop iteration.
if (possPrime % possDivisor == 0L) continue outerloop;
}
// This possible prime passed all tests, so it's an actual prime.
sum += possPrime;
}
return sum;
}

Prime factorization algorithm fails for big numbers

I have run into a weird issue for problem 3 of Project Euler. The program works for other numbers that are small, like 13195, but it throws this error when I try to crunch a big number like 600851475143:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at euler3.Euler3.main(Euler3.java:16)
Here's my code:
//Number whose prime factors will be determined
long num = 600851475143L;
//Declaration of variables
ArrayList factorsList = new ArrayList();
ArrayList primeFactorsList = new ArrayList();
//Generates a list of factors
for (int i = 2; i < num; i++)
{
if (num % i == 0)
{
factorsList.add(i);
}
}
//If the integer(s) in the factorsList are divisable by any number between 1
//and the integer itself (non-inclusive), it gets replaced by a zero
for (int i = 0; i < factorsList.size(); i++)
{
for (int j = 2; j < (Integer) factorsList.get(i); j++)
{
if ((Integer) factorsList.get(i) % j == 0)
{
factorsList.set(i, 0);
}
}
}
//Transfers all non-zero numbers into a new list called primeFactorsList
for (int i = 0; i < factorsList.size(); i++)
{
if ((Integer) factorsList.get(i) != 0)
{
primeFactorsList.add(factorsList.get(i));
}
}
Why is it only big numbers that cause this error?
Your code is just using Integer, which is a 32-bit type with a maximum value of 2147483647. It's unsurprising that it's failing when used for numbers much bigger than that. Note that your initial loop uses int as the loop variable, so would actually loop forever if it didn't throw an exception. The value of i will go from the 2147483647 to -2147483648 and continue.
Use BigInteger to handle arbitrarily large values, or Long if you're happy with a limited range but a larger one. (The maximum value of long / Long is 9223372036854775807L.)
However, I doubt that this is really the approach that's expected... it's going to take a long time for big numbers like that.
Not sure if it's the case as I don't know which line is which - but I notice your first loop uses an int.
//Generates a list of factors
for (int i = 2; i < num; i++)
{
if (num % i == 0)
{
factorsList.add(i);
}
}
As num is a long, its possible that num > Integer.MAX_INT and your loop is wrapping around to negative at MAX_INT then looping until 0, giving you a num % 0 operation.
Why does your solution not work?
Well numbers are discrete in hardware. Discrete means thy have a min and max values. Java uses two's complement, to store negative values, so 2147483647+1 == -2147483648. This is because for type int, max value is 2147483647. And doing this is called overflow.
It seems as if you have an overflow bug. Iterable value i first becomes negative, and eventually 0, thus you get java.lang.ArithmeticException: / by zero. If your computer can loop 10 million statements a second, this would take 1h 10min to reproduce, so I leave it as assumption an not a proof.
This is also reason trivially simple statements like a+b can produce bugs.
How to fix it?
package margusmartseppcode.From_1_to_9;
public class Problem_3 {
static long lpf(long nr) {
long max = 0;
for (long i = 2; i <= nr / i; i++)
while (nr % i == 0) {
max = i;
nr = nr / i;
}
return nr > 1 ? nr : max;
}
public static void main(String[] args) {
System.out.println(lpf(600851475143L));
}
}
You might think: "So how does this work?"
Well my tough process went like:
(Dynamical programming approach) If i had list of primes x {2,3,5,7,11,13,17, ...} up to value xi > nr / 2, then finding largest prime factor is trivial:
I start from the largest prime, and start testing if devision reminder with my number is zero, if it is, then that is the answer.
If after looping all the elements, I did not find my answer, my number must be a prime itself.
(Brute force, with filters) I assumed, that
my numbers largest prime factor is small (under 10 million).
if my numbers is a multiple of some number, then I can reduce loop size by that multiple.
I used the second approach here.
Note however, that if my number would be just little off and one of {600851475013, 600851475053, 600851475067, 600851475149, 600851475151}, then my approach assumptions would fail and program would take ridiculously long time to run. If computer could execute 10m statements per second it would take 6.954 days, to find the right answer.
In your brute force approach, just generating a list of factors would take longer - assuming you do not run out of memory before.
Is there a better way?
Sure, in Mathematica you could write it as:
P3[x_] := FactorInteger[x][[-1, 1]]
P3[600851475143]
or just FactorInteger[600851475143], and lookup the largest value.
This works because in Mathematica you have arbitrary size integers. Java also has arbitrary size integer class called BigInteger.
Apart from the BigInteger problem mentioned by Jon Skeet, note the following:
you only need to test factors up to sqrt(num)
each time you find a factor, divide num by that factor, and then test that factor again
there's really no need to use a collection to store the primes in advance
My solution (which was originally written in Perl) would look something like this in Java:
long n = 600851475143L; // the original input
long s = (long)Math.sqrt(n); // no need to test numbers larger than this
long f = 2; // the smallest factor to test
do {
if (n % f == 0) { // check we have a factor
n /= f; // this is our new number to test
s = (long)Math.sqrt(n); // and our range is smaller again
} else { // find next possible divisor
f = (f == 2) ? 3 : f + 2;
}
} while (f < s); // required result is in "n"

Sum of numbers under 10,000 that are multiples of 3, 5 or 7 in Java

I know how to get the program to add up the sums of the multiple for each of 3, 5 and 7, but I'm not sure how I'd get the program to only use each number once. For example, I can get the program to find out all of the numbers and add them up for 3 and then do the same for 5, but then the number 15 would be in the final number twice. I'm not sure exactly how I'd get it to only take the number once. Thanks for any help.
While the generate-and-test approach is simple to understand, it is also not very efficient if you want to run this on larger numbers. Instead, we can use the inclusion-exclusion principle.
The idea is to first sum up too many numbers by looking at the multiples of 3, 5 and 7 separately. Then we subtract the ones we counted twice, i.e. multiples of 3*5, 3*7 and 5*7. But now we subtracted too much and need to add back the multiples of 3*5*7 again.
We start by finding the sum of all integers 1..n which are multiples of k. First, we find out how many there are, m = n / k, rounded down thanks to integer division. Now we just need to sum up the sequence k + 2*k + 3*k + ... + m*k. We factor out the k and get k * (1 + 2 + ... + m).
This is a well-known arithmetic series, which we know sums to k * m * (m + 1)/2 (See triangle number).
private long n = 9999;
private long multiples(long k) {
long m = n / k;
return k * m * (m + 1) / 2:
}
Now we just use inclusion-exclusion to get the final sum:
long sum = multiples(3) + multiples(5) + multiples(7)
- multiples(3*5) - multiples(3*7) - multiples(5*7)
+ multiples(3*5*7);
This will scale much better to larger n than just looping over all the values, but beware of overflow and change to BigIntegers if necessary.
The easiest approach would be to use a for loop thus:
int sum = 0;
for(int i=1; i<10000; i++)
{
if (i % 3 == 0 || i % 5 == 0 || i % 7 == 0)
sum += i;
}
Use a Set to store the unique multiples, and then sum the values of the Set.
I would use a Set. This way you are guaranteed that you won't get any duplicates if they are your main problem.
One simple solution would be to add each number thats a multiple of 3,5, or 7 to an Answer list. And then as you work thru each number, make sure that its not already in the answer list.
(pseudo-code)
List<int> AnswerList;
List<int> MultiplesOfFive;
List<int> MultiplesOfSeven;
List<int> MultiplesOfThree;
for (int i = 0 ; i < 10000; i++)
{
if ( i % 3 == 0 && AnswserList.Contains(i) == false)
{
MultiplesOfThree.Add(i);
AnswerList.Add(i);
}
if ( i % 5 == 0 && AnswserList.Contains(i) == false)
{
MultiplesOfFive.Add(i);
AnswerList.Add(i);
}
if ( i % 7 == 0 && AnswserList.Contains(i) == false)
{
MultiplesOfSeven.Add(i);
AnswerList.Add(i);
}
}
for the solution that loops 1 to 1000 use i<=10000 otherwise it'll skip 10000 itself which is a multiple of 5. Apologies, for some reason i can't post this as a comment

Bounding this program to determine the sum of reciprocal integers not containing zero

Let A denote the set of positive integers whose decimal representation does not contain the digit 0. The sum of the reciprocals of the elements in A is known to be 23.10345.
Ex. 1,2,3,4,5,6,7,8,9,11-19,21-29,31-39,41-49,51-59,61-69,71-79,81-89,91-99,111-119, ...
Then take the reciprocal of each number, and sum the total.
How can this be verified numerically?
Write a computer program to verify this number.
Here is what I have written so far, I need help bounding this problem as this currently takes too long to complete:
Code in Java
import java.util.*;
public class recip
{
public static void main(String[] args)
{
int current = 0; double total = 0;
while(total < 23.10245)
{
if(Integer.toString(current).contains("0"))
{
current++;
}
else
{
total = total + (1/(double)current);
current++;
}
System.out.println("Total: " + total);
}
}
}
This is not that hard when approached properly.
Assume for example that you want to find the sum of reciprocals of all integers starting (i.e. the left-most digits) with 123 and ending with k non-zero digits. Obviously there are 9k such integers and the reciprocal of each of these integers is in the range 1/(124*10k) .. 1/(123*10k). Hence the sum of reciprocals of all these integers is bounded by (9/10)k/124 and (9/10)k/123.
To find bounds for sum of all reciprocals starting with 123 one has to add up the bounds above for every k>=0. This is a geometric serie, hence it can be derived that the sum of reciprocals of integers starting with 123 is bounded by 10*(9/10)k/124 and 10*(9/10)k/123.
The same method can of course be applied for any combination of left-most digits.
The more digits we examine on the left, the more accurate the result becomes.
Here is an implementation of this approach in python:
def approx(t,k):
"""Returns a lower bound and an upper bound on the sum of reciprocals of
positive integers starting with t not containing 0 in its decimal
representation.
k is the recursion depth of the search, i.e. we append k more digits
to t, before approximating the sum. A larger k gives more accurate
results, but takes longer."""
if k == 0:
return 10.0/(t+1), 10.0/t
else:
if t > 0:
low, up = 1.0/t, 1.0/t
else:
low, up = 0, 0
for i in range(10*t+1, 10*t+10):
l,u = approx(i, k-1)
low += l
up += u
return low, up
Calling approx(0, 8) for example gives the lower and upper bound:
23.103447707... and 23.103448107....
which is close to the claim 23.10345 given by the OP.
There are methods that converge faster to the sum in question, but they require more math.
A much better approximation of the sum can be found here. A generalization of the problem are the Kempner series.
For all values of current greater than some threshold N, 1.0/(double)current will be sufficiently small that total does not increase as a result of adding 1.0/(double)current. Thus, the termination criterion should be something like
while(total != total + (1.0/(double)current))
instead of testing against the limit that is known a priori. Your loop will stop when current reaches this special value of N.
I suspect that casting to string and then checking for the character '0' is the step that takes too long. If you want to avoid all zeroes, might help to increase current thus:
(Edited -- thanks to Aaron McSmooth)
current++;
for( int i = 10000000; i >= 10; i = i / 10 )
{
if ( current % i ) == 0
{
current = current + ( i / 10 );
}
}
This is untested, but the concept should be clear: whenever you hit a multiple of a power of ten (e.g. 300 or 20000), you add the next lower power of 10 (in our examples 10 + 1 and 1000 + 100 + 10 + 1, respectively) until there are no more zeroes in your number.
Change your while loop accordingly and see if this doesn't help performance to the point were your problem becomes manageable.
Oh, and you might want to restrict the System.out output a bit as well. Would every tenth, one hundreth or 10000th iteration be enough?
Edit the second:
After some sleep, I suspect my answer might be a little short-sighted (blame the late hour, if you will). I simply hoped that, oh, one million iterations of current would get you to the solution and left it at that, instead of calculating the correction cases using log( current ) etc.
On second thought, I see two problems with this whole problem. One is that your target number of 23.10345 is a leeeeettle to round for my tastes. After all, you are adding thousands of items like "1/17", "1/11111" and so on, with infinite decimal representations, and it is highly unlikely that they add up to exactly 23.10345. If some specialist for numerical mathematics says so, fine -- but then I'd like to see the algorithm by which they arrived at this conclusion.
The other problem is related to the first and concerns the limited in-memory binary representation of your rational numbers. You might get by using BigDecimals, but I have my doubts.
So, basically, I suggest you reprogram the numerical algorithm instead of going for the brute force solution. Sorry.
Edit the third:
Out of curiosity, I wrote this in C++ to test my theories. It's run for 6 minutes now and is at about 14.5 (roughly 550 mio. iterations). We'll see.
Current version is
double total = 0;
long long current = 0, currPowerCeiling = 10, iteration = 0;
while( total < 23.01245 )
{
current++;
iteration++;
if( current >= currPowerCeiling )
currPowerCeiling *= 10;
for( long long power = currPowerCeiling; power >= 10; power = power / 10 )
{
if( ( current % power ) == 0 )
{
current = current + ( power / 10 );
}
}
total += ( 1.0 / current );
if( ! ( iteration % 1000000 ) )
std::cout << iteration / 1000000 << " Mio iterations: " << current << "\t -> " << total << std::endl;
}
std::cout << current << "\t" << total << std::endl;
Calculating currPowerCeiling (or however one might call this) by hand saves some log10 and pow calculations each iteration. Every little bit helps -- but it still takes forever...
Edit the fourth:
Status is around 66,000 mio iterations, total is up to 16.2583, runtime is at around 13 hours. Not looking good, Bobby S. -- I suggest a more mathematical approach.
How about storing the current number as a byte array where each array element is a digit 0-9? That way, you can detect zeroes very quickly (comparing bytes using == instead of String.contains).
The downside would be that you'll need to implement the incrementing yourself instead of using ++. You'll also need to devise a way to mark "nonexistent" digits so that you don't detect them as zeroes. Storing -1 for nonexistent digits sounds like a reasonable solution.
For a signed 32-bit integer, this program will never stop. It will actually converge towards -2097156. Since the maximum harmonic number (the sum of integral reciprocals from 1 to N) of a signed 32-bit integer is ~14.66, this loop will never terminate, even when current wraps around from 2^31 - 1 to -2^31. Since the reciprocal of the largest negative 32-bit integer is ~-4.6566e-10, every time current returns to 0, the sum will be negative. Given that the largest number representable by a double such that number + + 1/2^31 == number is 2^52/2^31, you get roughly -2097156 as the converging value.
Having said that, and assuming you don't have a direct way of calculating the harmonic number of an arbitrary integer, there are a few things you can do to speed up your inner loop. First, the most expensive operation is going to be System.out.println; that has to interact with the console in which case your program will eventually have to flush the buffer to the console (if any). There are cases where that may not actually happen, but since you are using that for debugging they are not relevant to this question.
However, you also spend a lot of time determining whether a number has a zero. You can flip that test around to generate ranges of integers such that within that range you are guaranteed not to have an integer with a zero digit. That is really simple to do incrementally (in C++, but trivial enough to convert to Java):
class c_advance_to_next_non_zero_decimal
{
public:
c_advance_to_next_non_zero_decimal(): next(0), max_set_digit_index(0)
{
std::fill_n(digits, digit_count, 0);
return;
}
int advance_to_next_non_zero_decimal()
{
assert((next % 10) == 0);
int offset= 1;
digits[0]+= 1;
for (int digit_index= 1, digit_value= 10; digit_index<=max_set_digit_index; ++digit_index, digit_value*= 10)
{
if (digits[digit_index]==0)
{
digits[digit_index]= 1;
offset+= digit_value;
}
}
next+= offset;
return next;
}
int advance_to_next_zero_decimal()
{
assert((next % 10)!=0);
assert(digits[0]==(next % 10));
int offset= 10 - digits[0];
digits[0]+= offset;
assert(digits[0]==10);
// propagate carries forward
for (int digit_index= 0; digits[digit_index]==10 && digit_index<digit_count; ++digit_index)
{
digits[digit_index]= 0;
digits[digit_index + 1]+= 1;
max_set_digit_index= max(digit_index + 1, max_set_digit_index);
}
next+= offset;
return next;
}
private:
int next;
static const size_t digit_count= 10; // log10(2**31)
int max_set_digit_index;
int digits[digit_count];
};
What the code above does is to iterate over every range of numbers such that the range only contains numbers without zeroes. It works by determining how to go from N000... to N111... and from N111... to (N+1)000..., carrying (N+1) into 1(0)000... if necessary.
On my laptop, I can generate the harmonic number of 2^31 - 1 in 8.73226 seconds.
public class SumOfReciprocalWithoutZero {
public static void main(String[] args) {
int maxSize=Integer.MAX_VALUE/10;
long time=-System.currentTimeMillis();
BitSet b=new BitSet(maxSize);
setNumbersWithZeros(10,maxSize,b);
double sum=0.0;
for(int i=1;i<maxSize;i++)
{
if(!b.get(i))
{
sum+=1.0d/(double)i;
}
}
time+=System.currentTimeMillis();
System.out.println("Total: "+sum+"\nTimeTaken : "+time+" ms");
}
static void setNumbersWithZeros(int srt,int end,BitSet b)
{
for(int j=srt;j<end;j*=10)
{
for(int i=1;i<=10;i++)
{
int num=j*i;
b.set(num);
}
if(j>=100)
setInbetween(j, b);
}
}
static void setInbetween(int strt,BitSet b)
{
int bitToSet;
bitToSet=strt;
for(int i=1;i<=10;i++)
{
int nxtInt=-1;
while((nxtInt=b.nextSetBit(nxtInt+1))!=strt)
{
b.set(bitToSet+nxtInt);
}
nxtInt=-1;
int lim=strt/10;
while((nxtInt=b.nextClearBit(nxtInt+1))<lim)
{
b.set(bitToSet+nxtInt);
}
bitToSet=strt*i;
}
}
}
This is an implementation using BitSet.I calculated the sum of reciprocal's for all integer's in range (1-Integer.MAX_VALUE/10).The sum comes upto 13.722766931560747.This is the maximum I could calculate using BitSet since the maximum range for BitSet is Integer.MAX_VALUE.I need to divide it by 10 and limit the range to avoid overflow.But there is significant improvement in speed.I'm just posting this code in-case it might give you some new idea to improve your code.(Increase your memory using the VM argument -Xmx[Size>350]m)
Output:
Total: 13.722766931560747
TimeTaken : 60382 ms
UPDATE:
Java Porting of a previous , deleted answer :
public static void main(String[] args) {
long current =11;
double tot=1 + 1.0/2 + 1.0/3 + 1.0/4 + 1.0/5 + 1.0/6 + 1.0/7 + 1.0/8 + 1.0/9;
long i=0;
while(true)
{
current=next_current(current);
if(i%10000!=0)
System.out.println(i+" "+current+" "+tot);
for(int j=0;j<9;j++)
{
tot+=(1.0/current + 1.0/(current + 1) + 1.0/(current + 2) + 1.0/(current + 3) + 1.0/(current + 4) +
1.0/(current + 5) + 1.0/(current + 6) + 1.0/(current + 7) + 1.0/(current + 8));
current += 10;
}
i++;
}
}
static long next_current(long n){
long m=(long)Math.pow(10,(int)Math.log10(n));
boolean found_zero=false;
while(m>=1)
{
if(found_zero)
n+=m;
else if((n/m)%10==0)
{
n=n-(n%m)+m;
found_zero=true;
}
m=m/10;
}
return n;
}

Categories