Được rồi, đây là câu trả lời của tôi chỉ sử dụng phân số tiếp tục .
Đầu tiên chúng ta hãy lấy một số thuật ngữ ở đây.
Gọi X = p / q là phân số chưa biết.
Gọi Q (X, p / q) = sign (X - p / q) là hàm truy vấn: nếu nó là 0, chúng tôi đã đoán số và nếu nó +/- 1 cho chúng tôi biết dấu hiệu lỗi của chúng tôi .
Các ký hiệu thông thường để tiếp tục phân số là A = [a 0 ; a 1 , a 2 , a 3 , ... a k ]
= a 0 + 1 / (a 1 + 1 / (a 2 + 1 / (a 3 + 1 / (... + 1 / a k ) ...)))
Chúng tôi sẽ làm theo thuật toán sau cho 0 <p / q <1.
Khởi tạo Y = 0 = [0], Z = 1 = [1], k = 0.
Vòng lặp bên ngoài : Điều kiện tiên quyết là:
Y và Z là các phân số liên tiếp của k + 1 số hạng giống hệt nhau ngoại trừ phần tử cuối cùng, chúng khác nhau 1, sao cho Y = [y 0 ; y 1 , y 2 , y 3 , ... y k ] và Z = [y 0 ; y 1 , y 2 , y 3 , ... y k + 1]
(-1) k (YX) <0 <(-1) k (ZX), hoặc đơn giản hơn, với k chẵn, Y <X <Z và k lẻ, Z <X <Y.
Mở rộng bậc của phân số tiếp tục thêm 1 bước mà không thay đổi giá trị của các số. Nói chung, nếu các số hạng cuối cùng là y k và y k + 1, chúng ta đổi thành [... y k , y k + 1 = ∞] và [... y k , z k + 1 = 1]. Bây giờ tăng k thêm 1.
Vòng lặp bên trong : Điều này về cơ bản giống với câu hỏi phỏng vấn của @ templatetypedef về các số nguyên. Chúng tôi thực hiện tìm kiếm nhị phân hai giai đoạn để đến gần hơn:
Vòng lặp bên trong 1 : y k = ∞, z k = a, và X nằm giữa Y và Z.
Nhân đôi số hạng cuối của Z: Tính M = Z nhưng với m k = 2 * a = 2 * z k .
Truy vấn số chưa biết: q = Q (X, M).
Nếu q = 0, chúng ta có câu trả lời và chuyển sang bước 17.
Nếu q và Q (X, Y) có dấu trái dấu, nghĩa là X nằm giữa Y và M, do đó đặt Z = M và chuyển sang bước 5.
Nếu không, hãy đặt Y = M và chuyển sang bước tiếp theo:
Vòng lặp bên trong 2. y k = b, z k = a, và X nằm giữa Y và Z.
Nếu a và b khác 1, hoán đổi Y và Z, chuyển sang bước 2.
Thực hiện tìm kiếm nhị phân: tính M trong đó m k = floor ((a + b) / 2 và truy vấn q = Q (X, M).
Nếu q = 0, chúng ta đã hoàn thành và chuyển sang bước 17.
Nếu q và Q (X, Y) có dấu trái dấu, nghĩa là X nằm giữa Y và M, vì vậy đặt Z = M và chuyển sang bước 11.
Nếu không, q và Q (X, Z) có dấu hiệu trái dấu, nghĩa là X nằm giữa Z và M, vì vậy hãy đặt Y = M và chuyển sang bước 11.
Thực hiện: X = M.
Một ví dụ cụ thể cho X = 16/113 = 0,14159292
Y = 0 = [0], Z = 1 = [1], k = 0
k = 1:
Y = 0 = [0; ∞] < X, Z = 1 = [0; 1] > X, M = [0; 2] = 1/2 > X.
Y = 0 = [0; ∞], Z = 1/2 = [0; 2], M = [0; 4] = 1/4 > X.
Y = 0 = [0; ∞], Z = 1/4 = [0; 4], M = [0; 8] = 1/8 < X.
Y = 1/8 = [0; 8], Z = 1/4 = [0; 4], M = [0; 6] = 1/6 > X.
Y = 1/8 = [0; 8], Z = 1/6 = [0; 6], M = [0; 7] = 1/7 > X.
Y = 1/8 = [0; 8], Z = 1/7 = [0; 7]
--> the two last terms differ by one, so swap and repeat outer loop.
k = 2:
Y = 1/7 = [0; 7, ∞] > X, Z = 1/8 = [0; 7, 1] < X,
M = [0; 7, 2] = 2/15 < X
Y = 1/7 = [0; 7, ∞], Z = 2/15 = [0; 7, 2],
M = [0; 7, 4] = 4/29 < X
Y = 1/7 = [0; 7, ∞], Z = 4/29 = [0; 7, 4],
M = [0; 7, 8] = 8/57 < X
Y = 1/7 = [0; 7, ∞], Z = 8/57 = [0; 7, 8],
M = [0; 7, 16] = 16/113 = X
--> done!
Tại mỗi bước tính toán M, phạm vi của khoảng giảm. Có lẽ khá dễ dàng để chứng minh (mặc dù tôi sẽ không làm điều này) rằng khoảng thời gian giảm đi một hệ số ít nhất là 1 / sqrt (5) ở mỗi bước, điều này sẽ cho thấy rằng thuật toán này là O (log q) bước.
Lưu ý rằng điều này có thể được kết hợp với câu hỏi phỏng vấn ban đầu của templatetypedef và áp dụng cho bất kỳ số hữu tỉ p / q nào, không chỉ giữa 0 và 1, bằng cách tính toán đầu tiên Q (X, 0), sau đó cho cả hai số nguyên dương / âm, giới hạn giữa hai liên tiếp số nguyên, và sau đó sử dụng thuật toán trên cho phần phân số.
Khi có cơ hội tiếp theo, tôi sẽ đăng một chương trình python thực hiện thuật toán này.
chỉnh sửa : ngoài ra, lưu ý rằng bạn không cần phải tính phân số tiếp theo mỗi bước (sẽ là O (k), có những giá trị gần đúng một phần cho phân số tiếp tục có thể tính bước tiếp theo từ bước trước đó trong O (1). )
chỉnh sửa 2 : Định nghĩa đệ quy của xấp xỉ từng phần:
Nếu A k = [a 0 ; a 1 , a 2 , a 3 , ... a k ] = p k / q k , thì p k = a k p k-1 + p k-2 , và q k = a k q k-1 + q k-2 . (Nguồn: Niven & Zuckerman, xuất bản lần thứ 4, Định lý 7.3-7.5. Xem thêm Wikipedia )
Ví dụ: [0] = 0/1 = p 0 / q 0 , [0; 7] = 1/7 = p 1 / q 1 ; vì vậy [0; 7, 16] = (16 * 1 + 0) / (16 * 7 + 1) = 16/113 = p 2 / q 2 .
Điều này có nghĩa là nếu hai phân số tiếp tục Y và Z có cùng số hạng ngoại trừ số cuối cùng và phân số tiếp tục không bao gồm số hạng cuối cùng là p k-1 / q k-1 , thì chúng ta có thể viết Y = (y k p k- 1 + p k-2 ) / (y k q k-1 + q k-2 ) và Z = (z k p k-1 + p k-2 ) / (z k q k-1 + q k-2 ). Từ đó có thể cho thấy rằng | YZ | giảm ít nhất một hệ số là 1 / sqrt (5) tại mỗi khoảng thời gian nhỏ hơn được tạo ra bởi thuật toán này, nhưng đại số dường như vượt xa tôi vào lúc này. :-(
Đây là chương trình Python của tôi:
import math
# Return a function that returns Q(p0/q0,p/q)
# = sign(p0/q0-p/q) = sign(p0q-q0p)*sign(q0*q)
# If p/q < p0/q0, then Q() = 1; if p/q < p0/q0, then Q() = -1; otherwise Q()=0.
def makeQ(p0,q0):
def Q(p,q):
return cmp(q0*p,p0*q)*cmp(q0*q,0)
return Q
def strsign(s):
return '<' if s<0 else '>' if s>0 else '=='
def cfnext(p1,q1,p2,q2,a):
return [a*p1+p2,a*q1+q2]
def ratguess(Q, doprint, kmax):
# p2/q2 = p[k-2]/q[k-2]
p2 = 1
q2 = 0
# p1/q1 = p[k-1]/q[k-1]
p1 = 0
q1 = 1
k = 0
cf = [0]
done = False
while not done and (not kmax or k < kmax):
if doprint:
print 'p/q='+str(cf)+'='+str(p1)+'/'+str(q1)
# extend continued fraction
k = k + 1
[py,qy] = [p1,q1]
[pz,qz] = cfnext(p1,q1,p2,q2,1)
ay = None
az = 1
sy = Q(py,qy)
sz = Q(pz,qz)
while not done:
if doprint:
out = str(py)+'/'+str(qy)+' '+strsign(sy)+' X '
out += strsign(-sz)+' '+str(pz)+'/'+str(qz)
out += ', interval='+str(abs(1.0*py/qy-1.0*pz/qz))
if ay:
if (ay - az == 1):
[p0,q0,a0] = [pz,qz,az]
break
am = (ay+az)/2
else:
am = az * 2
[pm,qm] = cfnext(p1,q1,p2,q2,am)
sm = Q(pm,qm)
if doprint:
out = str(ay)+':'+str(am)+':'+str(az) + ' ' + out + '; M='+str(pm)+'/'+str(qm)+' '+strsign(sm)+' X '
print out
if (sm == 0):
[p0,q0,a0] = [pm,qm,am]
done = True
break
elif (sm == sy):
[py,qy,ay,sy] = [pm,qm,am,sm]
else:
[pz,qz,az,sz] = [pm,qm,am,sm]
[p2,q2] = [p1,q1]
[p1,q1] = [p0,q0]
cf += [a0]
print 'p/q='+str(cf)+'='+str(p1)+'/'+str(q1)
return [p1,q1]
và đầu ra mẫu cho ratguess(makeQ(33102,113017), True, 20)
:
p/q=[0]=0/1
None:2:1 0/1 < X < 1/1, interval=1.0; M=1/2 > X
None:4:2 0/1 < X < 1/2, interval=0.5; M=1/4 < X
4:3:2 1/4 < X < 1/2, interval=0.25; M=1/3 > X
p/q=[0, 3]=1/3
None:2:1 1/3 > X > 1/4, interval=0.0833333333333; M=2/7 < X
None:4:2 1/3 > X > 2/7, interval=0.047619047619; M=4/13 > X
4:3:2 4/13 > X > 2/7, interval=0.021978021978; M=3/10 > X
p/q=[0, 3, 2]=2/7
None:2:1 2/7 < X < 3/10, interval=0.0142857142857; M=5/17 > X
None:4:2 2/7 < X < 5/17, interval=0.00840336134454; M=9/31 < X
4:3:2 9/31 < X < 5/17, interval=0.00379506641366; M=7/24 < X
p/q=[0, 3, 2, 2]=5/17
None:2:1 5/17 > X > 7/24, interval=0.00245098039216; M=12/41 < X
None:4:2 5/17 > X > 12/41, interval=0.00143472022956; M=22/75 > X
4:3:2 22/75 > X > 12/41, interval=0.000650406504065; M=17/58 > X
p/q=[0, 3, 2, 2, 2]=12/41
None:2:1 12/41 < X < 17/58, interval=0.000420521446594; M=29/99 > X
None:4:2 12/41 < X < 29/99, interval=0.000246366100025; M=53/181 < X
4:3:2 53/181 < X < 29/99, interval=0.000111613371282; M=41/140 < X
p/q=[0, 3, 2, 2, 2, 2]=29/99
None:2:1 29/99 > X > 41/140, interval=7.21500721501e-05; M=70/239 < X
None:4:2 29/99 > X > 70/239, interval=4.226364059e-05; M=128/437 > X
4:3:2 128/437 > X > 70/239, interval=1.91492009996e-05; M=99/338 > X
p/q=[0, 3, 2, 2, 2, 2, 2]=70/239
None:2:1 70/239 < X < 99/338, interval=1.23789953207e-05; M=169/577 > X
None:4:2 70/239 < X < 169/577, interval=7.2514738621e-06; M=309/1055 < X
4:3:2 309/1055 < X < 169/577, interval=3.28550190148e-06; M=239/816 < X
p/q=[0, 3, 2, 2, 2, 2, 2, 2]=169/577
None:2:1 169/577 > X > 239/816, interval=2.12389981991e-06; M=408/1393 < X
None:4:2 169/577 > X > 408/1393, interval=1.24415093544e-06; M=746/2547 < X
None:8:4 169/577 > X > 746/2547, interval=6.80448470014e-07; M=1422/4855 < X
None:16:8 169/577 > X > 1422/4855, interval=3.56972657711e-07; M=2774/9471 > X
16:12:8 2774/9471 > X > 1422/4855, interval=1.73982239227e-07; M=2098/7163 > X
12:10:8 2098/7163 > X > 1422/4855, interval=1.15020646951e-07; M=1760/6009 > X
10:9:8 1760/6009 > X > 1422/4855, interval=6.85549088053e-08; M=1591/5432 < X
p/q=[0, 3, 2, 2, 2, 2, 2, 2, 9]=1591/5432
None:2:1 1591/5432 < X < 1760/6009, interval=3.06364213998e-08; M=3351/11441 < X
p/q=[0, 3, 2, 2, 2, 2, 2, 2, 9, 1]=1760/6009
None:2:1 1760/6009 > X > 3351/11441, interval=1.45456726663e-08; M=5111/17450 < X
None:4:2 1760/6009 > X > 5111/17450, interval=9.53679318849e-09; M=8631/29468 < X
None:8:4 1760/6009 > X > 8631/29468, interval=5.6473816179e-09; M=15671/53504 < X
None:16:8 1760/6009 > X > 15671/53504, interval=3.11036635336e-09; M=29751/101576 > X
16:12:8 29751/101576 > X > 15671/53504, interval=1.47201634215e-09; M=22711/77540 > X
12:10:8 22711/77540 > X > 15671/53504, interval=9.64157420569e-10; M=19191/65522 > X
10:9:8 19191/65522 > X > 15671/53504, interval=5.70501257346e-10; M=17431/59513 > X
p/q=[0, 3, 2, 2, 2, 2, 2, 2, 9, 1, 8]=15671/53504
None:2:1 15671/53504 < X < 17431/59513, interval=3.14052228667e-10; M=33102/113017 == X
Vì Python xử lý phép toán biginteger ngay từ đầu và chương trình này chỉ sử dụng phép toán số nguyên (ngoại trừ các phép tính khoảng), nên nó sẽ hoạt động với các số hữu tỉ tùy ý.
sửa 3 : Phác thảo chứng minh rằng đây là O (log q), không phải O (log ^ 2 q):
Trước tiên, lưu ý rằng cho đến khi tìm thấy số hữu tỉ, số bước n k cho mỗi số hạng phân số tiếp tục mới chính xác là 2b (a_k) -1 trong đó b (a_k) là số bit cần thiết để biểu diễn a_k = ceil (log2 (a_k )): đó là b (a_k) bước để mở rộng "mạng lưới" của tìm kiếm nhị phân và b (a_k) -1 bước để thu hẹp nó). Xem ví dụ trên, bạn sẽ lưu ý rằng số bước luôn là 1, 3, 7, 15, v.v.
Bây giờ chúng ta có thể sử dụng quan hệ lặp lại q k = a k q k-1 + q k-2 và quy nạp để chứng minh kết quả mong muốn.
Hãy phát biểu theo cách này: giá trị của q sau N k = sum (n k ) bước cần thiết để đạt đến số hạng thứ k có giá trị nhỏ nhất: q> = A * 2 cN đối với một số hằng số cố định A, c. (vì vậy để đảo ngược, chúng ta sẽ nhận được rằng số bước N là <= (1 / c) * log 2 (q / A) = O (log q).)
Các trường hợp cơ bản:
- k = 0: q = 1, N = 0, do đó q> = 2 N
- k = 1: với N = 2b-1 bước, q = a 1 > = 2 b-1 = 2 (N-1) / 2 = 2 N / 2 / sqrt (2).
Điều này ngụ ý A = 1, c = 1/2 có thể cung cấp giới hạn mong muốn. Trong thực tế, q có thể không nhân đôi mỗi số hạng (ví dụ: [0; 1, 1, 1, 1, 1] có hệ số tăng trưởng là phi = (1 + sqrt (5)) / 2) vì vậy hãy sử dụng c = 1 / 4.
Hướng dẫn:
đối với số hạng k, q k = a k q k-1 + q k-2 . Một lần nữa, với n k = 2b-1 bước cần thiết cho số hạng này, a k > = 2 b-1 = 2 (n k -1) / 2 .
Vậy a k q k-1 > = 2 (N k -1) / 2 * q k-1 > = 2 (n k -1) / 2 * A * 2 N k-1 /4 = A * 2 N k / 4 / sqrt (2) * 2 n k / 4 .
Argh - phần khó khăn ở đây là nếu k = 1, q có thể không tăng nhiều cho một số hạng đó, và chúng ta cần sử dụng q k-2 nhưng nó có thể nhỏ hơn nhiều so với q k-1 .