1

I'm trying to write a program in C that will solve the following cryptarithm:

one + one = two

seven is prime

nine is a perfect square

Namely, I need to find the numerical values for the words one, two, seven and nine where each letter (o, n, e, t, w, s, v, i) is assigned a numerical value and the complete number also meets all of the above conditions.

I was thinking along the lines of creating an int array for each of the words and then 1) checking if each word meets the condition (e.g is a prime for "seven") and then 2) checking if each integer in the array is consistant with the value of the other words, where the other words also are found to meet their respective conditions.

I can't really see this working though as I would have to continuously convert the int array to a single int throughout every iteration and then I'm not sure how I can simultaneously match each element in the array with the other words.

Perhaps knowing the MIN and MAX numerical range that must be true for each of the words would be useful?

Any ideas?

false
  • 10,264
  • 13
  • 101
  • 209
Martin
  • 1,060
  • 2
  • 16
  • 28
  • Forgot to add the digit assigned to the first letter of each of the words must not be zero. – Martin Apr 14 '13 at 09:15
  • Each letter is assigned single digit value, or could be whatever numerical value (a=281 for example) ? – Freeman Lambda Apr 14 '13 at 09:43
  • @FreemanLambda Yes each letter must be a single digit value, and must be consistant with the other words (i.e the "e" in "seven" must be the same as the "e" in "one" . – Martin Apr 14 '13 at 10:03

3 Answers3

5

For a brute-force (ish) method, I'd start with the prime seven, and use the Sieve of Eratosthenes to get all the prime numbers up to 99999. You could discard all answers where the 2nd and 4th digit aren't the same. After that you could move on to the square nine, because three of the digits are determined by the prime seven. That should narrow down the possibilities nicely, and then you can just use the answer of @pmg to finish it off :-).

Update: The following C# program seems to do it

bool[] poss_for_seven = new bool[100000];       // this will hold the possibilities for `seven`
for (int seven = 0; seven < poss_for_seven.Length; seven++)
    poss_for_seven[seven] = (seven > 9999);     // `seven` must have 5 digits
// Sieve of Eratosthenes to make `seven` prime
for (int seven = 2; seven < poss_for_seven.Length; seven++) {
    for (int j = 2 * seven; j < poss_for_seven.Length; j += seven) {
        poss_for_seven[j] = false;
    }
}
// look through the array poss_for_seven[], considering each possibility in turn
for (int seven = 10000; seven < poss_for_seven.Length; seven++) {
    if (poss_for_seven[seven]) {
        int second_digit = ((seven / 10) % 10);
        int fourth_digit = ((seven / 1000) % 10);
        if (second_digit == fourth_digit) {
            int e = second_digit;
            int n = (seven % 10);   // NB: `n` can't be zero because otherwise `seven` wouldn't be prime
            for (int i = 0; i < 10; i++) {
                int nine = n * 1000 + i * 100 + n * 10 + e;
                int poss_sqrt = (int)Math.Floor(Math.Sqrt(nine) + 0.1); // 0.1 in case of of rounding error
                if (poss_sqrt * poss_sqrt == nine) {
                    int o = ((2 * e) % 10); // since 2 * `one` = `two`, we now know `o`
                    int one = o * 100 + n * 10 + e;
                    int two = 2 * one;
                    int t = ((two / 100) % 10);
                    int w = ((two / 10) % 10);
                    // turns out that `one`=236, `two`=472, `nine` = 3136.
                    // look for solutions where `s` != `v` with `s` and `v' different from `o`, `n`, `e`,`t`, `w` and `i`
                    int s = ((seven / 10000) % 10);
                    int v = ((seven / 100) % 10);
                    if (s != v && s != o && s != n && s != e && s != t && s != w && s != i && v != o && v != n && v != e && v != t && v != w && v != i) {
                        System.Diagnostics.Trace.WriteLine(seven + "," + nine + "," + one + "," + two);
                    }
                }
            }
        }
    }
}

It seems that nine is always equal to 3136, so that one = 236 and two = 472. However, there are 21 possibiliites for seven. If one adds the constraint that no two digits can take the same value (which is what the C# code above does), then it reduces to just one possibility (although a bug in my code meant this answer originally had 3 possibilities):

seven,nine,one,two
56963,3136,236,472
Stochastically
  • 7,616
  • 5
  • 30
  • 58
  • Thanks, i'll try this together with @pmg's solution for the last part. – Martin Apr 14 '13 at 10:05
  • Thanks! I went through it and made sure I understood exactly what you did, then wrote a version in C and checked the 3 solutions by hand (which are correct). You were also right in adding the extra contraint in at the end as the questions asks to "assign distinct numbers to distinct letters", which I interpret as meaning that I can only use each number for one letter.Thanks again. – Martin Apr 14 '13 at 13:31
  • In the second solution S = 7 and W = 7. In the third solution V = 7 and W = 7. – Freeman Lambda Apr 20 '13 at 19:38
  • Well spotted @FreemanLambda :-). There was a bug in my code because I had t = ((two / 100) % 100) and w = ((two / 100) % 10), whereas I should have had t = ((two / 100) % 10) and w = ((two / 10) % 10). Anyway, I've edited it and corrected it now. – Stochastically Apr 21 '13 at 07:06
3

I just found the time to build a c program to solve your cryptarithm. I think that tackling the problem mathematicaly, prior to starting the brute force programming, will heavily increase the speed of the output.

Some math (number theory): Since ONE + ONE = TWO, O cant be larget than 4, because ONE + ONE would result 4 digits. Also O cant be 0. TWO end with O and is an even number, because it is 2 * ONE. Applying these 3 filters to O, the possible values remain O= {2,4} Hence E can be {1,2,6,7} because (E+E) modulus 10 must be = O. More specificaly, O=2 implicates E={1,6} and O=4 implicates E={2,7} Now lets filter N. Given that SEVEN is prime, N must be an odd number. Also N cant be 5, because all that ends with 5 is divisible by 5. Hence N={1,3,7,9}

Now that we have reduced the possibilites for the most ocurring characters (O,E,N), we are ready to hit this cryptarith with all of our brutality, having iterations drastically reduced.

Heres the C code:

#include <stdio.h>
#include <math.h>

#define O 0
#define N 1
#define E 2
#define T 3
#define W 4
#define S 5
#define V 6
#define I 7

bool isPerfectSquare(int number);
bool isPrime(int number);
void printSolutions(int countSolutions);
int filterNoRepeat(int unfilteredCount);

int solutions[1000][8]; // solution holder
int possibilitiesO[2] = {2,4}; 
int possibilitiesN[4] = {1,3,7,9};
int possibilitiesE[4] = {1,6,2,7};

void main() {
    int countSolutions = 0;
    int numberOne;
    // iterate to fill up the solutions array by: one + one = two
    for(int o=0;o<2;o++) {
        for(int n=0;n<4;n++) {
            for(int e=2*o;e<2*o+2;e++) { // following code is iterated 2*4*2 = 16 times
                numberOne = 100*possibilitiesO[o] + 10*possibilitiesN[n] + possibilitiesE[e];
                int w = ((2*numberOne)/10)%10;
                int t = ((2*numberOne)/100)%10;
                // check if NINE is a perfect square
                for(int i=0;i<=9;i++) { // i can be anything ----- 10 iterations
                    int numberNine = 1000*possibilitiesN[n] + 100*i + 10*possibilitiesN[n] + possibilitiesE[e];
                    if(isPerfectSquare(numberNine)) {
                        // check if SEVEN is prime
                        for(int s=1;s<=9;s++) { // s cant be 0 ------ 9 iterations
                            for(int v=0;v<=9;v++) { // v  can be anything other than s ------- 10 iterations
                                if(v==s) continue;
                                int numberSeven = 10000*s + 1000*possibilitiesE[e] + 100*v + 10*possibilitiesE[e] + possibilitiesN[n];
                                if(isPrime(numberSeven)) { // store solution
                                    solutions[countSolutions][O] = possibilitiesO[o];
                                    solutions[countSolutions][N] = possibilitiesN[n];
                                    solutions[countSolutions][E] = possibilitiesE[e];
                                    solutions[countSolutions][T] = t;
                                    solutions[countSolutions][W] = w;
                                    solutions[countSolutions][S] = s;
                                    solutions[countSolutions][V] = v;
                                    solutions[countSolutions][I] = i;
                                    countSolutions++;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    // 16 * 9 * 10 * 10 = 14400 iterations in the WORST scenario, conditions introduced reduce MOST of these iterations to 1 if() line
    // iterations consumed by isPrime() function are not taken in count in the aproximation above.
    // filter solutions so that no two letter have the same digit
    countSolutions = filterNoRepeat(countSolutions);
    printSolutions(countSolutions); // voila!
}

bool isPerfectSquare(int number) { // check if given number is a perfect square
    double root = sqrt((double)number);
    if(root==floor(root)) return true;
    else return false;
}

bool isPrime(int number) { // simple algoritm to determine if given number is prime, check interval from sqrt(number) to number/2 with a step of +2
    int startValue = sqrt((double)number);
    if(startValue%2==0) startValue--; // make it odd
    for(int k=startValue;k<number/2;k+=2) {
        if(number%k==0) return false;
    }
    return true;
}

void printSolutions(int countSolutions) {
    for(int k=0;k<countSolutions;k++) {
        int one = 100*solutions[k][O] + 10*solutions[k][N] + solutions[k][E];
        int two = 100*solutions[k][T] + 10*solutions[k][W] + solutions[k][O];
        int seven = 10000*solutions[k][S] + 1000*solutions[k][E] + 100*solutions[k][V] + 10*solutions[k][E] + solutions[k][N];
        int nine = 1000*solutions[k][N] + 100*solutions[k][I] + 10*solutions[k][N] + solutions[k][E];
        printf("ONE: %d, TWO: %d, SEVEN: %d, NINE %d\n",one,two,seven,nine);
    }
}

int filterNoRepeat(int unfilteredCount) {
    int nrSol = 0;
    for(int k=0;k<unfilteredCount;k++) {
        bool isValid = true;
        for(int i=0;i<7;i++) { // if two letters match, solution is not valid
            for(int j=i+1;j<8;j++) {
                if(solutions[k][i]==solutions[k][j]) {
                    isValid = false;
                    break;
                }
            }
            if(!isValid) break;
        }
        if(isValid) { // store solution
            for(int i=0;i<8;i++) {
                solutions[nrSol][i] = solutions[k][i];
            }
            nrSol++;
        }
    }
    return nrSol;
}

You can try the code yourself if you are still interested in this :P. The result is one single solution: ONE: 236, TWO: 472, SEVEN: 56963, NINE: 3136 This solution is the same as Stochastically's solutions, confirming the correctness of both algorithms i think :). Thanks for providing this nice cryptarithm and have a nice day!

Freeman Lambda
  • 3,567
  • 1
  • 25
  • 33
2

Brute force FTW!

#define ONE ((o*100) + (n*10) + e)
#define TWO ((t*100) + (w*10) + o)
#define SEVEN ((s*10000) + (e*1010) + (v*100) + n)
#define NINE ((n*1010) + (i*100) + e)

for (o = 1; o < 10; o++) {                /* 1st digit cannot be zero (one) */
  for (n = 1; n < 10; n++) {              /* 1st digit cannot be zero (nine) */
    if (n == o) continue;
    for (e = 0; n < 10; n++) {
      if (e == n) continue;
      if (e == o) continue;
              /* ... */
                      if (ONE + ONE == TWO) /* whatever */;
              /* ... */
    }
  }
}
pmg
  • 106,608
  • 13
  • 126
  • 198