11from random import Random
22
3- # Convert an integer into its English language name.
3+ __googol = 10 ** 100
44
55# http://lcn2.github.io/mersenne-english-name/tenpower/tenpower.html
66__pows = (("thousand" , 3 ), ("million" , 6 ), ("billion" , 9 ),
2121 )
2222
2323# Dictionary comprehension, analogous to list comprehension.
24- __pows = { p :n for (n , p ) in __pows }
24+ __pows = {p : n for n , p in __pows }
25+
2526
26- # Return the English name of a three-digit integer.
2727def __int_to_eng (n ):
28- if n < 20 : # Numbers 0 to 19 with a simple lookup table.
28+ """Return the English name of a three-digit positive number."""
29+ assert 0 < n < 1000
30+ if n < 20 : # Numbers 0 to 19 with a simple lookup table.
2931 return ["ERROR" , "one" , "two" , "three" , "four" , "five" ,
3032 "six" , "seven" , "eight" , "nine" , "ten" , "eleven" ,
3133 "twelve" , "thirteen" , "fourteen" , "fifteen" ,
3234 "sixteen" , "seventeen" , "eighteen" , "nineteen" ][n ]
33- elif n < 100 : # Numbers 20 to 99, tens again with a lookup table.
34- tens = ["" , "" , "twenty" , "thirty" , "forty" , "fifty" ,
35+ elif n < 100 : # Numbers 20 to 99, again with a lookup table.
36+ tens = ["ERROR " , "ERROR " , "twenty" , "thirty" , "forty" , "fifty" ,
3537 "sixty" , "seventy" , "eighty" , "ninety" ][n // 10 ]
36- return tens if n % 10 == 0 else f"{ tens } -{ __int_to_eng (n % 10 )} "
37- else : # Numbers 100 to 999
38- if n % 100 == 0 :
39- return f"{ __int_to_eng (n // 100 )} hundred"
40- else :
41- return f"{ __int_to_eng (n // 100 )} hundred and { __int_to_eng (n % 100 )} "
42-
43- __googol = 10 ** 100
38+ return tens if n % 10 == 0 else f"{ tens } -{ __int_to_eng (n % 10 )} "
39+ else : # Numbers 100 to 999
40+ name = f"{ __int_to_eng (n // 100 )} hundred"
41+ if n % 100 != 0 :
42+ name += f"and { __int_to_eng (n % 100 )} "
43+ return name
44+
4445
45- # Construct the English name of any integer.
4646def int_to_english (n ):
47- if n < 0 : # Negative numbers
47+ """Construct the English name of the given integer n."""
48+ if n < 0 : # Negative numbers
4849 return "minus " + int_to_english (- n )
49- if n == 0 : # Zero as a special case
50+ if n == 0 : # Zero as a special case
5051 return "zero"
51- if n >= __googol : # huge numbers
52+ if n >= __googol : # Huge numbers
5253 first = int_to_english (n // __googol )
5354 rest = int_to_english (n % __googol )
54- return f"{ first } googol" if rest == "zero" else f"{ first } googol and { rest } "
55+ name = f"{ first } googol"
56+ if rest != "zero" :
57+ name += "and {rest}"
58+ return name
5559 # Otherwise, break the number into blocks of three and convert.
5660 result , p = [], 0
5761 while n > 0 :
58- trip , n = n % 1000 , n // 1000
62+ trip , n = n % 1000 , n // 1000
5963 if trip > 0 :
6064 if p == 0 :
6165 result .append (__int_to_eng (trip ))
@@ -65,15 +69,16 @@ def int_to_english(n):
6569 return " " .join (reversed (result ))
6670
6771# Find an autogram, a text that describes in English how many times
68- # each letter appears inside it. As with many of the 109 lab problems,
72+ # each letter appears inside it. As with many of the 109 lab problems,
6973# this example was inspired by the works of the late great Martin
7074# Gardner and his collected columns on recreational mathematics in
7175# the Scientific American magazine.
72-
73- def autogram_finder (text , rng , verbose = True , perturb = 20 ):
76+
77+
78+ def autogram_finder (text , rng , verbose = True , perturb = 20 ):
7479 letters , rounds = "abcdefghijklmnopqrstuvwxyz" , 0
7580 count , best = [rng .randint (2 , 50 ) for c in letters ], 0
76-
81+
7782 while True :
7883 rounds += 1
7984 # Fill in text placeholders with names for each number.
@@ -85,14 +90,15 @@ def autogram_finder(text, rng, verbose=True, perturb = 20):
8590 lfill = filled .lower ()
8691 actual = [lfill .count (c ) for c in letters ]
8792 # Find the letters whose counts are correct.
88- same = "" .join ([c for (i , c ) in enumerate (letters ) if count [i ] == actual [i ]])
93+ same = "" .join ([c for (i , c ) in enumerate (letters )
94+ if count [i ] == actual [i ]])
8995 # Replace the previous best solution, if this one is better.
9096 if len (same ) > best :
9197 best = len (same )
9298 if verbose :
9399 print (f"\n { filled } " )
94- print (f"Actual: { ', ' .join ([f'{ l } :{ c } ' for ( l , c ) in zip (letters , actual )])} " )
95- print (f"Matched { len (same )} letters '{ same } ' in { rounds } rounds ." )
100+ print (f"Actual: { ', ' .join ([f'{ l } :{ c } ' for l , c in zip (letters , actual )])} " )
101+ print (f"Matched { len (same )} letters '{ same } '." )
96102 if best == 26 :
97103 return filled
98104 count = actual
@@ -108,15 +114,16 @@ def autogram_finder(text, rng, verbose=True, perturb = 20):
108114# $c is the count for the character c, and $$ is the total
109115# number of letters in the text. (That one makes the search
110116# far more difficult.)
111-
112- text = """This zesty, bookish and joyful quip was composed by
117+
118+
119+ __text = """This zesty, bookish and joyful quip was composed by
113120Ilkka Kokkarinen to serve as an example for this course, and it
114121contains $a a's, $b b's, $c c's, $d d's, $e e's, $f f's, $g g's,
115122$h h's, $i i's, $j j's, $k k's, $l l's, $m m's, $n n's, $o o's,
116123$p p's, $q q's, $r r's, $s s's, $t t's, $u u's, $v v's, $w w's,
117124$x x's, $y y's and finally, to top it all off, $z z's."""
118- text = text .replace ("\n " , " " )
119- text = text .replace ("\t " , " " )
125+ __text = __text .replace ("\n " , " " )
126+ __text = __text .replace ("\t " , " " )
120127
121128# This zesty, bookish and joyful quip was composed by Ilkka
122129# Kokkarinen to serve as an example for this course, and it
@@ -133,12 +140,12 @@ def autogram_finder(text, rng, verbose=True, perturb = 20):
133140 for x in [42 , 3 ** 7 , 6 ** 20 , - (2 ** 100 ), 9 ** 200 , 10 ** 500 ]:
134141 print (f"{ x } written in English is { int_to_english (x )} ." )
135142 print ("Here are integers 0-100 sorted in alphabetical order:" )
136- print (sorted (range (0 , 101 ), key = int_to_english ))
143+ print (sorted (range (0 , 101 ), key = int_to_english ))
137144 print ("Here are integers 0-100 sorted in order of name lengths:" )
138- print (sorted (range (0 , 101 ), key = lambda x : (len (int_to_english (x )), x )))
145+ print (sorted (range (0 , 101 ), key = lambda x : (len (int_to_english (x )), x )))
139146 print ("The numbers that do not contain the letter 'o':" )
140147 print ([x for x in range (1000 ) if 'o' not in int_to_english (x )])
141-
148+
142149 # When using a randomized algorithm, it is good to used a fixed
143150 # seed to make the results repeatable.
144- autogram_finder (text , Random (9999 ), perturb = 0 )
151+ autogram_finder (__text , Random (12345 ), True , 20 )
0 commit comments