n 자리 정밀도를 가진 대략적인 부동 소수점 수


9

우리는 r0과 1 사이 의 부동 소수점 숫자 와 integer p입니다.

r최소 p자릿수 이상의 근사값을 가진 가장 작은 분모를 가진 정수의 분수를 찾으십시오 .

  • 입력 : r(부동 소수점 수) 및 p(정수).
  • 출력 : ab정수
    • a/b(float)는 숫자 r까지 근사값 p입니다.
    • b 가능한 양의 정수가 가장 작습니다.

예를 들면 다음과 같습니다.

  • 경우 r=0.14159265358979p=9,
  • 그 결과는 a=4687b=33102,
  • 왜냐하면 4687/33102=0.1415926530119026.

모든 솔루션은 이론적으로 임의 정밀도 유형으로 작동해야하지만 구현의 고정밀 유형으로 인한 제한은 중요하지 않습니다.

정밀도는 " 0." 뒤에있는 숫자의 수를 의미합니다 r. 따라서 r=0.0123and p=3이면로 a/b시작해야합니다 0.012. p소수 부분의 첫 번째 숫자 r가 0이면 정의되지 않은 동작이 허용됩니다.

승 기준 :

  • 알고리즘 적으로 가장 빠른 알고리즘이 이깁니다. 속도는 O (p)로 측정됩니다.
  • 가장 빠른 알고리즘이 여러 개인 경우 가장 짧은 알고리즘이 승리합니다.
  • 본인의 답변은 가능한 승자 세트에서 제외됩니다.

추신 : 수학 부분은 실제로 훨씬 쉽습니다 . 게시물 을 읽는 것이 좋습니다 .

답변:


7

자바 스크립트, 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것은 testcase 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]}(완전히 골프를 치지 않은) 라인을 따라 무언가로 단순화 할 수 있다고 생각했습니다 .

@ 닐 120-> 70 바이트. :)
tsh

우와, 훨씬 낫다!

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

온라인으로 사용해보십시오!

Laikoni 덕분에 2 바이트 절약

나는 /math/2432123/how-to-find-the-fraction-of-integers-with-the-small-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**p( r= 1-1/10**p유사한 경우) 다음 10**p단계 가 있습니다 1/2, 1/3, 1/4, ..... 더 나은 해결책이 있지만 지금은이 문제를 해결할 시간이 없습니다.


코드 골프는 2 차 목표 일 뿐이지 만를 사용하여 f=2 바이트를 절약하고 저장할 수 있습니다 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 사이클의 관점에서 가장 빠른 솔루션에 가깝습니다.
peterh-Reinstate Monica
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.