İfadeleri anlamlı rakamlarla değerlendirin


10

Bir ifade verildiğinde, göreviniz onu değerlendirmektir. Bununla birlikte, cevabınız gerekenden daha fazla basamak gösteremez, çünkü bu gerçeklikten daha hassas ölçümlere sahip olduğu izlenimini verir.

Bir sayının sahip olduğu önemli rakamların sayısı, bir ondalık nokta varsa, sonunda sıfırlar da dahil olmak üzere, bilimsel gösterimde yazıldığında kaç basamak vardır. Örneğin, 1200bunun için 2 önemli rakamlar vardır 1.2*10^3, ancak 1200., 4 önemli rakamlar vardır ve 1200.05 önemli rakam bulunmaktadır.

İki sayı eklenirken, sonuç, en az anlamlı basamağı en soldaki sayı ile aynı sayıda yere yuvarlanmalıdır. Örneğin, 1200 + 3 = 1200(1200, yüzlerce yere yuvarlandığından beri yüzlerce yere yuvarlanır) 1200.01 + 3 = 1203, ve 4.59 + 2.3 = 6.9. 5Yukarı yuvarladığını unutmayın . Aynı kural çıkarma için de geçerlidir. 0olanlar yuvarlanır. Toplama ve çıkarma işleminin anlamlı basamak sayısına bağlı olmadığını unutmayın. Örneğin,999 + 2.00 = 1001çünkü 999 birincilere yuvarlanır ve 2.00 yüzüncü yere yuvarlanır; daha az yere yuvarlanmış olan 999'dur, bu nedenle sonuç 1001.00 olanın da yuvarlatılması gerekir. Benzer şekilde, 300 + 1 - 300 tam olarak 1'e eşittir, ancak 300 yüzlerce yere yuvarlanır, bu nedenle nihai sonuç da yüzlerce yere yuvarlanır ve 0 300 verir. + 1 - 300. Diğer el.

İki sayıyı çarparken veya bölerken, sayının en az anlamlı basamak içeren anlamlı basamak sayısına yuvarlayın. Örneğin 3.839*4=20, kesin değer, o zamandan bu yana 15.356yuvarlandığı için sadece bir anlamlı rakam vardır. Benzer şekilde, her iki sayının da önemli bir rakamı vardır, ancak her iki sayının da 3 önemli rakamı vardır. 1 anlamlı rakama sahip olarak tanımlanmıştır.204100/4=30100./4.00=25.00

İfadeler yalnızca içerecektir *, /, +ve -, (ve parantez). İşlem sırası takip edilmeli ve sonuçlar her işlemden sonra yuvarlanmalıdır . Parantezler bir dizi toplama veya çıkarma veya bir çarpma ve bölme dizisinde bırakılırsa, tüm işlemler tamamlandıktan sonra yuvarlayın. Örneğin, 6*0.4*2 = 5(bir önemli rakam), 0.4*(2*6)=0.4*10=4ve (6*0.4)*2=2*2=4.

Girdi : İfadeyi ()*/+-ve rakamları içeren bir dize . İşleri basitleştirmek için, -negatif sayıları belirtmek için değil, sadece çıkarma operatörü olarak kullanılır; ancak cevaplar yine de negatif olabilir ve -ön ek olarak istenebilir.

Çıktı : İfadenin sonucu, değerlendirilir ve doğru basamak sayısına yuvarlanır. Bunun 25için yanlış olduğunu unutmayın 25.0.

Test senaryoları :

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

Kenar durumu: sorununu düşünün 501*2.0. Kesin değer 1002. Baskı 1002çok önemli rakamlar verir (2'ye ihtiyacımız olduğunda 4) ama 1000çok az verir (2'ye ihtiyacımız olduğunda 1). Bu durumda, programınız 1000yine de yazdırmalıdır .

Bu kaynak önemli basamakları da açıklar: http://www.purplemath.com/modules/rounding2.htm


" Aynı sayıda yer " ile ne demek istiyorsun ? Bu " aynı sayıda önemli rakam " ile aynı mı? Eklemek için bir kenar kılıfı istiyorsanız 999 + 2.00,.
Peter Taylor

Elbette 300 + 1 - 300bir dizi toplama ve çıkarma, bu yüzden sonuna kadar yuvarlanması gerekmez. (300 + 1) - 300sıfır olur.
Neil

Yanıtlar:


9

Java 11, 1325 1379 1356 1336 1290 bayt

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+"$","");}}

Kenar durumunu düzeltmek için +54 bayt 501*2.0(daha 1002önce sonuç verdi , ancak şimdi doğru 1000).

Şimdi bu zorluğun neden neredeyse iki yıldır cevaplanmadığını anlıyorum ..> .Bu zorluğun bir şey söyleyen Hollanda dilinden daha özel vakaları var ..
Java kesinlikle bu tür zorluklar (veya herhangi bir kod golfü için doğru dil değil) bu konu için meydan okuma ..; p), ama böyle zor bir meydan okuma girişiminde bulunmaya yetecek kadar iyi bildiğim tek dil.

StringBoşluksuz giriş biçimi (buna izin verilmiyorsa, s=s.replace(" ","")yöntemin üstüne (+19 bayt) ekleyebilirsiniz ).

Çevrimiçi deneyin.

Açıklama:

Uzun yazı için özür dilerim.

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

Bu bölüm parantez içeren girdi için kullanılır. Ayrılan parçaları alır ve özyinelemeli çağrılar kullanır.

  • 0.4*(2*6)olur 0.4*Anerede, Abir özyinelemeli çağrıdırc(2*6)
  • (8.3*0.02)+(1.*(9*4)+2.2)olur A+Bnerede, Abir özyinelemeli çağrı c(8.3*0.02)ve Bardışık bir çağrı c(1.*(9*4)+2.2)sırayla olur → 1.*C+2.2nerede Cbir özyinelemeli çağrıdırc(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;

Bu birinci çevrim değerleri doldurmak için kullanılan Mve kburada, Mönemli ölçüde şekiller ile ilgili olarak en büyük tam sayıyı uzunluklu kbüyük ondalık sayı-uzunluk.

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

Bu ikinci çevrim diziler doldurmak için kullanılan Ave bayrıca bir değer olarak q, Aönemli rakamların miktarı, beşleştirmek için gelen sıfır ile tamsayılar tutun Mve qnoktalar göz ardı düşük uzunluktadır.

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

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

Girişi değerlendirmek için bir JavaScript motoru kullanır ve bu değer Rçift olarak kaydedilir .

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

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

Bu m, dizideki en küçük değere ayarlanır A.

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

Bu m, birden çok faktöre göre değişiklik gösterir.

  • 999+2.00 = 1001.0& m=3,q=3olur m=4(çünkü m==M(her ikisi de 3) → R%1==0( 1001.0ondalık değeri yoktur) → (int)R/10%10<1( → (int)1001.0/10olur 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q<=1( 4-3<=1) → böylece mtamsayı bölümünün uzunluğu olur "1001"( 4))
  • 3.839*4 = 15.356& m=1,q=1kalır m=1(çünkü m==M(her ikisi de 1) → R%1!=0( 15.356ondalık değerleri vardır) → R<=99R%10!=0( 15.356%10==5.356) → m!=0maynı kalır ( 1))
  • 4*7*3 = 84.0& m=1,q=1kalır m=1(çünkü m==M(her ikisi de 1) → R%1==0( 84.0ondalık değeri yoktur) → (int)R/10%10>=1( → (int)84/10olur 88%10>=1) → R<=99R%10!=0( 84%10==4) → m!=0→ böylece maynı kalır ( 1))
  • 6.0+4.0 = 10.0& m=2,q=2olur m=3(çünkü m!=M( m=2, M=1) → R<=99R%10==0( 10%10==0) → mtoplamın uzunluğu olur R(eksi nokta) "10.0".length()-1( 3))
  • 0-8.8 = -8.8& m=0,q=1olur m=1(çünkü m!=M( m=0, M=1) → R<=99R%10!=0( -8.8%10==-8.8) → m<1molur 1)
  • 501*2.0 = 1001.0& m=3,q=2olur m=2(çünkü m==M(her ikisi de 3) → R%1==0( 1001.0ondalık değeri yoktur) → (int)R/10%10<1( → (int)1001.0/10olur 100100%10<1) → "1001".length()>m( 4>3) → "1001".length()-q>1( 4-2>1) → böylece ( ) molurq2

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

Şimdi Rdayalı yuvarlanır m.

  • 1001.0& m=4olur1001.0
  • 0.258Ve m=3olur 0.26(nedeniyle abs(R)<1, m-1( 2) yerine m=3iç kullanılır MathContext)
  • -8.8& m=1olur-9.0
  • 1002.0& m=2olur1000.0

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

Bu, Rgerekirse tam sayı kısmını değiştirir .

  • 300.+1-300. = 1.0& m=3,M=3kalır 1.0(çünkü m>=MRaynı kalır ( 1.0))
  • 0.4*10 = 4.0& m=1,M=2kalır 4.0(çünkü m<M(10^(M-m))/10<=R( (10^1)/10<=4.010/10<=4.01<=4.0) → Raynı kalır ( 4.0))
  • 300+1-300 = 1.0Ve m=1,M=3olur 0.0(nedeniyle m<M(10^(M-m))/10>R( (10^2)/10>1.0100/10>1.010>1.0) → çok Rolur 0.0çünkü 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`

Bu setler Riçin rString olarak ve değiştirir birden çok faktöre dayalı.

  • 1203.0& m=4,k=2Olur 1203.(çünkü k>=1böylece → rolur 1001.000; r.length()>=m( 8>=4) → r.contains(".")r.length()>=m( 8>=4) → dizinden alt dize 0kadar m+1( 5))
  • 6.9& m=2,k=2Kalır 6.9(çünkü k>=1→ yüzden rolur 6.900; r.length()>=m( 5>=2) → r.contains(".")r.length()>=m( 5>=2) → dizinden alt dize 0kadar m+1( 3))
  • 1.0& m=3,k=0Olur 1(çünkü k<1böylece → rolur 1; r.length()<m( 1<3) → dizinden alt dize 0kadar r.length()( 1))
  • 25.0& m=4,k=4Olur 25.00(çünkü k>=1böylece → rolur 25.00000; r.length()>=m( 8>=4) → r.contains(".")r.length()>+m( 8>=4) → dizinden alt dize 0kadar m+1( 5))
  • 0& m=1,k=0Kalır 0(çünkü k<1böylece → rkalır 0; r.length()>=m( 1>=1) → !r.contains(".")→ dizinden alt dize 0kadar m( 1))

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

Bu, gerekirse sıfırları tekrar tamsayı kısmına geri koyar.

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

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

Sonunda, herhangi bir nokta kaldırıldıktan sonra sonucu döndürüyoruz.

  • 1203. olur 1203
  • 5. olur 5

Kesinlikle birkaç yüz bayt tarafından golf edilebilir, ama şimdi çalıştığı için memnunum. Davaların her birini ve meydan okumada ne sorulduğunu anlamak zaten biraz zaman aldı. Ve sonra yukarıdaki sonuca ulaşmak için birçok deneme yanılma, test etme ve tekrar test etme gerekiyordu. Ve bu açıklamayı yukarıda yazarken kullanılmayan ± 50 baytlık bir kod daha kaldırabiliyordum.


1
Upvoted. Ama spec 501*2.0bunu çıktı için gerektirir gibi görünüyor 1000( 1000 yine de her iki şekilde değil, "hala" olarak yorumlamak çıktı ). Yine de muhteşem iş.
Weijun Zhou

1
@WeijunZhou Geri bildiriminiz için teşekkür ederiz! Tekrar düşündüm ve başka bir durumu kırmadan kenar kasasını düzelttim. :)
Kevin Cruijssen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.