17

A friend of mine sent me this question. I haven't really been able to come up with any kind of algorithm to solve this problem.

You are provided with a no. say 123456789 and two operators * and +. Now without changing the sequence of the provided no. and using these operators as many times as you wish, evaluate the given value:

eg: given value 2097
Solution: 1+2+345*6+7+8+9

Any ideas on how to approach problems like these?

moinudin
  • 134,091
  • 45
  • 190
  • 216
Gaurav
  • 681
  • 3
  • 10
  • 21
  • 1
    Brute force is always an option. :P (There is an algorithm, I just don't know what it is/is called.) – Chris Lutz Jan 01 '11 at 10:17
  • I am struggling to even use brute force on this one. some how I can't manage to generate all possible expressions. Can you give any hints on how to go about this? – Gaurav Jan 01 '11 at 10:21
  • @Jeff, I know :D i was attempting to answer Gaurav's question.... – st0le Jan 01 '11 at 10:23
  • 6
    @Gaurav: Think of it this way, you have `n` digits and 2 operators. Each operator can go anywhere in between the digits or be omitted. That gives you 3 possible operations between digits, `+`, `*` or none. Just find all the combinations of digits and operations in your expressions then it's just a matter of parsing it. – Jeff Mercado Jan 01 '11 at 10:28
  • You have to generate a number with any given sequence or particularly with "123456789" – Vikram.exe Jan 01 '11 at 10:29
  • @Vikram.exe 123456789 is just an example. The input can be any sequence. – Gaurav Jan 01 '11 at 10:30
  • @Jeff M : Thanks, but I'd already thought of that. But I can't figure out how to get my code to do this brute force for me. Can you show me a sample pseudo code for generating all such expressions? – Gaurav Jan 01 '11 at 10:33
  • @Gaurav: To be honest, I can't think of a good way to code it in C very easily at the moment. Without any ready-built data structures, it would take a lot of work to do. This sort of thing I'd be writing in C# or Python like the others. :) – Jeff Mercado Jan 01 '11 at 11:07
  • Generating the expressions is relatively easy (but somewhat ugly) - 8 nested loops will do it. Evaluating the expressions is harder, because you need to handle the operator precedence properly. Is it permissible to generate a C program which can then be compiled? – John La Rooy Jan 01 '11 at 13:15
  • well 8 nested loops because you know the length of the input here. In general the length of the input can be much bigger. And operator precedence shouldn't really be a problem once you generate all possible expression. If you uniformly evaluate all of them you are bound to find an answer. – Gaurav Jan 01 '11 at 13:54
  • For what it's worth, 2012 = 12+34*56+7+89 – Jim Ward Feb 07 '11 at 21:43

5 Answers5

29

One of the easiest ways to do it is using shell expansion in BASH:

#!/bin/sh

for i in 1{,+,*}2{,+,*}3{,+,*}4{,+,*}5{,+,*}6{,+,*}7{,+,*}8{,+,*}9; do
    if [ $(( $i )) == 2097 ]; then
        echo $i = 2097
    fi
done

which gives:

$ sh -c '. ./testequation.sh'
12*34+5*6*7*8+9 = 2097
12*3*45+6*78+9 = 2097
1+2+345*6+7+8+9 = 2097
PP.
  • 10,764
  • 7
  • 45
  • 59
  • 2
    +1 for finding such an amazingly compact (and elegant) solution. – Eric Pi Jan 01 '11 at 23:40
  • +1: Really beautiful. However, how fast is it? How does it work for larger numbers? Up to a million, for starters. – darioo Jan 02 '11 at 10:31
  • 1
    I'm inclined to community wiki this answer because I wasn't the first one to come up with it. I saw an almost identical question like this on stackoverflow about a year ago and was just as amazed at this solution as you all are. It takes less than a second on a small virtual machine for this solution but of course would take exponentially longer for longer problems. – PP. Jan 02 '11 at 14:52
  • But... what about unary +? Your answers add 9 at the end, but you could have added +9, or maybe even +(+9)... – Ben Voigt Jan 02 '11 at 21:39
  • I did some frequency analysis on all possible combinations of the problem and discovered the answer 145 appears 12 times (the most frequent). Highest possible answer is 123456789 and lowest possible answer is 44. – PP. Jan 06 '11 at 10:29
12

There are not that many solutions - this python program takes under a second to bruteforce them all

from itertools import product

for q in product(("","+","*"), repeat=8):
    e = ''.join(i+j for i,j in zip('12345678',q))+'9'
    print e,'=',eval(e)

Here is a sample run through grep

$ python sums.py | grep 2097
12*34+5*6*7*8+9 = 2097
12*3*45+6*78+9 = 2097
1+2+345*6+7+8+9 = 2097

General solution is a simple modification

from itertools import product

def f(seq):
    for q in product(("","+","*"), repeat=len(seq)-1):
        e = ''.join(i+j for i,j in zip(seq[:-1],q))+seq[-1]
        print e,'=',eval(e)
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
2

This isn't the easiest way, but I tried to write "optimized" code : genereting all the 3^(n-1) strings is expensive, and you've to evaluate a lot of them; I still used bruteforce, but cutting unproductive "subtrees" ( and the source is C, as requested in the title)

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"

#define B 10

void rec_solve(char *, char *, int, char, int, char, int, char *);

int main(int argc, char** argv) {
    char *n, *si = malloc(0);
    if (argc < 2) {
        printf("Use : %s num sum", argv[0]);
    } else {
        n = calloc(strlen(argv[1]), sizeof (char));
        strncpy(n, argv[1], strlen(argv[1]));
        rec_solve(n, si, 0, '+', 0, '+', atoi(argv[2]), n);
    }
    return 0;
}

void rec_solve(char *str, char *sig, int p, char ps, int l, char ls, int max, char *or) {
    int i, len = strlen(str), t = 0, siglen = strlen(sig), j, k;
    char *mul;
    char *add;
    char *sub;

    if (p + l <= max) {
        if (len == 0) {
            k = (ls == '+') ? p + l : p*l;

            if ((k == max) && (sig[strlen(sig) - 1] == '+')) {
                for (i = 0; i < strlen(or) - 1; i++) {
                    printf("%c", or[i]);
                    if (sig[i] && (sig[i] != ' '))
                        printf("%c", sig[i]);
                }
                printf("%c\n", or[i]);
            }
        } else {
            for (i = 0; i < len; i++) {
                t = B * t + (str[i] - '0');

                if (t > max)
                    break;

                sub = calloc(len - i - 1, sizeof (char));
                strncpy(sub, str + i + 1, len - i - 1);

                mul = calloc(siglen + i + 1, sizeof (char));
                strncpy(mul, sig, siglen);

                add = calloc(strlen(sig) + i + 1, sizeof (char));
                strncpy(add, sig, siglen);

                for (j = 0; j < i; j++) {
                    add[siglen + j] = ' ';
                    mul[siglen + j] = ' ';
                }

                add[siglen + i] = '+';
                mul[siglen + i] = '*';

                switch (ps) {
                    case '*':
                        switch (ls) {
                            case '*':
                                rec_solve(sub, add, p*l, '*', t, '+',max, or);
                                rec_solve(sub, mul, p*l, '*', t, '*',max, or);
                                break;
                            case '+':
                                rec_solve(sub, add, p*l, '+', t, '+',max, or);
                                rec_solve(sub, mul, p*l, '+', t, '*',max, or);
                                break;
                        }
                    case '+':
                        switch (ls) {
                            case '*':
                                rec_solve(sub,add,p, '+',l*t,'+',max, or);
                                rec_solve(sub,mul,p, '+',l*t,'*',max, or);
                                break;
                            case '+':
                                rec_solve(sub,add,p + l,'+',t,'+',max, or);
                                rec_solve(sub,mul,p + l,'+',t,'*',max, or);
                                break;
                        }
                        break;
                }
            }
        }
    }
}
kaharas
  • 597
  • 2
  • 17
  • 39
2

Here's an implementation of a non-recursive C bruteforce version that will work for any set of digits (with reasonable values in the 32-bit range and not just for the example above). Now complete. :)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* simple integer pow() function */
int pow(int base, int pow)
{
    int i, res = 1;
    for (i = 0; i < pow; i++)
        res *= base;
    return res;
}

/* prints a value in base3 zeropadded */
void zeropad_base3(int value, char *buf, int width)
{
    int length, dif;

    _itoa(value, buf, 3);
    length = strlen(buf);
    dif = width - length;

    /* zeropad the rest */
    memmove(buf + dif, buf, length+1);
    if (dif)
        memset(buf, '0', dif);
}

int parse_factors(char **expr)
{
    int num = strtol(*expr, expr, 10);
    for ( ; ; )
    {
        if (**expr != '*')
            return num;
        (*expr)++;
        num *= strtol(*expr, expr, 10);
    }
}

/* evaluating using recursive descent parser */
int evaluate_expr(char* expr)
{
    int num = parse_factors(&expr);
    for ( ; ; )
    {
        if (*expr != '+')
            return num;
        expr++;
        num += parse_factors(&expr);
    }
}

void do_puzzle(const char *digitsString, int target)
{
    int i, iteration, result;
    int n = strlen(digitsString);
    int iterCount = pow(3, n-1);
    char *exprBuf = (char *)malloc(2*n*sizeof(char));
    char *opBuf = (char *)malloc(n*sizeof(char));

    /* try all combinations of possible expressions */
    for (iteration = 0; iteration < iterCount; iteration++)
    {
        char *write = exprBuf;

        /* generate the operation "opcodes" */
        zeropad_base3(iteration, opBuf, n-1);

        /* generate the expression */
        *write++ = digitsString[0];
        for (i = 1; i < n; i++)
        {
            switch(opBuf[i-1])
            {
            /* case '0' no op */
            case '1': *write++ = '+'; break;
            case '2': *write++ = '*'; break;
            }
            *write++ = digitsString[i];
        }
        *write = '\0';

        result = evaluate_expr(exprBuf);
        if (result == target)
            printf("%s = %d\n", exprBuf, result);
    }

    free(opBuf);
    free(exprBuf);
}

int main(void)
{
    const char *digits = "123456789";
    int target = 2097;
    do_puzzle(digits, target);
    return 0;
}
12*34+5*6*7*8+9 = 2097
12*3*45+6*78+9 = 2097
1+2+345*6+7+8+9 = 2097
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • I would never have thought of using `itoa()` with base 3... Kinda gross I have to say :-P but it works! – j_random_hacker Jan 02 '11 at 10:11
  • @random: Yeah, it's just one of those things I noticed about numbers long ago. Great for when you need to generate all combinations of `n` amount of digits easily. :) – Jeff Mercado Jan 02 '11 at 10:15
  • 1
    Can't resist pointing out that you can do an "in-place incrementer" quite easily: `void zeropad_base3(char *lastdigit) { while (*lastdigit == '3') { *lastdigit-- = '0'; } ++*lastdigit; }` Quicker and gets rid of 2 parameters! :) – j_random_hacker Jan 02 '11 at 10:34
  • @random: Now _that_ I wouldn't have been able to come up with. At least not as compact as that. :) – Jeff Mercado Jan 02 '11 at 10:56
  • @random: Rather, I wouldn't have been willing to write it that way. :P – Jeff Mercado Jan 02 '11 at 11:20
  • @Jeff: Sure, it has an element of *danger* to it... :) – j_random_hacker Jan 02 '11 at 13:17
  • 1
    @random: p.s., I generally try to write my functions in a general, reusable way. The point of the `zeropad_base3()` function was to print some number to a buffer in base3 (zeropadded). Something `sprintf()` was lacking that I wanted. Had I written it as you suggested (which is perfectly fine BTW), it would have fewer options for reuse or potential expansion as is and was a bit too specialized for my tastes. Not saying it is wrong or anything, it's just not a goal of mine to write it like that. :) – Jeff Mercado Jan 03 '11 at 23:12
0

You could work backwards and try to test for all possibilities that could give solution;

e.g:

1 (something) 9 = 10

1*9=10 - false

1/9=10 - false

1-9=10 - false

1+9=10 - True

So, basically brute force - but it is re-usable code given that the input could be different.

Mayur Birari
  • 5,837
  • 8
  • 34
  • 61
Gyonka
  • 3
  • 2