Valuta le espressioni con cifre significative


10

Data un'espressione, il tuo compito è valutarla. Tuttavia, la tua risposta non può mostrare più cifre del necessario, in quanto ciò dà l'impressione di avere misurazioni più precise della realtà.

Il numero di cifre significative che un numero ha è quante cifre ha quando è scritto in notazione scientifica, inclusi gli zeri alla fine se è presente un punto decimale. Ad esempio, 1200ha 2 cifre significative perché è 1.2*10^3ma 1200.ha 4 cifre significative e 1200.05 cifre significative.

Quando si aggiungono due numeri, il risultato deve essere arrotondato allo stesso numero di posizioni del numero la cui cifra meno significativa è più a sinistra. Ad esempio, 1200 + 3 = 1200(arrotondato alla posizione delle centinaia poiché 1200 è arrotondato alla posizione delle centinaia) 1200.01 + 3 = 1203, e 4.59 + 2.3 = 6.9. Si noti che 5arrotonda per eccesso. Questa stessa regola si applica alla sottrazione. 0è arrotondato al posto. Si noti che l'aggiunta e la sottrazione non dipendono dal numero di cifre significative. Per esempio,999 + 2.00 = 1001perché 999 è arrotondato al primo posto e 2.00 è arrotondato al centesimo posto; quello arrotondato a meno posti è 999, quindi il risultato, 1001,00, dovrebbe essere arrotondato anche a quelli posti. Allo stesso modo, 300 + 1 - 300 è esattamente uguale a 1, ma 300 è arrotondato alla posizione delle centinaia, quindi il risultato finale dovrebbe essere arrotondato alla posizione delle centinaia, dando 0. 300. + 1 - 300. sarebbe uguale a 1 sulla l'altra mano.

Quando si moltiplicano o si dividono due numeri, arrotondare al numero di cifre significative del numero con le cifre meno significative. Ad esempio, 3.839*4=20poiché il valore esatto 15.356, arrotondato a 20da, 4ha solo una cifra significativa. Allo stesso modo, 100/4=30poiché entrambi i numeri hanno una cifra significativa, ma 100./4.00=25.0poiché entrambi i numeri hanno 3 cifre significative. 0è definito per avere 1 cifra significativa.

Espressioni conterrà solo *, /, +, e -, (e tra parentesi). L'ordine delle operazioni dovrebbe essere seguito e i risultati dovrebbero essere arrotondati dopo ogni operazione. Se le parentesi vengono escluse in una serie di aggiunte o sottrazioni o in una serie di moltiplicazioni e divisioni, arrotondare dopo aver completato tutte le operazioni. Ad esempio, 6*0.4*2 = 5(una cifra significativa), mentre 0.4*(2*6)=0.4*10=4e (6*0.4)*2=2*2=4.

Input : una stringa, con un'espressione contenente ()*/+-e cifre. Per semplificare le cose, -verrà utilizzato solo come operatore di sottrazione, non per indicare numeri negativi; le risposte, tuttavia, potrebbero essere ancora negative e richiederebbero -come prefisso.

Output : il risultato dell'espressione, valutato e arrotondato al numero corretto di cifre. Si noti che 25non è corretto per 25.0.

Casi di prova :

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

Edge case: considera il problema di 501*2.0. Il valore esatto è 1002. La stampa 1002fornisce troppe cifre significative (4, quando ne abbiamo bisogno 2) ma ne 1000fornisce troppo poche (1, quando ne abbiamo bisogno 2). In questo caso, il programma dovrebbe 1000comunque stampare .

Questa fonte spiega anche cifre significative: http://www.purplemath.com/modules/rounding2.htm


Cosa intendi con " lo stesso numero di posti "? È lo stesso di " lo stesso numero di cifre significative "? Se si desidera una custodia per bordi da aggiungere 999 + 2.00,.
Peter Taylor,

Sicuramente 300 + 1 - 300è una serie di aggiunte e sottrazioni, quindi non è necessario arrotondare fino alla fine. (300 + 1) - 300sarebbe zero.
Neil,

Risposte:


9

Java 11, 1325 1379 1356 1336 1290 byte

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 byte per correggere il caso limite 501*2.0(dato il risultato 1002prima, ma ora corretto 1000).

Adesso capisco perché questa sfida è stata senza risposta per quasi due anni ..>.> Questa sfida ha casi speciali di lingua olandese, che è tutto dire ..
Java non è certamente la lingua giusta per questo tipo di sfide (o qualsiasi codegolf sfida per quella materia ..; p), ma è l'unica lingua che conosco abbastanza bene da tentare anche una sfida difficile come questa.

Formato di input come Stringsenza spazi (se ciò non è consentito, è possibile aggiungere s=s.replace(" ","")(+19 byte) all'inizio del metodo).

Provalo online.

Spiegazione:

Ci scusiamo per il lungo 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)));}

Questa parte viene utilizzata per input contenenti parentesi. Otterrà le parti separate e utilizzerà le chiamate ricorsive.

  • 0.4*(2*6)diventa 0.4*A, dove Aè una chiamata ricorsiva ac(2*6)
  • (8.3*0.02)+(1.*(9*4)+2.2)diventa A+B, dove si Atrova una chiamata ricorsiva c(8.3*0.02)e Buna chiamata ricorsiva a c(1.*(9*4)+2.2)→ che a sua volta diventa 1.*C+2.2, dove si Ctrova una chiamata ricorsiva 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;

Questo primo ciclo viene utilizzato per riempire i valori Me k, dove si Mtrova la lunghezza intera più grande rispetto alle cifre significative e kla lunghezza decimale più grande.

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

Questo secondo ciclo viene utilizzato per riempire le matrici Ae bil valore q, dove Aè la quantità di cifre significative, bcontenere gli interi con zero iniziali da abbinare Med qè la lunghezza più bassa ignorando i punti.

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

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

Utilizza un motore JavaScript per valutare l'input, che verrà salvato Rcome doppio.

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

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

Questo imposta msul valore più piccolo nell'array A.

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

Questo si modifica in mbase a più fattori.

  • 999+2.00 = 1001.0& m=3,q=3diventa m=4(perché m==M(entrambi 3) → R%1==0( 1001.0non ha valori decimali) → (int)R/10%10<1( (int)1001.0/10diventa 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q<=1( 4-3<=1) → quindi mdiventa la lunghezza dell'intero-parte "1001"( 4))
  • 3.839*4 = 15.356e m=1,q=1rimane m=1(perché m==M(entrambi 1) → R%1!=0( 15.356ha valori decimali) → R<=99R%10!=0( 15.356%10==5.356) → m!=0→ quindi mrimane lo stesso ( 1))
  • 4*7*3 = 84.0e m=1,q=1rimane m=1(perché m==M(entrambi 1) → R%1==0( 84.0non ha valori decimali) → (int)R/10%10>=1( (int)84/10diventa 88%10>=1) → R<=99R%10!=0( 84%10==4) → m!=0→ quindi mrimane lo stesso ( 1))
  • 6.0+4.0 = 10.0& m=2,q=2diventa m=3(perché m!=M( m=2, M=1) → R<=99R%10==0( 10%10==0) → così mdiventa la lunghezza del totale R(meno il punto) "10.0".length()-1( 3))
  • 0-8.8 = -8.8& m=0,q=1diventa m=1(perché m!=M( m=0, M=1) → R<=99R%10!=0( -8.8%10==-8.8) → m<1→ così mdiventa 1)
  • 501*2.0 = 1001.0& m=3,q=2diventa m=2(perché m==M(entrambi 3) → R%1==0( 1001.0non ha valori decimali) → (int)R/10%10<1( (int)1001.0/10diventa 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q>1( 4-2>1) → così mdiventa q( 2))

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

Ora Rè arrotondato in base a m.

  • 1001.0e m=4diventa1001.0
  • 0.258& m=3diventa 0.26(perché abs(R)<1, m-1( 2) invece di m=3essere utilizzato all'interno MathContext)
  • -8.8e m=1diventa-9.0
  • 1002.0e m=2diventa1000.0

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

Questo modifica la parte intera di Rse necessario.

  • 300.+1-300. = 1.0e m=3,M=3rimane 1.0(perché m>=M→ quindi Rrimane lo stesso ( 1.0))
  • 0.4*10 = 4.0e m=1,M=2rimane 4.0(perché m<M(10^(M-m))/10<=R( (10^1)/10<=4.010/10<=4.01<=4.0) → quindi Rrimane uguale ( 4.0))
  • 300+1-300 = 1.0& m=1,M=3diventa 0.0(perché m<M(10^(M-m))/10>R( (10^2)/10>1.0100/10>1.010>1.0) → così Rdiventa a 0.0causa di 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`

Questo imposta Ra rcome stringa, e lo modifica basa su molteplici fattori.

  • 1203.0& m=4,k=2diventa 1203.(perché k>=1→ così rdiventa 1001.000; r.length()>=m( 8>=4) → r.contains(".")r.length()>=m( 8>=4) → sottostringa dall'indice 0a m+1( 5))
  • 6.9& m=2,k=2resta 6.9(perché k>=1→ così rdiventa 6.900; r.length()>=m( 5>=2) → r.contains(".")r.length()>=m( 5>=2) → sottostringa dall'indice 0a m+1( 3))
  • 1.0& m=3,k=0diventa 1(perché k<1→ così rdiventa 1; r.length()<m( 1<3) → sottostringa dall'indice 0a r.length()( 1))
  • 25.0& m=4,k=4diventa 25.00(perché k>=1→ così rdiventa 25.00000; r.length()>=m( 8>=4) → r.contains(".")r.length()>+m( 8>=4) → sottostringa dall'indice 0a m+1( 5))
  • 0& m=1,k=0rimane 0(perché k<1→ così rrimane 0; r.length()>=m( 1>=1) → !r.contains(".")→ sottostringa dall'indice 0a m( 1))

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

In questo modo, se necessario, vengono ripristinati nuovamente gli zero finali nella parte intera.

  • r="12"e R=1200.0diventar="1200"
  • r="1"e R=10.0diventar="10"
  • r="8"e R=80.0diventar="80"

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

E infine restituiamo il risultato, dopo aver rimosso tutti i punti finali.

  • 1203. diventa 1203
  • 5. diventa 5

Può sicuramente essere giocato a golf di circa duecento byte, ma sono contento che funzioni ora. Ci è voluto un po 'di tempo per capire ciascuno dei casi e ciò che era stato chiesto nella sfida. E poi ci sono voluti molti tentativi, test e test per ottenere il risultato sopra. E mentre scrivevo questa spiegazione sopra sono stato in grado di rimuovere altri ± 50 byte di codice inutilizzato.


1
Upvoted. Ma le specifiche sembrano richiederlo 501*2.0per l'output 1000(dovresti 1000 comunque output , che interpreto come "fermo", non in entrambi i casi ). Magnifico lavoro comunque.
Weijun Zhou

1
@WeijunZhou Grazie per il feedback! Ci ho ripensato e sono stato in grado di risolvere il caso limite senza rompere altri casi. :)
Kevin Cruijssen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.