Project Euler: Problem 17

https://projecteuler.net/problem=17

If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total.

If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, how many letters would be used?

NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) contains 23 letters and 115 (one hundred and fifteen) contains 20 letters. The use of "and" when writing out numbers is in compliance with British usage.

First write a number_to_words(n) function that takes an integer n between 1 and 1000 inclusive and returns a list of words for the number as described above


In [1]:
import numpy as np

In [2]:
numbers = {0: "", 1:"one", 2:"two", 3:"three", 4:"four", 5:"five", 6:"six", 7:"seven", 8:"eight", 9:"nine", 10:"ten", 11:"eleven",
          12:"twelve", 13:"thirteen", 14:"fourteen", 15:"fifteen", 16:"sixteen", 17:"seventeen", 18:"eighteen", 19:"nineteen",
          20:"twenty", 30:"thirty", 40:"forty", 50:"fifty", 60:"sixty", 70:"seventy", 80:"eighty", 90:"ninety", 100:"hundred", 
           1000:"onethousand"}


#I did this part with two functions to make my method more straightforward
# The first function takes a number and breaks it into 1000s, 100s, 10s and 1s. 
# It also adjusts for perculiar numbers such as 'eleven and 'thirteen'. The second function
# takes the number list from the first function and turns it into words.

def number_in_parts(n):
    #this breaks up a number into parts
    #an entry of 526 would return a list of [500, 20, 6]
    broken_number = []
    length = len(str(n))
    for i in range(length):
        f = int(str(n)[i]) * 10**(length-(i+1))
        broken_number.append(f)
    for i in range(len(broken_number)):
        if broken_number[i] == 10:
            broken_number[i]= broken_number[i] + broken_number[i+1]
            broken_number[i+1] = 0
    return(broken_number) 


def number_to_words(entry):
    """Given a number n between 1-1000 inclusive return a list of words for the number."""
    letters = []
    for number in entry:
    # if 100-900, adding "1-9" and "hundred"
        if number >= 100 and number < 1000:
            n = number/100
            letters.append(numbers[n])
            letters.append(numbers[100])
        else:
            letters.append(numbers[number])
    # adding an "and" for anything above 99 with nonzero terms in the 10s or 1s place
    if (letters[-1] != "" or letters[-2] != "") and len(entry) > 2: 
        letters.append("and")
    final = "".join(letters)
    return(final)

Now write a set of assert tests for your number_to_words function that verifies that it is working as expected.


In [3]:
assert number_in_parts(123) == [100, 20, 3]
assert number_in_parts(112) == [100, 12, 0]
assert number_in_parts(1000) == [1000, 0, 0, 0]
assert number_to_words(number_in_parts(100)) == "onehundred"
assert number_to_words(number_in_parts(112)) == "onehundredtwelveand"
assert number_to_words(number_in_parts(589)) == "fivehundredeightynineand"
assert number_to_words(number_in_parts(102)) == "onehundredtwoand"
assert number_to_words(number_in_parts(140)) == "onehundredfortyand"

In [4]:
assert True # use this for grading the number_to_words tests.

Now define a count_letters(n) that returns the number of letters used to write out the words for all of the the numbers 1 to n inclusive.


In [5]:
def count_letters(n):
    """Count the number of letters used to write out the words for 1-n inclusive."""
    x = 0
    for i in range(n):
        x += len(number_to_words(number_in_parts(i+1)))
    return x

Now write a set of assert tests for your count_letters function that verifies that it is working as expected.


In [6]:
assert count_letters(1) == 3
assert count_letters(2) == 6
assert count_letters(3) == 11
assert count_letters(500) - count_letters(499) == len("fivehundred")
assert count_letters(459) - count_letters(458) == len(number_to_words(number_in_parts(459))) == len("fourhundredandfiftynine")
assert count_letters(1000) - count_letters(999) == len("onethousand")
assert count_letters(412) - count_letters(411) == len("fourhundredandtwelve")
assert count_letters(330) - count_letters(328) == len("threehundredandthirtythreehundredandtwentynine")

In [7]:
assert True # use this for grading the count_letters tests.

Finally used your count_letters function to solve the original question.


In [8]:
print(count_letters(1000))


21124

In [ ]:
assert True # use this for gradig the answer to the original question.