Évaluez les expressions avec des chiffres significatifs

10

Étant donné une expression, votre tâche consiste à l'évaluer. Cependant, votre réponse ne peut pas afficher plus de chiffres que nécessaire, car cela donne l'impression d'avoir des mesures plus précises que la réalité.

Le nombre de chiffres significatifs d'un nombre est le nombre de chiffres qu'il a lorsqu'il est écrit en notation scientifique, y compris les zéros à la fin si un point décimal est présent. Par exemple, 1200a 2 chiffres significatifs parce que c'est 1.2*10^3mais 1200.a 4 chiffres significatifs et 1200.0a 5 chiffres significatifs.

Lors de l'ajout de deux nombres, le résultat doit être arrondi au même nombre de places que le nombre dont le chiffre le moins significatif est le plus à gauche. Par exemple, 1200 + 3 = 1200(arrondi à la centaine puisque 1200 est arrondi à la centaine) 1200.01 + 3 = 1203,, et 4.59 + 2.3 = 6.9. Notez que 5arrondit. Cette même règle s'applique à la soustraction. 0est arrondi à la place de ceux. Notez que l'ajout et la soustraction ne dépendent pas du nombre de chiffres significatifs. Par exemple,999 + 2.00 = 1001parce que 999 est arrondi à la place des uns et 2,00 à la place des centièmes; celui arrondi à moins de places est 999, donc le résultat, 1001,00, devrait être arrondi à la place de ceux aussi. De même, 300 + 1 - 300 est exactement égal à 1, mais 300 est arrondi à la place des centaines, donc le résultat final devrait également être arrondi à la place des centaines, donnant 0. 300. + 1 - 300. serait égal à 1 sur la d'autre part.

Lorsque vous multipliez ou divisez deux nombres, arrondissez au nombre de chiffres significatifs du nombre avec les chiffres les moins significatifs. Par exemple, 3.839*4=20car la valeur exacte,, 15.356arrondie à 20depuis 4n'a qu'un seul chiffre significatif. De même, 100/4=30puisque les deux nombres ont un chiffre significatif, mais 100./4.00=25.0puisque les deux nombres ont 3 chiffres significatifs. 0est défini comme ayant 1 chiffre significatif.

Les expressions ne contiendra *, /, +et -, (et entre parenthèses). L'ordre des opérations doit être suivi et les résultats doivent être arrondis après chaque opération. Si des parenthèses sont omises dans une chaîne d'additions ou de soustractions ou une chaîne de multiplications et de divisions, arrondissez une fois toutes les opérations terminées. Par exemple, 6*0.4*2 = 5(un chiffre significatif), tandis que 0.4*(2*6)=0.4*10=4et (6*0.4)*2=2*2=4.

Entrée : une chaîne, avec une expression contenant ()*/+-et des chiffres. Pour simplifier les choses, -ne sera utilisé que comme opérateur de soustraction, pas pour signifier des nombres négatifs; cependant, les réponses pourraient toujours être négatives et nécessiteraient -un préfixe.

Sortie : résultat de l'expression, évalué et arrondi au nombre correct de chiffres. Notez que 25c'est incorrect pour 25.0.

Cas de test :

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

Cas Edge: Considérez le problème de 501*2.0. La valeur exacte est 1002. L'impression 1002donne trop de chiffres significatifs (4, quand nous avons besoin de 2) mais 1000donne trop peu (1, quand nous avons besoin de 2). Dans ce cas, votre programme devrait 1000quand même imprimer .

Cette source explique également les chiffres significatifs: http://www.purplemath.com/modules/rounding2.htm

soktinpk
la source
Qu'entendez-vous par " le même nombre de places "? Est-ce la même chose que " le même nombre de chiffres significatifs "? Si vous voulez un cas limite pour l' addition, 999 + 2.00.
Peter Taylor
Il 300 + 1 - 300s'agit certainement d' une chaîne d'additions et de soustractions, il n'est donc pas nécessaire de l'arrondir jusqu'à la fin. (300 + 1) - 300serait zéro.
Neil

Réponses:

9

Java 11, 1325 1379 1356 1336 1290 octets

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 octets pour corriger le cas de bord 501*2.0(a donné le résultat 1002avant, mais maintenant correct 1000).

Je comprends maintenant pourquoi ce défi était sans réponse depuis presque deux ans ..>.> Ce défi a des cas plus particuliers que la langue néerlandaise, ce qui veut dire quelque chose ..
Java est certainement pas la bonne langue pour ce genre de défis (ou tout codegolf défi d'ailleurs ..; p), mais c'est la seule langue que je connais assez bien pour même tenter un défi difficile comme celui-ci.

Format d'entrée Stringsans espaces (si cela n'est pas autorisé, vous pouvez ajouter s=s.replace(" ","")(+19 octets) en haut de la méthode).

Essayez-le en ligne.

Explication:

Désolé pour le long post.

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

Cette partie est utilisée pour les entrées contenant des parenthèses. Il récupérera les parties séparées et utilisera les appels récursifs.

  • 0.4*(2*6)devient 0.4*A, où Aest un appel récursif àc(2*6)
  • (8.3*0.02)+(1.*(9*4)+2.2)devient A+B, où Aest un appel récursif à c(8.3*0.02)et Bun appel récursif à c(1.*(9*4)+2.2)→ qui devient à son tour 1.*C+2.2, où Cest un appel récursif à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;

Cette première boucle est utilisée pour remplir les valeurs Met k, où Mest la plus grande longueur entière concernant les chiffres significatifs et kla plus grande longueur décimale.

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

Cette deuxième boucle est utilisée pour remplir les tableaux Aet bainsi que la valeur q, où Aest le nombre de chiffres significatifs, btenir les entiers avec des zéros en tête pour correspondre M, et qest la plus petite longueur sans tenir compte des points.

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

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

Utilise un moteur JavaScript pour évaluer l'entrée, qui sera enregistrée en Rdouble.

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

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

Cela définit mla plus petite valeur du tableau A.

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

Cela modifie en mfonction de plusieurs facteurs.

  • 999+2.00 = 1001.0& m=3,q=3devient m=4(parce que m==M(les deux 3) → R%1==0( 1001.0n'a pas de valeurs décimales) → (int)R/10%10<1( (int)1001.0/10devient 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q<=1( 4-3<=1) → mdevient ainsi la longueur de la partie entière "1001"( 4))
  • 3.839*4 = 15.356& m=1,q=1reste m=1(parce que m==M(les deux 1) → R%1!=0( 15.356a des valeurs décimales) → R<=99R%10!=0( 15.356%10==5.356) → m!=0mreste donc le même ( 1))
  • 4*7*3 = 84.0& m=1,q=1reste m=1(parce que m==M(les deux 1) → R%1==0( 84.0n'a pas de valeurs décimales) → (int)R/10%10>=1( (int)84/10devient 88%10>=1) → R<=99R%10!=0( 84%10==4) → m!=0mreste donc le même ( 1))
  • 6.0+4.0 = 10.0& m=2,q=2devient m=3(car m!=M( m=2, M=1) → R<=99R%10==0( 10%10==0) → mdevient ainsi la longueur du total R(moins le point) "10.0".length()-1( 3))
  • 0-8.8 = -8.8& m=0,q=1devient m=1(parce que m!=M( m=0, M=1) → R<=99R%10!=0( -8.8%10==-8.8) → m<1mdevient ainsi 1)
  • 501*2.0 = 1001.0& m=3,q=2devient m=2(parce que m==M(les deux 3) → R%1==0( 1001.0n'a pas de valeurs décimales) → (int)R/10%10<1( (int)1001.0/10devient 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q>1( 4-2>1) → mdevient ainsi q( 2))

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

Maintenant Rest arrondi en fonction de m.

  • 1001.0& m=4devient1001.0
  • 0.258& m=3devient 0.26(car abs(R)<1, m-1( 2) au lieu de m=3est utilisé à l'intérieur MathContext)
  • -8.8& m=1devient-9.0
  • 1002.0& m=2devient1000.0

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

Cela modifie la partie entière de Rsi nécessaire.

  • 300.+1-300. = 1.0& m=3,M=3reste 1.0(parce que m>=MRreste donc le même ( 1.0))
  • 0.4*10 = 4.0& m=1,M=2reste 4.0(parce que m<M(10^(M-m))/10<=R( (10^1)/10<=4.010/10<=4.01<=4.0) → Rreste donc le même ( 4.0))
  • 300+1-300 = 1.0& m=1,M=3devient 0.0(parce que m<M(10^(M-m))/10>R( (10^2)/10>1.0100/10>1.010>1.0) → Rdevient ainsi à 0.0cause de 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`

Il est défini Rsur rString et le modifie en fonction de plusieurs facteurs.

  • 1203.0& m=4,k=2devient 1203.(car k>=1rdevient ainsi 1001.000; r.length()>=m( 8>=4) → r.contains(".")r.length()>=m( 8>=4) → sous-chaîne de l'index 0à m+1( 5))
  • 6.9& m=2,k=2reste 6.9(car k>=1rdevient ainsi 6.900; r.length()>=m( 5>=2) → r.contains(".")r.length()>=m( 5>=2) → sous-chaîne de l'index 0à m+1( 3))
  • 1.0& m=3,k=0devient 1(car k<1rdevient ainsi 1; r.length()<m( 1<3) → sous-chaîne de l'index 0à r.length()( 1))
  • 25.0& m=4,k=4devient 25.00(car k>=1rdevient ainsi 25.00000; r.length()>=m( 8>=4) → r.contains(".")r.length()>+m( 8>=4) → sous-chaîne de l'index 0à m+1( 5))
  • 0& m=1,k=0séjours 0(parce que k<1rreste donc 0; r.length()>=m( 1>=1) → !r.contains(".")→ sous-chaîne de l'index 0à m( 1))

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

Cela ramène les zéros de fin à la partie entière si nécessaire.

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

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

Et enfin, nous retournons le résultat, après avoir supprimé tous les points de fin.

  • 1203. devient 1203
  • 5. devient 5

Peut certainement être joué par quelques centaines d'octets, mais je suis juste content que cela fonctionne maintenant. Il a déjà fallu un certain temps pour comprendre chacun des cas et ce qui était demandé dans le défi. Et puis il a fallu beaucoup d'essais et d'erreurs, de tests et de retests pour arriver au résultat ci-dessus. Et en écrivant cette explication ci-dessus, j'ai pu supprimer encore ± 50 octets de code inutilisé.

Kevin Cruijssen
la source
1
A voté. Mais la spécification semble exiger cela 501*2.0pour la sortie 1000(vous devriez 1000 quand même sortir , ce que j'interprète comme "toujours", pas dans les deux cas ). Magnifique travail quand même.
Weijun Zhou
1
@WeijunZhou Merci pour vos commentaires! J'y ai repensé et j'ai pu réparer le bord du boîtier sans casser aucun autre cas. :)
Kevin Cruijssen