Errores de redondeo fuera de control

14

Antecedentes

Recientemente ha sido contratado por una pequeña empresa de contabilidad. El mundo de la contabilidad es algo extraño para usted, por lo que no está seguro de seguir todas las pautas profesionales. En particular, no sabes cuándo debes redondear todos esos números y en qué dirección, por lo que la mayoría de las veces simplemente lo alejas y esperas lo mejor.

Entrada

Su entrada es una sola cadena que representa un cálculo simple. Contiene cierto número de enteros no negativos delimitados por los caracteres +-*/. La cadena se lee de izquierda a derecha y se ignoran las reglas de precedencia normales, por lo que "23+1*3/4"significa "comenzar con 23, sumar 1, multiplicar por 3 y dividir por 4", el resultado es 18. La entrada no contendrá números que comiencen con 0(excepto en 0sí mismo), ni una división por cero.

Salida

En cada etapa del cálculo, puede redondear el resultado hacia arriba o hacia abajo al entero más cercano o mantenerlo como está. Finalmente, redondeas hacia arriba o hacia abajo para obtener un resultado entero. Su salida es la lista de enteros que pueden resultar de dicho cálculo, ordenados y sin duplicados.

Reglas

Puede escribir un programa completo o una función. El conteo de bytes más bajo gana, y las lagunas estándar no se permiten.

Casos de prueba

"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]
Zgarb
fuente
¿El programa tiene que funcionar para todas las entradas posibles (independientemente del tamaño del número), una entrada de tamaño limitado o solo los casos de prueba?
orlp
@orlp Debería funcionar al menos cuando todos los números de entrada y los resultados intermedios están por debajo de, digamos, 10 millones en valor absoluto. La empresa de contabilidad es pequeña, después de todo.
Zgarb
Tome nota del caso de prueba 1/3*9, que puede fallar si usa números de coma flotante.
Claudiu
@Claudiu Gracias, lo agregué al desafío.
Zgarb

Respuestas:

4

J 84 bytes

Comenzando desde una lista de 1 elemento, la función mantiene todos los números intermedios posibles en la lista evaluando la siguiente expresión y agregando sus copias redondeadas hacia arriba y hacia abajo.

Jugará más al golf y agregará una explicación mañana. No puedo encontrar formas obvias de jugar más al golf.

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

Pasa todas las pruebas.

Uso:

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

Pruébalo aquí.

randomra
fuente
¿Cómo los manejas como racionales en lugar de flotadores? ¿Está eso integrado en J? (Complete J noob aquí)
Claudiu
@Claudiu En cada evaluación forzo números de precisión extendidos (en este caso, racionales) agregando la letra xal final de la lista.
randomra
3

Python 2, 220 caracteres

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)))

Mantiene una lista de todos los números posibles y, en cada paso, genera tres números para cada número de la lista, incluso si hay duplicados. Por lo tanto, la complejidad del tiempo de ejecución es exponencial. Sin embargo, funciona instantáneamente para estos pequeños ejemplos. Los engaños se eliminan al final.

Se utiliza fractions.Fractionpara hacer una división exacta, evitando inexactitudes de coma flotante.

Agregue 5 caracteres ( r=map(X,g)-> r=set(map(X,g))) para aumentar drásticamente el rendimiento.

Claudiu
fuente
Aquí hay un campo de golf fácil para comenzar: \Des una clase de caracteres predefinida para emparejar sin dígitos
Sp3000
@orlp: arreglado ahora! (Creo que ..)
Claudiu
@Claudiu: eso debería ser r"(\D)"o "(\\D)". Además, si usa Python 3, puede reemplazar la indexación Fcon asignación destacada, por ejemplo:, A,B,*F=Fuse Ay en Blugar de F[0]y F[1], y elimine F=F[2:].
Mac
@ Mac: "\D"termina trabajando de todos modos y es más corto. No es una secuencia de escape válida, por lo que Python solo incluye el \ y Dtextualmente. Buen consejo de Python3 en realidad, lo comprobaré, aunque tendré que reemplazar los backticks repr()y convertir el mapresultado en una lista. La asignación destacada es algo que desearía que Python 2 tuviera ...
Claudiu
2

Python, 421 370 354 bytes

Lo siento, por favor tengan paciencia conmigo. Soy realmente nuevo en Python (solo estaba buscando un lenguaje que admita fractiosn) y utilicé todos los pocos trucos que conocía para acortar el código, pero todavía es un monstruo considerando que hay una solución de Python de casi la mitad del tamaño. Aprendí mucho y pensé en enviarlo de todos modos =)

Nueva versión gracias a @ kirbyfan64sos y @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)))

Versión antigua

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
falla
fuente
Por un lado, podría reemplazar algunas de las sangrías de espacio con pestañas (generalmente apesta, pero funciona bien en el código de golf: una pestaña == 1 carácter). También puede usar un dict en lugar de varios ifs ( d={'+': operator.add, '-': operator.sub, ...}; d[op](a, b)). Además, [floor(k) for k in n]puede acortarse map(floor, n)y las n.addllamadas pueden hacerse n.extend([floor(f), ceil(f), f]).
kirbyfan64sos
Wow muchas gracias, voy a tratar de implementarlos! Ya contaba las sangrías como pestañas, pero tuve que convertirlas en espacios aquí.
flawr
También puede usar espacios individuales; Deberían funcionar.
kirbyfan64sos
Por lo que puedo ver, Fsolo se usa una vez, por lo que podría hacer from fractions import*y guardar algunos bytes. Lo mismo con math. Elimina los espacios alrededor =, son innecesarios. Además, debe asignar la entrada a en slugar de la codificación rígida.
Zgarb
@flawr Eliminar todos los espacios opcionales. Además, debe poder aceptar cualquier entrada. Use en s=input()lugar de s = "1/3*9", elimine sus comentarios, etc.
mbomb007
1

Mathematica, 134

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

MATLAB, 283 caracteres

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

Sin golf:

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

Mientras escribía esto, me di cuenta de que hay una forma aún más corta de hacerlo, que agregaré una vez que termine de escribirlo.

AJMansfield
fuente
0

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
Alex
fuente
1
Aquí hay mucho golf por hacer, principalmente eliminando espacios en blanco superflores y eligiendo nombres de var más cortos
gato