Express a number with only 0-9 and the four operations

14

1

Explanation

Befunge is a two-dimensional program that uses stacks.

That means, to do 5 + 6, you write 56+, meaning:

56+
5    push 5 into stack
 6   push 6 into stack
  +  pop the first two items in the stack and add them up, and push the result into stack

(to those of you who do not know stacks, "push" just means add and "pop" just means take off)

However, as the intelligent of you have observed, we cannot push the number 56 directly into the stack.

To do so, we must write 78* instead, which multiplies 7 and 8 and pushes the product into the stack.

Details

Input can be taken in any format, meaning it can be STDIN or not, at the programmer's discretion.

The input will be a positive integer (no bonus for including 0 or negative integers).

The output will be a string consisting of only these characters: 0123456789+-*/ (I would not use % modulo.)

The goal is to find the shortest string that can represent the input, using the format described above.

For example, if the input is 123, then the output would be 67*99*+. The output should be evaluated from left to right.

If there are more than one acceptable outputs (e.g. 99*67*+ is also acceptable), any one can be printed (no bonus for printing all of them).

Further Explanation

If you still do not understand how 67*99*+ evaluates to 123, here is a detailed explanation.

stack    |operation|explanation
          67*99*+
[6]       6         push 6 to stack
[6,7]      7        push 7 to stack
[42]        *       pop two from stack and multiply, then put result to stack
[42,9]       9      push 9 to stack
[42,9,9]      9     push 9 to stack
[42,81]        *    pop two from stack and multiply, then put result to stack
[123]           +   pop two from stack and add, then put result to stack

TL;DR

The program needs to find the shortest string that can represent the input (number), using the format specified above.

Notes

This is a challenge, so the shortest code in bytes wins.

Disambiguation

The - can either be x-y or y-x, at the programmer's discretion. However, the choice must be consistent within the solution. Likewise for the /.

Sample program

Lua, 1862 bytes (try it online)

Since I am the author, I will not golf it at all.

Explanation:

This uses the depth-first search method.

More about depth-first search: here.

The program:

local input = (...) or 81

local function div(a,b)
    if b == 0 then
        return "error"
    end
    local result = a/b
    if result > 0 then
        return math.floor(result)
    else
        return math.ceil(result)
    end
end

local function eval(expr)
    local stack = {}
    for i=1,#expr do
        local c = expr:sub(i,i)
        if c:match('[0-9]') then
            table.insert(stack, tonumber(c))
        else
            local a = table.remove(stack)
            local b = table.remove(stack)
            if a and b then
                if c == '+' then
                    table.insert(stack, a+b)
                elseif c == '-' then
                    table.insert(stack, b-a)
                elseif c == '*' then
                    table.insert(stack, a*b)
                elseif c == '/' then
                    local test = div(b,a)
                    if test == "error" then
                        return -1
                    else
                        table.insert(stack, a+b)
                    end
                end
            else
                return -1
            end
        end
    end
    return table.remove(stack) or -1
end

local samples, temp = {""}, {}

while true do
    temp = {}
    for i=1,#samples do
        local s = samples[i]
        table.insert(temp, s..'0')
        table.insert(temp, s..'1')
        table.insert(temp, s..'2')
        table.insert(temp, s..'3')
        table.insert(temp, s..'4')
        table.insert(temp, s..'5')
        table.insert(temp, s..'6')
        table.insert(temp, s..'7')
        table.insert(temp, s..'8')
        table.insert(temp, s..'9')
        table.insert(temp, s..'+')
        table.insert(temp, s..'-')
        table.insert(temp, s..'*')
        table.insert(temp, s..'/')
    end
    for i=1,#temp do
        if input == eval(temp[i]) then
            print(temp[i])
            return
        end
    end
    samples = temp
end

Bonus

A cake for you if you use Befunge (or any variant of it) to write the code.

Leaky Nun

Posted 2016-03-30T12:48:13.407

Reputation: 45 011

3It may be hard to decide, given an answer, if it always produces the sortest string. One idea would be to generate a large set of say 30--50 numbers and score by the sum of all output string length. However, I'm not sure how to combine that score with code length – Luis Mendo – 2016-03-30T13:42:31.727

@DonMuesli See my sample solution. – Leaky Nun – 2016-03-30T13:48:04.840

What's the maximum possible input? – mbomb007 – 2016-03-30T16:55:05.977

@mbomb007 As large as is possible (just set a reasonable boundary). – Leaky Nun – 2016-03-30T16:57:01.580

@Mego Modified. – Leaky Nun – 2016-03-30T17:31:47.567

4

Subset of this.

– Addison Crump – 2016-03-31T15:34:01.340

@CoolestVeto Well this gets more answer than that (but flag if you want). – Leaky Nun – 2016-03-31T15:42:47.350

@KennyLau Yeah, no, duplicates should never be around. – Addison Crump – 2016-03-31T16:23:06.270

2

Copying over my thoughts from chat: "I thought about it but I'd argue that the subset makes things a lot simpler because 1) no hex, 2) no floats, 3) no duplication and 4) positive only"

– Sp3000 – 2016-03-31T16:28:40.143

1@CoolestVeto this one is different enough to invalidate the old answers. – Rɪᴋᴇʀ – 2016-03-31T17:03:03.363

@EasterlyIrk Both of those answers were invalidated by the old question's rule changes, the question, as it stands, is a subset of the current rules for that question. I think Sp3000's argument is good enough, though. – Addison Crump – 2016-03-31T17:05:02.177

1@CoolestVeto I think the other challenge should be closed as a duplicate of this one. – mbomb007 – 2016-03-31T21:25:52.077

In my opinion, the two challenges are different enough, and eventually the other should be closed. That's why I voted to reopen – edc65 – 2016-03-31T22:06:43.540

As an extra argument that this is not a duplicate: My perl solution is based on the fact that all operators are binary. It wouldn't work if one of the allowed operators is to duplicate the top of the stack – Ton Hospel – 2016-04-01T05:56:00.757

1All numbers below 2^32 can be generated with a string of at most 29 characters (15 values and 14 operators) while forcing no overflow in the intermediate values. The lowest number needing the full 29 characters is 676156706. – Ton Hospel – 2016-05-11T20:08:10.333

Answers

4

Python 2, 278 bytes

My best solution, which everytime gives shortest answer. (but very slow)

def e(c):
 s=[];x,y=s.append,s.pop
 while c:
  d,c=c[0],c[1:]
  if"/"<d<":":x(d)
  else:a,b=y(),y();x(str(eval(b+d+a)))
 return int(y())
def g(v):
 s="0123456789+-*";t=list(s)
 while 1:
  for x in t:
   try:
    if e(x)==v:return x
   except:0
  t=[x+y for x in t for y in s]

Python 2, 437 bytes

This solution is longer, but very fast (not brute force). And I'm quite sure, that it always return shortest possible result.

r=range;l=len;a=input()
def f(n):
 if n<=9:return str(n)
 for d in r(9,1,-1):
  if n%d==0:return f(n/d)+"%d*"%d
 h=sum(map(int,list(str(n))))%9
 return f(n-h)+"%d+"%h
m={x:f(x) for x in r(a*9)}
for b in m:
 if a-b in m and l(m[b])+l(m[a-b])+1<l(m[a]):m[a]=m[a-b]+m[b]+"+"
 if a+b in m and l(m[b])+l(m[a+b])+1<l(m[a]):m[a]=m[a+b]+m[b]+"-"
 if b!=0 and a%b==0 and a/b in m and l(m[b])+l(m[a/b])+1<l(m[a]):m[a]=m[a/b]+m[b]+"*"
print m[a]

pbochniak

Posted 2016-03-30T12:48:13.407

Reputation: 57

2

Welcome to PPCG! Hope you'll have a great time here.

– Leaky Nun – 2016-03-30T14:31:37.497

1@pbochinak I think I might have found a valid one. f(6551) returns 25*8*9*7+9*8+ (13 characters), while 9999***52*- (11 characters) is a better one. Verified with my own eval function above (in the question). – Leaky Nun – 2016-03-30T15:47:34.437

4@pbochniak As the comment before mine points out, this answer is invalid in its current state. It is recommended to temporarily delete it while you're working on a fix (if nothing else, to prevent it from attracting downvotes). – Dennis – 2016-03-30T18:38:17.843

1your while can just be while c: – Ven – 2016-03-31T12:14:13.837

You can use ; to separate assignments to variables (which saves bytes in indented blocks), ven's tip, get rid of whitespace between a symbol and anything else, and t can go. – CalculatorFeline – 2016-03-31T12:49:46.260

4

Perl, 134 133 132 128 bytes

Includes +5 for -Xlp (2 extra because the code contains ')

Run with the target number on STDIN:

perl -Xlp befour.pl <<< 123

befour.pl:

@1{1..9}=1..9;$.+=2,map{for$a(%1){"+-*/"=~s%.%"\$1{\$-=$a$&$_/'$1{$a}$1{$_}$&'=~/^.{$.}\$/}||=\$&"%eegr}}%1until$\=$1{$_}}{

It has no artificial limits and is conceptually somewhat efficient but has terrible run times nevertheless even though I sacrificed a few bytes to speed it up. Generating a length 11 solution (e.g. target number 6551) takes about 5 hours on my system.

Sacrificing 7 more bytes makes the speed somewhat more bearable.

@1{1..9}=1..9;$.+=2,map{for$a(@a){"+-*/"=~s%.%"\$1{\$-=$a$&$_/'$1{$a}$1{$_}$&'=~/^.{$.}\$/}||=\$&"%eegr}}@a=keys%1until$\=$1{$_}}{

17 minutes for a length 11 solution, about 5 hours for a length 13 solution. The first number that needs length 15 is 16622 which takes about 2 days. The first number that needs length 17 is 73319.

Notice that it assumes that division returns an integer by truncating towards 0 (per the befunge 93 specification)

Ton Hospel

Posted 2016-03-30T12:48:13.407

Reputation: 14 114

What do the dollar-signs do? (I don't speak Perl at all) – Leaky Nun – 2016-03-31T14:13:44.060

1@KennyLau $ accesses the scalar value. Where in most languages you would write a=4, perl will use $a=4. But is also used for a scalar access of more complex variables. E.g. $a{$b} fetches from hash (map, dictionary)%a the scalar value keyed on $b – Ton Hospel – 2016-03-31T14:42:40.050

2

C, 550 545 bytes

#define L strlen
#define y strcpy
#define t strcat
char c[9999][99];i=1,k=3;main(j){for(;i<10;i++)*c[i]=i+'0';for(;k--;){
for(i=1;i<9999;i++)for(j=1;j<=i;j++)*c[i]&&*c[j]&&(i+j>9998||*c[i+j]&&
L(c[i+j])<L(c[i])+L(c[j])+2||t(t(y(c[i+j],c[i]),c[j]),"+"),
i*j>9998||*c[i*j]&&L(c[i*j])<L(c[i])+L(c[j])+2||t(t(y(c[i*j],c[i]),c[j]),"*"));
for(i=9999;--i;)for(j=i;--j;)*c[i]&&*c[j]&&(*c[i/j]&&
L(c[i/j])<L(c[i])+L(c[j])+2||t(t(y(c[i/j],c[i]),c[j]),"/"),
*c[i-j]&&L(c[i-j])<L(c[i])+L(c[j])+2||t(t(y(c[i-j],c[i]),c[j]),"-"));}
scanf("%d",&i);printf("%s",c[i]);}

550 545 bytes after deleting the unnecessary newlines (all but the three newlines after the preprocessing directives).

@Kenny Lau - It can receive as input an integer between 1 and 9998, but I think that the range of input for which an optimal solution is computed is smaller than 9998. On the other hand, both ranges can be extended, if the memory allows it.

The program cannot push onto the stack any number higher than 9998. (9998 can be modified.) I ran the program in a different version, iterating over the outer loop (the one with k) for as long as there is improvement for any number between 1 and 9998 (as in Dijkstra's algorithm). After three iterations there is no improvement. So to save bytes, I hardcoded k=3.

To extend the range, two things are necessary - modifying the constants 9999 and 9998, running it with a variable number of iterations over the outter loop for as long as there is improvement, to see how long it takes until no improvement takes place, then modify the constant k=3 to that value.

mIllIbyte

Posted 2016-03-30T12:48:13.407

Reputation: 1 129

Welcome to PPCG! Hope you'll have a great time here.

– Leaky Nun – 2016-03-30T16:37:29.880

This passes my 6551 test perfectly. What is the effective range of this program? – Leaky Nun – 2016-03-30T16:40:35.503

I believe it is 9999. Can you please add this information to your solution? – Leaky Nun – 2016-03-30T16:41:49.440

It should be 9998. Also, you can eat some bytes by initializing i, j, and k before main(). – Leaky Nun – 2016-03-30T16:48:38.697

You can still eat some bytes by using i instead of n. – Leaky Nun – 2016-03-30T16:49:14.060

1@Kenny Lau - Thank you for the edit. About extending the range, I noticed that it actually takes a little more to extend the range. I included that information in the answer. – mIllIbyte – 2016-03-30T17:18:53.723

2

Python 2, 284 bytes

Disclaimer: Takes freaking forever for some values ... but should be guaranteed to always return the shortest string, and has no artificially imposed range limit ... even works on negative values. :)

def f(v):
 i,z=0,'+-*/'
 while 1:
  s=('%x'%i).translate(__import__('string').maketrans('abcd',z),'ef');t=s;q=[];a,p=q.append,q.pop;i+=1
  try:
   while t:
    o,t=t[0],t[1:]
    if o in z:n,m=p(),p();a(eval(`m`+o+`n`))
    else:a(int(o))
   if p()==v and not q:return s
  except:pass

Algorithm:

  • Start with i = 0
  • Take the string representing the hex value of i, and replace abcd with +-*/ respectively, and remove any ef
  • Attempt to process string as postfix notation (RPN)
  • If successful, and result matches input value, return the string used.
  • Otherwise, increment i and try again.

Ken 'Joey' Mosher

Posted 2016-03-30T12:48:13.407

Reputation: 136

"[t]akes [...] forever for some values" Have you tested it? What values? – Leaky Nun – 2016-03-31T13:44:08.390

@KennyLau I just wrote a test that's calculating f(i) from 0 <= i <= 6551 (to capture the 6551 value you used to invalidate @pbochniak 's original submission).

Right now, it's been running for only a few minutes, and here's the last output from the test:

91 : 49+7* 3.020 s (total 108.174 s, worst 89: 5.827 s) Update - it just finished with value 92: 92 : 149+7*+ 258.761 s (total 366.935 s, worst 92: 258.761 s) – Ken 'Joey' Mosher – 2016-03-31T16:47:39.693

@KennyLau : Test has been running over an hour, and only up to value 113 ... see full test output here (pastebin) if you're interested ...

– Ken 'Joey' Mosher – 2016-03-31T17:49:38.473

2

Python 2, 182 bytes

n=input()
L=[[[],""]]
while 1:
 s,c=L.pop(0);L+=[[s+[i],c+`i`]for i in range(10)]+(s[1:]and[[s[:-2]+[eval(`s[-2]`+o+`s[-1]`)],c+o]for o in"/+-*"[s[-1]==0:]])
 if[n]==s[-1:]:print c;E

So obscenely slow, I've left it running for an hour on input 221 and it still hasn't terminated. A great deal of the slowness is because I'm using a list as a queue for a breadth-first search, and .pop(0) is O(n) for lists.

L is just a queue containing (stack, code to reach stack) pairs. At each step, digits are always added, and operators are performed if the stack has at least two elements. Division is only performed if the last element is not 0, although I have a strong suspicion that division is never necessary (although I have no way of proving it, but I have checked this is the case up to 500).

The program terminates via a NameError after printing the result (eventually).

Sp3000

Posted 2016-03-30T12:48:13.407

Reputation: 58 729

What is the ;E at the end doing? – Leaky Nun – 2016-03-31T15:32:56.150

@KennyLau That's the NameError for termination, since E is not defined anywhere else – Sp3000 – 2016-03-31T15:33:25.130

Wow, such cleverness. – Leaky Nun – 2016-03-31T15:38:52.330

1

CJam, 79

ri:M;A,:s:L;{L2m*{s,Y=},{~:A+AS*~!"/+-*">\f{\+}~}%Y2+:Y;_L@+:L;{S*~M=},:R!}gR0=

Try it online

This is horribly inefficient, but given enough memory and time, it eventually works. 123 runs out of memory with 16GB, but 120 and 125 are ok.

aditsu quit because SE is EVIL

Posted 2016-03-30T12:48:13.407

Reputation: 22 326

1

Pyth - 35 bytes

Brute force. A weird thing is that the new implicit input actually hurts my score because it seems to be working for .v pyth_eval also.

.V1IKfqQ.x.v+jd_T\;N^s+"+-*/"UTbhKB

Try it online here.

Maltysen

Posted 2016-03-30T12:48:13.407

Reputation: 25 023

0

Python 3, 183 bytes

e,n=enumerate,input()
v=list('0123456789')+[' '*n]*n*2
for i,s in e(v):
 for j,t in e(v):
  for o,k in zip('+*-',(i+j,i*j,i-j)):
   if 9<k<2*n:v[k]=min(v[k],s+t+o,key=len)
print(v[n])

Speed isn't totally unreasonable (123, 221, 1237, 6551 finish on the order of seconds or minutes). Changing the if statement to if j<=i and <k<2*n speeds it up more, at the cost of 9 more bytes. I left out division (/), because I can't see how it would be needed.

RootTwo

Posted 2016-03-30T12:48:13.407

Reputation: 1

Hint: division is needed. – Leaky Nun – 2016-05-01T05:19:34.367