For me it is intuitively simple .. you divide more than you multiply and that's that.
Anyway I have spent almost 3h on this problem 20m writing a solution in Ruby and ... 2h 30m running it.
On my home machine I have a Ruby 1.8.7 on Windows 7 environment and my computer a Core i7 860 8GB RAM machine (good enough I would say). I gave up at around 1 AM while it was half done.
Here is the code.
def chain_collatz(n,cache)
chain_length=1
puts "-> #{n}"
if n==1
return chain_length
else
if cache[n]!=nil
puts "cache_hit"
chain_length+=cache[n]
return chain_length
end
if n%2==0
n=n/2
else
n=3*n+1
end
chain_length+=chain_collatz(n,cache)
cache[n]=chain_length
return chain_length
end
end
#puts chain_collatz(10100,cache)
#puts "cache"
#puts cache.keys
cache=Hash.new
def get_number(n,cache)
max_chain_length=1
vnumber=13
max_number=1
for i in 13..n
chain_length=chain_collatz(i,cache)
max_number=i if chain_length>max_chain_length
max_chain_length = chain_length if chain_length > max_chain_length
number=i
puts "Done with number #{number}"
end
return max_number
end
puts get_number 1000000,cache
After a lot of pain it ran and gave the correct result. Found it only this morning.
After having the problem solved I rewrote it in Java to see whether I could have it any faster:
The result?
import java.util.HashMap;
import java.util.Map;
public class Collatz {
public static void main(String args[]) {
Map m = new HashMap();
Collatz collatz = new Collatz();
long startTime =System.currentTimeMillis();
System.out.println(collatz.getMaxCollatzTo(1000000l, m));
long endTime =System.currentTimeMillis();
System.out.println (" Execution took" +(endTime-startTime) +" milisseconds");
}
public Long chainCollatz(Long number, Map map) {
Long chainLength = 1l;
if (number == 1) {
return 1l;
} else {
if (map.containsKey(number)) {
// System.out.println("Cache hit" + number);
chainLength += map.get(number);
return chainLength;
}
if (number % 2 == 0) {
number = number / 2;
} else {
number = 3 * number + 1;
}
chainLength += chainCollatz(number, map);
map.put(number, chainLength);
return chainLength;
}
}
public Long getMaxCollatzTo(Long number, Map map) {
Long maxChainLength = 1l;
Long maxChainForNumber = 1l;
for (Long i = 13l; i < number; i++) {
Long chainLength = chainCollatz(i, map);
if (chainLength > maxChainLength) {
maxChainLength = chainLength;
maxChainForNumber = i;
}
// System.out.println("Done with "+i);
}
return maxChainForNumber;
}
}
The Java code took only 50 seconds to run.
Anyway puzzled by this I started optimizing the code in both Ruby and Java .. removing all but essential console outputs.
Finally I got to test it on a Linux OS with similar processing power (Ruby implementation in Windows really sucks I think).
Final result.
Ruby 1.8.7 11.7 seconds
Java 6(1.6.0.22) 1.471 seconds
Tested also with ruby 1.9 and got it down to 3 seconds .
This puts Java on top of my list for Project Euler problems and makes me question the ability of Ruby to run unoptimized code.
But still for prototyping it's great.