Вне контроля ошибки округления


14

Фон

Вас недавно наняла небольшая бухгалтерская фирма. Мир бухгалтерского учета несколько чужд для вас, поэтому вы не уверены, что следуете всем профессиональным правилам. В частности, вы не знаете, когда вам следует округлять все эти числа и в каком направлении, поэтому большую часть времени вы просто набираете его и надеетесь на лучшее.

вход

Ваш ввод представляет собой одну строку, которая представляет простой расчет. Он содержит некоторое количество неотрицательных целых чисел, разделенных символами +-*/. Строка читается слева направо, и нормальные правила приоритета игнорируются, поэтому "23+1*3/4"означает «начать с 23, добавить 1, умножить на 3 и разделить на 4», в результате получится 18. Ввод не будет содержать чисел, начинающихся с 0(кроме 0себя), ни деление на ноль.

Выход

На каждом этапе вычислений вы можете либо округлить результат вверх или вниз до ближайшего целого числа, либо оставить его как есть. Наконец, вы округляете вверх или вниз, чтобы получить целочисленный результат. Ваш вывод - это список целых чисел, которые могут возникнуть в результате такого расчета, отсортированный и без дубликатов.

правила

Вы можете написать либо полную программу, либо функцию. Побеждает меньшее количество байтов, и стандартные лазейки запрещены.

Тестовые случаи

"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]

Должна ли программа работать для всех возможных входов (независимо от размера числа), ввода ограниченного размера или только для тестовых случаев?
Орл

@orlp Должно работать по крайней мере, когда все входные числа и промежуточные результаты ниже, скажем, 10 миллионов в абсолютном значении. Бухгалтерская фирма маленькая, в конце концов.
Згарб

Запишите тестовый пример 1/3*9, который может не сработать, если вы используете числа с плавающей запятой.
Клавдиу

@Claudiu Спасибо, я добавил это к проблеме.
Згарб

Ответы:


4

J, 84 байта

Начиная со списка из 1 элемента, функция сохраняет все возможные промежуточные числа в списке путем выравнивания следующего выражения и добавления округленных копий вверх и вниз.

Будет ли гольф дальше и добавим объяснения завтра. Не могу найти очевидных способов играть в гольф больше.

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

Проходит все тесты.

Использование:

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

Попробуй это здесь.


Как вы относитесь к ним как к рациональным, а не к поплавкам - это встроено в J? (Полный J noob здесь)
Claudiu

@Claudiu На каждом eval я вынуждаю числа повышенной точности (в данном случае рациональные числа), добавляя букву xв конце списка.
randomra

3

Python 2, 220 символов

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

Он хранит список всех возможных чисел и на каждом шаге генерирует три числа для каждого числа в списке, даже если есть дубликаты. Таким образом, сложность во время выполнения является экспоненциальной. Тем не менее, это работает мгновенно для этих небольших примеров. Dupes удаляются в конце.

Оно использует fractions.Fraction для точного деления, избегая неточностей с плавающей запятой.

Добавьте 5 символов ( r=map(X,g)-> r=set(map(X,g))), чтобы значительно повысить производительность.


Вот простой гольф, с которого можно начать: \Dпредопределенный класс символов для сопоставления с нецифровыми
символами

@orlp: Исправлено сейчас! (Я думаю ..)
Клавдиу

@Claudiu: что должно быть r"(\D)"или "(\\D)". Кроме того, если вы используете Python 3, вы можете заменить индексирование на Fприсвоение со звездочкой, например:, A,B,*F=Fиспользовать Aи Bвместо F[0]и F[1], и избавиться от F=F[2:].
Mac

@Mac: "\D"все равно работает и короче. Это недопустимая escape-последовательность, поэтому Python просто включает \ и Dдословно. Хороший совет Python3, на самом деле, я проверю его, хотя мне придется заменить обратные галочки на repr()и превратить mapрезультат в список. Помеченное назначение - это то, что я хотел бы, чтобы Python 2 имел ..
Claudiu

2

Python, 421 370 354 байта

Извините, пожалуйста, потерпите меня. Я действительно новичок в python (я просто искал язык, который поддерживает fractiosn) и использовал все несколько хитростей, которые я знал, для сокращения кода, но это все еще чудовище, учитывая, что есть решение на python почти вдвое меньшего размера. Я многому научился и думал, что все равно отправлю это =)

Новая версия благодаря @ kirbyfan64sos и @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)))

Старая версия

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

Во-первых, вы можете заменить некоторые отступы пробелом на табуляцию (обычно это отстой, но хорошо работает в кодовом гольфе: символ табуляции == 1). Вы также можете использовать dict вместо нескольких ifs ( d={'+': operator.add, '-': operator.sub, ...}; d[op](a, b)). Кроме того, [floor(k) for k in n]могут быть сокращены до map(floor, n), и n.addзвонки могут стать n.extend([floor(f), ceil(f), f]).
kirbyfan64sos

Ух, большое спасибо, я постараюсь их реализовать! Я уже считал отступы как вкладки, но мне пришлось преобразовать их в пробелы здесь.
flawr

Вы также можете просто использовать одиночные пробелы; они должны работать.
kirbyfan64sos

Насколько я вижу, вы используете Fтолько один раз, так что вы можете сделать from fractions import*и сохранить несколько байтов. То же самое с math. Уберите места вокруг =, они не нужны. Кроме того, вы должны назначить вход sвместо жесткого кодирования.
Згарб

@flawr Удалить все дополнительные пробелы. Кроме того, вы должны быть в состоянии принять любой ввод. Используйте, s=input()а не s = "1/3*9"удаляйте свои комментарии и т. Д.
mbomb007

1

Математика, 134

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

0

MATLAB, 283 символа

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

При написании этого я понял, что есть еще более короткий способ сделать это, и я добавлю его, как только закончу писать.


0

VBA, 347 байт

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

1
Здесь достаточно много игры в гольф, в первую очередь устраняя лишние пробелы и выбирая более короткие имена вар
кошка
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.