ประเมินนิพจน์ด้วยตัวเลขนัยสำคัญ


10

รับการแสดงออกหน้าที่ของคุณคือการประเมินมัน อย่างไรก็ตามคำตอบของคุณไม่สามารถแสดงตัวเลขได้เกินความจำเป็นเนื่องจากจะทำให้การวัดมีความแม่นยำมากกว่าความเป็นจริง

จำนวนตัวเลขที่สำคัญที่ตัวเลขมีคือจำนวนหลักที่มีเมื่อเขียนด้วยสัญกรณ์ทางวิทยาศาสตร์รวมถึงเลขศูนย์ท้ายถ้ามีจุดทศนิยม ตัวอย่างเช่น1200มีตัวเลขนัยสำคัญ 2 ตัวเพราะเป็น1.2*10^3แต่1200.มีตัวเลขนัยสำคัญ 4 ตัวและ1200.0มีตัวเลขนัยสำคัญ 5 ตัว

เมื่อเพิ่มตัวเลขสองตัวผลลัพธ์ควรถูกปัดเศษเป็นจำนวนสถานที่เดียวกันกับหมายเลขที่มีตัวเลขนัยสำคัญน้อยที่สุดทางด้านซ้ายสุด ยกตัวอย่างเช่น1200 + 3 = 1200(ปัดเศษไปยังสถานที่หลายร้อยตั้งแต่ 1200 จะถูกปัดเศษไปยังสถานที่หลายร้อย) และ1200.01 + 3 = 1203 4.59 + 2.3 = 6.9โปรดทราบว่า5ปัดเศษขึ้น กฎเดียวกันนี้ใช้กับการลบ 0ถูกปัดเศษเป็นตำแหน่ง โปรดทราบว่าการเพิ่มและการลบไม่ได้ขึ้นอยู่กับจำนวนของตัวเลขที่สำคัญ ตัวอย่างเช่น,999 + 2.00 = 1001เนื่องจาก 999 จะถูกปัดเศษเป็นตำแหน่งที่หนึ่งและ 2.00 ถูกปัดเศษเป็นตำแหน่งที่หนึ่ง ที่ถูกปัดเศษให้น้อยที่สุดคือ 999 ดังนั้นผลลัพธ์ที่ได้คือ 1001.00 ควรถูกปัดเศษให้ตรงกับที่เช่นกัน ในทำนองเดียวกัน 300 + 1 - 300 มีค่าเท่ากับ 1 แต่ 300 จะถูกปัดเป็นร้อยตำแหน่งดังนั้นผลลัพธ์สุดท้ายควรถูกปัดเศษเป็นร้อยด้วย 0 0. 300 + 1 - 300 จะเท่ากับ 1 ใน ตรงกันข้าม.

เมื่อคูณหรือหารตัวเลขสองตัวให้ปัดเศษให้เป็นจำนวนหลักที่มีนัยสำคัญของตัวเลขด้วยตัวเลขนัยสำคัญน้อยที่สุด ตัวอย่างเช่น3.839*4=20เนื่องจากค่าที่แน่นอน15.356, ปัดเศษเป็น20ตั้งแต่4มีเพียงตัวเลขที่สำคัญเท่านั้น ในทำนองเดียวกัน100/4=30เนื่องจากตัวเลขทั้งสองมีตัวเลขนัยสำคัญหนึ่ง แต่100./4.00=25.0เนื่องจากตัวเลขทั้งสองมีตัวเลขนัยสำคัญ 3 ตัว 0ถูกกำหนดให้มี 1 ตัวเลขนัยสำคัญ

การแสดงออกจะมีเฉพาะ*, /, +และ-, (และวงเล็บ) ควรปฏิบัติตามคำสั่งของการดำเนินงานและควรปัดผลลัพธ์หลังการดำเนินการทุกครั้ง หากวงเล็บถูกปล่อยไว้ในสตริงของการเพิ่มหรือการลบหรือสตริงของการคูณและการหารแล้วปัดหลังจากการดำเนินการทั้งหมดเสร็จสมบูรณ์ ยกตัวอย่างเช่น6*0.4*2 = 5(หนึ่งรูปอย่างมีนัยสำคัญ) ในขณะที่และ0.4*(2*6)=0.4*10=4(6*0.4)*2=2*2=4

อินพุต : สตริงที่มีนิพจน์ที่มี()*/+-และดิจิต เพื่อให้สิ่งต่าง ๆ ง่ายขึ้น-จะใช้เป็นตัวดำเนินการลบเท่านั้นไม่ใช่เพื่อแสดงถึงจำนวนลบ อย่างไรก็ตามคำตอบอาจเป็นค่าลบและจะต้องใช้-คำนำหน้า

เอาท์พุท : ผลลัพธ์ของการแสดงออกประเมินและปัดเศษให้เป็นจำนวนหลักที่ถูกต้อง โปรดทราบว่าไม่ถูกต้องสำหรับ2525.0

กรณีทดสอบ :

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

กรณีขอบ: 501*2.0พิจารณาปัญหาของ 1002ค่าที่แน่นอนคือ การพิมพ์1002ให้ตัวเลขที่สำคัญมากเกินไป (4 เมื่อเราต้องการ 2) แต่1000ให้น้อยเกินไป (1 เมื่อเราต้องการ 2) ในกรณีนี้โปรแกรมของคุณควรพิมพ์1000ต่อไป

แหล่งที่มานี้อธิบายตัวเลขที่สำคัญเช่นกัน: http://www.purplemath.com/modules/rounding2.htm


คุณหมายถึงอะไร " จำนวนสถานที่เดียวกัน "? นั่นคือ " ตัวเลขที่มีนัยสำคัญเท่ากัน " หรือไม่? 999 + 2.00หากคุณต้องการกรณีขอบสำหรับนอกจากนี้
Peter Taylor

แน่นอนว่า300 + 1 - 300เป็นสตริงของการบวกและการลบดังนั้นจึงไม่จำเป็นต้องปัดเศษจนกว่าจะสิ้นสุด (300 + 1) - 300จะเป็นศูนย์
Neil

คำตอบ:


9

Java 11, 1325 1379 1356 1336 1290 ไบต์

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 ไบต์เพื่อแก้ไขปัญหาตัวเรือนขอบ501*2.0(ให้ผลลัพธ์มา1002ก่อน แต่ตอนนี้ถูกต้อง1000)

ตอนนี้ฉันเข้าใจแล้วว่าทำไมความท้าทายนี้ถึงไม่ได้ตอบมาเป็นเวลาเกือบสองปี .. >> ความท้าทายนี้มีกรณีพิเศษมากกว่าภาษาดัตช์ซึ่งพูดอะไรบางอย่าง ..
Java ไม่ได้เป็นภาษาที่เหมาะสมสำหรับความท้าทายประเภทนี้ (หรือ codegolf ใด ๆ ) ความท้าทายสำหรับเรื่องนั้น .. ; p) แต่มันเป็นภาษาเดียวที่ฉันรู้ดีพอที่จะลองใช้ความท้าทายที่ยากเช่นนี้

รูปแบบการป้อนข้อมูลStringโดยไม่มีช่องว่าง (หากไม่ได้รับอนุญาตคุณสามารถเพิ่มs=s.replace(" ","")(+19 ไบต์) ที่ด้านบนของวิธีการ)

ลองออนไลน์

คำอธิบาย:

ขออภัยสำหรับการโพสต์ยาว

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

ส่วนนี้ใช้สำหรับอินพุตที่มีวงเล็บ มันจะได้รับชิ้นส่วนที่แยกจากกันและใช้การโทรซ้ำ

  • 0.4*(2*6)จะกลายเป็น0.4*Aที่Aเรียกซ้ำไปc(2*6)
  • (8.3*0.02)+(1.*(9*4)+2.2)จะกลายเป็นA+Bที่Aมีการโทรซ้ำไปที่c(8.3*0.02)และBการโทรซ้ำไปที่c(1.*(9*4)+2.2)→ซึ่งจะกลายเป็น1.*C+2.2ที่ซึ่งCการโทรซ้ำไปที่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;

วงแรกนี้จะใช้ในการกรอกค่าMและkที่Mมีความยาวจำนวนเต็มที่ใหญ่ที่สุดเกี่ยวกับตัวเลขที่สำคัญและkความยาวทศนิยมที่ใหญ่ที่สุด

  • 1200+3.0กลายเป็นM=2, k=1( 12, .0)
  • 999+2.00กลายเป็นM=3, k=2( 999, .00)
  • 300.+1-300.กลายเป็น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];

ลูปที่สองนี้ใช้เพื่อเติมอาร์เรย์Aและbค่าqซึ่งAเป็นจำนวนตัวเลขที่สำคัญbเก็บจำนวนเต็มด้วยเลขศูนย์นำหน้าเพื่อจับคู่Mและqเป็นความยาวต่ำสุดโดยไม่คำนึงถึงจุด

  • 1200+3.0กลายเป็นA=[2, 5] (12, 00030), b=[1200, 0003.0]และq=2( 30)
  • 999+2.00กลายเป็นA=[3, 5] (999, 00200), b=[999, 002.00]และq=3(ทั้งสอง999และ200)
  • 300.+1-300.กลายเป็นA=[3, 3, 3] (300, 001, 300), b=[300., 001, 300.]และq=1( 1)
  • 501*2.0กลายเป็นA=[3, 4] (501, 0020), b=[501, 002.0]และq=2( 20)

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

ใช้เอนจิ้น JavaScript เพื่อประเมินการป้อนข้อมูลซึ่งจะบันทึกRเป็นสองเท่า

  • 1200+3.0 กลายเป็น R=1203.0
  • 999+2.00 กลายเป็น R=1001.0
  • 300.+1-300. กลายเป็น R=1.0

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

ชุดนี้จะคุ้มค่าที่เล็กที่สุดในอาร์เรย์mA

  • A=[2, 5] กลายเป็น m=2
  • A=[3, 5] กลายเป็น m=3
  • A=[3, 3, 3] กลายเป็น 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

การปรับเปลี่ยนนี้mขึ้นอยู่กับหลายปัจจัย

  • 999+2.00 = 1001.0& m=3,q=3กลายเป็นm=4(เพราะm==M(ทั้งสอง3) → R%1==0( 1001.0ไม่มีค่าทศนิยม) → (int)R/10%10<1( (int)1001.0/10กลายเป็น100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q<=1( 4-3<=1) →ดังนั้นจึงmกลายเป็นความยาวของจำนวนเต็มส่วน"1001"( 4)
  • 3.839*4 = 15.356& m=1,q=1พักm=1(เพราะm==M(ทั้งสอง1) → R%1!=0( 15.356มีค่าทศนิยม) R<=99→→ R%10!=0( 15.356%10==5.356) m!=0→ดังนั้นจึงmยังคงเหมือนเดิม ( 1)
  • 4*7*3 = 84.0& m=1,q=1stay m=1(เพราะm==M(ทั้ง ( 1)) → R%1==0( 84.0ไม่มีค่าทศนิยม) → (int)R/10%10>=1( (int)84/10กลายเป็น88%10>=1) R<=99→→ R%10!=0( 84%10==4) m!=0→ดังนั้นmยังคงเหมือนเดิม ( 1)
  • 6.0+4.0 = 10.0และm=2,q=2จะกลายเป็นm=3(เพราะm!=M( m=2, M=1) → R<=99R%10==0( 10%10==0) →จึงmกลายเป็นความยาวของทั้งหมดที่R(ลบจุด) "10.0".length()-1( 3))
  • 0-8.8 = -8.8และm=0,q=1จะกลายเป็นm=1(เพราะm!=M( m=0, M=1) →การR<=99→การR%10!=0( -8.8%10==-8.8) →การm<1→การเพื่อให้mกลายเป็น1)
  • 501*2.0 = 1001.0& m=3,q=2กลายเป็นm=2(เพราะm==M(ทั้งคู่3) → R%1==0( 1001.0ไม่มีค่าทศนิยม) → (int)R/10%10<1( (int)1001.0/10กลายเป็น100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q>1( 4-2>1) →ดังนั้นmกลายเป็นq( 2)

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

ตอนนี้เป็นโค้งมนขึ้นอยู่กับRm

  • 1001.0& m=4กลายเป็น1001.0
  • 0.258& m=3กลายเป็น0.26(เพราะabs(R)<1, m-1( 2) แทนที่จะm=3ใช้ภายในMathContext)
  • -8.8& m=1กลายเป็น-9.0
  • 1002.0& m=2กลายเป็น1000.0

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

วิธีนี้จะแก้ไขส่วนจำนวนเต็มของRหากจำเป็น

  • 300.+1-300. = 1.0& m=3,M=3พัก1.0(เพราะm>=MRอยู่เหมือนเดิม ( 1.0))
  • 0.4*10 = 4.0และm=1,M=2การเข้าพัก4.0(เพราะm<M(10^(M-m))/10<=R( (10^1)/10<=4.010/10<=4.01<=4.0) →เพื่อให้Rการเข้าพักเดียวกัน ( 4.0))
  • 300+1-300 = 1.0และm=1,M=3จะกลายเป็น0.0(เพราะm<M(10^(M-m))/10>R( (10^2)/10>1.0100/10>1.010>1.0) →จึงRกลายเป็น0.0เพราะ 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`

ชุดนี้Rจะrเป็นสตริงและปรับเปลี่ยนมันขึ้นอยู่กับหลายปัจจัย

  • 1203.0& m=4,k=2กลายเป็น1203.(เพราะk>=1→ so rกลายเป็น1001.000; r.length()>=m( 8>=4) r.contains(".")→→ r.length()>=m( 8>=4) →สตริงย่อยจากดัชนี0ถึงm+1( 5)
  • 6.9& m=2,k=2stay 6.9(เพราะk>=1rกลายเป็น6.900; r.length()>=m( 5>=2) r.contains(".")→→ r.length()>=m( 5>=2) →สตริงย่อยจากดัชนี0ถึงm+1( 3)
  • 1.0& m=3,k=0กลายเป็น1(เพราะk<1→ so rกลายเป็น1; r.length()<m( 1<3) →สตริงย่อยจากดัชนี0ถึงr.length()( 1))
  • 25.0& m=4,k=4กลายเป็น25.00(เพราะk>=1→ so rกลายเป็น25.00000; r.length()>=m( 8>=4) r.contains(".")→→ r.length()>+m( 8>=4) →สตริงย่อยจากดัชนี0ถึงm+1( 5)
  • 0& m=1,k=0stay 0(เพราะk<1→ดังนั้นrอยู่0; r.length()>=m( 1>=1) !r.contains(".")→→สตริงย่อยจากดัชนี0ถึงm( 1))

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

ซึ่งจะทำให้ศูนย์ต่อท้ายกลับไปที่ส่วนจำนวนเต็มถ้าจำเป็น

  • r="12"& R=1200.0กลายเป็นr="1200"
  • r="1"& R=10.0กลายเป็นr="10"
  • r="8"& R=80.0กลายเป็นr="80"

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

และในที่สุดเราก็กลับผลลัพธ์หลังจากเราลบจุดใด ๆ ที่ตามมา

  • 1203. กลายเป็น 1203
  • 5. กลายเป็น 5

สามารถเล่นกอล์ฟได้สองร้อยไบต์แน่นอน แต่ฉันแค่ดีใจที่มันใช้งานได้ ใช้เวลาสักครู่เพื่อทำความเข้าใจแต่ละกรณีและสิ่งที่ถูกถามในการท้าทาย และจากนั้นก็ใช้การทดลองและข้อผิดพลาดมากมายทดสอบและทดสอบซ้ำเพื่อให้ได้ผลลัพธ์ดังกล่าว และในขณะที่เขียนคำอธิบายนี้ด้านบนฉันสามารถลบอีก± 50 ไบต์ของรหัสที่ไม่ได้ใช้ ..


1
upvoted แต่สเป็คดูเหมือนจะต้องการที่501*2.0จะเอาท์พุท1000(คุณควรเอาท์พุท1000 ต่อไปซึ่งฉันตีความว่า "ยัง" ไม่ทางใดทางหนึ่ง ) งานที่งดงามอยู่ดี
Weijun Zhou

1
@ WeijunZhou ขอบคุณสำหรับข้อเสนอแนะ! ฉันเคยคิดมาบ้างแล้วและสามารถแก้ไขปัญหาตัวเรือนได้โดยไม่ทำให้เคสแตก :)
Kevin Cruijssen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.