Here is the Euler problem referenced, it says:
The n\$^{th}\$ term of the sequence of triangle numbers is given by, t\$_n\$ = ½n(n+1); so the first ten triangle numbers are:
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, ...
By converting each letter in a word to a number corresponding to its alphabetical position and adding these values we form a word value. For example, the word value for SKY is 19 + 11 + 25 = 55 = t\$_{10}\$. If the word value is a triangle number then we shall call the word a triangle word.
Using words.txt (right click and 'Save Link/Target As...'), a 16K text file containing nearly two-thousand common English words, how many are triangle words?
My solution is as follows:
import re
import string
#generate a key to map the letters in our strings to
letters = string.ascii_uppercase
letters_map = {letter: idx for idx, letter in enumerate(string.ascii_uppercase, start=1)}
#open the file and parse on quoted values
with open('words.txt') as f:
words = sorted(re.findall(r'"(.*?)"', f.read()))
#max word score can only be Z*len(n), recall 'Z' : 26 in letters_map
max_word_score = len(max(words, key=len))*26
#generate a list of triangle_numbers to search for using max_score as upper bound
n = 1
triangle_numbers = []
while 1/2.0*n*(n+1) <= max_word_score:
triangle_numbers.append(1/2.0*n*(n+1))
n += 1
#finally use a nested for loop to return number of triangle numbers in our now summed words
count = 0
for word in words:
if sum(letters_map[char] for char in word) in triangle_numbers:
count += 1
%%timeit
100 loops, best of 3: 3.67 ms per loop
I'm curious if there are any ways I can rewrite this with similar or better performance so that it is more pythonic. I'm curious about a few things more specifically:
1) Is it possible to rewrite this better using itertools.ifilter
and itertools.imap
using letters_map
and the filter being the list of triangle_numbers
, to greater affect than simply using a nested for
loop and an if
statement to filter?
2) Is my while
construction extremely not pythonic? It felt awkward writing it the way it was and I wasn't sure what a better way of operating under that framework would be (needing to count up to some arbitrary ceiling, in this case max_word_score
.)
3) Should I be getting in the habit of wrapping these items in mini-functions in order to make it more readable/replicable? Would that end up slowing runtime down? What sort of effect would that have in general.
4) Any other pythonizations I can use to better effect?