When solving a combinatorial (optimization) problem, using a suitable modelling language is always nice. I'm partial to MiniZinc (http://www.minizinc.org/) myself, where Send more money would look like this
include "globals.mzn";
var 1..9: S;
var 0..9: E;
var 0..9: N;
var 0..9: D;
var 1..9: M;
var 0..9: O;
var 0..9: R;
var 0..9: Y;
constraint 1000 * S + 100 * E + 10 * N + D
+ 1000 * M + 100 * O + 10 * R + E
= 10000 * M + 1000 * O + 100 * N + 10 * E + Y;
constraint alldifferent([S,E,N,D,M,O,R,Y]);
solve satisfy;
output [ " ",show(S),show(E),show(N),show(D),"\n",
"+ ",show(M),show(O),show(R),show(E),"\n",
"= ",show(M),show(O),show(N),show(E),show(Y),"\n"];
A couple of nice things to note are the initial domains (defining the non-zero aspect of S and M), the use of alldifferent to make sure all variables have unique values, and letting the solving-strategy be determined by the back-end solver used.
A straightforward translation of the first program to Python 3 runs in 11 seconds:
digits = set(range(10))
def choose(choices, fn):
for value in choices:
fn(value)
def guard(condition, fn):
if condition:
fn()
def ten(*digits):
return int(''.join(map(str, digits)))
choose(digits - {0}, lambda s:
choose(digits - {s}, lambda e:
choose(digits - {s, e}, lambda n:
choose(digits - {s, e, n}, lambda d:
choose(digits - {0, s, e, n, d}, lambda m:
choose(digits - {s, e, n, d, m}, lambda o:
choose(digits - {s, e, n, d, m, o}, lambda r:
choose(digits - {s, e, n, d, m, o, r}, lambda y:
(lambda
send = ten(s, e, n, d),
more = ten(m, o, r, e),
money = ten(m, o, n, e, y):
guard(send + more == money, lambda:
print("{} + {} = {}".format(send, more, money))
))()))))))))
#lang racket
(define (ten digits)
(apply (compose string->number string-append)
(map number->string digits)))
(define (solve)
(for* ([s (range 1 10)]
[e (remove s (range 0 10))]
[n (remove* (list s e) (range 0 10))]
[d (remove* (list s e n) (range 0 10))]
[m (remove* (list 0 s e n d) (range 0 10))]
[o (remove* (list s e n d m) (range 0 10))]
[r (remove* (list s e n d m o) (range 0 10))]
[y (remove* (list s e n d m o r) (range 0 10))]
#:when (= (+ (ten (list s e n d))
(ten (list m o r e)))
(ten (list m o n e y))))
(displayln
(format
" S E N D ~a ~a ~a ~a~n + M O R E + ~a ~a ~a ~a~n------------ ------------~n M O N E Y ~a ~a ~a ~a ~a"
s e n d m o r e m o n e y))))
Excuse the ugly format line :)
sendmore.rkt> (time (solve))
S E N D 9 5 6 7
+ M O R E + 1 0 8 5
------------ ------------
M O N E Y 1 0 6 5 2
cpu time: 7124 real time: 7120 gc time: 80
I suspect he used something slow to emulate sets in Perl 5. I get:
$ time python3 send.py
9567 + 1085 = 10652
real 0m16.388s
user 0m16.299s
sys 0m0.049s
$ time perl send.pl
9567 + 1085 = 10652
real 0m16.838s
user 0m16.732s
sys 0m0.063s
That makes more sense; I didn't think Perl was as slow as he claimed.
You can even get a similarly-structured program in C with macro abuse and C99's compound literals:
#include <stdio.h>
#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__}) / sizeof(int))
#define FOR_EACH_DIGIT(v) for (int v = 0; v < 10; v++)
#define EXCEPT(v, ...) if (in(v, (int[]){__VA_ARGS__}, NUMARGS(__VA_ARGS__))) continue
#define FOR_EACH_DIGIT_EXCEPT(v, ...) FOR_EACH_DIGIT(v) { EXCEPT(v, __VA_ARGS__);
#define TEN(...) ten((int[]){__VA_ARGS__}, NUMARGS(__VA_ARGS__))
int in(int v, int a[], int n) {
for (int i = 0; i < n; i++) if (a[i] == v) return 1;
return 0;
}
int ten(int a[], int n) {
int v = 0;
for (int i = 0; i < n; i++) v = v * 10 + a[i];
return v;
}
int main() {
FOR_EACH_DIGIT_EXCEPT(s, 0)
FOR_EACH_DIGIT_EXCEPT(e, s)
FOR_EACH_DIGIT_EXCEPT(n, s, e)
FOR_EACH_DIGIT_EXCEPT(d, s, e, n)
FOR_EACH_DIGIT_EXCEPT(m, 0, s, e, n, d)
FOR_EACH_DIGIT_EXCEPT(o, s, e, n, d, m)
FOR_EACH_DIGIT_EXCEPT(r, s, e, n, d, m, o)
FOR_EACH_DIGIT_EXCEPT(y, s, e, n, d, m, o, r)
int send = TEN(s, e, n, d), more = TEN(m, o, r, e), money = TEN(m, o, n, e, y);
if (send + more == money) printf("%d + %d = %d\n", send, more, money);
}}}}}}}}
return 0;
}
Definitely not idiomatic C, but at least it's fast (0.07 seconds).
(define d::int)
(define e::int)
(define m::int)
(define n::int)
(define o::int)
(define r::int)
(define s::int)
(define y::int)
(assert
(and
(and (>= d 0) (<= d 9))
(and (>= e 0) (<= e 9))
(and (>= m 0) (<= m 9))
(and (>= n 0) (<= n 9))
(and (>= o 0) (<= o 9))
(and (>= r 0) (<= r 9))
(and (>= s 0) (<= s 9))
(and (>= y 0) (<= y 9))
(and (/= d e) (/= d m) (/= d n) (/= d o) (/= d r) (/= d s) (/= d y))
(and (/= e m) (/= e n) (/= e o) (/= e r) (/= e s) (/= e y))
(and (/= m n) (/= m o) (/= m r) (/= m s) (/= m y))
(and (/= n o) (/= n r) (/= n s) (/= n y))
(and (/= o r) (/= o s) (/= o y))
(and (/= r s) (/= r y))
(/= s y)
(=
(+
(+ (* s 1000) (* e 100) (* n 10) d)
(+ (* m 1000) (* o 100) (* r 10) e)
)
(+ (* m 10000) (* o 1000) (* n 100) (* e 10) y)
)
)
)
(check)
(show-model)
which gives
sat
(= m 1)
(= r 8)
(= y 2)
(= o 0)
(= s 9)
(= n 6)
(= d 7)
(= e 5)
./bin/yices money.ys 0.01s user 0.00s system 76% cpu 0.012 total