n桁の精度の近似浮動小数点数


9

r0と1の間の浮動小数点数と整数がありpます。

r少なくともp-桁の精度で近似する最小の分母を持つ整数の割合を見つけます。

  • 入力:(r浮動小数点数)およびp(整数)。
  • 出力:ab整数、どこ
    • a/b(floatとして)桁rまで近似しpます。
    • b そのような正の整数の可能な最小のです。

例えば:

  • 場合r=0.14159265358979p=9
  • そして結果はa=4687and b=33102
  • なぜなら4687/33102=0.1415926530119026

理論的には、任意のソリューションは任意精度型で機能する必要がありますが、実装の固定精度型によって引き起こされる制限は問題になりません。

精度とは、「0.」の後の桁数を意味しrます。したがって、との場合r=0.0123p=3a/b始まる必要があり0.012ます。pの小数部の最初の桁rが0の場合、未定義の動作は許容されます。

勝利基準:

  • アルゴリズム的に最も速いアルゴリズムが優先されます。速度はO(p)で測定されます。
  • 最速のアルゴリズムが複数ある場合は、最短のアルゴリズムが優先されます。
  • 私の回答は、勝者候補から除外されています。

Ps数学の部分は実際にはかなり簡単に見えるので、この投稿を読むことをお勧めします。

回答:


7

JavaScript、O(10 p)&72バイト

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]}

ループが最大でO(10 p)回の反復の後に行われることを証明するのは簡単です。

Neilのアイデアのおかげで、50バイト節約できました。


なぜあなたはpadEndand をいじっているのですmatchか?slice各文字列を正しい長さにしてから差し引くことはできませんか?
Neil

@ニール申し訳ありませんが、あなたの意見を聞き取れませんでした。追加されたものpadEndは、テストケースf(0.001,2)とに使用されますf(0.3,2)
tsh

私はあなたが(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]}(完全にゴルフされていない)の線に沿って何かに簡略化できると考えていました。
Neil

@Neil 120-> 70バイト。:)
tsh

おっと、それははるかに良いです!
Neil

4

ハスケル、O(10 P最悪の場合)121の 119バイト

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)]

オンラインでお試しください!

ライコニのおかげで2バイト節約

私は/math/2432123/how-to-find-the-fraction-of-integers-with-the-smallest-denominator-matching-an-iのアルゴリズムを使用しました。

各ステップで、新しい間隔は前の間隔の半分です。したがって、間隔のサイズは、2**-nここで、n現在のステップです。のとき2**-n < 10**-p、正しい近似が確実に得られます。でもn > 4*pそれなら2**-n < 2**-(4*p) == 16**-p < 10**-p。結論は、アルゴリズムはであるということですO(p)

編集orlpがコメントで指摘しているように、上記の主張は誤りです。 最悪の場合r = 1/10**pr= 1-1/10**p同様)、10**pステップがあります1/2, 1/3, 1/4, ...。より良い解決策がありますが、今はこれを修正する時間はありません。


私はコードゴルフが二次的な目標にすぎないことを知っていますが、をドロップしてf=2バイトを節約できますz<-floor.(*10^p),u<-a+c,v<-b+d
ライコニ2017

@Laikoni私は2バイトを数えませんでした。f=HaskellコードでTIO を削除する方法がわかりません。
jferard 2017

-cppコンパイラフラグを追加f=\ して、ヘッダーに書き込むことができます。オンラインでお試しください!
ライコニ2017

「各ステップで、新しい間隔は前の間隔の半分です。」どうやってこれを知っていますか?最初のステップは1/2ですが、次のステップは、例えば、1/2と1/1の中央値が2/3になるため、間隔が半分になるわけではありません。
orlp

@orlpあなたは完全に正しいです。私は楽観的すぎて、最悪の場合、複雑さはO(10 ^ p)です。私にはもっと良い解決策がありますが、今それを書く時間はありません。
jferard 2017

0

C、473バイト(コンテキストなし)、O(p)、非競合

このソリューションでは、この優れた投稿で説明されている数学の部分を使用します。calc()回答サイズのみ計算しました。

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

また、少なくとも従来のマシンでは、CPUサイクルの意味でおそらく最も高速なソリューションに近づきます。
peterh-モニカを2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.