Saturday, September 3, 2011

Python solution of Project Euler, problem 5

What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?

I enjoyed working on this problem. It's short, simple, and didn't take very long. I learned nothing new about Python, but I enjoyed it because of the optimizations I realized that brought the execution time to about 0.4 seconds.  The key optimizations are the following:

  1. When checking the divisibility of the numbers [1,20], you really only need to check [11,20] as the [2,10] are implicitly checked (for example, if 20 divides n, we know 10, 5, 4, and 2 divide n, if 14 divides n, we know 7 and 2 divide n, and so on).
  2. We can increment each value by 380 to significantly reduce the number of numbers tried. Why 380? Well, my first guess was to just increment by 20, since it's the highest number that must be divisible by a value, but then I realized that 19 * 2, 19 * 3, ..., 19 * 19 are never divisible by 20, so we increment by 19 * 20 = 380. Does this have to do with the fact 19 is prime? I'm not sure, can somebody prove it? The difference in performance was huge between my first solution, where I incremented 20, and the current solution (about a 1000% increase).

Python Implementation:
def main():
  val, found = 380, False
  while not found:
    # numbers below 11 do not need to be checked because they are already
    # implicitly checked by the higher numbers; e.g., if 20 divides val, so
    # does 10, same for 18 and 9, 16 and 8, etc.
    found = val % 20 == 0 and val % 19 == 0 and val % 18 == 0 and \
        val % 17 == 0 and val % 16 == 0 and val % 15 == 0 and \
        val % 14 == 0 and val % 13 == 0 and val % 12 == 0 and val % 11 == 0
    if not found:
      val += 380
  return val

if '__main__' == __name__:
  print main()

The fact that Python short circuits the conditional after the first failure is important. It may also be more elegant to put the values [20,11] in a range and iterate them, but the iteration introduces more operation and a part of my goal is to make these solutions as efficient as possible (while also being readable and not using too many bad practices).

2 comments:

  1. Nice optimization. And I believe that the increment of 380 works because of the primality of 19. Similarly, 17*9*19*20 would further optimize this and 13*7*17*9*19*20 would take it even further.

    I've posted a general solution for this which should work for all 'reasonable' values of N: http://pastebin.com/jcH1Nh9Z

    I've used the same factorization method (optimized here) that I used in Problem 3.

    ReplyDelete
  2. How can we prove that only multiples of 17*9*19*20 or 13*7*9*19*20 can can only be divisible by 20, if that's what you are inferring? How did you come up with those numbers?

    ReplyDelete