N basamaklı hassasiyetle yaklaşık kayan nokta sayısı


9

r0 ile 1 arasında bir kayan nokta sayımız ve bir tam sayı var p.

En küçük paydaya sahip tam sayı kesirini bulun r, en azından pbasamaklı hassasiyetle yaklaşın.

  • Girişler: r(bir kayan nokta sayısı) ve p(tamsayı).
  • Çıktılar: ave btamsayılar, burada
    • a/b(kayan nokta olarak) rakamlara rkadar yaklaşır p.
    • b mümkün olan en küçük pozitif tamsayıdır.

Örneğin:

  • eğer r=0.14159265358979ve p=9,
  • Sonra sonucudur a=4687ve b=33102,
  • çünkü 4687/33102=0.1415926530119026.

Herhangi bir çözümün teorik olarak keyfi hassasiyet tipleriyle çalışması gerekir, ancak uygulamaların sabit hassasiyetli tiplerinden kaynaklanan sınırlamalar önemli değildir.

Kesinlik, " 0." içindeki rakamlardan sonra gelen basamak sayısı anlamına gelir r. Böylece, eğer r=0.0123ve p=3sonra a/bbaşlamalıdır 0.012. pKesirli kısmının ilk haneleri r0 ise, tanımsız davranış kabul edilebilir.

Kazanma kriterleri:

  • Algoritmik olarak en hızlı algoritma kazanır. Hız O (p) cinsinden ölçülür.
  • En hızlı çoklu algoritmalar varsa, en kısa kazanır.
  • Kendi cevabım olası kazananlar kümesinden çıkarıldı.

Ps parçası aslında göründüğü kadar çok daha kolaydır matematik, Okumayı önermek bu yazı.

Yanıtlar:


7

JavaScript, O (10 p ) ve 72 bayt

r=>p=>{for(a=0,b=1,t=10**p;(a/b*t|0)-(r*t|0);a/b<r?a++:b++);return[a,b]}

Döngünün en fazla O (10 p ) tekrarından sonra yapılacağını kanıtlamak önemsizdir .

Neil'in fikri sayesinde çok teşekkürler, 50 bayt tasarruf edin.


Neden padEndve ile uğraşıyorsun match? sliceHer dizeyi doğru uzunlukta yapıp sonra çıkaramaz mısınız ?
Neil

@Neil Üzgünüm, fikrini yakalamamıştım. Eklenen padEndtestcase f(0.001,2)ve için kullanılır f(0.3,2).
tsh

Ben (r,p)=>{for(a=0,b=1;`${a/b}`.slice(0,p+2)-`${r}`.slice(0,p+2);a/b<r?a++:b++);return[a,b]}(tamamen golf değil) çizgileri boyunca bir şey aşağı basitleştirmek düşünüyorum düşünüyordum .
Neil

@ 120 -> 70 bayt. :)
tsh

Vay, bu çok daha iyi!
Neil

4

Haskell , O (10 p ) En kötü durumda 121 119 bayt

g(0,1,1,1)
g(a,b,c,d)r p|z<-floor.(*10^p),u<-a+c,v<-b+d=last$g(last$(u,v,c,d):[(a,b,u,v)|r<u/v])r p:[(u,v)|z r==z(u/v)]

Çevrimiçi deneyin!

Laikoni sayesinde 2 bayt kaydedildi

Algoritmayı /math/2432123/how-to-find-the-fraction-of-integers-with-the-smallest-denominator-matching-an-i adresinden kullandım .

Her adımda, yeni aralık önceki aralığın yarısıdır. Böylece, aralık boyutu, geçerli adım 2**-nnerede nolur. Ne zaman 2**-n < 10**-p, doğru yaklaşıma sahip olduğumuzdan eminiz. Oysa eğer n > 4*po zaman 2**-n < 2**-(4*p) == 16**-p < 10**-p. Sonuç, algoritmanın olduğu O(p).

EDIT Bir yorumda orlp tarafından işaret edildiği gibi, yukarıdaki iddia yanlıştır. En kötü durumda, r = 1/10**p( r= 1-1/10**pbenzer), olacak 10**padımlar: 1/2, 1/3, 1/4, .... Daha iyi bir çözüm var, ama şu anda bunu düzeltmek için zamanım yok.


Kod golf sadece ikincil hedef olduğunu biliyorum, ama f=iki bayt bırakıp kaydedebilirsiniz z<-floor.(*10^p),u<-a+c,v<-b+d.
Laikoni

@Laikoni İki baytı saymadım. f=Haskell kodunda TIO'dan nasıl kaldırılacağını bilmiyorum .
jferard

-cppDerleyici bayrağını ekleyebilir ve f=\ başlığa yazabilirsiniz : Çevrimiçi deneyin!
Laikoni

"Her adımda, yeni aralık önceki aralığın yarısıdır." Bunu nasıl biliyorsun? İlk adım 1/2, evet, ama sonra bir sonraki adım örneğin 1/2 ve 1/1'in ortancasıdır ve bu aralık aralığını yarıya indirmez.
orlp

@orlp Mutlak mutlusun. Çok iyimserdim ve karmaşıklık en kötü durumda O (10 ^ p). Daha iyi bir çözümüm var ama şimdi yazmak için zamanım yok.
jferard

0

C, 473 bayt (bağlamsız), O (p), rakip olmayan

Bu çözüm, bu mükemmel yazıda ayrıntılı matematik bölümünü kullanır . Sadece calc()cevap büyüklüğünü hesapladım .

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

void calc(float r, int p, int *A, int *B) {
  int a=0, b=1, c=1, d=1, e, f;
  int tmp = r*pow(10, p);
  float ivl = (float)(tmp) / pow(10, p);
  float ivh = (float)(tmp + 1) / pow(10, p);

  for (;;) {
    e = a + c;
    f = b + d;

    if ((ivl <= (float)e/f) && ((float)e/f <= ivh)) {
      *A = e;
      *B = f;
      return;
    }

    if ((float)e/f < ivl) {
      a = e;
      b = f;
      continue;
    } else {
      c = e;
      d = f;
      continue;
    }
  }
}

int main(int argc, char **argv) {
  float r = atof(argv[1]);
  int p = atoi(argv[2]), a, b;
  calc(r, p, &a, &b);
  printf ("a=%i b=%i\n", a, b);
  return 0;
}

Ayrıca, en azından geleneksel makinelerde, cpu döngüleri anlamında mümkün olan en hızlı çözüme yaklaşıyor.
peterh - Monica'yı
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.