I learned about New Merology while reading Professor Stewart’s Cabinet of Mathematical Curiosities. The original idea was created by Lee Sallows and I found the original paper here. The updated version is here. I think that either Ian Stewart or Lee Sallows have explained this better than I can, here’s just a short introduction.

So called gematria deals with assigning distinct values to letters thus giving each word a numerical value. Let’s call a number name perfect if the sum of the letter values equals the number. For example (in English):

O + N + E = 1

T + W + O = 2

This is impossible to attain with the typical letter value assignment A=1, B=2, C=3 etc. To overcome this problem Lee Sallows invented New Merology which says that the letter value can be zero or negative, or not necessarily a whole number- although in this case we deal with integers only. Letter values must be still distinct. Lee Sallows starts his exploration of the possible values with O=-2, N=2 and E=1. Then O+N+E equals 1 and if W=-3 and T=7 it necessarily follows that T+W+O=2, making both ONE and TWO perfect numbers. In his paper he finally arrives at the most perfect possible solutions for English and French. This is reportedly verified by a Pascal program.

I have originally written a Python program to generate the Perfect Number Names solutions for Polish and English. It tried to arrive at the dependencies between letters e.g. (in Polish)

D + Z + I + E + S + I + Ę + Ć = 10

D + Z + I + E + W + I + Ę + Ć = 9

(after subtracting both sides)

S – W = 1

so S=W+1, which can lead to further discovered dependencies. But the remaining part of the program was still brute force and made it quite slow. And I was thinking that there must be some better way to solve this since it was just a list of simultaneous Diophantine equations with additional constraints (distinct letter values). I abandoned this puzzle temporarily but kept it in the back on my mind.

Z3

And now Z3Prover enters the stage. It can be used for many things and may be an overkill in this particular case. Still it is very simple to use –  it allows me to provide the equations and constraints and practically instantly receive the information if these are satisfiable or not. Moreover the positive result is accompanied with a model, in this case the mapping of letters to their values!

So here are the inputs:

  1. Equations e.g. Z+E+R+O=0, O+N+E=1 etc. up to MAX_NUM
  2. Distinctness constraints: A!=B, A!=C… B!=C etc. for all A…Z
  3. Minimum: abs(A) <= MIN_VAL for all letters A…Z

The algorithm

Initially I start with only inputs 1 & 2: the list of equations including numerals up to MAX_NUM (usually less than 20) and the distinctness constraints. If these are not satisfiable, I reduce MAX_NUM until I find the list of the numerals that is satisfiable under the constraints. (Bisection would be an improvement here).

Once I establish the largest satisfiable list of perfect number names i.e. know the MAX_NUM, another search is made, this time using all inputs 1 & 2 & 3 and going upward if current MIN_VAL condition makes the constraints unsatisfiable. (MIN_VAL  is the smallest maximum absolute value of all used letters). This search must end since a solution existed in the previous algorithm step. The obvious improvement is again to use the bisection instead of the linear search and is left as an exercise for the reader.

This results in the longest possible list of perfect number names and the smallest maximum absolute value of all letters.

Language competition

Which language among Dutch, English, French, German, Italian, Polish, Portuguese and Spanish has the longest list of perfect number names? Make your guess. The output of my program is provided below with the answer.

Note: I’m treating letters with diacritics/accents as distinct from the base letters which makes sense for Polish. In Lee Sallows’ solution for French he did not distinguish between E in DEUX and É in ZÉRO, I do, even though all the character mapping makes the program harder to follow.

****************************************
DE numerals from 0 up to 16 absolute value within 12
a=11 b=-4 c=-9 d=8 e=10 f=-1 h=5 i=-12 l=2 n=3 ö=7 r=-3 s=0 t=1 u=-7 ü=4 v=9 w=12 z=-8

   n   u   l   l   =   0
  +3  -7  +2  +2   =   0

   e   i   n   s   =   1
 +10 -12  +3  +0   =   1

   z   w   e   i   =   2
  -8 +12 +10 -12   =   2

   d   r   e   i   =   3
  +8  -3 +10 -12   =   3

   v   i   e   r   =   4
  +9 -12 +10  -3   =   4

   f   ü   n   f   =   5
  -1  +4  +3  -1   =   5

   s   e   c   h   s   =   6
  +0 +10  -9  +5  +0   =   6

   s   i   e   b   e   n   =   7
  +0 -12 +10  -4 +10  +3   =   7

   a   c   h   t   =   8
 +11  -9  +5  +1   =   8

   n   e   u   n   =   9
  +3 +10  -7  +3   =   9

   z   e   h   n   =  10
  -8 +10  +5  +3   =  10

   e   l   f   =  11
 +10  +2  -1   =  11

   z   w   ö   l   f   =  12
  -8 +12  +7  +2  -1   =  12

   d   r   e   i   z   e   h   n   =  13
  +8  -3 +10 -12  -8 +10  +5  +3   =  13

   v   i   e   r   z   e   h   n   =  14
  +9 -12 +10  -3  -8 +10  +5  +3   =  14

   f   ü   n   f   z   e   h   n   =  15
  -1  +4  +3  -1  -8 +10  +5  +3   =  15

   s   e   c   h   z   e   h   n   =  16
  +0 +10  -9  +5  -8 +10  +5  +3   =  16

****************************************
NL numerals from 0 up to 13 absolute value within 12
a=4 c=-11 d=-10 e=3 é=1 f=2 g=5 h=7 i=0 j=12 l=6 n=-1 r=10 s=-8 t=8 u=-5 v=-9 w=-12 z=11

   n   u   l   =   0
  -1  -5  +6   =   0

   é   é   n   =   1
  +1  +1  -1   =   1

   t   w   e   e   =   2
  +8 -12  +3  +3   =   2

   d   i   r   e   =   3
 -10  +0 +10  +3   =   3

   v   i   e   r   =   4
  -9  +0  +3 +10   =   4

   v   i   j   f   =   5
  -9  +0 +12  +2   =   5

   z   e   s   =   6
 +11  +3  -8   =   6

   z   e   v   e   n   =   7
 +11  +3  -9  +3  -1   =   7

   a   c   h   t   =   8
  +4 -11  +7  +8   =   8

   n   e   g   e   n   =   9
  -1  +3  +5  +3  -1   =   9

   t   i   e   n   =  10
  +8  +0  +3  -1   =  10

   e   l   f   =  11
  +3  +6  +2   =  11

   t   w   a   a   l   f   =  12
  +8 -12  +4  +4  +6  +2   =  12

   d   e   r   t   i   e   n   =  13
 -10  +3 +10  +8  +0  +3  -1   =  13

****************************************
PT numerals from 0 up to 11 absolute value within 20
a=-11 c=9 d=-4 e=-6 ê=3 i=-10 m=-3 n=-8 o=5 q=17 r=-19 s=11 t=8 u=4 v=18 z=20

   z   e   r   o   =   0
 +20  -6 -19  +5   =   0

   u   m   =   1
  +4  -3   =   1

   d   o   i   s   =   2
  -4  +5 -10 +11   =   2

   t   r   ê   s   =   3
  +8 -19  +3 +11   =   3

   q   u   a   t   r   o   =   4
 +17  +4 -11  +8 -19  +5   =   4

   c   i   n   c   o   =   5
  +9 -10  -8  +9  +5   =   5

   s   e   i   s   =   6
 +11  -6 -10 +11   =   6

   s   e   t   e   =   7
 +11  -6  +8  -6   =   7

   o   i   t   o   =   8
  +5 -10  +8  +5   =   8

   n   o   v   e   =   9
  -8  +5 +18  -6   =   9

   d   e   z   =  10
  -4  -6 +20   =  10

   o   n   z   e   =  11
  +5  -8 +20  -6   =  11

****************************************
FR numerals from 0 up to 13 absolute value within 15
a=15 c=-9 d=14 e=6 é=5 f=2 h=13 i=8 n=7 o=-5 p=-2 q=-1 r=-3 s=10 t=-7 u=-6 x=-12 z=3

   z   é   r   o   =   0
  +3  +5  -3  -5   =   0

   u   n   =   1
  -6  +7   =   1

   d   e   u   x   =   2
 +14  +6  -6 -12   =   2

   t   r   o   i   s   =   3
  -7  -3  -5  +8 +10   =   3

   q   u   a   t   r   e   =   4
  -1  -6 +15  -7  -3  +6   =   4

   c   i   n   q   =   5
  -9  +8  +7  -1   =   5

   s   i   x   =   6
 +10  +8 -12   =   6

   s   e   p   t   =   7
 +10  +6  -2  -7   =   7

   h   u   i   t   =   8
 +13  -6  +8  -7   =   8

   n   e   u   f   =   9
  +7  +6  -6  +2   =   9

   d   i   x   =  10
 +14  +8 -12   =  10

   o   n   z   e   =  11
  -5  +7  +3  +6   =  11

   d   o   u   z   e   =  12
 +14  -5  -6  +3  +6   =  12

   t   r   e   i   z   e   =  13
  -7  -3  +6  +8  +3  +6   =  13

****************************************
PL numerals from 0 up to 13 absolute value within 10
a=7 c=-4 ć=1 d=-8 e=5 ę=-10 i=8 j=-1 m=-7 n=0 o=-2 p=6 r=-5 s=4 ś=-6 t=9 w=3 y=-3 z=2

   z   e   r   o   =   0
  +2  +5  -5  -2   =   0

   j   e   d   e   n   =   1
  -1  +5  -8  +5  +0   =   1

   d   w   a   =   2
  -8  +3  +7   =   2

   t   r   z   y   =   3
  +9  -5  +2  -3   =   3

   c   z   t   e   r   y   =   4
  -4  +2  +9  +5  -5  -3   =   4

   p   i   ę   ć   =   5
  +6  +8 -10  +1   =   5

   s   z   e   ś   ć   =   6
  +4  +2  +5  -6  +1   =   6

   s   i   e   d   e   m   =   7
  +4  +8  +5  -8  +5  -7   =   7

   o   s   i   e   m   =   8
  -2  +4  +8  +5  -7   =   8

   d   z   i   e   w   i   ę   ć   =   9
  -8  +2  +8  +5  +3  +8 -10  +1   =   9

   d   z   i   e   s   i   ę   ć   =  10
  -8  +2  +8  +5  +4  +8 -10  +1   =  10

   j   e   d   e   n   a   ś   c   i   e   =  11
  -1  +5  -8  +5  +0  +7  -6  -4  +8  +5   =  11

   d   w   a   n   a   ś   c   i   e   =  12
  -8  +3  +7  +0  +7  -6  -4  +8  +5   =  12

   t   r   z   y   n   a   ś   c   i   e   =  13
  +9  -5  +2  -3  +0  +7  -6  -4  +8  +5   =  13

****************************************
ES numerals from 0 up to 11 absolute value within 20
a=-9 c=20 d=4 e=7 h=10 i=-19 n=-5 o=-11 r=-16 s=9 t=3 u=17 v=-17 z=18

   c   e   r   o   =   0
 +20  +7 -16 -11   =   0

   u   n   o   =   1
 +17  -5 -11   =   1

   d   o   s   =   2
  +4 -11  +9   =   2

   t   r   e   s   =   3
  +3 -16  +7  +9   =   3

   c   u   a   t   r   o   =   4
 +20 +17  -9  +3 -16 -11   =   4

   c   i   n   c   o   =   5
 +20 -19  -5 +20 -11   =   5

   s   e   i   s   =   6
  +9  +7 -19  +9   =   6

   s   i   e   t   e   =   7
  +9 -19  +7  +3  +7   =   7

   o   c   h   o   =   8
 -11 +20 +10 -11   =   8

   n   u   e   v   e   =   9
  -5 +17  +7 -17  +7   =   9

   d   i   e   z   =  10
  +4 -19  +7 +18   =  10

   o   n   c   e   =  11
 -11  -5 +20  +7   =  11

****************************************
IT numerals from 0 up to 10 absolute value within 10
a=0 c=-8 d=-7 e=5 i=10 n=-4 o=1 q=-2 r=-5 s=-9 t=3 u=4 v=7 z=-1

   z   e   r   o   =   0
  -1  +5  -5  +1   =   0

   u   n   o   =   1
  +4  -4  +1   =   1

   d   u   e   =   2
  -7  +4  +5   =   2

   t   r   e   =   3
  +3  -5  +5   =   3

   q   u   a   t   t   r   o   =   4
  -2  +4  +0  +3  +3  -5  +1   =   4

   c   i   n   q   u   e   =   5
  -8 +10  -4  -2  +4  +5   =   5

   s   e   i   =   6
  -9  +5 +10   =   6

   s   e   t   t   e   =   7
  -9  +5  +3  +3  +5   =   7

   o   t   t   o   =   8
  +1  +3  +3  +1   =   8

   n   o   v   e   =   9
  -4  +1  +7  +5   =   9

   d   i   e   c   i   =  10
  -7 +10  +5  -8 +10   =  10

****************************************
EN numerals from 0 up to 12 absolute value within 10
e=-2 f=-6 g=0 h=-7 i=7 l=9 n=2 o=1 r=4 s=3 t=10 u=5 v=6 w=-9 x=-4 z=-3

   z   e   r   o   =   0
  -3  -2  +4  +1   =   0

   o   n   e   =   1
  +1  +2  -2   =   1

   t   w   o   =   2
 +10  -9  +1   =   2

   t   h   r   e   e   =   3
 +10  -7  +4  -2  -2   =   3

   f   o   u   r   =   4
  -6  +1  +5  +4   =   4

   f   i   v   e   =   5
  -6  +7  +6  -2   =   5

   s   i   x   =   6
  +3  +7  -4   =   6

   s   e   v   e   n   =   7
  +3  -2  +6  -2  +2   =   7

   e   i   g   h   t   =   8
  -2  +7  +0  -7 +10   =   8

   n   i   n   e   =   9
  +2  +7  +2  -2   =   9

   t   e   n   =  10
 +10  -2  +2   =  10

   e   l   e   v   e   n   =  11
  -2  +9  -2  +6  -2  +2   =  11

   t   w   e   l   v   e   =  12
 +10  -9  -2  +9  +6  -2   =  12

As you see German won with perfect number names possible for 0…16.

Python code

 

# -*- coding: UTF-8 -*-
# Copyright 2016 TKK
from z3 import sat, Int, Solver

NUMERALS = {
  "PL" : "zero,jeden,dwa,trzy,cztery,pięć,sześć,siedem,osiem,dziewięć,dziesięć,jedenaście,dwanaście,trzynaście,czternaście".split(','),
  "DE" : "null,eins,zwei,drei,vier,fünf,sechs,sieben,acht,neun,zehn,elf,zwölf,dreizehn,vierzehn,fünfzehn,sechzehn,siebzehn".split(','),
  "FR" : "zéro,un,deux,trois,quatre,cinq,six,sept,huit,neuf,dix,onze,douze,treize,quatorze,quinze,seize".split(','),
  "IT" : "zero,uno,due,tre,quattro,cinque,sei,sette,otto,nove,dieci,unidici,dodici,tredici,quattordici,quindici,sedici".split(','),
  "ES" : "cero,uno,dos,tres,cuatro,cinco,seis,siete,ocho,nueve,diez,once,doce,trece,catorce,quince,dieciséis,diecisete".split(','),
  "PT" : "zero,um,dois,três,quatro,cinco,seis,sete,oito,nove,dez,onze,doze,treze,quatorze,quinze,dezesseis".split(','),
  "NL" : "nul,één,twee,dire,vier,vijf,zes,zeven,acht,negen,tien,elf,twaalf,dertien,veertien,vijftien".split(','),
  "EN" : "zero,one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thirteen,fourteen".split(',')
}

CHARMAP = { 'ć': 'c_', 'ę': 'e_', 'ś': 's_', 'ö': 'o_', 'ü': 'u_', 'é': 'e_acute', 'ê': 'e_circ' }

def find_num(numerals, limit, charmap):
  letters = sorted(set(charmap.get(x, x) for x in "".join(numerals)))

  my_sym = {}
  for l in letters:
    my_sym[l] = Int(l)

  s = Solver()
  for i, w in enumerate(numerals):
    s.add(sum([ my_sym[charmap.get(c, c)] for c in w ]) == i)

  for i, let in enumerate(letters):
    if limit is not None:
      s.add(my_sym[let] >= -limit)
      s.add(my_sym[let] <= limit)

    for let2 in letters[i + 1:]:
      s.add(my_sym[let] != my_sym[let2] )

  chk = s.check()
  model = None
  if chk == sat:
    model = s.model()
  return chk, model

def find_longest_existing(numerals, charmap): 
  for lennum in range(len(numerals), 0, -1):
    ret, model = find_num(numerals[:lennum], None, charmap)
    if ret == sat:
      return lennum
  return 0

def find_best_assignment(numerals, charmap):
  for m in range(5, 100):
    ret, model = find_num(numerals, m, charmap)
    if ret == sat:
      return m, model
  return None, None

def pretty_print_solution(lang, range_max, abs_max, model, charmap):
    rev_charmap = dict(zip(charmap.values(), charmap.keys()))
    print("*" * 40)
    print(lang, "numerals from 0 up to", range_max - 1, "absolute value within", abs_max)
    #print(NUMERALS[lang][:v])
    sp = sorted((str(k), model[k].as_long()) for k in model)
    sval = dict(sp)
    print(" ".join(["{}={}".format(rev_charmap.get(p[0], p[0]), p[1]) for p in sp ]))
    print()
    
    for i, word in enumerate(NUMERALS[lang][:range_max]):
        let, num, nsum = "", "", 0
        for ch in word:
            let += "{: >4}".format(ch)
            chval = sval[CHARMAP.get(ch, ch)]
            num += "{: >+4}".format(chval)
            nsum += chval

        let += "   ={: >4}".format(i)
        num += "   ={: >4}".format(nsum)
        print(let)
        print(num)
        print()    

def main():
  for lang in NUMERALS:
    v = find_longest_existing(NUMERALS[lang], CHARMAP)
    
    ret, model = find_best_assignment(NUMERALS[lang][:v], CHARMAP)
    if ret and model:
        pretty_print_solution(lang, v, ret, model, CHARMAP)    
   
if __name__ == "__main__":
  main()