0

This is my solution for the 5th problem in Project Euler. Is there any way to improve the while loop conditional instead of using the sum?

table = range(1,21)
result = 1
pf = 2
while sum(table) > len(table):
   flag = False
   for x,y in enumerate(table):
      if y % pf == 0:
         table[x] = y/pf 
         flag = True
   if flag:
      result *= pf
   else:
      pf += 1
print result
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Veritas
  • 2,150
  • 3
  • 16
  • 40

3 Answers3

3

It's always clearer to write a program using a higher-order function instead of a loop because all of the extraneous variables that control the operation of the loop disappear. Here's a program that performs the same computation as yours, but the while and for loops disappear, as do the variables table, result, pf, and flag; variables x and y remain, but limited to a support role in an auxiliary function rather than as part of the main calculation:

>>> from fractions import gcd
>>> def lcm(x,y): return x*y/gcd(x,y)
...
>>> print reduce(lcm,range(1,21))
232792560
user448810
  • 17,381
  • 4
  • 34
  • 59
  • I disagree that it's always clearer - you always need to consider whether the code becomes more readable by turning for loops into functions. – Simeon Visser Jan 30 '14 at 13:53
  • @SimeonVisser: Of course anything can be abused. But hiding the plumbing makes most programs clearer and simpler. – user448810 Jan 30 '14 at 14:18
1

Use a constant. It's easier to understand when you want to go back and try a different value too.

MAX_DIVISOR = 20
table = range(1,MAX_DIVISOR + 1)
result = 1
pf = 2
while pf < MAX_DIVISOR + 1:
   flag = False
   for x,y in enumerate(table):
      if y % pf == 0:
     table[x] = y/pf 
     flag = True
   if flag:
      result *= pf
   else:
      pf += 1
print result
JoeClacks
  • 384
  • 1
  • 4
  • How does this conditional work? pf is the prime factor I'm using to divide the elements in the table if it's a factor of theirs. The algorithm stops when all items in the table are 1. I don't understand why pf has to be smaller than the max element in the table. Well I do but how does it make sense as the loop conditional? – Veritas Jan 30 '14 at 08:49
  • Fixed the error in the while loop condition. If you looked at the contents of `table` while it was working you would have seen that it continues incrementing `pf` until it reaches 19 - the last prime number. In fact your version will always keep working until it reaches the last prime number before the target number. – JoeClacks Jan 30 '14 at 18:58
  • This version (with the fixed conditional) just runs until the last number. So the only difference will be at most the [Prime gap](http://en.wikipedia.org/wiki/Prime_gap) which runs on around the order of **O((ln(n))^2)** - mostly insignificant. In fact if we wanted to make this algorithm significantly better we could set `pf` to just the sequence of prime numbers - avoiding having to test any composite numbers. – JoeClacks Jan 30 '14 at 18:59
0

You could use any() to test the table, like so:

while any(n != 1 for n in table):
    # do stuff

I think this is clearer than sum(table) > len(table).

Also, as @JoeClacks recommended, use a constant.

Complete revised solution:

MAX_DIVISOR = 20
table = list(range(1, MAX_DIVISOR+1))

result = 1
pf = 2

while any(n != 1 for n in table):
    assert pf <= MAX_DIVISOR
    flag = False
    for i,n in enumerate(table):
        if n % pf == 0:
            table[i] = n//pf
            flag = True
    if flag:
        result *= pf
    else:
        pf += 1

print(result)

I added an assert to make sure that pf only has legal values; this isn't needed as long as there is no bug in the code, but it can be a good idea to make code sanity-check itself.

I used i and n for the index and the number, rather than x and y.

I used Python's integer division operator // rather than /, so the code will work the same on Python 3.x as on Python 2.x. Also, the way I wrote the print statement it will work equally well in Python 3.x and Python 2.x.

I changed the indentation to steps of 4 spaces in accordance with PEP 8.

http://www.python.org/dev/peps/pep-0008/

Note: I like this algorithm for solving this problem. It's elegant. Did you invent this, get it from a book, or what?

EDIT: Actually, Project Euler problem 5 has been discussed before here on StackOverflow. Here is an answer that I just compared to the above answer. This one is almost exactly ten times faster than the one above. It's a bit trickier though!

https://stackoverflow.com/a/8026041/166949

Community
  • 1
  • 1
steveha
  • 74,789
  • 21
  • 92
  • 117
  • The algorithm just occurred to me yesterday but it probably already exists. By the way, the any function expresses the condition much better than the sum I'm using. What I had in mind was replacing that with a prime factor conditional but I'm not even sure if that's possible. – Veritas Jan 30 '14 at 08:58