Using Python, I am trying to solve problem #4 of the Project Euler problems. Can someone please tell me what I am doing incorrectly? The problem is to Find the larg
Wow, this approach improves quite a bit over other implementations on this page, including mine.
Instead of
we
It's not hard to prove that on each diagonal, the closer x and y are to each other, the higher the product. So we can start from the middle, x = y
(or x = y + 1
for odd diagonals) and still do the same short-circuit optimizations as before. And because we can start from the highest diagonals, which are the shortest ones, we are likely to find the highest qualifying palindrome much sooner.
maxFactor = 999
minFactor = 100
def biggest():
big_x, big_y, max_seen, prod = 0, 0, 0, 0
for r in xrange(maxFactor, minFactor-1, -1):
if r * r < max_seen: break
# Iterate along diagonals ("ribs"):
# Do rib x + y = r + r
for i in xrange(0, maxFactor - r + 1):
prod = (r + i) * (r - i)
if prod < max_seen: break
if is_palindrome(prod):
big_x, big_y, max_seen = r+i, r-i, prod
# Do rib x + y = r + r - 1
for i in xrange(0, maxFactor - r + 1):
prod = (r + i) * (r - i - 1)
if prod < max_seen: break
if is_palindrome(prod):
big_x, big_y, max_seen = r+i,r-i-1, prod
return big_x, big_y, max_seen
# biggest()
# (993, 913, 906609)
Instead of calling is_palindrome() 6124 times, we now only call it 2228 times. And the total accumulated time has gone from about 23 msec down to about 9!
I'm still wondering if there is a perfectly linear (O(n)) way to generate a list of products of two sets of numbers in descending order. But I'm pretty happy with the above algorithm.
This is what I did in Java:
public class Euler0004
{
//assumes positive int
static boolean palindrome(int p)
{
//if there's only one char, then it's
// automagically a palindrome
if(p < 10)
return true;
char[] c = String.valueOf(p).toCharArray();
//loop over the char array to check that
// the chars are an in a palindromic manner
for(int i = 0; i < c.length / 2; i++)
if(c[i] != c[c.length-1 - i])
return false;
return true;
}
public static void main(String args[]) throws Exception
{
int num;
int max = 0;
//testing all multiples of two 3 digit numbers.
// we want the biggest palindrome, so we
// iterate backwards
for(int i = 999; i > 99; i--)
{
// start at j == i, so that we
// don't calc 999 * 998 as well as
// 998 * 999...
for(int j = i; j > 99; j--)
{
num = i*j;
//if the number we calculate is smaller
// than the current max, then it can't
// be a solution, so we start again
if(num < max)
break;
//if the number is a palindrome, and it's
// bigger than our previous max, it
// could be the answer
if(palindrome(num) && num > max)
max = num;
}
}
//once we've gone over all of the numbers
// the number remaining is our answer
System.out.println(max);
}
}
If your program is running slow, and you have nested loops like this:
for z in range(100, 1000):
for y in range(100, 1000):
for x in range(1, 1000000):
Then a question you should ask yourself is: "How many times will the body of the innermost loop execute?" (the body of your innermost loop is the code that starts with: x = str(x)
)
In this case, it's easy to figure out. The outer loop will execute 900 times. For each iteration the middle loop will also execute 900 times – that makes 900×900, or 810,000, times. Then, for each of those 810,000 iterations, the inner loop will itself execute 999,999 times. I think I need a long to calculate that:
>>> 900*900*999999
809999190000L
In other words, you're doing your palindrome check almost 810 billion times. If you want to make it into the Project Euler recommended limit of 1 minute per problem, you might want to optimise a little :-) (see David's comment)
Some efficiency issues:
def is_palindrome(n): s = str(n) return s == s[::-1] def biggest(): big_x, big_y, max_seen = 0,0, 0 for x in xrange(999,99,-1): for y in xrange(x, 99,-1): # so we don't double count if x*y < max_seen: continue # since we're decreasing, # nothing else in the row can be bigger if is_palindrome(x*y): big_x, big_y, max_seen = x,y, x*y return big_x,big_y,max_seen biggest() # (993, 913, 906609)
rather than enumerating all products of 3-digit numbers (~900^2 iterations), enumerate all 6- and 5-digit palyndromes (this takes ~1000 iterations); then for each palyndrome decide whether it can be represented by a product of two 3-digit numbers (if it can't, it should have a 4-digit prime factor, so this is kind of easy to test).
also, you are asking about problem #4, not #3.
Here's some general optimizations to keep in mind. The posted code handles all of this, but these are general rules to learn that might help with future problems:
1) if you've already checked z = 995, y = 990, you don't need to check z = 990, y = 995. Greg Lind handles this properly
2) You calculate the product of z*y and then you run x over a huge range and compare that value to y*z. For instance, you just calculated 900*950, and then you run x from 1000 to 1M and see if x = 900*950. DO you see the problem with this?
3) Also, what happens to the following code? (this is why your code is returning nothing, but you shouldn't be doing this anyway)
x = str(100)
y = 100
print x == y
4) If you figure out (3), you're going to be printing a lot of information there. You need to figure out a way to store the max value, and only return that value at the end.
5) Here's a nice way to time your Euler problems:
if __name__ == "__main__":
import time
tStart = time.time()
print "Answer = " + main()
print "Run time = " + str(time.time() - tStart)