Make mathematics with minimal matchsticks

15

0

Meta-background

This was set as a question on Puzzling, and the instant reaction was "well, someone will just solve that by computer". There was a debate about just how complex a program to solve this would have to be. Well, "how complex does this program have to be" is pretty much the definition of , so maybe PPCG can settle the issue?

Background

A matchstick equation is basically a normal mathematical equation, but where the digits and operators are constructed physically by placing matchsticks onto a table. (The main relevant feature of matchsticks here is that they're fairly rigid and have a constant length; sometimes people use other objects instead, such as cotton swabs.)

For this challenge, we don't need to define specific rules for how the matchsticks are arranged (like the linked challenge does); rather, we just care about how many matchsticks we'll need to represent an expression that evaluates to a given number.

The task

Here's an alphabet of digits and mathematical operators you can use, each having a cost in matchsticks:

  • 0, costing 6 matchsticks
  • 1, costing 2 matchsticks
  • 2, costing 5 matchsticks
  • 3, costing 5 matchsticks
  • 4, costing 4 matchsticks
  • 5, costing 5 matchsticks
  • 6, costing 6 matchsticks
  • 7, costing 3 matchsticks
  • 8, costing 7 matchsticks
  • 9, costing 6 matchsticks
  • +, costing 2 matchsticks
  • -, costing 1 matchstick
  • ×, costing 2 matchsticks

(You may represent × as * in your program's output if you wish, in order to avoid needing to use non-ASCII characters. In most encodings, × takes up more bytes than *, and so I imagine that most programs will want to take advantage of this leeway.)

You need to write a program that takes a nonnegative integer as input (via any reasonable means), and produces an expression that evaluates to that integer as output (again via any reasonable means). Additionally, the expression must be nontrivial: it must contain at least one operator +, -, or ×. Finally, the expression you output must be the cheapest (or tied for the cheapest) in terms of total matchstick cost, among all outputs that otherwise comply with the specification.

Clarifications

  • You can form multiple-digit numbers via outputting multiple digits in a row (e.g. 11-1 is a valid output to produce 10). Just to be completely precise, the resulting number gets interpreted in decimal. This sort of concatenation isn't an operation that works on intermediate results; only on literal digits that appear in the original expression.
  • For the purpose of this challenge. +, -, and × are infix operators; they need an argument to their left and to their right. You aren't allowed to use them in prefix position like +5 or -8.
  • You don't have parentheses (or any other way to control precedence) available. The expression evaluates according to the typical default precedence rules (multiplications happen first, and then the additions and subtractions are evaluated left to right).
  • You don't have access to any mathematical operators or constants other than the ones listed above; "lateral thinking" solutions are often accepted at Puzzling, but it doesn't make sense to require a computer to come up with them itself, and over here on PPCG, we like it to be objective whether or not a solution is correct.
  • The usual integer overflow rules apply: your solution must be able to work for arbitrarily large integers in a hypothetical (or perhaps real) version of your language in which all integers are unbounded by default, but if your program fails in practice due to the implementation not supporting integers that large, that doesn't invalidate the solution.
  • If you use the same digit or operator more than once, you have to pay its matchstick cost each time you use it (because, obviously, you can't reuse the same physical matchsticks in two different locations on the table).
  • There's no time limit; brute-force solutions are acceptable. (Although if you have a solution that's faster than brute force, feel free to post it even if it's longer; seeing how alternative approaches compare is always interesting.)
  • Although writing an explanation of your code is never required, it's likely to be a good idea; solutions are often very hard to read (especially to people not familiar with the language they're written in), and it can be hard to evaluate (and thus vote on) a solution unless you understand how it works.

Victory condition

As a challenge, answers with fewer bytes are considered better. However, as usual, feel free to post answers with different approaches, or in specific languages even if they're more verbose than certain other languages; the goal of golf is really to see how far you can optimize a particular program, and doing things this way gives us lots of potential programs to optimize. So don't be discouraged if someone submits a solution using a completely different approach, or a completely different language, and gets a much shorter answer; it may well be that your answer is better-optimized and shows more skill, and voters on PPCG often appreciate that.

user62131

Posted 2017-03-28T16:34:54.697

Reputation:

Jeez, whats the highest number we need to handle? My current attempt wouldn't run beyond like... maybe 20 on TIO. – Magic Octopus Urn – 2017-03-28T16:44:07.883

@carusocomputing: Arbitrarily high in theory, but if you can't get over 20 within a reasonable time in practice, that's entirely acceptable. – None – 2017-03-28T16:44:48.813

4Do you have any test cases? – Luke – 2017-03-28T18:32:01.913

I really wish this were a single operation, spread against multiple competitions. The multiplication is a divisor problem, but adding in addition and subtraction really complicates things. I have a solution that works, but not for the addition and subtraction; making that work perfectly will be tedious. – Magic Octopus Urn – 2017-03-28T20:16:58.747

@carusocomputing: You might be interested in this challenge, then. I suspect the challenge with multiplication only is significantly different and would require rather different solution techniques to get a good score.

– None – 2017-03-28T20:38:11.380

Answers

1

PHP, 241 Bytes

Online Version

function m($i){for(;$s<strlen($i);)$e+="6255456376"[$i[$s++]];return$e;}foreach($r=range(0,2*$a=$argv[1])as$v)foreach($r as$w)$x[$v+$w]["$v+$w"]=$x[$v*$w]["$v*$w"]=1+$x[$v-$w]["$v-$w"]=m("$v")+m("$w")+1;echo array_search(min($x[$a]),$x[$a]);

Breakdown

function m($i){
    for(;$s<strlen($i);)$e+="6255456376"[$i[$s++]];return$e; #return the count of the matchstick for an integer
}

foreach($r=range(0,2*$a=$argv[1])as$v) # limit to an input to 300 in the online version
foreach($r as$w)
       $x[$v+$w]["$v+$w"]=  #fill the 2D array in the form [result][expression] = count matchsticks
       $x[$v*$w]["$v*$w"]=
       1+$x[$v-$w]["$v-$w"]=
       m("$v")+m("$w")+1;
echo $k=array_search(min($x[$a]),$x[$a]); # Output expression with a minium of matchsticks
echo"\t".$x[$a][$k]; #optional Output of the count of the matchsticks

Way with a little better performance

function m($i){
for(;$s<strlen($i);)
$e+="6255456376"[$i[$s++]];return$e;} #return the count of the matchstick for an integer
foreach($r=range(0,2*$a=$argv[1])as$v)
foreach($r as$w){$c=m("$v")+m("$w")+1;
if($a==$v+$w)$x["$v+$w"]=1+$c; # fill array if value equal input
if($a==$v*$w)$x["$v*$w"]=1+$c;
if($a==$v-$w)$x["$v-$w"]=$c;}
echo $k=array_search(min($x),$x); # Output expression with a minium of matchsticks
    echo"\t".$x[$k]; #optional Output of the count of the matchsticks

Support of negative integers

Version with negative integers

function m($i){
    $e=$i<0?1:0; # raise count for negative integers
    for($s=0;$s<strlen($i);)$e+=[6,2,5,5,4,5,6,3,7,6][$i[$s++]];return$e; #return the count of the matchstick for an integer
}
$q=sqrt(abs($argv[1]));
$l=max(177,$q);
$j=range(-$l,$l); # for second loop for better performance
foreach($r=range(min(0,($a=$argv[1])-177),177+$a)as$v) 
foreach($j as$w){$c=m("$v")+m("$w")+1;  
    if($a==$v+$w)$x["$v+$w"]=1+$c; # fill array if value equal input
    if($a==$v*$w)$x["$v*$w"]=1+$c;
    if($a==$v-$w)$x["$v-$w"]=$c;
    if($a==$w-$v)$x["$w-$v"]=$c; # added for integers <0
}
echo $k=array_search(min($x),$x); # Output expression with a minium of matchsticks
echo"\t".$x[$k]; #optional Output of the count of the matchsticks

Jörg Hülsermann

Posted 2017-03-28T16:34:54.697

Reputation: 13 026

Oh snap, this works on negative numbers too! – Magic Octopus Urn – 2017-03-28T20:34:46.620

@carusocomputing not really it could been that a solution exists with less matchsticks cause negative numbers are only added by subtraction. in this cases you should check the absolute value too and add an one – Jörg Hülsermann – 2017-03-28T20:43:10.477

I don't think the literal 333 would be acceptable here, although you could likely fix it via making it some function of the input. (The program might well run much slower, so you could keep the hardcoded version for testing.) – None – 2017-03-28T20:47:07.653

@JörgHülsermann smart man... I only checked a few low numbers, but it seemed to work for a few of them. – Magic Octopus Urn – 2017-03-28T20:52:35.357

The output for 1000 is not optimal. This gives 8*125 for 21 matchsticks, but 1000-1 is better with 19. – mbomb007 – 2017-03-28T20:52:46.583

@mbomb007 replace 333 with 2*$argv[1] and it should be fixed but I found no online version that runs not in an error – Jörg Hülsermann – 2017-03-28T21:06:11.760

1@ais523 done 333 is replaced with 2*input – Jörg Hülsermann – 2017-03-28T21:10:42.137

@carusocomputing after the change to 2* input it should not work with negative numbers – Jörg Hülsermann – 2017-03-28T21:14:22.143

@mbomb007 1000 1111-111 are 15 Matchsticks. I have no idea what ist wrong with your program – Jörg Hülsermann – 2017-03-28T22:45:30.190

@carusocomputing I have added a version for negative integers – Jörg Hülsermann – 2017-03-29T00:27:47.357

@JörgHülsermann What program? I don't have one. – mbomb007 – 2017-03-29T02:18:17.593

1You can index strings: $e+="6255456376"[$i[$s++]];. – manatwork – 2017-03-29T06:23:31.687

1

Python2, 1̶9̶8̶ ̶b̶y̶t̶e̶s̶ 182 bytes thanks to math_junkie

def f(n,c=dict(zip('0123456789+-*',map(int,'6255456376212'))),e=[(0,'')]):
 while 1:
    v=(m,s)=min(e);e.remove(v)
    try:
     if eval(s)==n:return s
    except:0
    e+=[(m+c[x],s+x)for x in c]

This algorithm does nothing to exclude prefix versions of + and -, but they will either be worse than, or equal to and appear later in the search, their infix counterparts. Because it uses the keyword argument e mutably, it will give invalid results if called multiple times per session. To fix this, use f(n, e=[(0,'')]) instead of just f(n). Note that the four-spaced indents represent tabs, so this will only work with Python 2.

I also have an ungolfed and optimized version which runs quickly even for quite large numbers:

from heapq import heappop, heappush

def f(n):
    digits = list('0123456789')
    ops =['+','-','*','']
    costs = dict(zip(digits + ops, [6,2,5,5,4,5,6,3,7,6,2,1,2,0]))
    expressions = [(costs[d], abs(n - int(d)), int(d), d) for d in digits[1:]]
    seen = set()
    while 1:
        cost, d, k, expression = heappop(expressions)
        if d == 0:
            return expression
        for op in ops:
            if op in '+-' and k in seen:
                continue
            for digit in digits:
                if op and digit == '0':
                    continue
                expression1 = expression + op + digit
                k1 = eval(expression1)
                d1 = abs(n - k1)
                if d1 == 0:
                    return expression1
                heappush(expressions, (cost+costs[op]+costs[digit], d1, k1, expression1))
        seen.add(k)

user1502040

Posted 2017-03-28T16:34:54.697

Reputation: 2 196

Some minor suggested golfs: TIO (182 bytes)

– math junkie – 2017-03-29T02:29:24.317