Moved 1 to 5 to Python and learned a ton.

main
Felix Martin 2019-07-13 23:58:38 -04:00
parent 414cf4b074
commit b246d56acd
13 changed files with 402 additions and 2 deletions

4
.gitignore vendored
View File

@ -1,6 +1,6 @@
todo.txt
euler.sublime-workspace
euler.sublime-project
euler-python.sublime-project
euler-python.sublime-workspace
*.swp
__pycache__
.ipynb_checkpoints

11
python/e001.py Normal file
View File

@ -0,0 +1,11 @@
def get_sum_of_natural_dividable_by_3_and_5_below(m):
return sum([x for x in range(m) if x % 3 == 0 or x % 5 == 0])
def euler_001():
return get_sum_of_natural_dividable_by_3_and_5_below(1000)
assert(get_sum_of_natural_dividable_by_3_and_5_below(10) == 23)
assert(euler_001() == 233168)
print("e001.py: {}".format(euler_001()))

24
python/e002.py Normal file
View File

@ -0,0 +1,24 @@
from lib_fibonacci import fibonacci_generator_smaller
def euler_002():
fs = [f for f in fibonacci_generator_smaller(4000000) if f % 2 == 0]
return sum(fs)
assert(euler_002() == 4613732)
print("e002.py: {}".format(euler_002()))
def euler_002_simple():
""" Sometimes it is nice to keep it simple. Instead of using three
library functions just implement one straightforward function. """
r, a, b = 0, 0, 1
while b < 4000000:
if b % 2 == 0:
r += b
a, b = b, a + b
return r
assert(euler_002() == euler_002_simple())

9
python/e003.py Normal file
View File

@ -0,0 +1,9 @@
from lib_prime import prime_factors
def euler_003():
return prime_factors(600851475143)[-1]
assert(euler_003() == 6857)
print("e003.py: {}".format(euler_003()))

66
python/e004.py Normal file
View File

@ -0,0 +1,66 @@
from lib_misc import is_palindrome
def euler_004():
r = 0
for a in range(999, 99, -1):
for b in range(a, r // a, -1):
if is_palindrome(a * b) and a * b > r:
r = a * b
return r
def euler_004_original():
""" The solution that I came up with originally. """
r = 0
for a in range(999, 99, -1):
if a * a < r:
break
for b in range(a, 99, -1):
c = a * b
if c > r and is_palindrome(c):
r = c
return r
def euler_004_forum():
"""
A solution from etatsui in the project Euler forum.
It is impressive to see how much fast it is relying on
mathematical tricks:
11(9091a + 910b + 100c) = mn;
Let:
11 * 10 < m < 11 * 90
"""
for a in range(9, 0, -1):
for b in range(9, -1, -1):
for c in range(9, -1, -1):
num = 9091 * a + 910 * b + 100 * c
for m in range(90, 9, -1):
if num % m == 0:
if num / m > 999:
break
else:
result = num * 11
return result
assert(euler_004() == 906609)
print("e004.py: {}".format(euler_004()))
def time_tests():
from timeit import timeit
assert(euler_004() == 906609)
assert(euler_004_original() == 906609)
assert(euler_004_forum() == 906609)
print(timeit(lambda: euler_004(), number=100))
print(timeit(lambda: euler_004_original(), number=100))
print(timeit(lambda: euler_004_forum(), number=100))
# time_tests()
# 0.5044660240000667
# 0.7896412069985672
# 0.04794573199978913

29
python/e005.py Normal file
View File

@ -0,0 +1,29 @@
from lib_prime import prime_factors_count
from lib_misc import product
def get_number_divisible(n):
""" Returns the lowest number that is divisible
by all numbers from 1 to n. We calculate the factors
for all numbers and make sure that the minimum number
for each factor is included in the solution. """
f = {}
for i in range(2, n + 1):
for prime, count in prime_factors_count(i).items():
try:
f[prime] = max(f[prime], count)
except KeyError:
f[prime] = count
n = product([prime**count for prime, count in f.items()])
return n
assert(get_number_divisible(10) == 2520)
def euler_005():
return get_number_divisible(20)
assert(euler_005() == 232792560)
print("e005.py: {}".format(euler_005()))

5
python/e006.py Normal file
View File

@ -0,0 +1,5 @@
def euler_006():
return 0
assert(euler_006() == 233168)
print("e006.py: {}".format(euler_006()))

44
python/lib_fibonacci.py Normal file
View File

@ -0,0 +1,44 @@
def fibonacci_generator():
"""
Fibonacci generator function that starts with 1, 1, 2, 3, ...
:returns: generator that yields fibonacci numbers
"""
a = 0
b = 1
yield b
while True:
a, b = b, (a + b)
yield b
def fibonacci_generator_smaller(n):
"""
:param n: generator yields all fibonacci numbers smaller n
:returns: generator that yields fibonacci numbers
"""
g = fibonacci_generator()
x = next(g)
while x < n:
yield x
x = next(g)
def fibonacci_nth(n):
"""
:param n: index of fibonacci that should be returned
:returns: nth fibonacci number
"""
if n < 1:
return 0
g = fibonacci_generator()
i = 1
x = next(g)
while i < n:
i += 1
x = next(g)
return x

View File

@ -0,0 +1,35 @@
import unittest
try:
from .lib_fibonacci import fibonacci_generator
from .lib_fibonacci import fibonacci_generator_smaller
from .lib_fibonacci import fibonacci_nth
except ModuleNotFoundError:
from lib_fibonacci import fibonacci_generator
from lib_fibonacci import fibonacci_generator_smaller
from lib_fibonacci import fibonacci_nth
class TestFibonacciMethods(unittest.TestCase):
def test_fibonacci_generator(self):
g = fibonacci_generator()
fs = [next(g) for _ in range(10)]
self.assertEqual(fs, [1, 1, 2, 3, 5, 8, 13, 21, 34, 55])
self.assertNotEqual(fs, [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55])
def test_fibonacci_generator_smaller(self):
g = fibonacci_generator_smaller(20)
self.assertEqual(list(g), [1, 1, 2, 3, 5, 8, 13])
def test_fibonacci_nth(self):
self.assertEqual(fibonacci_nth(0), 0)
self.assertEqual(fibonacci_nth(1), 1)
self.assertEqual(fibonacci_nth(2), 1)
self.assertEqual(fibonacci_nth(3), 2)
self.assertEqual(fibonacci_nth(4), 3)
self.assertEqual(fibonacci_nth(5), 5)
self.assertEqual(fibonacci_nth(6), 8)
if __name__ == '__main__':
unittest.main()

78
python/lib_misc.py Normal file
View File

@ -0,0 +1,78 @@
def get_digits_reversed(n):
"""
Returns a list of digits for n.
"""
digits, rest = [], n
while rest != 0:
digits.append(rest % 10)
rest //= 10
return digits
def is_palindrome_string(n):
"""
Checks whether n is a palindrome number
with a string based algorithm.
:param n: number to check
:returns: boolean
"""
s = str(n)
return s == s[::-1]
def is_palindrome_integer(n):
"""
Checks whether n is a palindrome number
with an integer based algorithm.
:param n: number to check
:returns: boolean
"""
digits = get_digits_reversed(n)
digits_count = len(digits)
for i in range(digits_count // 2):
if digits[i] != digits[digits_count - i - 1]:
return False
return True
def time_is_palindrom():
""" I just want to check which option is faster. """
from timeit import timeit
print(timeit(lambda: is_palindrome_integer(82445254428), number=100000))
print(timeit(lambda: is_palindrome_string(82445254428), number=100000))
# > 0.42330633100027626
# > 0.07605635199979588
is_palindrome = is_palindrome_string
def get_item_counts(l):
"""
Counts how often each element is part of the list.
:param l: a list
:returns: a dictionary
"""
d = {}
for e in l:
try:
d[e] += 1
except KeyError:
d[e] = 1
return d
def product(l):
"""
Calculates the product of all items in the list.
:param l: a list
:returns: a number that is the product of all elements in the list
"""
from functools import reduce
import operator
return reduce(operator.mul, l, 1)

47
python/lib_misc_tests.py Normal file
View File

@ -0,0 +1,47 @@
import unittest
try:
from .lib_misc import is_palindrome_integer
from .lib_misc import is_palindrome_string
from .lib_misc import get_digits_reversed
from .lib_misc import get_item_counts
from .lib_misc import product
except ModuleNotFoundError:
from lib_misc import is_palindrome_integer
from lib_misc import is_palindrome_string
from lib_misc import get_digits_reversed
from lib_misc import get_item_counts
from lib_misc import product
class TestPrimeMethods(unittest.TestCase):
def test_is_palindrome_integer(self):
self.assertEqual(is_palindrome_integer(2), True)
self.assertEqual(is_palindrome_integer(888), True)
self.assertEqual(is_palindrome_integer(9009), True)
self.assertEqual(is_palindrome_integer(23), False)
self.assertEqual(is_palindrome_integer(9008), False)
def test_is_palindrome_string(self):
self.assertEqual(is_palindrome_string(2), True)
self.assertEqual(is_palindrome_string(888), True)
self.assertEqual(is_palindrome_string(9009), True)
self.assertEqual(is_palindrome_string(23), False)
self.assertEqual(is_palindrome_string(9008), False)
def test_get_digits_reversed(self):
self.assertEqual(get_digits_reversed(3), [3])
self.assertEqual(get_digits_reversed(1234), [4, 3, 2, 1])
self.assertEqual(get_digits_reversed(1000), [0, 0, 0, 1])
def test_get_item_counts(self):
self.assertEqual(get_item_counts([]), {})
self.assertEqual(get_item_counts([1, 1, 3]), {1: 2, 3: 1})
def test_product(self):
self.assertEqual(product([2, 4, 8]), 64)
self.assertEqual(product([]), 1)
if __name__ == '__main__':
unittest.main()

27
python/lib_prime.py Normal file
View File

@ -0,0 +1,27 @@
try:
from lib_misc import get_item_counts
except ModuleNotFoundError:
from .lib_misc import get_item_counts
def prime_factors(n):
# TODO: Look into using a prime wheel instead.
factors = []
rest = n
divisor = 2
while rest % divisor == 0:
factors.append(divisor)
rest //= divisor
divisor = 3
while divisor * divisor <= rest:
while rest % divisor == 0:
factors.append(divisor)
rest //= divisor
divisor += 2
if rest != 1:
factors.append(rest)
return factors
def prime_factors_count(n):
return get_item_counts(prime_factors(n))

25
python/lib_prime_tests.py Normal file
View File

@ -0,0 +1,25 @@
import unittest
try:
from .lib_prime import prime_factors
from .lib_prime import prime_factors_count
except ModuleNotFoundError:
from lib_prime import prime_factors
from lib_prime import prime_factors_count
class TestPrimeMethods(unittest.TestCase):
def test_prime_factors(self):
self.assertEqual(prime_factors(2), [2])
self.assertEqual(prime_factors(5), [5])
self.assertEqual(prime_factors(10), [2, 5])
self.assertEqual(prime_factors(13), [13])
self.assertEqual(prime_factors(147), [3, 7, 7])
def test_prime_factors_count(self):
self.assertEqual(prime_factors_count(2), {2: 1})
self.assertEqual(prime_factors_count(147), {3: 1, 7: 2})
if __name__ == '__main__':
unittest.main()