유의미한 수치로 표현 평가


10

식이 주어지면 작업을 평가해야합니다. 그러나 실제보다 정확한 측정 결과를 얻을 수 있으므로 답에 필요한 것보다 더 많은 자릿수를 표시 할 수 없습니다.

숫자가 갖는 유효 숫자의 수는 과학적 표기법으로 쓸 때 소수점이있는 경우 끝에 0을 포함하여 보유한 자릿수입니다. 예를 들어, 1200유효 숫자 는 4 1.2*10^3이지만 1200.유효 숫자 1200.0는 5 이므로 유효 숫자가 2 입니다.

두 개의 숫자를 추가 할 때 결과는 가장 작은 자릿수가 가장 왼쪽에있는 숫자와 같은 수로 반올림되어야합니다. 예를 들어, 1200 + 3 = 1200(백의 자리 반올림 1200 이후 백의 자리 반올림), 1200.01 + 3 = 12034.59 + 2.3 = 6.9. 참고 5라운드를. 동일한 규칙이 빼기에 적용됩니다. 0그 자리에 반올림됩니다. 더하기와 빼기는 유효 자릿수에 의존하지 않습니다. 예를 들어999 + 2.00 = 1001999는 한 자리로 반올림되고 2.00은 백 자리로 반올림되기 때문입니다. 소수의 자리로 반올림 한 숫자는 999이므로 결과 1001.00도 같은 자리로 반올림해야합니다. 마찬가지로 300 + 1-300은 정확히 1과 같지만 300은 수백 자리로 반올림되므로 최종 결과도 수백 자리로 반올림되어 0. 300. + 1-300이됩니다. 반면.

두 숫자를 곱하거나 나누는 경우 유효 숫자가 가장 적은 숫자의 유효 숫자로 반올림하십시오. 예를 들어 3.839*4=20정확한 값인 15.356으로 반올림 20되므로 4중요한 숫자가 하나만 나타납니다. 마찬가지로 100/4=30두 숫자 모두 하나의 유효 숫자를 갖지만 100./4.00=25.0두 숫자 모두 3 개의 유효 숫자를 갖기 때문입니다. 01 개의 유효 숫자를 갖도록 정의됩니다.

표현 만 포함 *, /, +,와 -, (괄호). 작업 순서를 따라야하며 모든 작업 후에 결과가 반올림되어야합니다 . 괄호가 덧셈 또는 뺄셈의 문자열이나 곱셈과 나눗셈의 문자열에 빠져 있다면, 모든 연산이 완료된 후 둥글게됩니다. 예를 들어, 6*0.4*2 = 5(한 가지 중요한 수치) while 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하면 중요한 수치가 너무 많지만 (2가 필요할 때는 4), 1000너무 적습니다 (1은 2가 필요할 때). 이 경우 1000어쨌든 프로그램이 인쇄되어야 합니다.

이 소스는 유효 숫자도 설명합니다 : http://www.purplemath.com/modules/rounding2.htm


" 같은 장소 수 "란 무엇을 의미 합니까? " 같은 유효 숫자 " 와 같은 가요? 엣지 케이스를 추가하려면 999 + 2.00.
피터 테일러

확실히 300 + 1 - 300덧셈과 뺄셈의 문자열이므로 끝날 때까지 반올림 할 필요가 없습니다. (300 + 1) - 3000이 될 것입니다.
Neil

답변:


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

이 문제는 거의 2 년 ..>.>이 무언가를 말하고있는 네덜란드의 언어보다 더 특별한 경우가 있습니다 .. 도전에 대한 답이 왜 지금은 이해
자바는 확실히 도전의이 종류에 적합한 언어가 아닙니다 (또는 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.0M=2, k=1( 12, .0) 가된다
  • 999+2.00M=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일치하는 선행 0으로 정수를 유지 Mq점을 무시 최저 길이이다.

  • 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=3(둘 999200)
  • 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=3m=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=1stays m=1( m==M(둘 다 1) → R%1!=0( 15.356십진수 값을 가짐) → R<=99R%10!=0( 15.356%10==5.356) → m!=0m그대로 유지 ( 1))
  • 4*7*3 = 84.0& m=1,q=1stays 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!=0m그대로 유지 ( 1)
  • 6.0+4.0 = 10.0& m=2,q=2m=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.0m=3,q=2된다 m=2(하기 때문에 m==M(모두 3) → R%1==0( 1001.010 진값이 없다) → (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.258m=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>=MR동일 하게 유지 되므로 ( 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.0m=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.0m=4,k=2된다 1203.(있기 k>=1때문에 → r된다 1001.000; r.length()>=m( 8>=4) → r.contains(".")r.length()>=m( 8>=4) → 지수로부터 서브 스트링 0m+1( 5))
  • 6.9m=2,k=2숙박 6.9(때문에 k>=1→ 그렇게 r된다 6.900, r.length()>=m( 5>=2) → r.contains(".")r.length()>=m( 5>=2) → 인덱스에서 하위 문자열 0m+1( 3))
  • 1.0m=3,k=0된다 1(있기 k<1때문에 → r된다 1; r.length()<m( 1<3) → 지수로부터 서브 스트링 0r.length()( 1))
  • 25.0m=4,k=4된다 25.00(있기 k>=1때문에 → r된다 25.00000; r.length()>=m( 8>=4) → r.contains(".")r.length()>+m( 8>=4) → 지수로부터 서브 스트링 0m+1( 5))
  • 0& m=1,k=0stays 0( k<1r유지되므로 0; r.length()>=m( 1>=1) → !r.contains(".")→ 인덱스 0에서 m( 1) 까지 하위 문자열 )

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

필요한 경우 후행 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출력 1000(당신이 출력해야 1000 어쨌든 , 나는 "아직",하지로 해석하는 어느 쪽이든 ). 어쨌든 훌륭한 작품.
Weijun Zhou

1
@WeijunZhou 피드백에 감사드립니다! 나는 그것에 대해 다시 한 번 생각하고 다른 경우를 중단하지 않고 엣지 케이스를 수정할 수있었습니다. :)
Kevin Cruijssen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.