Evaluar expresiones con cifras significativas.

10

Dada una expresión, su tarea es evaluarla. Sin embargo, su respuesta no puede mostrar más dígitos de los necesarios, ya que esto da la impresión de tener mediciones más precisas que la realidad.

El número de cifras significativas que tiene un número es cuántos dígitos tiene cuando se escribe en notación científica, incluidos los ceros al final si está presente un punto decimal. Por ejemplo, 1200tiene 2 cifras significativas porque es 1.2*10^3pero 1200.tiene 4 cifras significativas y 1200.0tiene 5 cifras significativas.

Al sumar dos números, el resultado debe redondearse al mismo número de lugares que el número cuyo dígito menos significativo se encuentra más a la izquierda. Por ejemplo, 1200 + 3 = 1200(redondeado al lugar de las centenas ya que 1200 se redondea al lugar de las centenas) 1200.01 + 3 = 1203, y 4.59 + 2.3 = 6.9. Tenga en cuenta que se 5redondea. Esta misma regla se aplica a la resta. 0se redondea al lugar de las unidades. Tenga en cuenta que sumar y restar no dependen del número de dígitos significativos. Por ejemplo,999 + 2.00 = 1001porque 999 se redondea al lugar de las unidades y 2.00 se redondea al lugar de las centésimas; el redondeado a menos lugares es 999, por lo que el resultado, 1001.00, también debe redondearse al lugar de las unidades. Del mismo modo, 300 + 1 - 300 es exactamente igual a 1, pero 300 se redondea al lugar de las centenas, por lo que el resultado final también se debe redondear al lugar de las centenas, dando 0. 300. + 1 - 300. sería igual a 1 en el otra mano

Al multiplicar o dividir dos números, redondea al número de dígitos significativos del número con los dígitos menos significativos. Por ejemplo, 3.839*4=20debido a que el valor exacto 15.356, se redondea a 20como 4solo tiene una cifra significativa. Del mismo modo, 100/4=30dado que ambos números tienen una cifra significativa, pero 100./4.00=25.0dado que ambos números tienen 3 cifras significativas. 0se define para tener 1 figura significativa.

Expresiones solamente contendrá *, /, +, y -, (y entre paréntesis). Se debe seguir el orden de las operaciones y los resultados se deben redondear después de cada operación. Si los paréntesis se dejan fuera en una cadena de sumas o restas o en una cadena de multiplicaciones y divisiones, entonces redondea después de completar todas las operaciones. Por ejemplo, 6*0.4*2 = 5(una cifra significativa), while 0.4*(2*6)=0.4*10=4y (6*0.4)*2=2*2=4.

Entrada : una cadena, con una expresión que contiene ()*/+-y dígitos. Para simplificar las cosas, -solo se utilizará como un operador de resta, no para significar números negativos; Sin embargo, las respuestas aún podrían ser negativas y requerirían -como prefijo.

Salida : El resultado de la expresión, evaluada y redondeada al número correcto de dígitos. Tenga en cuenta que 25es incorrecto para 25.0.

Casos de prueba :

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

Caso de borde: considere el problema de 501*2.0. El valor exacto es 1002. La impresión 1002da demasiadas cifras significativas (4, cuando necesitamos 2) pero 1000da muy pocas (1, cuando necesitamos 2). En este caso, su programa debería imprimir de 1000todos modos.

Esta fuente también explica dígitos significativos: http://www.purplemath.com/modules/rounding2.htm

soktinpk
fuente
¿Qué quieres decir con " el mismo número de lugares "? ¿Es eso lo mismo que " el mismo número de cifras significativas "? Si desea un caso de borde para la adición 999 + 2.00,.
Peter Taylor
Seguramente 300 + 1 - 300es una cadena de sumas y restas, por lo que no necesita redondearse hasta el final. (300 + 1) - 300Sería cero.
Neil

Respuestas:

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 para arreglar el caso de borde 501*2.0(dio resultado 1002antes, pero ahora correcto 1000).

Ahora entiendo por qué este desafío no tuvo respuesta durante casi dos años ...>.> Este desafío tiene casos más especiales que el idioma holandés, que dice algo ...
Java ciertamente no es el idioma adecuado para este tipo de desafíos (o cualquier codegolf desafío para el caso ...; p), pero es el único lenguaje que conozco lo suficientemente bueno como para incluso intentar un desafío difícil como este.

Formato de entrada como Stringsin espacios (si eso no está permitido, puede agregar s=s.replace(" ","")(+19 bytes) en la parte superior del método).

Pruébalo en línea.

Explicación:

Perdón por la larga publicación.

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

Esta parte se usa para entradas que contienen paréntesis. Obtendrá las partes separadas y usará llamadas recursivas.

  • 0.4*(2*6)se convierte 0.4*A, donde Aes una llamada recursiva ac(2*6)
  • (8.3*0.02)+(1.*(9*4)+2.2)se convierte A+B, donde Aes una llamada recursiva c(8.3*0.02)y Buna llamada recursiva a c(1.*(9*4)+2.2)→ que a su vez se convierte 1.*C+2.2, donde Ces una llamada recursiva ac(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;

Este primer ciclo se usa para completar los valores My k, donde Mes la mayor longitud entera con respecto a cifras significativas y kla mayor longitud de decimales.

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

Este segundo bucle se usa para llenar las matrices Ay btambién el valor q, donde Aes la cantidad de cifras significativas, bmantener los enteros con ceros a la izquierda para coincidir M, y qes la longitud más baja sin tener en cuenta los puntos.

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

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

Utiliza un motor de JavaScript para evaluar la entrada, que se guardará Rcomo doble.

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

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

Esto establece mel valor más pequeño en la matriz A.

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

Esto se modifica en mfunción de múltiples factores.

  • 999+2.00 = 1001.0& se m=3,q=3convierte en m=4(porque m==M(ambos 3) → R%1==0( 1001.0no tiene valores decimales) → (int)R/10%10<1(se (int)1001.0/10convierte en 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q<=1( 4-3<=1) → entonces se mconvierte en la longitud de la parte entera "1001"( 4))
  • 3.839*4 = 15.356& m=1,q=1permanece m=1(porque m==M(ambos 1) → R%1!=0( 15.356tiene valores decimales) → R<=99R%10!=0( 15.356%10==5.356) → m!=0→ así que mpermanece igual ( 1))
  • 4*7*3 = 84.0& m=1,q=1permanece m=1(porque m==M(ambos 1) → R%1==0( 84.0no tiene valores decimales) → (int)R/10%10>=1(se (int)84/10convierte en 88%10>=1) → R<=99R%10!=0( 84%10==4) → m!=0→ así que mpermanece igual ( 1))
  • 6.0+4.0 = 10.0& se m=2,q=2convierte en m=3(porque m!=M( m=2, M=1) → R<=99R%10==0( 10%10==0) → se mconvierte en la longitud del total R(menos el punto) "10.0".length()-1( 3))
  • 0-8.8 = -8.8& se m=0,q=1convierte m=1(porque m!=M( m=0, M=1) → R<=99R%10!=0( -8.8%10==-8.8) → m<1→ se mconvierte en 1)
  • 501*2.0 = 1001.0& se m=3,q=2convierte en m=2(porque m==M(ambos 3) → R%1==0( 1001.0no tiene valores decimales) → (int)R/10%10<1(se (int)1001.0/10convierte en 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q>1( 4-2>1) → se mconvierte en q( 2)

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

Ahora Rse redondea según m.

  • 1001.0y se m=4convierte1001.0
  • 0.258& se m=3convierte en 0.26(porque abs(R)<1, m-1( 2) en lugar de m=3se usa dentro MathContext)
  • -8.8y se m=1convierte-9.0
  • 1002.0y se m=2convierte1000.0

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

Esto modifica la parte entera de Rsi es necesario.

  • 300.+1-300. = 1.0& m=3,M=3permanece 1.0(porque m>=M→ entonces Rpermanece igual ( 1.0))
  • 0.4*10 = 4.0& m=1,M=2permanece 4.0(porque m<M(10^(M-m))/10<=R( (10^1)/10<=4.010/10<=4.01<=4.0) → Rpermanece igual ( 4.0))
  • 300+1-300 = 1.0& se m=1,M=3convierte en 0.0(porque m<M(10^(M-m))/10>R( (10^2)/10>1.0100/10>1.010>1.0) → entonces se Rconvierte en 0.0debido a 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`

Este conjuntos Ra rcomo secuencia, y lo modifica basa en varios factores.

  • 1203.0& se m=4,k=2convierte 1203.(porque k>=1→ entonces se rconvierte en 1001.000; r.length()>=m( 8>=4) → r.contains(".")r.length()>=m( 8>=4) → subcadena del índice 0a m+1( 5))
  • 6.9& m=2,k=2permanece 6.9(porque k>=1→ entonces se rconvierte en 6.900; r.length()>=m( 5>=2) → r.contains(".")r.length()>=m( 5>=2) → subcadena del índice 0a m+1( 3))
  • 1.0& se m=3,k=0convierte 1(porque k<1→ se rconvierte en 1; r.length()<m( 1<3) → subcadena del índice 0a r.length()( 1))
  • 25.0& se m=4,k=4convierte 25.00(porque k>=1→ entonces se rconvierte en 25.00000; r.length()>=m( 8>=4) → r.contains(".")r.length()>+m( 8>=4) → subcadena del índice 0a m+1( 5))
  • 0& se m=1,k=0queda 0(porque k<1→ así se rqueda 0; r.length()>=m( 1>=1) → !r.contains(".")→ subcadena del índice 0a m( 1))

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

Esto hace que los ceros finales vuelvan a la parte entera si es necesario.

  • r="12"y se R=1200.0convierter="1200"
  • r="1"y se R=10.0convierter="10"
  • r="8"y se R=80.0convierter="80"

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

Y finalmente devolvemos el resultado, después de haber eliminado los puntos finales.

  • 1203. se convierte 1203
  • 5. se convierte 5

Definitivamente se puede jugar un par de cientos de bytes, pero me alegra que esté funcionando ahora. Ya tomó un tiempo entender cada uno de los casos y lo que se preguntaba en el desafío. Y luego se necesitaron muchas pruebas y errores, pruebas y pruebas para llegar al resultado anterior. Y mientras escribía esta explicación arriba pude eliminar otros ± 50 bytes de código no utilizado ...

Kevin Cruijssen
fuente
1
Votado Pero la especificación parece requerir que se 501*2.0genere 1000(debería emitir de 1000 todos modos , lo que interpreto como "todavía", de ninguna manera ). Magnífico trabajo de todos modos.
Weijun Zhou
1
@WeijunZhou ¡Gracias por los comentarios! Lo he pensado nuevamente y pude arreglar el caso sin romper ningún otro caso. :)
Kevin Cruijssen