-
Notifications
You must be signed in to change notification settings - Fork 2
Python
Python happens to be my favorite language. This language, introduced in 1991, is a simple, dynamic, strongly typed, multiparadigm language language known for its small syntax and large library. Although Python's philosophy is that there should only be one way to do something, the multiparadigm nature of the language allows us to write Euler1 in lots of different ways.
Python does straight Procedural - the Missionary Position of paradigms:
#!/usr/bin/python
# Euler1 in Python
def euler1(x):
result = 0
for i in range(x):
if i%3==0 or i%5==0:
result += i
return result
print ("Euler1 =", euler1(1000))
This can be made a little more elegant using Python's built-in support for iterators - generators:
#!/usr/bin/python3
# Euler1 in Python
def euler(n):
i = 0
while i < n:
if i%3==0 or i%5==0:
yield i
i += 1
def euler1(n):
return sum(euler(n))
print ("Euler1 =", euler1(1000))
Python gives you OOP. Here's a somewhat contrived version in which I create a class with a constructor, a member variable, and a method. It's contrived because OOP is way overkill for so simple a problem:
#!/usr/bin/python3
# Euler1 in Python
class Euler1:
def __init__(self, size):
self.size = size
def solve(self):
self.result = 0
for i in range(self.size):
if i%3==0 or i%5==0:
self.result += i
euler1 = Euler1(1000)
euler1.solve()
print ("Euler1 =", euler1.result)
Python fully supports the Functional paradigm. Here's an example where the problem is decomposed into the canonical Lambda, Map, Filter, and Reduce. Obviously myMap() here does nothing - it merely returns what's passed in; it's just included for illustration. Notice one of the hallmarks of the Functional paradigm - no mutable state. There is no variable manipulation here - only declarations:
#!/usr/bin/python3
# Euler1 in Python
from functools import reduce
myMap = lambda size: map(lambda x: x, range(size))
myFilter = lambda ints: filter(lambda x: x%3==0 or x%5==0, ints)
myReduce = lambda filtered: reduce(lambda x,y: x+y, filtered)
print ("Euler1 =", myReduce(myFilter(myMap(1000))))
Here is another Functional construct in Python - List Comprehension - a loop, a condition, and an accumulator, all in one line - very nice! This is probably the most idiomatic Python (Pythonic) example here (even though Guido hates lambdas):
#!/usr/bin/python3
# Euler1 in Python
euler1 = lambda x: sum(i for i in range(x) if i%3==0 or i%5==0)
print ("Euler1 =", euler1(1000))
Here's a version where we generate three ranges - multiples of 3, 5, and 15, and we subtract the multiples of 15 since they're going to be the duplicate values found in the other two sequences:
#!/usr/bin/python3
# Euler1 in Python
def euler1(size):
return sum(range(3, size, 3)) \
+ sum(range(5, size, 5)) \
- sum(range(15, size, 15))
print ("Euler1 =", euler1(1000))
Here's a different version - I simply sum up three collections of ints up to 1000, one step 3, one step 15 starting at 5, and one step 15 starting at 10. This covers all the ints divisible by 3 or 5 with no overlaps. Cool!
#!/usr/bin/python3
# Euler1 in Python
def euler1(size):
return sum(range(3, size, 3)) \
+ sum(range(5, size, 15)) \
+ sum(range(10, size, 15))
print ("Euler1 =", euler1(1000))
Python's extremely flexible nature even supports the Prototype paradigm. Classes are actually containers that are modifiable at runtime. Check this out - our class is nothing more than a dictionary into which we can shove whatever we want:
#!/usr/bin/python3
# Euler1 in Python
class Euler1:
pass
euler1 = Euler1()
euler1.range = 1000
euler1.solver = lambda x: sum(i for i in range(x) if i%3==0 or i%5==0)
euler1.result = euler1.solver(euler1.range)
print ("Euler1 =", euler1.result)
To illustrate this, we can rewrite the last example using Python's built-in dictionary object. One limitation - Python's dictionary requires its objects to be fully resolved before being added, so I have to create temporary variables for solver and result before they get added:
#!/usr/bin/python3
# Euler1 in Python
euler1 = dict()
euler1[range] = 1000
solver = lambda x: sum(i for i in range(x) if i%3==0 or i%5==0)
euler1[solver] = solver
result = euler1[solver] (euler1[range])
euler1[result] = result
print ("Euler1 =", euler1[result])
Python doesn't do tail recursion well, though. Apparently Guido doesn't believe in it. The following, while logically correct, only narrowly misses blowing the stack - Python's stack is only 1000 deep by default:
#!/usr/bin/python3
# Euler1 in Python
import sys
sys.setrecursionlimit(1500) # Set the maximum recursion depth to 1500
def euler1(n, acc=0):
if n==1: return acc
if n%3==0 or n%5==0:
return euler1(n-1, acc+n)
else:
return euler1(n-1, acc)
print ("Euler1 =", euler1(999))
Let's try a variant based on QuickSort! Here we recursively break the list up in half and then reassemble it. Instead of sorting each half, though, we'll filter the pivot value, converting it to 0 if it's not divisible. I was actually able to run this out to 60M places without blowing the stack:
#!/usr/bin/python3
# Euler1 in Python
def euler1(L):
if len(L) == 0: return 0
# calculate the index at which the list should be divided into two sublists
pivot = round(len(L)/2)
# call the function recursively with a sublist of the left half of the original list
pre = euler1(L[:pivot])
# call the function recursively with a sublist of the right half of the original list
post = euler1(L[pivot+1:])
# check if the number at the pivot index is a multiple of 3 or 5
# and save the result in val variable
val = ((L[pivot]%3==0 or L[pivot]%5==0) and L[pivot] or 0)
# return the sum of the left sublist, val and the right sublist.
return pre + val + post
print ("Euler1 =", euler1(range(1,1000)))
One more example - I just found this elegant algorithm based on an observation by little Carl Friedrich Gauss. It operates in O(1) constant time. I tried it with 1 googol (1.0 × 10^100), and not only did Python actually handle this without choking, it took no measurable time to calculate! Don't sweat it if this seems inscrutable; click the blog link above for an explanation. Here is my version:
#!/usr/bin/python3
# Euler1 in Python
from math import floor;
def mySum(n, size):
return n * ((floor(size/n)**2 + floor(size/n)) / 2)
def euler1(size):
return floor(mySum(3,size) + mySum(5,size) - mySum(15,size))
print ("Euler1 =", euler1(999))
Now, to execute our Python script, simply call it:
$ ./euler1.py
Euler1 = 233168
Return home