有効数字で式を評価する


10

式が与えられたら、あなたの仕事はそれを評価することです。ただし、実際よりも正確な測定値があるという印象を与えるため、答えに必要以上の桁を表示することはできません。

数値が持つ有効数字の数は、小数点が存在する場合は末尾のゼロを含めて、科学表記法で記述した場合の桁数です。たとえば、に1200は2つの有効数字1.2*10^3があり1200.ますが、4つの有効数字と1200.05つの有効数字があるためです。

2つの数値を加算する場合、結果は、最下位桁が左端にある数値と同じ桁数に丸められる必要があります。たとえば、1200 + 3 = 1200(1200は100の位に丸められるため、100の位に丸められます)、、1200.01 + 3 = 1203および4.59 + 2.3 = 6.9。注5切り上げられます。これと同じルールが減算にも適用されます。01の位に丸められます。加算と減算は有効桁数に依存しないことに注意してください。例えば、999 + 2.00 = 1001999は1の位に丸められ、2.00は100の位に丸められるため。より少ない数に丸められたものは999なので、結果の1001.00も1の位に丸められる必要があります。同様に、300 + 1-300は正確に1に等しくなりますが、300は100の位に丸められるため、最終結果も100の位に丸められ、0になります。300。+ 1-300.は、一方。

2つの数値を乗算または除算する場合は、最下位桁のある数値の有効桁数に丸めます。たとえば3.839*4=20、正確な値は15.356、有効数字が1つしかない20ため、丸められ4ます。同様に100/4=30、両方の数値には1つの有効数字があるが100./4.00=25.0、両方の数値には3つの有効数字があるため。0有効桁数が1と定義されています。

式だけ含まれています*/+、および-、(括弧)。演算の順序に従い、すべての演算の後に結果を丸める必要があります。加算または減算の文字列、または乗算および除算の文字列で括弧が省略されている場合は、すべての演算が完了した後に丸めます。たとえば、6*0.4*2 = 5(有効数字1つ)、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は、有効数字が多すぎます(4、2 が必要な場合)が1000、少なすぎます(1、2が必要な場合)。この場合、プログラムは1000とにかく印刷する必要があります。

このソースは、有効数字についても説明しています:http : //www.purplemath.com/modules/rounding2.htm


同じ数の場所」とはどういう意味ですか?それは「同数の有効数字と同じですか?あなたは追加のためのエッジケースをしたい場合は、999 + 2.00
Peter Taylor

確か300 + 1 - 300に足し算と引き算の文字列なので、最後まで丸める必要はありません。(300 + 1) - 300ゼロになります。
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.01002以前は結果が出たが、現在は正しい1000)。

この課題は、ほぼ2年間、未回答した理由私は今...>理解しています。>この課題は何かを言っているオランダ語、より多くの特殊なケースを持っている...
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=112, .0
  • 999+2.00になるM=3, k=2999, .00
  • 300.+1-300.になるM=3, k=0300, .

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

この第二のループは、アレイを充填するために使用されるAb同様に値としてqA有効数字の量は、b一致するように先行ゼロを持つ整数を保持しM、そしてqドットを無視最小長さです。

  • 1200+3.0となりA=[2, 5] (12, 00030)b=[1200, 0003.0]q=230
  • 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=11
  • 501*2.0となりA=[3, 4] (501, 0020)b=[501, 002.0]q=220

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

JavaScriptエンジンを使用して入力を評価します。入力はRdoubleとして保存されます。

  • 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.0m=3,q=3m=4m==M(両方3)→ R%1==01001.010進値がないため)→ (int)R/10%10<1(に(int)1001.0/10なる100100%10<1)→ "1001".length()>m4>3)→ "1001".length()-q<=14-3<=1)→にmなるため、整数部"1001"4)の長さになります)
  • 3.839*4 = 15.356m=1,q=1滞在m=1(理由はm==M(両方1)→ R%1!=015.356小数点以下の値を持つ)→ R<=99R%10!=015.356%10==5.356)→ m!=0→のでm(滞在同じ1))
  • 4*7*3 = 84.0m=1,q=1滞在m=1(理由はm==M(両方1)→ R%1==084.0何十進値を持っていない)→ (int)R/10%10>=1(int)84/10となり88%10>=1)→ R<=99R%10!=084%10==4)→ m!=0→ので、m滞在同じ(1))
  • 6.0+4.0 = 10.0m=2,q=2なるm=3(なぜならm!=Mm=2, M=1)→ R<=99R%10==010%10==0)→そうm合計の長さとなるR(マイナスドット)"10.0".length()-13))
  • 0-8.8 = -8.8&にm=0,q=1なるm=1m!=Mm=0, M=1R<=99→→ R%10!=0-8.8%10==-8.8m<1→→そうmなるため1
  • 501*2.0 = 1001.0m=3,q=2m=2m==M(両方3)→ R%1==01001.010進数値がないため)→ (int)R/10%10<1(に(int)1001.0/10なる100100%10<1)→ "1001".length()>m4>3)→ "1001".length()-q>14-2>1)→そうmなるためq2)になる)

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

現在、にR基づいて丸められていmます。

  • 1001.0m=4なる1001.0
  • 0.258m=30.26(内で使用されるのではなくabs(R)<1m-12)でm=3あるためMathContext
  • -8.8m=1なる-9.0
  • 1002.0m=2なる1000.0

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

これによりR、必要に応じて整数部が変更されます。

  • 300.+1-300. = 1.0m=3,M=3滞在1.0m>=M→だからR同じまま(1.0)なので)
  • 0.4*10 = 4.0m=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`

これは文字列として設定Rrれ、複数の要素に基づいて変更されます。

  • 1203.0m=4,k=21203.k>=1→にrなるため1001.000r.length()>=m8>=4r.contains(".")→→ r.length()>=m8>=4)→インデックス0からm+15)へのサブストリング)
  • 6.9m=2,k=2stays 6.9k>=1→soにrなる6.900; r.length()>=m5>=2r.contains(".")→→ r.length()>=m5>=2)→インデックス0からm+13)への部分文字列)
  • 1.0m=3,k=01k<1→にrなるため1、; r.length()<m1<3)→インデックス0からr.length()1)への部分文字列)
  • 25.0m=4,k=425.00k>=1→にrなるため25.00000r.length()>=m8>=4r.contains(".")→→ r.length()>+m8>=4)→インデックス0からm+15)へのサブストリング)
  • 0m=1,k=0滞在0k<1→だからr滞在0; r.length()>=m1>=1)→→ !r.contains(".")インデックス0からm1)への部分文字列)

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 とにかく出力する必要があります。これはどちらにせよ「静止」と解釈します)。とにかく壮大な仕事。
Weijun Zhou

1
@WeijunZhouフィードバックをありがとう!もう一度考えてみたところ、他のケースを壊すことなく、エッジケースを修正することができました。:)
Kevin Cruijssen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.