Bewerten Sie Ausdrücke mit signifikanten Zahlen

10

Bei einem gegebenen Ausdruck besteht Ihre Aufgabe darin, ihn zu bewerten. Ihre Antwort kann jedoch nicht mehr Ziffern als nötig anzeigen, da dies den Eindruck erweckt, dass die Messungen präziser sind als die Realität.

Die Anzahl der signifikanten Ziffern einer Zahl gibt an, wie viele Ziffern sie in wissenschaftlicher Notation hat, einschließlich Nullen am Ende, wenn ein Dezimalpunkt vorhanden ist. Zum Beispiel 1200hat 2 signifikante Zahlen, weil es 1.2*10^3aber 1200.4 signifikante Zahlen und 1200.05 signifikante Zahlen hat.

Wenn Sie zwei Zahlen hinzufügen, sollte das Ergebnis auf die gleiche Anzahl von Stellen gerundet werden wie die Zahl, deren niedrigstwertige Ziffer am weitesten links liegt. Zum Beispiel 1200 + 3 = 1200(auf die Hunderterstelle gerundet, seit 1200 auf die Hunderterstelle gerundet) 1200.01 + 3 = 1203und 4.59 + 2.3 = 6.9. Beachten Sie, dass 5aufgerundet. Dieselbe Regel gilt für die Subtraktion. 0ist auf den einen Platz gerundet. Beachten Sie, dass das Addieren und Subtrahieren nicht von der Anzahl der signifikanten Stellen abhängt. Zum Beispiel,999 + 2.00 = 1001weil 999 auf die Einerstelle gerundet ist und 2,00 auf die Hundertstelstelle gerundet ist; Diejenige, die auf weniger Stellen gerundet wird, ist 999, daher sollte das Ergebnis 1001,00 ebenfalls auf die Stelle gerundet werden. In ähnlicher Weise ist 300 + 1 - 300 genau gleich 1, aber 300 ist auf die Hunderterstelle gerundet, so dass das Endergebnis auch auf die Hunderterstelle gerundet werden sollte, was 0 ergibt. 300. + 1 - 300. würde 1 auf der Stelle entsprechen andere Hand.

Runden Sie beim Multiplizieren oder Teilen von zwei Zahlen auf die Anzahl der signifikanten Stellen der Zahl mit den niedrigstwertigen Stellen. Zum Beispiel, 3.839*4=20weil der genaue Wert 15.356,, auf 20seitdem rundet, 4nur eine signifikante Zahl hat. Ebenso, 100/4=30da beide Zahlen eine signifikante Zahl haben, aber 100./4.00=25.0da beide Zahlen 3 signifikante Zahlen haben. 0ist definiert als 1 signifikante Zahl.

Ausdrücke werden nur enthalten *, /, +, und -, (und Klammern). Die Reihenfolge der Operationen sollte eingehalten und die Ergebnisse nach jeder Operation gerundet werden. Wenn Klammern in einer Folge von Additionen oder Subtraktionen oder einer Folge von Multiplikationen und Divisionen weggelassen werden, runden Sie nach Abschluss aller Operationen. Zum Beispiel 6*0.4*2 = 5(eine signifikante Zahl), während 0.4*(2*6)=0.4*10=4und (6*0.4)*2=2*2=4.

Eingabe : Eine Zeichenfolge mit einem Ausdruck, der ()*/+-Ziffern enthält. Zur Vereinfachung -wird nur als Subtraktionsoperator verwendet, nicht um negative Zahlen zu kennzeichnen; Antworten könnten jedoch immer noch negativ sein und würden -als Präfix benötigt.

Ausgabe : Das Ergebnis des Ausdrucks, ausgewertet und auf die richtige Anzahl von Stellen gerundet. Beachten Sie, dass dies 25für falsch ist 25.0.

Testfälle :

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

Randfall: Betrachten Sie das Problem von 501*2.0. Der genaue Wert ist 1002. Beim Drucken werden 1002zu viele signifikante Zahlen angegeben (4, wenn wir 2 benötigen), aber 1000zu wenige (1, wenn wir 2 benötigen). In diesem Fall sollte Ihr Programm 1000trotzdem drucken .

Diese Quelle erklärt auch wichtige Ziffern: http://www.purplemath.com/modules/rounding2.htm

soktinpk
quelle
Was meinst du mit " der gleichen Anzahl von Orten "? Ist das dasselbe wie " die gleiche Anzahl signifikanter Figuren "? Wenn Sie einen Randfall zum Hinzufügen wünschen , 999 + 2.00.
Peter Taylor
Sicherlich 300 + 1 - 300handelt es sich um eine Reihe von Additionen und Subtraktionen, die also nicht bis zum Ende gerundet werden müssen. (300 + 1) - 300wäre Null.
Neil

Antworten:

9

Java 11, 1325 1379 1356 1336 1290 Bytes

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 Bytes, um den Randfall zu beheben 501*2.0(Ergebnis 1002vorher gegeben, aber jetzt korrekt 1000).

Ich verstehe jetzt, warum diese Herausforderung fast zwei Jahre lang unbeantwortet blieb.>.> Diese Herausforderung hat mehr Sonderfälle als die niederländische Sprache, die etwas aussagt.
Java ist sicherlich nicht die richtige Sprache für diese Art von Herausforderungen (oder einen Codegolf) Herausforderung für diese Angelegenheit ..; p), aber es ist die einzige Sprache, die ich gut genug kenne, um überhaupt eine schwierige Herausforderung wie diese zu versuchen.

Eingabeformat wie Stringohne Leerzeichen (wenn dies nicht zulässig ist, können Sie s=s.replace(" ","")oben in der Methode (+19 Byte) hinzufügen ).

Probieren Sie es online aus.

Erläuterung:

Entschuldigung für den langen Beitrag.

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

Dieser Teil wird für Eingaben in Klammern verwendet. Es werden die getrennten Teile abgerufen und rekursive Aufrufe verwendet.

  • 0.4*(2*6)wird 0.4*A, wo Aist ein rekursiver Aufruf anc(2*6)
  • (8.3*0.02)+(1.*(9*4)+2.2)wird A+B, wo Aist ein rekursiver Aufruf an c(8.3*0.02)und Bein rekursiver Aufruf an c(1.*(9*4)+2.2)→ was wiederum wird 1.*C+2.2, wo Cist ein rekursiver Aufruf anc(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;

Diese erste Schleife wird verwendet, um die Werte zu füllen Mund kwobei Mdie größte Ganzzahllänge in Bezug auf signifikante Zahlen und kdie größte Dezimallänge ist.

  • 1200+3.0wird M=2, k=1( 12, .0)
  • 999+2.00wird M=3, k=2( 999, .00)
  • 300.+1-300.wird 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];

Diese zweite Schleife wird verwendet, um die Arrays Aund den bWert zu füllen q, wobei Adie Anzahl der signifikanten Zahlen bdie Ganzzahlen mit führenden Nullen übereinstimmen Mund qdie niedrigste Länge ohne Berücksichtigung von Punkten ist.

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

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

Verwendet eine JavaScript-Engine, um die Eingabe auszuwerten, die Rdoppelt gespeichert wird .

  • 1200+3.0 wird R=1203.0
  • 999+2.00 wird R=1001.0
  • 300.+1-300. wird R=1.0

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

Dies wird mauf den kleinsten Wert im Array gesetzt A.

  • A=[2, 5] wird m=2
  • A=[3, 5] wird m=3
  • A=[3, 3, 3] wird 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

Dies ändert sich mbasierend auf mehreren Faktoren.

  • 999+2.00 = 1001.0& m=3,q=3wird m=4(weil m==M(beide 3) → R%1==0( 1001.0hat keine Dezimalwerte) → (int)R/10%10<1( (int)1001.0/10wird 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q<=1( 4-3<=1) → wird so mdie Länge des ganzzahligen Teils "1001"( 4))
  • 3.839*4 = 15.356& m=1,q=1bleibt m=1(weil m==M(beide 1) → R%1!=0( 15.356hat Dezimalwerte) → R<=99R%10!=0( 15.356%10==5.356) → m!=0mbleibt also gleich ( 1))
  • 4*7*3 = 84.0& m=1,q=1bleibt m=1(weil m==M(beide 1) → R%1==0( 84.0hat keine Dezimalwerte) → (int)R/10%10>=1( (int)84/10wird 88%10>=1) → R<=99R%10!=0( 84%10==4) → m!=0mbleibt also gleich ( 1))
  • 6.0+4.0 = 10.0& m=2,q=2wird m=3(weil m!=M( m=2, M=1) → R<=99R%10==0( 10%10==0) → so mwird die Länge der Summe R(minus dem Punkt) "10.0".length()-1( 3))
  • 0-8.8 = -8.8& m=0,q=1wird m=1(weil m!=M( m=0, M=1) → R<=99R%10!=0( -8.8%10==-8.8) → m<1→ so mwird 1)
  • 501*2.0 = 1001.0& m=3,q=2wird m=2(weil m==M(beide 3) → R%1==0( 1001.0hat keine Dezimalwerte) → (int)R/10%10<1( (int)1001.0/10wird 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q>1( 4-2>1) → so mwird q( 2))

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

Jetzt Rwird basierend auf gerundet m.

  • 1001.0& m=4wird1001.0
  • 0.258& m=3Wird 0.26(weil abs(R)<1, m-1( 2) statt m=3verwendet innen MathContext)
  • -8.8& m=1wird-9.0
  • 1002.0& m=2wird1000.0

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

Dies ändert Rbei Bedarf den ganzzahligen Teil von .

  • 300.+1-300. = 1.0& m=3,M=3bleibt 1.0(weil m>=M→ so gleich Rbleibt ( 1.0))
  • 0.4*10 = 4.0& m=1,M=2bleibt 4.0(weil m<M(10^(M-m))/10<=R( (10^1)/10<=4.010/10<=4.01<=4.0) → so gleich Rbleibt ( 4.0))
  • 300+1-300 = 1.0& m=1,M=3Wird 0.0(weil m<M(10^(M-m))/10>R( (10^2)/10>1.0100/10>1.010>1.0) → so Rwird 0.0wegen 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`

Diese Sätze Rzu rals String und modifiziert es auf mehreren Faktoren.

  • 1203.0& m=4,k=2wird 1203.(weil k>=1→ so rwird 1001.000; r.length()>=m( 8>=4) → r.contains(".")r.length()>=m( 8>=4) → Teilzeichenfolge von Index 0zu m+1( 5))
  • 6.9& m=2,k=2bleibt 6.9(weil k>=1→ so rwird 6.900; r.length()>=m( 5>=2) → r.contains(".")r.length()>=m( 5>=2) → Teilzeichenfolge von Index 0zu m+1( 3))
  • 1.0& m=3,k=0wird 1(weil k<1→ so rwird 1; r.length()<m( 1<3) → Teilzeichenfolge von Index 0zu r.length()( 1))
  • 25.0& m=4,k=4wird 25.00(weil k>=1→ so rwird 25.00000; r.length()>=m( 8>=4) → r.contains(".")r.length()>+m( 8>=4) → Teilzeichenfolge von Index 0zu m+1( 5))
  • 0& m=1,k=0bleibt 0(weil k<1→ so rbleibt 0; r.length()>=m( 1>=1) → !r.contains(".")→ Teilzeichenfolge von Index 0zu m( 1))

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

Dadurch werden nachfolgende Nullen bei Bedarf wieder auf den ganzzahligen Teil zurückgesetzt.

  • r="12"& R=1200.0wirdr="1200"
  • r="1"& R=10.0wirdr="10"
  • r="8"& R=80.0wirdr="80"

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

Und schließlich geben wir das Ergebnis zurück, nachdem wir alle nachfolgenden Punkte entfernt haben.

  • 1203. wird 1203
  • 5. wird 5

Kann definitiv um ein paar hundert Bytes gespielt werden, aber ich bin nur froh, dass es jetzt funktioniert. Es dauerte bereits eine Weile, um jeden der Fälle und die Fragen der Herausforderung zu verstehen. Und dann war viel Versuch und Irrtum, Testen und erneutes Testen erforderlich, um zum obigen Ergebnis zu gelangen. Und während ich diese Erklärung oben schrieb, konnte ich weitere ± 50 Bytes nicht verwendeten Codes entfernen.

Kevin Cruijssen
quelle
1
Upvoted. Aber die Spezifikation scheint dies für die 501*2.0Ausgabe zu erfordern 1000(Sie sollten 1000 trotzdem ausgeben , was ich als "still" interpretiere, nicht so oder so ). Trotzdem großartige Arbeit.
Weijun Zhou
1
@WeijunZhou Danke für das Feedback! Ich habe noch einmal darüber nachgedacht und konnte den Randfall reparieren, ohne andere Fälle zu beschädigen. :)
Kevin Cruijssen