Số dấu phẩy động gần đúng với độ chính xác n chữ số


9

Chúng ta có một số dấu phẩy động rtừ 0 đến 1 và một số nguyên p.

Tìm phân số nguyên có mẫu số nhỏ nhất, gần đúng rvới ít nhất là pđộ chính xác kỹ thuật số.

  • Đầu vào: r(một số dấu phẩy động) và p(số nguyên).
  • Đầu ra: absố nguyên, trong đó
    • a/b(như float) xấp xỉ rcho đến khi pchữ số.
    • b là số nguyên dương nhỏ nhất có thể có.

Ví dụ:

  • nếu r=0.14159265358979p=9,
  • sau đó kết quả là a=4687b=33102,
  • 4687/33102=0.1415926530119026.

Bất kỳ giải pháp nào cũng phải hoạt động trên lý thuyết với các loại độ chính xác tùy ý, nhưng các hạn chế gây ra bởi các loại độ chính xác cố định của việc triển khai không thành vấn đề.

Độ chính xác có nghĩa là số chữ số sau " 0." in r. Vì vậy, nếu r=0.0123p=3, sau đó a/bnên bắt đầu với 0.012. Nếu các pchữ số đầu tiên của phần phân số rlà 0, hành vi không xác định được chấp nhận.

Đạt tiêu chí:

  • Thuật toán nhanh nhất chiến thắng. Tốc độ được đo bằng O (p).
  • Nếu có nhiều thuật toán nhanh nhất, thì chiến thắng ngắn nhất.
  • Câu trả lời của riêng tôi được loại trừ khỏi tập hợp những người chiến thắng có thể.

Phần toán học thực sự dễ dàng hơn nhiều vì có vẻ như, tôi đề nghị đọc bài này .

Câu trả lời:


7

JavaScript, O (10 p ) & 72 byte

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

Thật là tầm thường khi chứng minh rằng vòng lặp sẽ được thực hiện sau tối đa các lần lặp O (10 p ).

Rất cám ơn ý tưởng của Neil, tiết kiệm 50 byte.


Tại sao bạn loay hoay với padEndmatch? Bạn có thể chỉ slicemỗi chuỗi với độ dài chính xác và sau đó trừ chúng?
Neil

@Neil Xin lỗi tôi đã không nắm bắt được quan điểm của bạn. Việc thêm padEndđược sử dụng cho testcase f(0.001,2)f(0.3,2).
tsh

Tôi đã nghĩ rằng bạn có thể đơn giản hóa một cái gì đó dọc theo dòng (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]}(không hoàn toàn đánh gôn).
Neil

@Neil 120 -> 70 byte. :)
tsh

Whoa, tốt hơn nhiều!
Neil

4

Haskell , O (10 p ) trong trường hợp xấu nhất 121 119 byte

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

Hãy thử trực tuyến!

Đã lưu 2 byte nhờ Laikoni

Tôi đã sử dụng thuật toán từ /math/2432123/how-to-find-the-fraction-of-integers-with-the-smallest-denominator-matching-an-i .

Ở mỗi bước, khoảng mới là một nửa của khoảng trước đó. Vì vậy, kích thước khoảng là 2**-n, nơi nlà bước hiện tại. Khi nào 2**-n < 10**-p, chúng tôi chắc chắn có xấp xỉ đúng. Tuy nhiên, nếu n > 4*psau đó 2**-n < 2**-(4*p) == 16**-p < 10**-p. Kết luận là thuật toán này O(p).

EDIT Như được chỉ ra bởi orlp trong một bình luận, yêu cầu trên là sai. Trong trường hợp xấu nhất, r = 1/10**p( r= 1-1/10**ptương tự), sẽ có 10**pcác bước : 1/2, 1/3, 1/4, .... Có một giải pháp tốt hơn, nhưng tôi không có thời gian để khắc phục điều này.


Tôi biết mã golf chỉ là mục tiêu phụ, nhưng bạn có thể bỏ f=và lưu hai byte với z<-floor.(*10^p),u<-a+c,v<-b+d.
Laikoni

@Laikoni Tôi không đếm hai byte. Tôi không biết cách xóa f=trên TIO bằng mã Haskell.
jferard

Bạn có thể thêm -cppcờ trình biên dịch và viết f=\ vào tiêu đề: Hãy thử trực tuyến!
Laikoni

"Ở mỗi bước, khoảng mới là một nửa của khoảng trước đó." Làm thế nào bạn biết điều này? Bước đầu tiên là 1/2, vâng, nhưng sau đó bước tiếp theo là ví dụ phương tiện truyền thông là 1/2 và 1/1 cho 2/3, không giảm một nửa khoảng thời gian.
orlp

@orlp Bạn tuyệt đối đúng. Tôi đã quá lạc quan và sự phức tạp là O (10 ^ p) trong trường hợp xấu nhất. Tôi có một giải pháp tốt hơn nhưng không có thời gian để viết nó ngay bây giờ.
jferard

0

C, 473 byte (không có ngữ cảnh), O (p), không cạnh tranh

Giải pháp này sử dụng phần toán chi tiết trong bài tuyệt vời này . Tôi chỉ tính calc()vào kích thước câu trả lời.

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

Nó cũng gần giải pháp có thể nhanh nhất có thể theo nghĩa các chu kỳ cpu, ít nhất là trên các máy thông thường.
peterh - Phục hồi Monica
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.