Đánh giá biểu thức với các số liệu quan trọng


10

Đưa ra một biểu thức, nhiệm vụ của bạn là đánh giá nó. Tuy nhiên, câu trả lời của bạn không thể hiển thị nhiều chữ số hơn mức cần thiết, vì điều này mang lại cảm giác có số đo chính xác hơn thực tế.

Số lượng các số liệu có ý nghĩa mà một số có là có bao nhiêu chữ số khi được viết bằng ký hiệu khoa học, bao gồm các số 0 ở cuối nếu có dấu thập phân. Ví dụ, 1200có 2 con số đáng kể bởi vì nó có 1.2*10^3nhưng 1200.có 4 con số đáng kể và 1200.0có 5 con số đáng kể.

Khi thêm hai số, kết quả phải được làm tròn đến cùng một số vị trí với số có chữ số có nghĩa nhỏ nhất nằm xa nhất bên trái. Ví dụ: 1200 + 3 = 1200(làm tròn đến hàng trăm vị trí kể từ 1200 được làm tròn đến hàng trăm vị trí) 1200.01 + 3 = 1203, và 4.59 + 2.3 = 6.9. Lưu ý rằng làm 5tròn lên. Quy tắc tương tự này áp dụng cho phép trừ. 0được làm tròn đến những nơi. Lưu ý rằng việc cộng và trừ không phụ thuộc vào số lượng chữ số có nghĩa. Ví dụ,999 + 2.00 = 1001bởi vì 999 được làm tròn đến vị trí và 2,00 được làm tròn đến vị trí thứ một trăm; một vòng được làm tròn đến ít vị trí hơn là 999, do đó, kết quả, 1001.00, cũng nên được làm tròn đến các vị trí. Tương tự, 300 + 1 - 300 chính xác bằng 1, nhưng 300 được làm tròn đến hàng trăm, do đó, kết quả cuối cùng cũng sẽ được làm tròn đến hàng trăm, cho 0. 300. + 1 - 300. sẽ bằng 1 trên mặt khác.

Khi nhân hoặc chia hai số, làm tròn đến số chữ số có nghĩa của số có chữ số có nghĩa nhỏ nhất. Ví dụ: 3.839*4=20bởi vì giá trị chính xác 15.356, làm tròn 20từ đó 4chỉ có một con số đáng kể. Tương tự, 100/4=30vì cả hai số có một con số đáng kể, nhưng 100./4.00=25.0vì cả hai số có 3 con số đáng kể. 0được định nghĩa là có 1 con số đáng kể.

Expressions sẽ chỉ chứa *, /, +, và -, (và dấu ngoặc đơn). Thứ tự của các hoạt động nên được tuân theo và kết quả sẽ được làm tròn sau mỗi hoạt động. Nếu các dấu ngoặc đơn bị bỏ qua trong một chuỗi các phép cộng hoặc phép trừ hoặc một chuỗi các phép nhân và phép chia, thì làm tròn sau khi tất cả các thao tác được hoàn thành. Ví dụ: 6*0.4*2 = 5(một con số đáng kể), trong khi 0.4*(2*6)=0.4*10=4(6*0.4)*2=2*2=4.

Dữ liệu vào : Một chuỗi, với một biểu thức chứa ()*/+-và chữ số. Để đơn giản hóa mọi thứ, -sẽ chỉ được sử dụng như một toán tử trừ, không biểu thị số âm; câu trả lời, tuy nhiên, vẫn có thể là phủ định và sẽ yêu cầu -làm tiền tố.

Đầu ra : Kết quả của biểu thức, được đánh giá và làm tròn đến số chữ số chính xác. Lưu ý rằng 25không chính xác cho 25.0.

Các trường hợp thử nghiệm :

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

Trường hợp cạnh: Xem xét vấn đề của 501*2.0. Giá trị chính xác là 1002. In ấn 1002cho quá nhiều số liệu có ý nghĩa (4, khi chúng ta cần 2) nhưng 1000cho quá ít (1, khi chúng ta cần 2). Trong trường hợp này, chương trình của bạn vẫn nên in 1000.

Nguồn này cũng giải thích các chữ số có nghĩa: http://www.purplemath.com/modules/rounding2.htm


Bạn có ý nghĩa gì bởi " cùng một số địa điểm "? Có phải giống như " cùng số lượng nhân vật quan trọng " không? Nếu bạn muốn một trường hợp cạnh để thêm , 999 + 2.00.
Peter Taylor

Chắc chắn 300 + 1 - 300là một chuỗi các phép cộng và phép trừ, vì vậy không cần phải làm tròn cho đến khi kết thúc. (300 + 1) - 300sẽ bằng không.
Neil

Câu trả lời:


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 để sửa trường hợp cạnh 501*2.0(đã cho kết quả 1002trước đó, nhưng bây giờ đúng 1000).

Bây giờ tôi đã hiểu tại sao thử thách này không được trả lời trong gần hai năm ..>.> Thử thách này có nhiều trường hợp đặc biệt hơn tiếng Hà Lan, điều đang nói điều gì đó ..
Java chắc chắn không phải là ngôn ngữ phù hợp cho những thách thức này (hoặc bất kỳ loại tiền mã hóa nào thách thức cho vấn đề đó ..; p), nhưng đó là ngôn ngữ duy nhất tôi biết đủ tốt để thậm chí thực hiện một thử thách khó khăn như thế này.

Định dạng đầu vào như Stringkhông có khoảng trắng (nếu điều đó không được phép, bạn có thể thêm s=s.replace(" ","")(+19 byte) ở đầu phương thức).

Hãy thử trực tuyến.

Giải trình:

Xin lôi vi bai đăng dai.

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

Phần này được sử dụng cho đầu vào có chứa dấu ngoặc đơn. Nó sẽ nhận được các phần riêng biệt và sử dụng các cuộc gọi đệ quy.

  • 0.4*(2*6)trở thành 0.4*A, nơi Amột cuộc gọi đệ quy đếnc(2*6)
  • (8.3*0.02)+(1.*(9*4)+2.2)trở thành A+B, nơi Amột cuộc gọi đệ quy đến c(8.3*0.02)Bmột cuộc gọi đệ quy đến c(1.*(9*4)+2.2)→ lần lượt trở thành 1.*C+2.2, Ccuộc gọi đệ quy đếnc(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;

Vòng lặp đầu tiên này được sử dụng để điền vào các giá trị Mk, trong đó Mđộ dài số nguyên lớn nhất liên quan đến các số liệu có ý nghĩa và kđộ dài số thập phân lớn nhất.

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

Vòng lặp thứ hai này được sử dụng để điền vào các mảng Abcũng như giá trị q, trong đó Asố lượng các số liệu có ý nghĩa, bgiữ các số nguyên với các số 0 đứng đầu để khớp Mqlà các chấm có độ dài thấp nhất.

  • 1200+3.0trở thành A=[2, 5] (12, 00030), b=[1200, 0003.0]q=2( 30)
  • 999+2.00trở thành A=[3, 5] (999, 00200), b=[999, 002.00]q=3(cả hai 999200)
  • 300.+1-300.trở thành A=[3, 3, 3] (300, 001, 300), b=[300., 001, 300.]q=1( 1)
  • 501*2.0trở thành A=[3, 4] (501, 0020), b=[501, 002.0]q=2( 20)

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

Sử dụng một công cụ JavaScript để đánh giá đầu vào, sẽ được lưu Rthành hai lần.

  • 1200+3.0 trở thành R=1203.0
  • 999+2.00 trở thành R=1001.0
  • 300.+1-300. trở thành R=1.0

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

Điều này đặt mthành giá trị nhỏ nhất trong mảng A.

  • A=[2, 5] trở thành m=2
  • A=[3, 5] trở thành m=3
  • A=[3, 3, 3] trở thành 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

Điều này sửa đổi mdựa trên nhiều yếu tố.

  • 999+2.00 = 1001.0& m=3,q=3trở thành m=4(vì m==M(cả hai 3) → R%1==0( 1001.0không có giá trị thập phân) → (int)R/10%10<1( (int)1001.0/10trở thành 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q<=1( 4-3<=1) → vì vậy mtrở thành độ dài của phần nguyên "1001"( 4))
  • 3.839*4 = 15.356& m=1,q=1stay m=1(vì m==M(cả hai 1) → R%1!=0( 15.356có giá trị thập phân) → R<=99R%10!=0( 15.356%10==5.356) → m!=0→ vì vậy mgiữ nguyên ( 1))
  • 4*7*3 = 84.0& m=1,q=1stay m=1(vì m==M(cả hai 1) → R%1==0( 84.0không có giá trị thập phân) → (int)R/10%10>=1( (int)84/10trở thành 88%10>=1) → R<=99R%10!=0( 84%10==4) → m!=0→ vì vậy mgiữ nguyên ( 1))
  • 6.0+4.0 = 10.0& m=2,q=2trở thành m=3(vì m!=M( m=2, M=1) → R<=99R%10==0( 10%10==0) → vì vậy mtrở thành độ dài của tổng R(trừ dấu chấm) "10.0".length()-1( 3))
  • 0-8.8 = -8.8& m=0,q=1trở thành m=1(vì m!=M( m=0, M=1) → R<=99R%10!=0( -8.8%10==-8.8) → m<1→ vì vậy mtrở thành 1)
  • 501*2.0 = 1001.0& m=3,q=2trở thành m=2(vì m==M(cả hai 3) → R%1==0( 1001.0không có giá trị thập phân) → (int)R/10%10<1( (int)1001.0/10trở thành 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q>1( 4-2>1) → vì vậy mtrở thành q( 2))

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

Bây giờ Rđược làm tròn dựa trên m.

  • 1001.0& m=4trở thành1001.0
  • 0.258& m=3trở thành 0.26(bởi vì abs(R)<1, m-1( 2) thay vì m=3được sử dụng bên trong MathContext)
  • -8.8& m=1trở thành-9.0
  • 1002.0& m=2trở thành1000.0

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

Điều này sửa đổi phần nguyên Rnếu cần thiết.

  • 300.+1-300. = 1.0& m=3,M=3stay 1.0(vì m>=M→ vì vậy Rgiữ nguyên ( 1.0))
  • 0.4*10 = 4.0& m=1,M=2ở lại 4.0(vì m<M(10^(M-m))/10<=R( (10^1)/10<=4.010/10<=4.01<=4.0) → vì vậy Rgiữ nguyên ( 4.0))
  • 300+1-300 = 1.0& m=1,M=3trở thành 0.0(vì m<M(10^(M-m))/10>R( (10^2)/10>1.0100/10>1.010>1.0) → vì vậy Rtrở thành 0.0int(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`

Cái này đặt Rthành rString và sửa đổi nó dựa trên nhiều yếu tố.

  • 1203.0& m=4,k=2trở thành 1203.(vì k>=1→ vì vậy rtrở thành 1001.000; r.length()>=m( 8>=4) → r.contains(".")r.length()>=m( 8>=4) → chuỗi con từ chỉ mục 0đến m+1( 5))
  • 6.9& m=2,k=2stay 6.9(vì k>=1→ vì vậy rtrở thành 6.900; r.length()>=m( 5>=2) → r.contains(".")r.length()>=m( 5>=2) → chuỗi con từ chỉ mục 0đến m+1( 3))
  • 1.0& m=3,k=0trở thành 1(vì k<1→ vì vậy rtrở thành 1; r.length()<m( 1<3) → chuỗi con từ chỉ mục 0đến r.length()( 1))
  • 25.0& m=4,k=4trở thành 25.00(vì k>=1→ vì vậy rtrở thành 25.00000; r.length()>=m( 8>=4) → r.contains(".")r.length()>+m( 8>=4) → chuỗi con từ chỉ mục 0đến m+1( 5))
  • 0& m=1,k=0stay 0(vì k<1→ so rstay 0; r.length()>=m( 1>=1) → !r.contains(".")→ chuỗi con từ chỉ mục 0đến m( 1))

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

Điều này đặt các số 0 ở cuối một lần nữa trở lại phần nguyên nếu cần thiết.

  • r="12"& R=1200.0trở thànhr="1200"
  • r="1"& R=10.0trở thànhr="10"
  • r="8"& R=80.0trở thànhr="80"

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

Và cuối cùng chúng tôi trả lại kết quả, sau khi chúng tôi xóa bất kỳ dấu chấm nào.

  • 1203. trở thành 1203
  • 5. trở thành 5

Chắc chắn có thể bị đánh gôn bởi vài trăm byte, nhưng tôi rất vui vì giờ nó đã hoạt động. Phải mất một thời gian để hiểu từng trường hợp và những gì được hỏi trong thử thách. Và sau đó phải mất rất nhiều thử và sai, thử nghiệm và kiểm tra lại để có kết quả như trên. Và trong khi viết lời giải thích này ở trên, tôi đã có thể loại bỏ ± 50 byte mã không sử dụng khác ..


1
Nâng cao. Nhưng thông số kỹ thuật dường như yêu cầu 501*2.0đầu ra 1000( 1000 dù sao bạn cũng nên xuất ra , mà tôi hiểu là "tĩnh", không phải theo cách nào ). Công việc tuyệt vời nào.
Weijun Zhou

1
@WeijunZhou Cảm ơn bạn đã phản hồi! Tôi đã suy nghĩ lại một lần nữa và có thể khắc phục trường hợp cạnh mà không phá vỡ bất kỳ trường hợp nào khác. :)
Kevin Cruijssen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.