Erros de arredondamento fora de controle

14

fundo

Você foi contratado recentemente por uma pequena empresa de contabilidade. O mundo da contabilidade é um pouco estranho para você, então você não tem certeza se está seguindo todas as diretrizes profissionais. Em particular, você não sabe quando deve arredondar todos esses números e em que direção; portanto, na maioria das vezes, você apenas voa e espera o melhor.

Entrada

Sua entrada é uma única sequência que representa um cálculo simples. Ele contém um número de números inteiros não negativos delimitados pelos caracteres +-*/. A sequência lê da esquerda para a direita e as regras de precedência normais são ignoradas, portanto "23+1*3/4"significa "começar com 23, adicionar 1, multiplicar por 3 e dividir por 4", o resultado sendo 18. A entrada não conterá números que começam com 0(exceto 0ele próprio), nem uma divisão por zero.

Resultado

Em cada estágio do cálculo, você pode arredondar o resultado para cima ou para baixo até o número inteiro mais próximo ou mantê-lo como está. Por fim, você arredonda para cima ou para baixo para obter um resultado inteiro. Sua saída é a lista de números inteiros que podem resultar desse cálculo, classificados e sem duplicatas.

Regras

Você pode escrever um programa completo ou uma função. A contagem de bytes mais baixa vence e as brechas padrão não são permitidas.

Casos de teste

"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
fonte
O programa precisa trabalhar para todas as entradas possíveis (independentemente do tamanho do número), uma entrada de tamanho limitado ou apenas para os casos de teste?
orlp
@orlp Deverá funcionar pelo menos quando todos os números de entrada e resultados intermediários estiverem abaixo, digamos, de 10 milhões em valor absoluto. A empresa de contabilidade é pequena, afinal.
Zgarb 07/04
Anote o caso de teste 1/3*9, que pode falhar se você usar números de ponto flutuante.
Claudiu
@ Claudiu Obrigado, eu adicionei ao desafio.
Zgarb

Respostas:

4

J, 84 bytes

A partir de uma lista de 1 elemento, a função mantém todos os números intermediários possíveis na lista, avaliando a próxima expressão e adicionando cópias arredondadas para cima e para baixo.

Golf ainda mais e adicionar explicação amanhã. Não é possível encontrar maneiras óbvias de jogar mais.

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

Passa em todos os testes.

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

Experimente aqui.

randomra
fonte
Como você lida com eles como racionais, em vez de carros alegóricos - isso é incorporado ao J? (J noob completo aqui)
Claudiu
@ Claudiu A cada avaliação, forço números de precisão estendidos (neste caso, racionais), acrescentando a letra xno final da 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)))

Ele mantém uma lista de todos os números possíveis e, a cada etapa, gera três números para cada número na lista, mesmo que haja duplicatas. Portanto, a complexidade do tempo de execução é exponencial. No entanto, ele funciona instantaneamente para esses pequenos exemplos. Dupes são removidos no final.

Ele costuma fractions.Fractionfazer a divisão exata, evitando inexatitudes de ponto flutuante.

Adicione 5 caracteres ( r=map(X,g)-> r=set(map(X,g))) para aumentar drasticamente o desempenho.

Claudiu
fonte
Aqui está um golf fácil para começar com: \Dé uma classe de caracteres predefinidos para combinar os não-dígitos
SP3000
@orlp: Corrigido agora! (Eu acho que ..) #
1174 Claudiu
@ Claudiu: que deve ser r"(\D)"ou "(\\D)". Além disso, se você usa o Python 3, pode substituir a indexação Fpor uma atribuição com estrela, por exemplo:: A,B,*F=Fuse Ae em Bvez de F[0]e F[1], e se livre de F=F[2:].
Mac
@ Mac: "\D"acaba funcionando de qualquer maneira e é mais curto. Não é uma sequência de escape válida, portanto, o Python inclui apenas o \ e Dliteralmente. Boa dica do Python3, na verdade, vou dar uma olhada, apesar de precisar substituir os backticks repr()e transformar o mapresultado em uma lista. Atribuição Com estrela é algo que eu desejo Python 2 teve ..
Claudiu
2

Python, 421 370 354 bytes

Desculpe, por favor, tenha paciência comigo. Eu sou realmente novo em python (eu estava apenas procurando por uma linguagem que suporte o fractiosn) e usei todos os poucos truques que conhecia para encurtar o código, mas ainda é um monstro, considerando que existe uma solução em python de quase metade do tamanho. Aprendi muito e pensei em enviá-lo de qualquer maneira =)

Nova versão graças a @ kirbyfan64sos e @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)))

Versão antiga

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
flawr
fonte
Por um lado, você pode substituir alguns dos recuos do espaço por tabulações (geralmente é uma porcaria, mas funciona bem no código golf: um tab == 1 caractere). Você também pode usar um ditado em vez de vários ifs ( d={'+': operator.add, '-': operator.sub, ...}; d[op](a, b)). Além disso, [floor(k) for k in n]pode ser reduzido para map(floor, n)e as n.addchamadas podem se tornar n.extend([floor(f), ceil(f), f]).
precisa saber é o seguinte
Uau, muito obrigado, vou tentar implementá-los! Eu já contei os recuos como guias, mas tive que convertê-los em espaços aqui.
flawr
Você também pode usar apenas espaços únicos; eles deveriam trabalhar.
precisa saber é o seguinte
Tanto quanto eu posso ver, você usa Fapenas uma vez, para poder fazer from fractions import*e salvar alguns bytes. O mesmo com math. Remova os espaços ao redor =, eles são desnecessários. Além disso, você deve atribuir a entrada ao sinvés de codificação.
Zgarb 13/04
@flawr Remova todos os espaços opcionais. Além disso, você precisa aceitar qualquer entrada. Use s=input()em vez de s = "1/3*9", retire seus comentários, etc
mbomb007
1

Mathematica, 134

Union@Flatten@{Floor@#,Ceiling@#}&@ToExpression@StringReplace[#,x:("+"|"-"|"*"|"/"~~NumberString):>"//{Floor@#,#,Ceiling@#}"~~x~~"&"]&
alefalpha
fonte
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

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

Enquanto escrevia isso, percebi que havia uma maneira ainda mais curta de fazer isso, que acrescentarei assim que terminar de escrevê-lo.

AJMansfield
fonte
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
fonte
1
Há um monte de golfe a ser feito aqui, removendo principalmente os espaços em branco superflous e escolhendo mais curtos var nomes
gato