\n",
"\n",
"# Python Utilities for Project Euler\n",
"\n",
"After showing my utilities for [Advent of Code](http://adventofcode.org), I got some feedback:\n",
"\n",
" 1. Some of these are recipes in the `itertools` module (with different names).\n",
" 2. What about utilities for [Project Euler](https://projecteuler.net/about)?\n",
" \n",
"My answers: \n",
"\n",
" 1. I agree! I have renamed some of my utilities to be consistent with the `itertools` recipes.\n",
" 2. Here you go."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Imports\n",
"\n",
"In Project Euler I am writing short programs for my own consumption, so brevity is important, and I use `\"from\"` imports more often than I normally would:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from collections import defaultdict, deque, Counter, namedtuple, abc\n",
"from fractions import Fraction\n",
"from functools import lru_cache, wraps\n",
"from itertools import chain, cycle, islice, combinations, permutations, repeat, takewhile, zip_longest\n",
"from itertools import product as crossproduct, count as count_from\n",
"from math import ceil, floor, factorial, gcd, log, sqrt, inf\n",
"import random\n",
"import time"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Utilities\n",
"\n",
"Here are the general utility functions (and data objects) I define:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"million = 10 ** 6 # 1,000,000\n",
"Ø = frozenset() # Empty set\n",
"distinct = set # Function to return the distinct elements of a collection of hashables\n",
"identity = lambda x: x # The function that returns the argument\n",
"cat = ''.join # Concatenate strings\n",
"\n",
"def first(iterable, default=False):\n",
" \"Return the first element of an iterable, or default if it is empty.\"\n",
" return next(iter(iterable), default)\n",
"\n",
"def first_true(iterable, pred=None, default=None):\n",
" \"\"\"Returns the first true value in the iterable.\n",
" If no true value is found, returns *default*\n",
" If *pred* is not None, returns the first item\n",
" for which pred(item) is true.\"\"\"\n",
" # first_true([a,b,c], default=x) --> a or b or c or x\n",
" # first_true([a,b], fn, x) --> a if fn(a) else b if fn(b) else x\n",
" return next(filter(pred, iterable), default)\n",
"\n",
"def upto(iterable, maxval):\n",
" \"From a monotonically increasing iterable, generate all the values <= maxval.\"\n",
" # Why <= maxval rather than < maxval? In part because that's how Ruby's upto does it.\n",
" return takewhile(lambda x: x <= maxval, iterable)\n",
"\n",
"def multiply(numbers):\n",
" \"Multiply all the numbers together.\"\n",
" result = 1\n",
" for n in numbers:\n",
" result *= n\n",
" return result\n",
"\n",
"def transpose(matrix): return tuple(zip(*matrix))\n",
"\n",
"def isqrt(n):\n",
" \"Integer square root (rounds down).\"\n",
" return int(n ** 0.5)\n",
"\n",
"def ints(start, end):\n",
" \"The integers from start to end, inclusive. Equivalent to range(start, end+1)\"\n",
" return range(start, end+1)\n",
"\n",
"def groupby(iterable, key=identity):\n",
" \"Return a dict of {key(item): [items...]} grouping all items in iterable by keys.\"\n",
" groups = defaultdict(list)\n",
" for item in iterable:\n",
" groups[key(item)].append(item)\n",
" return groups\n",
"\n",
"def grouper(iterable, n, fillvalue=None):\n",
" \"\"\"Collect data into fixed-length chunks:\n",
" grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx\"\"\"\n",
" args = [iter(iterable)] * n\n",
" return zip_longest(*args, fillvalue=fillvalue)\n",
"\n",
"def overlapping(iterable, n):\n",
" \"\"\"Generate all (overlapping) n-element subsequences of iterable.\n",
" overlapping('ABCDEFG', 3) --> ABC BCD CDE DEF EFG\"\"\"\n",
" if isinstance(iterable, abc.Sequence):\n",
" yield from (iterable[i:i+n] for i in range(len(iterable) + 1 - n))\n",
" else:\n",
" result = deque(maxlen=n)\n",
" for x in iterable:\n",
" result.append(x)\n",
" if len(result) == n:\n",
" yield tuple(result)\n",
" \n",
"def pairwise(iterable):\n",
" \"s -> (s0,s1), (s1,s2), (s2, s3), ...\"\n",
" return overlapping(iterable, 2)\n",
" \n",
"def sequence(iterable, type=tuple):\n",
" \"Coerce iterable to sequence: leave it alone if it is already a sequence, else make it of type.\"\n",
" return iterable if isinstance(iterable, abc.Sequence) else type(iterable)\n",
"\n",
"def join(iterable, sep=''):\n",
" \"Join the itemsin iterable, converting each to a string first.\"\n",
" return sep.join(map(str, iterable))\n",
"\n",
"def grep(pattern, lines):\n",
" \"Print lines that match pattern.\"\n",
" for line in lines:\n",
" if re.search(pattern, line):\n",
" print(line)\n",
" \n",
"def nth(iterable, n, default=None):\n",
" \"Returns the nth item (or a default value).\"\n",
" return next(islice(iterable, n, None), default)\n",
"\n",
"def ilen(iterable):\n",
" \"Length of any iterable (consumes generators).\"\n",
" return sum(1 for _ in iterable)\n",
"\n",
"def quantify(iterable, pred=bool):\n",
" \"Count how many times the predicate is true.\"\n",
" return sum(map(pred, iterable))\n",
"\n",
"def powerset(iterable):\n",
" \"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)\"\n",
" seq = sequence(iterable)\n",
" return flatten(combinations(seq, r) for r in range(len(seq) + 1))\n",
"\n",
"def shuffled(iterable):\n",
" \"Create a new list out of iterable, and shuffle it.\"\n",
" new = list(iterable)\n",
" random.shuffle(new)\n",
" return new\n",
" \n",
"flatten = chain.from_iterable\n",
"\n",
"def int_cache(f):\n",
" \"\"\"Like lru_cache, but designed for functions that take a non-negative integer as argument;\n",
" when you ask for f(n), this caches all lower values of n first. That way, even if f(n) \n",
" recursively calls f(n-1), you will never run into recursionlimit problems.\"\"\"\n",
" cache = [] # cache[i] holds the result of f(i)\n",
" @wraps(f)\n",
" def memof(n):\n",
" for i in ints(len(cache), n):\n",
" cache.append(f(i))\n",
" return cache[n]\n",
" memof._cache = cache\n",
" return memof"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Primes\n",
"\n",
"My class `Primes` does what I need for the many Project Euler problems that involve primes:\n",
"\n",
"* Iterate through the primes up to 2 million.\n",
"* Instantly check whether an integer up to 2 million is a prime.\n",
"* With a bit more computation, check if, say, a 12-digit integer is prime.\n",
"\n",
"I precompute the primes up to 2 million, using \n",
"a [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes), and then cache\n",
"the primes as both a list (to iterate through) and a set (to check membership). If there are `n`\n",
"primes currently cached and you ask for `primes[n+1]` (either directly, or indirectly by iterating over `primes`), \n",
"then the cache will be automatically doubled in size. But if you just ask if, say, \"`123456789011 in primes`\",\n",
"then I use repeted trial division without extending the cache."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 144 ms, sys: 18.7 ms, total: 163 ms\n",
"Wall time: 190 ms\n"
]
}
],
"source": [
"class Primes:\n",
" \"\"\"Given `primes = Primes(2 * million)`, we can do the following:\n",
" * for p in primes: # iterate over infinite sequence of primes\n",
" * 37 in primes => True # primality test\n",
" * primes[0] => 2, primes[1] => 3 # nth prime\n",
" * primes[:5] => [2, 3, 5, 7, 11] # first 5 primes\n",
" * primes[5:9] => [13, 17, 19, 23] # slice of primes \n",
" * primes.upto(10) => 2, 3, 5, 7 # generate primes less than or equal to given value\"\"\"\n",
"\n",
" def __init__(self, n):\n",
" \"Create an iterable generator of primes, with initial cache of all primes <= n.\"\n",
" # sieve keeps track of odd numbers: sieve[i] is True iff (2*i + 1) has no factors (yet) \n",
" N = n // 2 # length of sieve\n",
" sieve = [True] * N\n",
" for i in range(3, isqrt(n) + 1, 2):\n",
" if sieve[i // 2]: # i is prime\n",
" # Mark start, start + i, start + 2i, ... as non-prime\n",
" start = i ** 2 // 2\n",
" sieve[start::i] = repeat(False, len(range(start, N, i)))\n",
" self._list = [2] + [2*i+1 for i in range(1, N) if sieve[i]]\n",
" self._set = set(self._list)\n",
" self.maxn = n # We have tested for all primes < self.maxn\n",
"\n",
" def __contains__(self, n):\n",
" \"Is n a prime?\"\n",
" # If n is small, look in _set; otherwise try prime factors up to sqrt(n)\n",
" if n <= self.maxn:\n",
" return n in self._set\n",
" else:\n",
" return not any(n % p == 0 for p in self.upto(n ** 0.5))\n",
"\n",
" def __getitem__(self, index):\n",
" \"Return the ith prime, or a slice: primes[0] = 2; primes[1] = 3; primes[1:4] = [3, 5, 7].\"\n",
" stop = (index.stop if isinstance(index, slice) else index)\n",
" if stop is None or stop < 0:\n",
" raise IndexError('Number of primes is infinite: https://en.wikipedia.org/wiki/Euclid%27s_theorem')\n",
" while len(self._list) <= stop:\n",
" # If asked for the ith prime and we don't have it yet, we will expand the cache.\n",
" self.__init__(2 * self.maxn)\n",
" return self._list[index]\n",
" \n",
" def upto(self, n):\n",
" \"Yield all primes <= n.\"\n",
" if self.maxn < n:\n",
" self.__init__(max(n, 2 * self.maxn))\n",
" return upto(self._list, n)\n",
" \n",
"%time primes = Primes(2 * million)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are 148,933 primes under 2 million, which is a small enough number that I'm not concerned with the memory consumed by `._list` and `._set`. If I needed to store 100 million primes, I would make different tradeoffs. For example, instead of a list and a set, I would probably just keep `sieve`, and make it be an `array('B')`. This would take less space (but for \"small\" sizes like 2 million, the current implementation is both faster and simpler).\n",
"\n",
"\n",
"# Factors\n",
"\n",
"Project Euler also has probems about prime factors, and divisors. I need to:\n",
"\n",
"* Quickly find the prime factors of any integer up to a million.\n",
"* With a bit more computation, find the prime factors of a 12-digit integer.\n",
"* Find the complete factorization of a number.\n",
"* Compute Euler's totient function.\n",
"\n",
"I will cache the factors of all the integers up to a million. To be more precise, I don't actually keep a list of all the factors of each integer; I only keep the largest prime factor. From that, I can easily compute all the other factors by repeated division. If asked for the factors of a number greater than a million, I do trial division until I get it under a million. In addition, `Factors` provides `totient(n)` for computing [Euler's totient function](https://en.wikipedia.org/wiki/Euler's_totient_function), or Φ(n), and `ndivisors(n)` for the total [number of divisors](http://primes.utm.edu/glossary/xpage/tau.html) of `n`."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"class Factors:\n",
" \"\"\"Given `factors = Factors(million)`, we can do the following:\n",
" * factors(360) => [5, 3, 3, 2, 2, 2] # prime factorization\n",
" * factors.largest[360] => 5 # largest prime factor\n",
" * distinct(factors(360)) => {2, 3, 5} # distinct prime factors\n",
" * factors.ndivisors(28) => 6 # How many positive integers divide n?\n",
" * factors.totient(36) => 12 # How many integers below n are relatively prime to n?\"\"\"\n",
" def __init__(self, maxn):\n",
" \"Initialize largest[n] to be the largest prime factor of n, for n < maxn.\"\n",
" self.largest = [1] * maxn\n",
" for p in primes.upto(maxn):\n",
" self.largest[p::p] = repeat(p, len(range(p, maxn, p)))\n",
" \n",
" def ndivisors(self, n):\n",
" \"The number of divisors of n.\"\n",
" # If n = a**x * b**y * ..., then ndivisors(n) = (x+1) * (y+1) * ...\n",
" exponents = Counter(self(n)).values()\n",
" return multiply(x + 1 for x in exponents)\n",
" \n",
" def totient(self, n):\n",
" \"Euler's Totient function, Φ(n): number of integers < n that are relatively prime to n.\"\n",
" # totient(n) = n∏(1 - 1/p) for p ∈ distinct(factors(n))\n",
" return int(n * multiply(1 - Fraction(1, p) for p in distinct(self(n))))\n",
" \n",
" def __call__(self, n):\n",
" \"Return a list of the numbers in the prime factorization of n.\"\n",
" result = []\n",
" # Need to make n small enough so that it is in the self.largest table\n",
" if n >= len(self.largest):\n",
" for p in primes:\n",
" while n % p == 0:\n",
" result.append(p)\n",
" n = n // p\n",
" if n < len(self.largest):\n",
" break\n",
" # Now n is in the self.largest table; divide by largest[n] repeatedly:\n",
" while n > 1:\n",
" p = self.largest[n]\n",
" result.append(p)\n",
" n = n // p\n",
" return result\n",
" \n",
"factors = Factors(million)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"148933"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(primes._list)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Tests\n",
"\n",
"Here are some unit tests (which also serve as usage examples):"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'pass'"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def tests():\n",
" global primes, factors\n",
" primes = Primes(2 * million)\n",
" factors = Factors(million)\n",
" \n",
" assert first('abc') == first(['a', 'b', 'c']) == 'a'\n",
" assert first(primes) == 2\n",
" assert cat(upto('abcdef', 'd')) == 'abcd'\n",
" assert multiply([1, 2, 3, 4]) == 24\n",
" assert transpose(((1, 2, 3), (4, 5, 6))) == ((1, 4), (2, 5), (3, 6))\n",
" assert isqrt(9) == 3 == isqrt(10)\n",
" assert ints(1, 100) == range(1, 101)\n",
" assert identity('anything') == 'anything'\n",
" assert groupby([-3, -2, -1, 1, 2], abs) == {1: [-1, 1], 2: [-2, 2], 3: [-3]}\n",
" assert sequence('seq') == 'seq'\n",
" assert sequence((i**2 for i in range(5))) == (0, 1, 4, 9, 16)\n",
" assert join(range(5)) == '01234'\n",
" assert join(range(5), ', ') == '0, 1, 2, 3, 4'\n",
" assert cat(['do', 'g']) == 'dog'\n",
" assert nth('abc', 1) == nth(iter('abc'), 1) == 'b'\n",
" assert quantify(['testing', 1, 2, 3, int, len], callable) == 2 # int and len are callable\n",
" assert quantify([0, False, None, '', [], (), {}, 42]) == 1 # Only 42 is truish\n",
" assert set(powerset({1, 2, 3})) == {(), (1,), (1, 2), (1, 2, 3), (1, 3), (2,), (2, 3), (3,)}\n",
" assert first_true([0, None, False, {}, 42, 43]) == 42\n",
" assert list(grouper(range(8), 3)) == [(0, 1, 2), (3, 4, 5), (6, 7, None)]\n",
" assert list(pairwise((0, 1, 2, 3, 4))) == [(0, 1), (1, 2), (2, 3), (3, 4)]\n",
" assert list(overlapping((0, 1, 2, 3, 4), 3)) == [(0, 1, 2), (1, 2, 3), (2, 3, 4)]\n",
" assert list(overlapping('abcdefg', 4)) == ['abcd', 'bcde', 'cdef', 'defg'] \n",
" @int_cache\n",
" def fib(n): return (n if n <= 1 else fib(n - 1) + fib(n - 2))\n",
" f = str(fib(10000))\n",
" assert len(f) == 2090 and f.startswith('33644') and f.endswith('66875')\n",
"\n",
" assert 37 in primes\n",
" assert primes[0] == 2 and primes[1] == 3 and primes[10] == 31\n",
" assert primes[:5] == [2, 3, 5, 7, 11]\n",
" assert primes[5:9] == [13, 17, 19, 23]\n",
" assert 42 not in primes\n",
" assert 1299721 in primes\n",
" assert million not in primes\n",
" assert (2 ** 13 - 1) in primes\n",
" assert (2 ** 31 - 1) in primes\n",
" assert list(primes.upto(33)) == [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]\n",
" assert primes.maxn == 2 * million # Make sure we didn't extend cache\n",
" assert len(primes._set) == len(primes._list) == 148933\n",
" \n",
" assert factors(720) == [5, 3, 3, 2, 2, 2, 2]\n",
" assert distinct(factors(720)) == {2, 3, 5}\n",
" assert factors(37) == [37]\n",
" assert distinct(factors(72990720)) == {2, 3, 5, 11}\n",
" assert factors.ndivisors(6) == 4\n",
" assert factors.ndivisors(28) == 6\n",
" assert factors.ndivisors(720) == 30\n",
" assert factors.largest[720] == 5\n",
" assert factors.totient(36) == 12\n",
" assert factors.totient(43) == 42\n",
" for n in (28, 36, 37, 99, 101): \n",
" assert list(primes.upto(n)) == list(upto(primes, n))\n",
" assert factors.totient(n) == quantify(gcd(n, d) == 1 for d in ints(1, n))\n",
" assert n == sum(factors.totient(d) for d in ints(1, n) if n % d == 0)\n",
"\n",
"\n",
"\n",
" return 'pass'\n",
"\n",
"tests()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Timing\n",
"\n",
"My implementation is fast enough to solve Project Euler problems, as you can see from the timing numbers below:\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 130 ms, sys: 14.7 ms, total: 144 ms\n",
"Wall time: 151 ms\n",
"CPU times: user 166 ms, sys: 10.4 ms, total: 177 ms\n",
"Wall time: 178 ms\n"
]
}
],
"source": [
"# Instantiate both primes and factors\n",
"%time primes = Primes(2 * million)\n",
"%time factors = Factors(million)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 5 µs, sys: 1e+03 ns, total: 6 µs\n",
"Wall time: 9.06 µs\n"
]
},
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Check primality for numbers in cache\n",
"%time 1000003 in primes"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 5 µs, sys: 0 ns, total: 5 µs\n",
"Wall time: 8.11 µs\n"
]
},
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%time 1000001 in primes"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 100 µs, sys: 1 µs, total: 101 µs\n",
"Wall time: 103 µs\n"
]
},
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Check primality for numbers beyond the cache\n",
"%time 2000003 in primes"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 10 µs, sys: 1e+03 ns, total: 11 µs\n",
"Wall time: 16 µs\n"
]
},
{
"data": {
"text/plain": [
"[19753, 5]"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Factor numbers in cache\n",
"%time factors(98765)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 9 µs, sys: 1 µs, total: 10 µs\n",
"Wall time: 11.9 µs\n"
]
},
{
"data": {
"text/plain": [
"[5, 5, 5, 5, 3, 3, 3, 3, 2, 2, 2, 2]"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%time factors(810000)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 8.38 ms, sys: 247 µs, total: 8.63 ms\n",
"Wall time: 11.3 ms\n"
]
},
{
"data": {
"text/plain": [
"[74843, 74843]"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Factor numbers beyond the cache\n",
"%time factors(74843 ** 2)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"31999727999744007303991249934683126567194480546211\n",
"CPU times: user 152 ms, sys: 2.27 ms, total: 155 ms\n",
"Wall time: 168 ms\n"
]
},
{
"data": {
"text/plain": [
"[1000003, 1000003, 1000003, 1999993, 1999993, 1999993, 1999993, 1999993]"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = 1000003 ** 3 * 1999993 ** 5\n",
"print(x)\n",
"%time factors(x)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 15.5 ms, sys: 810 µs, total: 16.3 ms\n",
"Wall time: 19.1 ms\n"
]
},
{
"data": {
"text/plain": [
"37550402023"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%time sum(primes.upto(million))"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 2.67 ms, sys: 152 µs, total: 2.82 ms\n",
"Wall time: 3.99 ms\n"
]
},
{
"data": {
"text/plain": [
"62260698721"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# sum of the first 100,000 primes\n",
"%time sum(primes[:100000])"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 62.9 ms, sys: 1.78 ms, total: 64.6 ms\n",
"Wall time: 70.9 ms\n"
]
},
{
"data": {
"text/plain": [
"1000003"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# First prime greater than a million\n",
"%time first(p for p in primes if p > million)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 23 ms, sys: 941 µs, total: 23.9 ms\n",
"Wall time: 26.7 ms\n"
]
},
{
"data": {
"text/plain": [
"19186879"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# sum of the integers up to 10,000 that have exactly 3 distinct factors\n",
"%time sum(n for n in range(1, 10000) if len(distinct(factors(n))) == 3)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 88.2 ms, sys: 2.42 ms, total: 90.6 ms\n",
"Wall time: 97.2 ms\n"
]
},
{
"data": {
"text/plain": [
"65796"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# sum of the integers up to 10,000 that have exactly 3 divisors\n",
"%time sum(n for n in range(1, 10000) if factors.ndivisors(n) == 3)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 326 ms, sys: 3.1 ms, total: 329 ms\n",
"Wall time: 337 ms\n"
]
},
{
"data": {
"text/plain": [
"30393486"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# The sum of the totient function of the integers up to 1000 \n",
"%time sum(map(factors.totient, range(1, 10000)))"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Project Euler Regression Testing\n",
"\n",
"My strategy for managing solutions to problems, and doing regression tests on them:\n",
"* My solution to problem 1 is the function `problem_1()`, which returns the solution when called (and so on for other problems).\n",
"* Once I have verified the answer to a problem (checking it on the Project Euler site), I store it in a dict called `solutions`.\n",
"* Running `verify()` checks that all `problem_`*n* functions return the correct solution. \n",
"\n",
"Project Euler asks participants not to publish solutions to problems, so I will comply, and instead show the solution to three fake problems:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def problem_1(N=100):\n",
" \"Sum of integers: Find the sum of all the integers from 1 to 100 inclusive.\"\n",
" return sum(ints(1, N))\n",
"\n",
"def problem_2(): \n",
" \"Two plus two: how much is 2 + 2?\"\n",
" return int('2' + '2')\n",
"\n",
"def problem_42():\n",
" \"What is life?\"\n",
" return 6 * 7\n",
"\n",
"solutions = {1: 5050, 2: 4} "
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Num Time Status Answer Problem Description Expected\n",
"=== ========== ====== ================ ===================== ========\n",
" 1 0.00 sec ok 5050 Sum of integers \n",
" 2 0.00 sec WRONG! 22 Two plus two 4\n",
" 42 0.00 sec NEW! 42 What is life? \n"
]
}
],
"source": [
"def verify(problem_numbers=range(1, 600)):\n",
" \"\"\"Main test harness function to verify problems. Pass in a collection of ints (problem numbers).\n",
" Prints a message giving execution time, and whether answer was expected.\"\"\"\n",
" print('Num Time Status Answer Problem Description Expected')\n",
" print('=== ========== ====== ================ ===================== ========')\n",
" for p in problem_numbers:\n",
" name = 'problem_{}'.format(p)\n",
" if name in globals():\n",
" fn = globals()[name]\n",
" t0 = time.time()\n",
" answer = fn()\n",
" t = time.time() - t0\n",
" desc = (fn.__doc__ or '??:').split(':')[0]\n",
" status = ('NEW!' if p not in solutions else \n",
" 'WRONG!' if answer != solutions[p] else\n",
" 'SLOW!' if t > 60 else\n",
" 'ok')\n",
" expected = (solutions[p] if status == 'WRONG!' else '')\n",
" print('{:3d} {:6.2f} sec {:>6} {:<16} {:<21} {}'\n",
" .format(p, t, status, answer, desc, expected))\n",
"\n",
"verify()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.1"
}
},
"nbformat": 4,
"nbformat_minor": 0
}