14
\$\begingroup\$

Background

You have recently been hired by a small accounting firm. The world of accounting is somewhat foreign to you, so you're not sure whether you are following all the professional guidelines. In particular, you don't know when you should round all those numbers, and in which direction, so most of the time you just wing it and hope for the best.

Input

Your input is a single string that represents a simple calculation. It contains some number of nonnegative integers delimited by the characters +-*/. The string reads from left to right, and the normal precedence rules are ignored, so "23+1*3/4" means "start with 23, add 1, multiply by 3, and divide by 4", the result being 18. The input will not contain numbers that start with 0 (except 0 itself), nor a division by zero.

Output

At each stage of the calculation, you can either round the result up or down to the nearest integer, or keep it as it is. Finally, you round either up or down to obtain a integer result. Your output is the list of integers that may result from such a calculation, sorted and without duplicates.

Rules

You can write either a full program or a function. The lowest byte count wins, and standard loopholes are disallowed.

Test Cases

"42" -> [42]
"2+0+4-0" -> [6]
"23+1*3/4" -> [18]
"5/2" -> [2,3]
"5/2+7/3*6-1" -> [17,18,19,23]
"23/2/2*30-170/3" -> [-7,-6,-2,-1,0,1,3,4]
"1/3*2*2*2*2*2*2" -> [0,16,20,21,22,24,32,64]
"1/3*9" -> [0,3,9]
\$\endgroup\$
4
  • \$\begingroup\$ Does the program have to work for all possible inputs (regardless of number size), a limited size input, or only the test cases? \$\endgroup\$
    – orlp
    Commented Apr 7, 2015 at 17:54
  • \$\begingroup\$ @orlp It should work at least when all input numbers and intermediate results are below, say, 10 million in absolute value. The accounting firm is small, after all. \$\endgroup\$
    – Zgarb
    Commented Apr 7, 2015 at 18:01
  • \$\begingroup\$ Take note of the test case 1/3*9, which may fail if you use floating point numbers. \$\endgroup\$
    – Claudiu
    Commented Apr 7, 2015 at 18:28
  • \$\begingroup\$ @Claudiu Thanks, I added it to the challenge. \$\endgroup\$
    – Zgarb
    Commented Apr 7, 2015 at 18:31

6 Answers 6

4
\$\begingroup\$

J, 84 bytes

Starting from a 1 element list the function keeps all the possible intermediate numbers in the list by evaling the next expression and adding it's up and down rounded copies.

Will golf further and add explanation tomorrow. Can't find obvious ways to golf it more.

f=.3 :'/:~~.<.>((,>.,<.)@".@(":@],''x'',;@[))&.>/|.(>@{.;_2<\}.);:y rplc''/'';''%'''

Passes all the tests.

Usage:

   f '1/3*2*2*2*2*2*2'
0 16 20 21 22 24 32 64
   f '1/3*9'
0 3 9

Try it here.

\$\endgroup\$
2
  • \$\begingroup\$ How do you deal with them as rationals instead of floats - is that built in to J? (Complete J noob here) \$\endgroup\$
    – Claudiu
    Commented Apr 8, 2015 at 8:36
  • \$\begingroup\$ @Claudiu At every eval I force extended precision numbers (in this case rationals) by appending the letter x at the end of the list. \$\endgroup\$
    – randomra
    Commented Apr 8, 2015 at 14:38
3
\$\begingroup\$

Python 2, 220 characters

import re,sys,math as m,fractions as f
X=f.Fraction
M=map
F=['+']+re.split("(\D)",sys.argv[1])
r=[X(0)]
while F:U=[eval('f.'+`n`+F[0]+F[1])for n in r];r=M(X,U+M(m.floor,U)+M(m.ceil,U));F=F[2:]
print sorted(set(M(int,r)))

It keeps a list of all possible numbers and at each step, generates three numbers for every number in the list, even if there are duplicates. Thus the run-time complexity is exponential. However, it works instantly for these small examples. Dupes are removed at the end.

It uses fractions.Fraction to do exact division, avoiding floating point inexactitudes.

Add 5 characters (r=map(X,g) --> r=set(map(X,g))) to dramatically increase performance.

\$\endgroup\$
4
  • \$\begingroup\$ Here's an easy golf to start off with: \D is a predefined character class for matching non-digits \$\endgroup\$
    – Sp3000
    Commented Apr 7, 2015 at 18:01
  • \$\begingroup\$ @orlp: Fixed now! (I think..) \$\endgroup\$
    – Claudiu
    Commented Apr 7, 2015 at 18:32
  • \$\begingroup\$ @Claudiu: that should be either r"(\D)" or "(\\D)". Also, if you use Python 3, you can replace the indexing into F with starred assignment, e.g.: A,B,*F=F, use A and B instead of F[0] and F[1], and get rid of F=F[2:]. \$\endgroup\$
    – Mac
    Commented Apr 9, 2015 at 3:00
  • \$\begingroup\$ @Mac: "\D" ends up working anyway and it's shorter. It's not a valid escape sequence so Python just includes the \ and D verbatim. Good Python3 tip actually, I'll check it out, though I'll have to replace backticks with repr() and turn the map result into a list. Starred assignment is something I wish Python 2 had.. \$\endgroup\$
    – Claudiu
    Commented Apr 9, 2015 at 5:10
2
\$\begingroup\$

Python, 421 370 354 bytes

Sorry, please bear with me. I am really new to python (I was just looking for a language that supports fractiosn) and used all of the few tricks I knew for shortening the code but it is still a monster considering that there is a python solution of almost half the size. I learned a lot and thought I'd submit it anyway=)

New Version thanks to @kirbyfan64sos and @Zgarb

from fractions import*
from math import*
import re,operator as z
s=input()
N=re.split(r'[\*\/\-\+]',s)
O=re.split(r'[0-9]+',s)[1:-1]
d={'+':z.add,'-':z.sub,'*':z.mul,'/':z.truediv}
l=[int(N[0])]#list of inters up to now
for i in range(len(O)): #iterate over all operations
    n=set()
    for f in l:
        f=d[O[i]](f,Fraction(int(N[i+1])))
        n.update([floor(f),ceil(f),f])
    l=n
print(set(map(floor,n)))

Old Version

from fractions import Fraction as F
from math import floor,ceil
import re
s=input()
N=re.split(r'[\*\/\-\+]',s)   #Numbers
O=re.split(r'[0-9]+',s)[1:-1] #Operators
l=[int(N[0])]
for i in range(len(O)): #Iterate over all operators
    n=set()
    for f in l:           #Iterate over all possible numbers
        g=F(int(N[i+1]))
        o=O[i]
        if o=='/':
            f/=g
        elif o=='*':
            f*=g
        elif o=='-':
            f-=g
        else:
            f+=g
        n.add(floor(f))  #Add all possible numbers to a new set 
        n.add(ceil(f))   # the 'set' structure prevents from having multiple copies
        n.add(f)         # which is a really nice feature
    l=n                #repeat
print(set([floor(k) for k in n])) #also remove the unrounded ones
\$\endgroup\$
6
  • \$\begingroup\$ For one thing, you could replace some of the space indents with tabs (it generally sucks, but works well in code golf: a tab == 1 character). You can also use a dict instead of several ifs (d={'+': operator.add, '-': operator.sub, ...}; d[op](a, b)). Also, [floor(k) for k in n] can be shortened to map(floor, n), and the n.add calls can become n.extend([floor(f), ceil(f), f]). \$\endgroup\$ Commented Apr 12, 2015 at 21:22
  • \$\begingroup\$ Wow thank you very much, I'm gonna try and implement them! I allready counted the indents as tabs but I had to convert them to spaces here. \$\endgroup\$
    – flawr
    Commented Apr 12, 2015 at 21:31
  • \$\begingroup\$ You can also just use single spaces; they should work. \$\endgroup\$ Commented Apr 12, 2015 at 22:01
  • \$\begingroup\$ As far as I can see, you use F only once, so you could do from fractions import* and save some bytes. Same with math. Remove the spaces around =, they are unnecessary. Also, you should assign the input to s instead of hard-coding. \$\endgroup\$
    – Zgarb
    Commented Apr 13, 2015 at 7:27
  • \$\begingroup\$ @flawr Remove every optional space. Also, you need to be able to accept any input. Use s=input() rather than s = "1/3*9", remove your comments, etc. \$\endgroup\$
    – mbomb007
    Commented Apr 13, 2015 at 21:17
1
\$\begingroup\$

Mathematica, 134

Union@Flatten@{Floor@#,Ceiling@#}&@ToExpression@StringReplace[#,x:("+"|"-"|"*"|"/"~~NumberString):>"//{Floor@#,#,Ceiling@#}"~~x~~"&"]&
\$\endgroup\$
0
\$\begingroup\$

MATLAB, 283 chars

function[u]=w(s)
s=[' ' strsplit(regexprep(s,'\D',' $& '))];s=reshape(s,[2,size(s,2)/2]);o=s(1,:);d=cellfun(@str2num,s(2,:));a=d(1);for n=2:size(o,2)switch o{n}case'+';a=a+d(n);case'-'a=a-d(n);case'/'a=a/d(n);case'*'a=a*d(n);end;a=[ceil(a);a;floor(a)];end;u=unique(a(mod(a,1)==0))end

Ungolfed:

function [u] = WingitRound(i)
    i=[' ' strsplit(regexprep(i,'\D',' $& '))];
    i=reshape(i,[2,size(i,2)/2]);

    o=i(1,:);
    n=cellfun(@str2num,i(2,:));

    a=n(1);

    for n=2:size(o,2)
        switch o{n}
            case '+'
                a = a + n(n);
            case '-'
                a = a - n(n);
            case '/'
                a = a / n(n);
            case '*'
                a = a * n(n);
        end
        a = [ceil(a);a;floor(a)];
    end

    u=unique(a(mod(a,1)==0)));
end

While writing this, I realized that there is an even shorter way of doing this, which I will add once I finish writing it.

\$\endgroup\$
0
\$\begingroup\$

VBA, 347 bytes

Function OoCRE(inp As String)
ct = 0
i = 1
Do While i < Len(inp)
c = Mid(inp, i, 1)
If Not IsNumeric(c) Then
ct = ct + 1
If ct = 2 Then
inp = Round(Application.Evaluate(Left(inp, i - 1))) & Right(inp, Len(inp) - (i - 1))
i = InStr(1, inp, c)
ct = 1
End If
End If
OoCRE = Round(Application.Evaluate(inp))
i = i + 1
Loop
End Function
\$\endgroup\$
1
  • 1
    \$\begingroup\$ There is quite a lot of golfing to be done here, primarily removing superflous whitespace and choosing shorter var names \$\endgroup\$
    – cat
    Commented Dec 8, 2015 at 1:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.