Оцініть вирази зі значущими цифрами


10

Давши вираз, ваше завдання - оцінити його. Однак у Вашій відповіді не може бути більше цифр, ніж потрібно, оскільки це створює враження, що Ви маєте більш точні вимірювання, ніж реальність.

Кількість значущих цифр, які має число, - це кількість цифр, якщо вона написана в науковій нотації, включаючи нулі в кінці, якщо є десяткова крапка. Наприклад, 1200має 2 значущі цифри, оскільки є, 1.2*10^3але 1200.має 4 значущі цифри і 1200.0має 5 значущих фігур.

При додаванні двох чисел результат слід округлювати до тієї ж кількості місць, що і число, найменш значущий показник якого знаходиться в лівій частині зліва. Наприклад, 1200 + 3 = 1200(округляється до сотень, оскільки 1200 округляється до сотень) 1200.01 + 3 = 1203, і 4.59 + 2.3 = 6.9. Зверніть увагу, що 5округлює. Це ж правило стосується віднімання. 0округляється до того місця. Зауважте, що додавання та віднімання не залежать від кількості значущих цифр. Наприклад,999 + 2.00 = 1001тому що 999 округлено до місця, а 2,00 - до сотого місця; одне округлене до менших місць дорівнює 999, тому результат, 1001,00, повинен бути також округнутим до тих, що місця. Аналогічно, 300 + 1 - 300 рівно дорівнює 1, але 300 округляється до сотень місця, тому кінцевий результат також слід округлювати до сотень місця, даючи 0. 300. + 1 - 300. було б рівним 1 на З іншого боку.

При множенні чи діленні двох чисел округляйте до числа значущих цифр числа з найменшими значущими цифрами. Наприклад, 3.839*4=20так як точне значення, 15.356, округляється до , 20так як 4має тільки один істотний показник. Аналогічно, 100/4=30оскільки обидва числа мають одну значну цифру, але 100./4.00=25.0оскільки обидва числа мають 3 значущі цифри. 0визначається як 1 значна цифра.

Вирази буде містити тільки *, /, +, і -, (і круглі дужки). Слід дотримуватися порядку операцій, а результати слід округляти після кожної операції. Якщо круглі дужки залишаються в рядку додавання чи віднімання або рядку множення та ділення, то обведіть їх після завершення всіх операцій. Наприклад, 6*0.4*2 = 5(одна значна цифра), а 0.4*(2*6)=0.4*10=4і (6*0.4)*2=2*2=4.

Введення : Рядок з виразом, що містить ()*/+-і цифри. Щоб спростити речі, -буде використовуватися лише як оператор віднімання, а не для позначення негативних чисел; відповіді, однак, все ще можуть бути негативними і вимагатимуть -як префікса.

Виведення : Результат вираження, оцінений і округлений до правильної кількості цифр. Зауважте, що 25це невірно для 25.0.

Тестові приклади :

3 + 0.5 --> 4
25.01 - 0.01 --> 25.00
4*7*3 --> 80
(4*7)*3 --> 90
(8.0 + 0.5)/(2.36 - 0.8 - 0.02) --> 5.7
6.0 + 4.0 --> 10.0
5.0 * 2.0 --> 10.0
1/(2.0 * (3.0 + 5.0)) --> 0.06
0.0020 * 129 --> 0.26
300 + 1 - 300 --> 0
0 - 8.8 --> -9
3*5/2*2 --> 20

Випадок краю: Розгляньте проблему 501*2.0. Точне значення 1002. Друк 1002дає занадто багато значущих цифр (4, коли нам потрібно 2), але 1000дає занадто мало (1, коли нам потрібно 2). У цьому випадку ваша програма 1000все одно повинна надрукуватись .

Це джерело також пояснює значні цифри: http://www.purplemath.com/modules/rounding2.htm


Що ви маєте на увазі під " однаковою кількістю місць "? Це те саме, що " однакова кількість значущих цифр "? Якщо ви хочете випадок краю для того, 999 + 2.00.
Пітер Тейлор

Безумовно 300 + 1 - 300, це рядок додавань і віднімань, тому їх не потрібно округляти до кінця. (300 + 1) - 300було б нульовим.
Ніл

Відповіді:


9

Java 11, 1325 1379 1356 1336 1290 байт

import java.math.*;String c(String s)throws Exception{String r="",T=r,a[],b[],z="\\.";int i=0,l,A[],M=0,m=s.length(),j,f=0,q=m;if(s.contains("(")){for(;i<m;){var c=s.charAt(i++);if(f<1){if(c==40){f=1;continue;}r+=c;}else{if(c==41&T.replaceAll("[^(]","").length()==T.replaceAll("[^)]","").length()){r+="x"+s.substring(i);break;}T+=c;}}return c(r.replace("x",c(T)));}else{for(a=s.split("[\\+\\-\\*/]"),A=new int[l=a.length];i<l;f=b.length>1&&(j=b[1].length())>f?j:f)M=(j=(b=a[i++].split(z))[0].length())>M?j:M;for(b=a.clone(),i=0;i<l;A[i]=b[i].contains(".")?j=b[i].length()-1:b[i].replaceAll("0*$","").length(),i++)for(q=(j=b[i].replace(".","").length())<q?j:q,j=a[i].split(z)[0].length();j++<M;)b[i]=0+b[i];double R=new Double(new javax.script.ScriptEngineManager().getEngineByName("JS").eval(s)+""),p;for(int x:A)m=x<m?x:m;m=m==M&R%1==0&(int)R/10%10<1&(j=(r=R+"").split(z)[0].length())>m?j-q>1?q:j:R>99?m:R%10==0?r.length()-1:m<1?1:m;R=new BigDecimal(R).round(new MathContext((R<0?-R:R)<1?m-1:m)).doubleValue();r=(m<M&(p=Math.pow(10,M-m))/10>R?(int)(R/p)*p:R)+"";l=r.length()-2;r=(r=f<1?r.replaceAll(z+"0$",""):r+"0".repeat(f)).substring(0,(j=r.length())<m?j:r.contains(".")?(j=r.replaceAll("^0\\.0+","").length())<m?m-~j:m+1:m);for(i=r.length();i++<l;)r+=0;return r.replaceAll(z+"$","");}}

+54 байти, щоб виправити крайовий регістр 501*2.0(давав результат 1002раніше, але тепер правильний 1000).

Тепер я розумію , чому цей виклик без відповіді в протягом майже двох років ..>.> Це завдання має більш приватні випадки , ніж на голландською мовою, який говорить що - то ..
Java, звичайно , неправильний мову для такого роду завдань (або будь-який codegolf виклик з цього питання ..; p), але це єдина мова, яку я знаю досить добре, щоб навіть спробувати складний виклик, як це.

Формат введення як Stringбез пробілів (якщо це заборонено, ви можете додати s=s.replace(" ","")(+19 байт) у верхній частині методу).

Спробуйте в Інтернеті.

Пояснення:

Вибачте за довгий пост.

if(s.contains("(")){
  for(;i<m;){
    var c=s.charAt(i++);
    if(f<1){
      if(c==40){
        f=1;
        continue;}
      r+=c;}
    else{
      if(c==41&T.replaceAll("[^(]","").length()==T.replaceAll("[^)]","").length()){
        r+="x"+s.substring(i);
        break;}
      T+=c;}}
  return c(r.replace("x",c(T)));}

Ця частина використовується для введення круглих дужок. Він отримає відокремлені частини та використовувати рекурсивні дзвінки.

  • 0.4*(2*6)стає 0.4*A, де Aрекурсивний дзвінокc(2*6)
  • (8.3*0.02)+(1.*(9*4)+2.2)стає A+B, де Aрекурсивний виклик c(8.3*0.02)і Bрекурсивний виклик c(1.*(9*4)+2.2)→, який, у свою чергу, стає 1.*C+2.2, де Cрекурсивний викликc(9*4)

for(a=s.split("[\\+\\-\\*/]"),A=new int[l=a.length];
    i<l;
    f=b.length>1&&(j=b[1].length())>f?j:f)
  M=(j=(b=a[i++].split(z))[0].length())>M?j:M;

Цей перший цикл використовується для заповнення значень Mі k, де Mнайбільша ціла довжина відносно значущих цифр та kнайбільша десяткова величина .

  • 1200+3.0стає M=2, k=1( 12, .0)
  • 999+2.00стає M=3, k=2( 999, .00)
  • 300.+1-300.стає M=3, k=0( 300, .)

for(b=a.clone(),i=0;
    i<l;
    A[i]=b[i].contains(".")?j=b[i].length()-1:b[i].replaceAll("0*$","").length(),i++)
  for(q=(j=b[i].replace(".","").length())<q?j:q,
      j=a[i].split(z)[0].length();
      j++<M;)
    b[i]=0+b[i];

Цей другий контур використовується для заповнення масивів Aі bтак само , як значення q, де Aце кількість значущих цифр, bпровести цілі числа з провідними нулями , щоб відповідати M, а qнайнижчий довжина без урахування точок.

  • 1200+3.0стає A=[2, 5] (12, 00030), b=[1200, 0003.0]і q=2( 30)
  • 999+2.00стає A=[3, 5] (999, 00200), b=[999, 002.00]і q=3999і 200)
  • 300.+1-300.стає A=[3, 3, 3] (300, 001, 300), b=[300., 001, 300.]і q=1( 1)
  • 501*2.0стає A=[3, 4] (501, 0020), b=[501, 002.0]і q=2( 20)

double R=new Double(new javax.script.ScriptEngineManager().getEngineByName("JS").eval(s)+"")

Використовує двигун JavaScript, щоб оцінити вхід, який буде збережено Rяк подвійний.

  • 1200+3.0 стає R=1203.0
  • 999+2.00 стає R=1001.0
  • 300.+1-300. стає R=1.0

for(int x:A)
  m=x<m?x:m;

Це встановлює mнайменше значення в масиві A.

  • A=[2, 5] стає m=2
  • A=[3, 5] стає m=3
  • A=[3, 3, 3] стає m=3

 m=m==M                // If `m` equals `M`
   &R%1==0             // and `R` has no decimal values (apart from 0)
   &(int)R/10%10<1     // and floor(int(R)/10) modulo-10 is 0
   &(j=(r=R+"").split(z)[0].length())>m?
                       // and the integer-length of R is larger than `m`:
    j-q>1?             //  If this integer-length of `R` minus `q` is 2 or larger:
     q                 //   Set `m` to `q` instead
    :                  //  Else:
     j                 //  Set `m` to this integer-length of `R`
   :R>99?              // Else-if `R` is 100 or larger:
    m                  //  Leave `m` the same
   :R%10==0?           // Else-if `R` modulo-10 is exactly 0:
    r.length()-1       //  Set `m` to the total length of `R` (minus the dot)
   :m<1?               // Else-if `m` is 0:
    1                  //  Set `m` to 1
   :                   // Else:
    m;                 //  Leave `m` the same

Це змінюється mна основі декількох факторів.

  • 999+2.00 = 1001.0& m=3,q=3стає m=4(тому що m==M(обидва 3) → R%1==0( 1001.0не має десяткових значень) → (int)R/10%10<1( (int)1001.0/10стає 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q<=1( 4-3<=1) → так mстає довжина цілочисельної частини "1001"( 4))
  • 3.839*4 = 15.356& m=1,q=1залишається m=1(тому що m==M(обидва 1) → R%1!=0( 15.356має десяткові значення) → R<=99R%10!=0( 15.356%10==5.356) → m!=0→ так mзалишається однаковим ( 1))
  • 4*7*3 = 84.0& m=1,q=1залишається m=1(тому що m==M(обидва 1) → R%1==0( 84.0не має десяткових значень) → (int)R/10%10>=1( (int)84/10стає 88%10>=1) → R<=99R%10!=0( 84%10==4) → m!=0→ так mзалишається однаковим ( 1))
  • 6.0+4.0 = 10.0& m=2,q=2стає m=3(тому що m!=M( m=2, M=1) → R<=99R%10==0( 10%10==0) → так mстає довжина загальної R(мінус крапка) "10.0".length()-1( 3))
  • 0-8.8 = -8.8& m=0,q=1стає m=1(тому що m!=M( m=0, M=1) → R<=99R%10!=0( -8.8%10==-8.8) → m<1→ так mстає 1)
  • 501*2.0 = 1001.0& m=3,q=2стає m=2(тому що m==M(обидва 3) → R%1==0( 1001.0не має десяткових значень) → (int)R/10%10<1( (int)1001.0/10стає 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q>1( 4-2>1) → так mстає q( 2)

R=new BigDecimal(R).round(new MathContext((R<0?-R:R)<1?m-1:m)).doubleValue();

Тепер Rокругляється на основі m.

  • 1001.0& m=4стає1001.0
  • 0.258& m=3Стає 0.26(бо abs(R)<1, m-1( 2) замість того , m=3використовується всередині MathContext)
  • -8.8& m=1стає-9.0
  • 1002.0& m=2стає1000.0

m<M&(p=Math.pow(10,M-m))/10>R?(int)(R/p)*p:R;

Це змінює цілу частину, Rякщо це необхідно.

  • 300.+1-300. = 1.0& m=3,M=3залишається 1.0(тому що m>=M→ так Rзалишається тим самим ( 1.0))
  • 0.4*10 = 4.0& m=1,M=2залишається 4.0(тому що m<M(10^(M-m))/10<=R( (10^1)/10<=4.010/10<=4.01<=4.0) → так Rзалишається тим самим ( 4.0))
  • 300+1-300 = 1.0& m=1,M=3стає 0.0(тому що m<M(10^(M-m))/10>R( (10^2)/10>1.0100/10>1.010>1.0) → так Rстає 0.0через int(R/(10^(M-m)))*(10^(M-m))( int(1.0/(10^2))*(10^2)int(1.0/100)*1000*1000)

r=(...)+"";                  // Set `R` to `r` as String (... is the part explained above)
l=r.length()-2;              // Set `l` to the length of `R` minus 2
r=(r=k<1?                    // If `k` is 0 (no decimal values in any of the input-numbers)
      r.replaceAll(z+"0$","")
                             //  Remove the `.0` at the end
     :                       // Else:
      r+"0".repeat(f)
                             //  Append `k` zeroes after the current `r`
  ).substring(0,             // Then take the substring from index `0` to:
     (j=r.length())<m?       //  If the total length of `r` is below `m`:
       j                     //   Leave `r` the same
     :r.contains(".")?       //  Else-if `r` contains a dot
       (j=r.replaceAll("^0\\.0+","").length())<m?
                             //   And `R` is a decimal below 1,
                             //   and its rightmost decimal length is smaller than `m`
        m-~j                 //    Take the substring from index 0 to `m+j+1`
                             //    where `j` is this rightmost decimal length
       :                     //   Else:
        m+1                  //    Take the substring from index 0 to `m+1`
     :                       //  Else:
      m);                    //   Take the substring from index 0 to `m`

Це встановлює Rв rякості рядка і модифікує його на основі декількох факторів.

  • 1203.0& m=4,k=2стає 1203.(тому що k>=1→ так rстає 1001.000; r.length()>=m( 8>=4) → r.contains(".")r.length()>=m( 8>=4) → підрядка від індексу 0до m+1( 5))
  • 6.9& m=2,k=2залишається 6.9(тому що k>=1→ так rстає 6.900; r.length()>=m( 5>=2) → r.contains(".")r.length()>=m( 5>=2) → підрядка від індексу 0до m+1( 3))
  • 1.0& m=3,k=0стає 1(тому що k<1→ так rстає 1; r.length()<m( 1<3) → підрядка від індексу 0до r.length()( 1))
  • 25.0& m=4,k=4стає 25.00(тому що k>=1→ так rстає 25.00000; r.length()>=m( 8>=4) → r.contains(".")r.length()>+m( 8>=4) → підрядка від індексу 0до m+1( 5))
  • 0& m=1,k=0залишається 0(тому що k<1→ так rзалишається 0; r.length()>=m( 1>=1) → !r.contains(".")→ підрядка від індексу 0до m( 1))

for(i=r.length();i++<l;)
  r+=0;

Це ставить нульові нулі знову до цілої частини, якщо це необхідно.

  • r="12"& R=1200.0стаєr="1200"
  • r="1"& R=10.0стаєr="10"
  • r="8"& R=80.0стаєr="80"

return r.replaceAll(z+"$","");

І, нарешті, ми повертаємо результат, після того як ми видалили всі краплі точки.

  • 1203. стає 1203
  • 5. стає 5

Однозначно можна пограти в гольф на пару сотень байтів, але я просто радий, що це працює зараз. Щоб зрозуміти кожен із випадків і те, що просили у виклику, минуло певний час. А потім знадобилося багато спроб та помилок, тестування та повторне тестування, щоб досягти результату вище. І написавши це пояснення вище, я зміг видалити ще ± 50 байт невикористаного коду.


1
Отримано. Але, схоже, специфікація вимагає цього 501*2.0для виведення 10001000 будь-якому випадку ви повинні виводити , що я трактую як "все-таки", не в будь-якому випадку ). Чудова робота все одно.
Вейджун Чжоу

1
@WeijunZhou Дякуємо за відгук! Я знову задумався над цим і зміг виправити крайню справу, не порушуючи жодної іншої справи. :)
Кевін Круїссен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.