Skip to content

Instantly share code, notes, and snippets.

@aaronmauro
Created November 28, 2020 02:30
Show Gist options
  • Save aaronmauro/b0560d6c7b44f53a75d2903cbe987deb to your computer and use it in GitHub Desktop.
Save aaronmauro/b0560d6c7b44f53a75d2903cbe987deb to your computer and use it in GitHub Desktop.
A reimplementation of Raymond Hettinger's alphametic puzzle solver.
#!/usr/bin/python
"""
This is a reimplementation of Raymond Hettinger's alphametics solver from 2009
on the website Activestate: https://code.activestate.com/recipes/576615-alphametics-solver/.
The program works to solve the alphametic puzzle called, Send + More = Money.
H.E. Dudeney published the problem in the July 1924 edition of The Strand
Magazine. The original problem appears within his articled title "Perplexities."
The text for the problem appears under the heading, "708.-VERBAL ARITHMETIC"
and reads as follows: ADDITION is an imposition, SEND + MORE = MONEY.
SUBTRACTION is as bad; EIGHT - FIVE = FOUR. MULTIPLICATION is vexation TWO *
TWO = THREE. DIVISION drives me mad. TWO) SEVEN (TWO / BOB = JOE / OVV /
VESN = VESN. In each puzzle every letter respresents a different digit, but
a letter does not necessarily stand for the same digit in the case of every
puzzle." These problems are typically completed by a focused adult in about
30 minutes. Raymond Hettinger's algorithm, then written in Python 2.6,
executed in ~14.4 seconds. Now, running Python 3.7.6 this alogrithm takes
~11.6 seconds to complete.
"""
__author__ = "Raymond Hettinger"
__editor__ = "Aaron Mauro"
__role__ = "researcher"
__institution__ = "Brock University"
__email__ = "amauro@brocku.ca"
__status__ = "prototype/experiment"
__version__ = "0.2"
from itertools import permutations
from re import findall
import time
def solve(s):
'''
Find solutions to alphametic equations such as:
solve('SEND + MORE == MONEY')
9567 + 1085 == 10652
'''
print("Problem:",s)
words = findall('[A-Za-z]+', s)
chars = set(''.join(words)) # characters to be substituted
assert len(chars) <= 10 # there are only ten possible digits
firsts = set(w[0] for w in words) # first letters of each of word
chars = ''.join(firsts) + ''.join(chars - firsts)
n = len(firsts) # chars[:n] cannot be assigned zero
for perm in permutations('0123456789', len(chars)):
if '0' not in perm[:n]:
trans = str.maketrans(chars, ''.join(perm))
equation = s.translate(trans)
try:
if eval(equation):
print(f"Solution: {equation}")
except ArithmeticError:
pass
start = time.time()
for alphametic in [
'SEND + MORE == MONEY',
'VIOLIN * 2 + VIOLA == TRIO + SONATA',
'SEND + A + TAD + MORE == MONEY',
'ZEROES + ONES == BINARY',
'DCLIZ + DLXVI == MCCXXV',
'COUPLE + COUPLE == QUARTET',
'FISH + N + CHIPS == SUPPER',
'SATURN + URANUS + NEPTUNE + PLUTO == PLANETS',
'EARTH + AIR + FIRE + WATER == NATURE',
('AN + ACCELERATING + INFERENTIAL + ENGINEERING + TALE + ' +
'ELITE + GRANT + FEE + ET + CETERA == ARTIFICIAL + INTELLIGENCE'),
'TWO * TWO == SQUARE',
'HIP * HIP == HURRAY',
'PI * R ** 2 == AREA',
'NORTH / SOUTH == EAST / WEST',
'NAUGHT ** 2 == ZERO ** 3',
'I + THINK + IT + BE + THINE == INDEED',
'DO + YOU + FEEL == LUCKY',
'NOW + WE + KNOW + THE == TRUTH',
'SORRY + TO + BE + A + PARTY == POOPER',
'SORRY + TO + BUST + YOUR == BUBBLE',
'STEEL + BELTED == RADIALS',
'ABRA + CADABRA + ABRA + CADABRA == HOUDINI',
'I + GUESS + THE + TRUTH == HURTS',
'LETS + CUT + TO + THE == CHASE',
'THATS + THE + THEORY == ANYWAY',
'1/(2*X-Y) == 1',
]:
solve(alphametic)
print(f"{time.time() - start}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment