Tìm vị trí của một phần trong cây Stern-Brocot


11

Cây Stern-Brocotcây nhị phân của các phân số trong đó mỗi phân số có được bằng cách thêm các tử số và mẫu số của hai phân số lân cận nó ở các cấp độ trên.

Nó được tạo bằng cách bắt đầu bằng 0/11/0dưới dạng "phân số điểm cuối" và từ đó, lặp đi lặp lại bằng cách đặt một phân số giữa mỗi cặp phân số liên tiếp bằng cách thêm tử số và mẫu số của các phân số đó với nhau, như vậy:

0.  0/1                                                             1/0
1.  0/1                             1/1                             1/0
2.  0/1             1/2             1/1             2/1             1/0
3.  0/1     1/3     1/2     2/3     1/1     3/2     2/1     3/1     1/0
4.  0/1 1/4 1/3 2/5 1/2 3/5 2/3 3/4 1/1 4/3 3/2 5/3 2/1 5/2 3/1 4/1 1/0

Trong mỗi lần lặp của cây Stern-Brocot ( nlần lặp thứ), có 2^n + 1các phần tử trong chuỗi, theo đó chúng ta có thể gán một phân số từ 0/2^nđến 2^n/2^n. Mỗi lần lặp mới chỉ cần chèn một phần "nửa chừng" giữa mỗi cặp phân số liên tiếp.

Điều này làm cho cây Stern-Brocot ánh xạ một-một giữa các số hữu tỷ dương và các phân số nhị phân giữa 0 và 1, do đó cũng đóng vai trò là một bằng chứng cho thấy hai tập hợp có cùng số lượng.

Nhiệm vụ của bạn là viết một chương trình hoặc hàm, cho tử số và mẫu số của một số hữu tỷ dương theo các giá trị thấp nhất, xác định phân số nhị phân tương ứng với vị trí của phân số đó trong cây Stern-Brocot.

Ví dụ về đầu vào và đầu ra được cung cấp dưới đây:

2/3 -> 3/8   (4th number in iteration 3)
4/7 -> 9/32  (between 1/2 and 3/5 in the chart above)
1/1 -> 1/2   (middle number in the first iteration)

Đầu vào bạn không cần hỗ trợ, nhưng được đưa vào để tham khảo:

0/1 -> 0/1   (0/1 is considered the left number)
1/0 -> 1/1   (1/0 is considered the rightmost number)

Chương trình ngắn nhất trong bất kỳ ngôn ngữ nào để đạt được mục tiêu này sẽ giành chiến thắng.


Có bất kỳ yêu cầu đầu vào / đầu ra? ví dụ: Chỉ là một chức năng đủ như trong giải pháp tham chiếu của bạn, hay nó cần phải là một chương trình độc lập? Liệu định dạng đầu ra phân số có vấn đề?
Darren Stone

Một chức năng là đủ. Tôi sẽ làm rõ hơn trong phần mô tả vấn đề.
Joe Z.

Tôi hơi muộn khi nghĩ về điều đó; Có lẽ tôi sẽ cố gắng làm rõ nó vào ngày mai.
Joe Z.

2
Ok, tôi nghĩ rằng sự lựa chọn mà bạn có trong đầu là gán cho mỗi độ sâu trong cây một mẫu số không đổi 2 ^ (độ sâu + 1) và tử số 1, 3, 5, 7, ... từ bên trái.
Peter Taylor

1
Một cách khác để xây dựng nó là số đầu tiên các nút của cây trong chiều rộng đầu tiên để bắt đầu lúc 1 (tức là 1/1 => 1, 1/2 => 2, 2/1 => 3, 1/3 => 4, vv). Nếu số được tạo cho một nút là n, thì 2^lg n(nhật ký nhị phân) là bit được đặt cao nhất nvà phân số nhị phân mong muốn là (2*(n - 2^lg n) + 1) / 2^(lg n + 1). (Bất cứ ai đang thử một giải pháp trình biên dịch mã trong một tập lệnh có bit-set-set-bit có thể sẽ muốn sử dụng phương pháp này).
Peter Taylor

Câu trả lời:


1

GolfScript ( 49 48 46 ký tự)

{0\@{}{@2*2$2$>!+@@{{\}3$)*}:j~1$-j}/\)\,?}:f;

hoặc là

{0:x;\{}{.2$<!2x*+:x){\}*1$-{\}x)*}/x@)@,?}:g;

Cả hai đều là các hàm lấy tử số và mẫu số trên ngăn xếp và để lại tử số và mẫu số trên ngăn xếp. Bản demo trực tuyến .

Ý tưởng cốt lõi được thể hiện bằng mã giả trong phần Toán học cụ thể 4.5 (p122 trong phiên bản của tôi):

while m != n do
    if m < n then (output(L); n := n - m)
             else (output(R); m := m - n)

Nếu chuỗi Ls và R được hiểu là giá trị nhị phân với L = 0 và R = 1 thì hai lần giá trị đó cộng với một là tử số và mẫu số dài hơn một bit.

Là một điểm thú vị đối với Golfscripters, đây là một trong những dịp hiếm hoi khi tôi thấy hữu ích. (Ok, tôi chỉ sử dụng nó như một bộ đếm vòng lặp, nhưng điều đó tốt hơn là không có gì).


1

Mathicala, 130 114 111 ký tự

f=#~g~0&;0~g~q_=q;p_~g~q_:=g[#,(Sign[p-#]+q)/2]&@FromContinuedFraction[ContinuedFraction@p/.{x___,n_}:>{x,n-1}]

Thí dụ:

f[2/3]

3/8

f[4/7]

9/32

f[1]

1/2


1

Hồng ngọc, 132 125

Rubied & đánh golf giải pháp tham khảo từ @JoeZ.

def t(n,d)u=k=0;v,j,f,g,b=[1,]*5;c=2
while(z=(f*d).<=>(g*n))!=0;z>0?(j,k=f,g):(u,v=f,g);b=b*2-z;f,g=u+j,v+k;c*=2;end
[b,c]end

Ví dụ sử dụng:

>> t(2,3)
=> [3, 8]
>> t(4,7)
=> [9, 32]
>> t(1,1)
=> [1, 2]

1

Ruby (69 ký tự) CoffeeScript (59 ký tự)

Đây là một hàm lấy tử số và mẫu số làm đối số và trả về một mảng chứa tử số và mẫu số sau mệnh đề.

g=(a,b,x=0,y=1)->c=a>=b;a&&g(a-b*c,b-a*!c,2*x+c,2*y)||[x,y]

Bản demo trực tuyến

Nó sử dụng cách tiếp cận tương tự như giải pháp GolfScript của tôi ở trên, nhưng dễ đọc hơn nhiều vì tôi có thể sử dụng 4 biến mà không phải lo lắng về quyền anh và bỏ hộp vào một mảng. Tôi đã chọn CoffeeScript vì nó không có các tiền tố biến với $(20 ký tự được lưu trong ví dụ PHP), có cú pháp định nghĩa hàm ngắn cho phép các giá trị tham số mặc định (vì vậy không cần phải bọc f(a,b,x,y)hàm g(a,b) = f(a,b,0,1)) và cho phép tôi sử dụng Booleans làm số nguyên trong biểu thức với các giá trị hữu ích. Đối với những người không biết điều đó, CoffeeScript không có toán tử ternary kiểu C tiêu chuẩn ( C?P:Q), nhưng tôi có thể thay thế C&&P||Qở đây vì Psẽ không bao giờ bị giả mạo.

Một cách khác có thể thanh lịch hơn, nhưng không thể thiếu ngắn hơn, là thay thế phép trừ lặp đi lặp lại bằng phép chia và modulo:

f=(a,b,x=0,y=1,p=0)->a&&f(b%a,a,(x+p<<b/a)-p,y<<b/a,1-p)||[x+p,y]

(65 ký tự; bản demo trực tuyến ). Viết nó theo cách này phơi bày mối quan hệ với thuật toán của Euclid.


Bạn không cần dấu ngoặc đơn a<bđể tiết kiệm cho bạn một char. Nội tuyến ccho thêm hai. Bạn cũng có thể xem xét cú pháp f=->a,b,x=0,y=1{...}cho định nghĩa thậm chí ngắn hơn.
Howard

@Howard, tôi không biết phiên bản Ruby nào bạn đang sử dụng, nhưng trên IDEOne tôi gặp lỗi cú pháp nếu tôi thử xóa các dấu ngoặc đơn đó hoặc sử dụng cú pháp hàm đó.
Peter Taylor

Hãy thử c=a<b ?với một không gian thêm sau b. Nếu không, dấu hỏi được coi là một phần của b.
Howard

0

Con trăn - 531

Một giải pháp vô căn cứ trong Python, để phục vụ như một giải pháp tham chiếu cuối cùng:

def sbtree(n, d): 
    ufrac = [0, 1]
    lfrac = [1, 0]
    frac = [1, 1]
    bfrac = [1, 2]
    while(frac[0] * d != frac[1] * n): 
        if(frac[0] * d > frac[1] * n): 
            # push it towards lfrac
            lfrac[0] = frac[0]
            lfrac[1] = frac[1]
            bfrac[0] = bfrac[0] * 2 - 1 
        elif(frac[0] * d < frac[1] * n): 
            # push it towards ufrac
            ufrac[0] = frac[0]
            ufrac[1] = frac[1]
            bfrac[0] = bfrac[0] * 2 + 1 
        frac[0] = ufrac[0] + lfrac[0]
        frac[1] = ufrac[1] + lfrac[1]
        bfrac[1] *= 2
    return bfrac

Nó chỉ đơn giản thực hiện một tìm kiếm nhị phân giữa các phần phân đoạn, lợi dụng thực tế là Mediant của bất kỳ hai phân số sẽ luôn luôn giữa các giá trị của hai phân số.


0

GolfScript, 54 ký tự

'/'/n*~][2,.-1%]{[{.~3$~@+@@+[\]\}*].2$?0<}do.@?'/'@,(

Đầu vào phải được đưa ra trên STDIN theo mẫu được chỉ định trong tác vụ. Bạn có thể thử mã trực tuyến .

> 4/7
9/32

> 9/7
35/64

> 5/1
31/32

0

Toán học 138

Không được sắp xếp hợp lý như thủ tục của alephalpha, nhưng đó là sản phẩm tốt nhất mà tôi có thể sản xuất cho đến nay.

q_~r~k_:=Nest[#+Sign@k/(2Denominator@# )&,q,Abs@k]  
g@d_:=
Module[{l=ContinuedFraction@d,p=-1},
l[[-1]]-=1;
(p=-p;# p)&/@l]
h[q_]:=Fold[r,1/2,g@q]

Kiểm tra

h[2/3]
h[4/7]
h[1]

3/8
9/32
1/2


0

JavaScript 186

f=(p1,q1,p2,q2)=>{if(p1*q2+1==p2*q1){return{p:p1+p2,q:q1+q2}}let p,q,pl=0,ql=1,ph=1,qh=0;for(;;){p=pl+ph;q=ql+qh;if(p*q1<=q*p1){pl=p;ql=q}else if(p2*q<=q2*p){ph=p;qh=q}else return{p,q}}}

có thể ít hơn, nhưng tôi thích golf có thể đọc được


0

Haskell , 125 byte

n((a,b):(c,d):r)=(a,b):(a+c,b+d):n((c,d):r)
n a=a
z=zip[0..]
t x=[(j,2^i)|(i,r)<-z$iterate n[(0,1),(1,0)],(j,y)<-z r,x==y]!!0

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

Đầu vào và đầu ra dưới dạng một cặp (n,d).

Giải thích ngắn gọn:

nxây dựng hàng tiếp theo từ hàng trước bằng cách xem xét từng cặp phân số và chèn cái mới vào giữa đệ quy và đệ quy (sẽ đặt phân số thứ hai ngay tại đó). Trường hợp cơ bản là rất đơn giản vì về cơ bản nó chỉ là chức năng nhận dạng. Các tchức năng lặp rằng chức năng vô hạn định dựa trên tình trạng ban đầu chỉ với hai phân ranh giới. tsau đó lập chỉ mục từng hàng ( i) và từng mục trong hàng ( j) và tìm phân số đầu tiên phù hợp với những gì chúng tôi đang tìm kiếm. Khi tìm thấy nó mang lại jlà tử số và 2^imẫu số.

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.