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
The question states:
What is the largest prime factor of the number 600851475143?
I solved this using C#, but the algorithm itself is language-agnostic.
private static long IsPrime(long input)
{
if ((input % 2) == 0)
{
return 2;
}
else if ((input == 1))
{
return 1;
}
else
{
long threshold = (Convert.ToInt64(Math.Sqrt(input)));
long tryDivide = 3;
while (tryDivide < threshold)
{
if ((input % tryDivide) == 0)
{
Console.WriteLine("Found a factor: " + tryDivide);
return tryDivide;
}
tryDivide += 2;
}
Console.WriteLine("Found a factor: " + input);
return -1;
}
}
private static long HighestPrimeFactor(long input)
{
bool searching = true;
long highestFactor = 0;
while (searching)
{
long factor = IsPrime(input);
if (factor != -1)
{
theFactors.Add(factor);
input = input / factor;
}
if (factor == -1)
{
theFactors.Add(input);
highestFactor = theFactors.Max();
searching = false;
}
}
return highestFactor;
}
I hope this helps without giving too much away.
Here is my solution:
polindroms = [(x, y, x * y) for x in range(100, 999) for y in range(100, 999) if str(x * y) == str(x * y)[::-1]]
print max(polindroms, key = lambda item : item[2])
The other advice here is great. This code also works. I start with 999 because we know the largest combination possible is 999*999. Not python, but some quickly done pseudo code.
public static int problem4()
{
int biggestSoFar=0;
for(int i = 999; i>99;i--){
for(int j=999; j>99;j--){
if(isPaladrome(i*j))
if(i*j>biggestSoFar)
biggestSoFar=i*j;
}
}
return biggestSoFar;
}
Try computing x from the product of z and y rather than checking every number from 1 to a million. Think about it: if you were asked to calculate 500*240, which is more efficient - multiplying them, or counting up from 1 until you find the right answer?
comparing string with an integer in
x == z*y
there are also logical errors
start in reverse order range(999, 99, -1)
. that'll be more efficient. remove third loop and second comparison altogether.
This adds a couple of optimizations to @GreggLind's good solution, cutting the run time in half:
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):
# Optim. 1: Nothing in any row from here on can be bigger.
if x*x < max_seen: break
for y in xrange(x, 99,-1): # so we don't double count
# Optim. 2: break, not continue
if x*y < max_seen: break # 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)
The line
if x*x < max_seen: break
means that once we get to the point where x is less than sqrt of largest palindrome yet seen, not only do we not need to investigate any more factors on that row; we don't even need to investigate any more rows at all, since all remaining rows would start from a number less than the current value of x.
This doesn't reduce the number of times we call is_palindrome()
, but it means many fewer iterations of the outer loop. The value of x
that it breaks on is 952, so we've eliminated the checking of 853 rows (albeit the "smaller" ones, thanks to the other break
).
I also noticed that
if x*y < max_seen: continue
should be
if x*y < max_seen: break
We are trying to short-circuit the whole row, not just the current iteration of the inner loop.
When I ran this script using cProfile, the cumulative time for biggest()
was about 56 msec on average, before the optimizations. The optimizations shrank it to about 23 msec. Either optimization alone would deliver most of that improvement, but the first one is slightly more helpful than the second.