Mengevaluasi ekspresi dengan angka signifikan

10

Diberikan ekspresi, tugas Anda adalah untuk mengevaluasinya. Namun, jawaban Anda tidak dapat menampilkan angka lebih dari yang diperlukan, karena ini memberi kesan memiliki pengukuran yang lebih tepat daripada kenyataan.

Jumlah angka signifikan yang dimiliki angka adalah berapa banyak digit yang dimilikinya ketika ditulis dalam notasi ilmiah, termasuk angka nol di bagian akhir jika terdapat angka desimal. Sebagai contoh, 1200memiliki 2 angka signifikan karena 1.2*10^3tetapi 1200.memiliki 4 angka signifikan dan 1200.0memiliki 5 angka signifikan.

Saat menambahkan dua angka, hasilnya harus dibulatkan ke jumlah tempat yang sama dengan angka yang digit paling signifikannya paling jauh ke kiri. Misalnya, 1200 + 3 = 1200(dibulatkan ke ratusan tempat sejak 1200 dibulatkan ke ratusan tempat) 1200.01 + 3 = 1203,, dan 4.59 + 2.3 = 6.9. Perhatikan bahwa 5dibulatkan ke atas. Aturan yang sama berlaku untuk pengurangan. 0dibulatkan ke tempat yang. Perhatikan bahwa menambah dan mengurangi tidak tergantung pada jumlah digit signifikan. Sebagai contoh,999 + 2.00 = 1001karena 999 dibulatkan ke tempat yang satu dan 2,00 dibulatkan ke tempat yang keseratus; yang dibulatkan ke tempat yang lebih sedikit adalah 999, jadi hasilnya, 1001,00, harus dibulatkan ke tempat yang juga. Demikian pula, 300 + 1 - 300 persis sama dengan 1, tetapi 300 dibulatkan ke ratusan tempat, sehingga hasil akhir juga harus dibulatkan ke tempat ratusan, memberikan 0, 300. + 1 - 300. akan sama dengan 1 pada sisi lain.

Saat mengalikan atau membagi dua angka, bulatkan ke angka digit signifikan dari angka dengan digit paling signifikan. Misalnya, 3.839*4=20karena nilai pastinya 15.356,, putaran ke 20karena 4hanya memiliki satu angka penting. Demikian pula, 100/4=30karena kedua angka memiliki satu angka signifikan, tetapi 100./4.00=25.0karena kedua angka memiliki 3 angka signifikan. 0didefinisikan memiliki 1 angka signifikan.

Ekspresi hanya akan berisi *, /, +, dan -, (dan tanda kurung). Urutan operasi harus diikuti dan hasilnya harus dibulatkan setelah setiap operasi. Jika tanda kurung ditinggalkan dalam serangkaian penambahan atau pengurangan atau serangkaian perkalian dan pembagian, maka bulat setelah semua operasi selesai. Misalnya, 6*0.4*2 = 5(satu angka penting), sementara 0.4*(2*6)=0.4*10=4dan (6*0.4)*2=2*2=4.

Input : String, dengan ekspresi yang mengandung ()*/+-dan digit. Untuk menyederhanakan banyak hal, -hanya akan digunakan sebagai operator pengurangan, bukan untuk menandakan angka negatif; jawaban, bagaimanapun, masih bisa negatif dan akan membutuhkan -awalan.

Output : Hasil dari ekspresi, dievaluasi dan dibulatkan ke jumlah digit yang benar. Catatan yang 25salah untuk 25.0.

Kasus uji :

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

Kasus tepi: Pertimbangkan masalah 501*2.0. Nilai pastinya adalah 1002. Mencetak 1002memberi terlalu banyak angka penting (4, ketika kita membutuhkan 2) tetapi 1000memberi terlalu sedikit (1, ketika kita membutuhkan 2). Dalam hal ini, program Anda harus 1000tetap mencetak .

Sumber ini menjelaskan angka-angka penting juga: http://www.purplemath.com/modules/rounding2.htm

soktinpk
sumber
Apa yang Anda maksud dengan " jumlah tempat yang sama "? Apakah itu sama dengan " jumlah angka signifikan yang sama "? Jika Anda ingin kasing tambahan 999 + 2.00,.
Peter Taylor
Tentunya 300 + 1 - 300adalah serangkaian penambahan dan pengurangan, jadi tidak perlu dibulatkan hingga akhir. (300 + 1) - 300akan menjadi nol.
Neil

Jawaban:

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 untuk memperbaiki kasus tepi 501*2.0(memberi hasil 1002sebelumnya, tetapi sekarang benar 1000).

Saya sekarang mengerti mengapa tantangan ini tidak dijawab selama hampir dua tahun ..>.> Tantangan ini memiliki kasus yang lebih khusus daripada bahasa Belanda, yang mengatakan sesuatu ..
Jawa tentu bukan bahasa yang tepat untuk tantangan semacam ini (atau codegolf apa pun) tantangan dalam hal ini;

Masukkan format Stringtanpa spasi (jika tidak diizinkan, Anda dapat menambahkan s=s.replace(" ","")(+19 byte) di bagian atas metode ini).

Cobalah online.

Penjelasan:

Maaf untuk posting lama.

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

Bagian ini digunakan untuk input yang mengandung tanda kurung. Ini akan mendapatkan bagian-bagian yang terpisah dan menggunakan panggilan rekursif.

  • 0.4*(2*6)menjadi 0.4*A, di mana Apanggilan rekursif untukc(2*6)
  • (8.3*0.02)+(1.*(9*4)+2.2)menjadi A+B, di mana Aadalah panggilan rekursif ke c(8.3*0.02)dan Bpanggilan rekursif ke c(1.*(9*4)+2.2)→ yang pada gilirannya menjadi 1.*C+2.2, di mana Cadalah panggilan rekursif untukc(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;

Loop pertama ini digunakan untuk mengisi nilai-nilai Mdan k, di mana Madalah bilangan bulat-panjang terbesar mengenai angka-angka penting dan kpanjang desimal terbesar.

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

Loop kedua ini digunakan untuk mengisi array Adan bjuga nilai q, di mana Ajumlah angka signifikan, bpegang bilangan bulat dengan nol terkemuka untuk mencocokkan M, dan qmerupakan titik-titik pengabaian dengan panjang terendah.

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

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

Menggunakan mesin JavaScript untuk mengevaluasi input, yang akan disimpan dalam Rdua kali lipat.

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

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

Ini menetapkan mnilai terkecil dalam array A.

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

Ini memodifikasi mberdasarkan beberapa faktor.

  • 999+2.00 = 1001.0& m=3,q=3menjadi m=4(karena m==M(keduanya 3) → R%1==0( 1001.0tidak memiliki nilai desimal) → (int)R/10%10<1( (int)1001.0/10menjadi 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q<=1( 4-3<=1) → jadi mmenjadi panjang bagian integer "1001"( 4))
  • 3.839*4 = 15.356& m=1,q=1tetap m=1(karena m==M(keduanya 1) → R%1!=0( 15.356memiliki nilai desimal) → R<=99R%10!=0( 15.356%10==5.356) → m!=0→ jadi mtetap sama ( 1))
  • 4*7*3 = 84.0& m=1,q=1tetap m=1(karena m==M(keduanya 1) → R%1==0( 84.0tidak memiliki nilai desimal) → (int)R/10%10>=1( (int)84/10menjadi 88%10>=1) → R<=99R%10!=0( 84%10==4) → m!=0→ jadi mtetap sama ( 1))
  • 6.0+4.0 = 10.0& m=2,q=2menjadi m=3(karena m!=M( m=2, M=1) → R<=99R%10==0( 10%10==0) → jadi mmenjadi panjang total R(minus titik) "10.0".length()-1( 3))
  • 0-8.8 = -8.8& m=0,q=1Menjadi m=1(karena m!=M( m=0, M=1) → R<=99R%10!=0( -8.8%10==-8.8) → m<1→ sehingga mmenjadi 1)
  • 501*2.0 = 1001.0& m=3,q=2menjadi m=2(karena m==M(keduanya 3) → R%1==0( 1001.0tidak memiliki nilai desimal) → (int)R/10%10<1( (int)1001.0/10menjadi 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q>1( 4-2>1) → jadi mmenjadi q( 2))

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

Sekarang Rdibulatkan berdasarkan m.

  • 1001.0& m=4menjadi1001.0
  • 0.258& m=3menjadi 0.26(karena abs(R)<1, m-1( 2) alih-alih m=3digunakan di dalam MathContext)
  • -8.8& m=1menjadi-9.0
  • 1002.0& m=2menjadi1000.0

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

Ini memodifikasi bagian integer Rjika perlu.

  • 300.+1-300. = 1.0& m=3,M=3tetap 1.0(karena m>=M→ jadi Rtetap sama ( 1.0))
  • 0.4*10 = 4.0& m=1,M=2tetap 4.0(karena m<M(10^(M-m))/10<=R( (10^1)/10<=4.010/10<=4.01<=4.0) → jadi Rtetap sama ( 4.0))
  • 300+1-300 = 1.0& m=1,M=3menjadi 0.0(karena m<M(10^(M-m))/10>R( (10^2)/10>1.0100/10>1.010>1.0) → jadi Rmenjadi 0.0karena 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`

Ini set Runtuk rsebagai String, dan memodifikasi itu didasarkan pada beberapa faktor.

  • 1203.0& m=4,k=2menjadi 1203.(karena k>=1→ begitu rmenjadi 1001.000; r.length()>=m( 8>=4) → r.contains(".")r.length()>=m( 8>=4) → substring dari indeks 0ke m+1( 5))
  • 6.9& m=2,k=2tetap 6.9(karena k>=1→ jadi rmenjadi 6.900; r.length()>=m( 5>=2) → r.contains(".")r.length()>=m( 5>=2) → substring dari indeks 0ke m+1( 3))
  • 1.0& m=3,k=0menjadi 1(karena k<1→ begitu rmenjadi 1; r.length()<m( 1<3) → substring dari indeks 0ke r.length()( 1))
  • 25.0& m=4,k=4menjadi 25.00(karena k>=1→ begitu rmenjadi 25.00000; r.length()>=m( 8>=4) → r.contains(".")r.length()>+m( 8>=4) → substring dari indeks 0ke m+1( 5))
  • 0& m=1,k=0tetap 0(karena k<1→ begitu rtetap 0; r.length()>=m( 1>=1) → !r.contains(".")→ substring dari indeks 0ke m( 1))

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

Ini menempatkan nol trailing kembali ke bagian integer jika perlu.

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

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

Dan akhirnya kami mengembalikan hasilnya, setelah kami menghapus titik-titik jejak.

  • 1203. menjadi 1203
  • 5. menjadi 5

Pasti bisa bermain golf dengan beberapa ratus byte, tapi saya senang ini berfungsi sekarang. Sudah butuh beberapa saat untuk memahami masing-masing kasus dan apa yang ditanyakan dalam tantangan. Dan kemudian butuh banyak trial-and-error, pengujian dan pengujian ulang untuk mendapatkan hasil di atas. Dan ketika menulis penjelasan ini di atas saya bisa menghapus ± 50 byte kode yang tidak terpakai lagi ..

Kevin Cruijssen
sumber
1
Terpilih. Tetapi spek tampaknya membutuhkan itu 501*2.0untuk keluaran 1000(Anda harus keluaran1000 tetap , yang saya tafsirkan sebagai "masih", bukan cara baik ). Pekerjaan yang luar biasa.
Weijun Zhou
1
@ WeijunZhou Terima kasih atas umpan baliknya! Saya telah memikirkannya lagi dan dapat memperbaiki casing tepi tanpa merusak case lainnya. :)
Kevin Cruijssen