n位精度的近似浮点数


9

我们有一个r介于0和1之间的浮点数和一个整数p

查找具有最小分母的整数分数,其分数近似r至少为- p位数。

  • 输入:(r一个浮点数)和p(整数)。
  • 输出:ab整数,其中
    • a/b(以浮点数表示)近似rp数字。
    • b 是可能的最小此类正整数。

例如:

  • 如果r=0.14159265358979p=9
  • 那么结果是a=4687b=33102
  • 因为4687/33102=0.1415926530119026

任何解决方案都必须在理论上适用于任意精度类型,但是由实现的固定精度类型引起的限制并不重要。

精度表示在“ 0.” 之后的数字位数r。因此,如果r=0.0123p=3a/b则应从开始0.012。如果的p小数部分的第一位数字为r0,则可以接受未定义的行为。

获胜标准:

  • 算法上最快的算法获胜。速度以O(p)为单位。
  • 如果有多个最快的算法,则最短者获胜。
  • 我自己的答案被排除在可能的获胜者之外。

ps数学部分实际上看起来似乎容易得多,我建议您阅读这篇文章。

Answers:


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个字节。


您为什么要摆弄padEndmatch?您不能只将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 120-> 70个字节。:)
tsh

哇,好多了!
尼尔

4

Haskell中,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)]

在线尝试!

感谢Laikoni,节省了2个字节

我使用了/math/2432123/how-to-find-the-fraction-of-integers-with-the-smallest-denominator-matching-an-i中的算法。

在每个步骤中,新间隔是前一个间隔的一半。因此,间隔大小为2**-nn当前步长为。当为时2**-n < 10**-p,我们确定具有正确的近似值。如果这样的n > 4*p2**-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=并使用保存两个字节z<-floor.(*10^p),u<-a+c,v<-b+d
Laikoni

@Laikoni我没有计算两个字节。我不知道如何f=在Haskell代码中的TIO 上删除。
jferard

您可以添加-cpp编译器标志并写入f=\ 标头:在线尝试!
Laikoni

“在每个步骤中,新间隔是先前间隔的一半。” 你怎么会知道这事?第一步是1/2,是,但是下一步是例如1/2和1/1的中位数给出2/3,这不会使间隔减半。
Orlp

@orlp你是绝对正确的。我太乐观了,在最坏的情况下,复杂度是O(10 ^ p)。我有一个更好的解决方案,但现在没有时间编写它。
jferard

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周期的角度来看,它也接近可能最快的解决方案,至少在传统机器上是如此。
彼得-恢复莫妮卡
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.