Có thuật toán nào để tính số fibonacci thứ n trong thời gian tuyến tính phụ không?
Có thuật toán nào để tính số fibonacci thứ n trong thời gian tuyến tính phụ không?
Câu trả lời:
Số n
Fibonacci thứ được đưa ra bởi
f(n) = Floor(phi^n / sqrt(5) + 1/2)
Ở đâu
phi = (1 + sqrt(5)) / 2
Giả sử rằng các hoạt động toán học nguyên thủy ( +
, -
, *
và /
) là O(1)
bạn có thể sử dụng kết quả này để tính toán n
thứ số Fibonacci trong O(log n)
thời gian ( O(log n)
vì lũy thừa trong công thức).
Trong C #:
static double inverseSqrt5 = 1 / Math.Sqrt(5);
static double phi = (1 + Math.Sqrt(5)) / 2;
/* should use
const double inverseSqrt5 = 0.44721359549995793928183473374626
const double phi = 1.6180339887498948482045868343656
*/
static int Fibonacci(int n) {
return (int)Math.Floor(Math.Pow(phi, n) * inverseSqrt5 + 0.5);
}
phi^n / sqrt(5) + 1/2
nơi đó phi = (1 + sqrt(5)) / 2
. Đây là sự thật. Thứ hai, tôi hiểu quan điểm mà những người khác đang đưa ra về độ dài của câu trả lời O(n)
nhưng tôi đã thêm một nhận xét vào câu trả lời của mình giả định rằng các phép toán nguyên thủy cần thời gian không đổi (tôi biết là không, trừ khi bạn ràng buộc các đầu vào). Quan điểm của tôi là chúng ta có thể tìm thấy số Fibonacci thứ n trong các O(log n)
phép toán số học.
Theo sau từ tham chiếu của Pillsy đến lũy thừa ma trận, sao cho ma trận
M = [1 1] [1 0]
sau đó
fib ( n ) = M n 1,2
Nâng ma trận lên lũy thừa bằng cách sử dụng phép nhân lặp lại không hiệu quả lắm.
Hai cách tiếp cận để tính lũy thừa ma trận là chia và chinh phục mang lại M n trong các bước O ( ln n ), hoặc phân rã giá trị riêng là thời gian không đổi, nhưng có thể gây ra lỗi do độ chính xác dấu phẩy động hạn chế.
Nếu bạn muốn một giá trị chính xác lớn hơn độ chính xác của việc triển khai dấu phẩy động, bạn phải sử dụng phương pháp O (ln n) dựa trên mối quan hệ này:
M n = ( M n / 2 ) 2 nếu n chẵn = M · M n -1 nếu n lẻ
Phân tích giá trị riêng trên M tìm được hai ma trận U và Λ sao cho Λ là đường chéo và
M = U Λ U -1 M n = ( U Λ U -1 ) n = U Λ U -1 U Λ U -1 U Λ U -1 ... n lần = U Λ Λ Λ ... U -1 = U Λ n U -1Nâng ma trận đường chéo Λ lên lũy thừa thứ n là một vấn đề đơn giản khi nâng từng phần tử trong Λ lên lũy thừa thứ n , vì vậy điều này đưa ra phương pháp O (1) nâng M lên lũy thừa thứ n . Tuy nhiên, các giá trị trong Λ không có khả năng là số nguyên, vì vậy một số lỗi sẽ xảy ra.
Định nghĩa Λ cho ma trận 2x2 của chúng ta là
Λ = [λ 1 0] = [0 λ 2 ]
Để tìm mỗi λ , chúng tôi giải
| M - λ I | = 0
cái nào cho
| M - λ I | = -λ (1 - λ) - 1 λ² - λ - 1 = 0
sử dụng công thức bậc hai
λ = (-b ± √ (b² - 4ac)) / 2a = (1 ± √5) / 2 {λ 1 , λ 2 } = {Φ, 1-Φ} trong đó Φ = (1 + √5) / 2
Nếu bạn đã đọc câu trả lời của Jason, bạn có thể thấy điều này sẽ đi đến đâu.
Giải quyết các eigenvectors X 1 và X 2 :
nếu X 1 = [ X 1,1 , X 1,2 ] M . X 1 1 = λ 1 X 1 X 1,1 + X 1,2 = λ 1 X 1,1 X 1,1 = λ 1 X 1,2 => X 1 = [Φ, 1] X 2 = [1-Φ, 1]
Các vectơ này cho U :
Ư = [ X 1,1 , X 2,2 ] [ X 1,1 , X 2,2 ] = [Φ, 1-Φ] [1, 1]
Đảo ngược chữ U bằng cách sử dụng
A = [ab] [cd] => A -1 = (1 / | A |) [d -b] [-ca]
nên U -1 được cho bởi
U -1 = (1 / (Φ - (1 - Φ)) [1 Φ-1] [-1 Φ] U -1 = (√5) -1 [1 Φ-1] [-1 Φ]
Kiểm tra sự tỉnh táo:
UΛU -1 = (√5) -1 [Φ 1-Φ]. [Φ 0]. [1 Φ-1] [1 1] [0 1-Φ] [-1 Φ] hãy để Ψ = 1-Φ, giá trị riêng khác vì Φ là một gốc của λ²-λ-1 = 0 so -ΨΦ = Φ²-Φ = 1 và Ψ + Φ = 1 UΛU -1 = (√5) -1 [Φ Ψ]. [Φ 0]. [1 -Ψ] [1 1] [0 Ψ] [-1 Φ] = (√5) -1 [Φ Ψ]. [Φ -ΨΦ] [1 1] [-Ψ ΨΦ] = (√5) -1 [Φ Ψ]. [Φ 1] [1 1] [-Ψ -1] = (√5) -1 [Φ²-Ψ² Φ-Ψ] [Φ-Ψ 0] = [Φ + Ψ 1] [1 0] = [1 1] [1 0] = M
Vì vậy, kiểm tra sự tỉnh táo giữ.
Bây giờ chúng ta có mọi thứ chúng ta cần để tính M n 1,2 :
M n = U Λ n U -1 = (√5) -1 [Φ Ψ]. [Φ n 0]. [1 -Ψ] [1 1] [0 Ψ n ] [-1 Φ] = (√5) -1 [Φ Ψ]. [Φ n -ΨΦ n ] [1 1] [-Ψ n Ψ n Φ] = (√5) -1 [Φ Ψ]. [Φ n Φ n -1 ] [1 1] [-Ψ n -Ψ n -1 ] as ΨΦ = -1 = (√5) -1 [Φ n +1 -Ψ n +1 Φ n -Ψ n ] [Φ n -Ψ n Φ n -1 -Ψ n -1 ]
vì thế
fib ( n ) = M n 1,2 = (Φ n - (1-Φ) n ) / √5
Mà đồng ý với công thức được đưa ra ở nơi khác.
Bạn có thể suy ra nó từ quan hệ tuần hoàn, nhưng trong kỹ thuật tính toán và mô phỏng, việc tính toán các giá trị riêng và giá trị riêng của ma trận lớn là một hoạt động quan trọng, vì nó mang lại sự ổn định và hài của hệ phương trình, cũng như cho phép nâng ma trận lên lũy thừa một cách hiệu quả.
Nếu bạn muốn số chính xác (là "bignum", chứ không phải là int / float), thì tôi e rằng
Điều đó là không thể!
Như đã nêu ở trên, công thức cho số Fibonacci là:
fib n = sàn (phi n / √5 + 1 / 2 )
fib n ~ = phi n / √5
Có bao nhiêu chữ số fib n
?
numDigits (fib n) = log (fib n) = log (phi n / √5) = log phi n - log √5 = n * log phi - log √5
numDigits (fib n) = n * const + const
đó là O ( n )
Vì kết quả được yêu cầu là của O ( n ), nó không thể được tính trong thời gian ít hơn O ( n ).
Nếu bạn chỉ muốn các chữ số thấp hơn của câu trả lời, thì bạn có thể tính toán theo thời gian tuyến tính con bằng phương pháp lũy thừa ma trận.
O(n*log n)
sắp xếp dựa trên so sánh của một dãy n
số trong đó mỗi số có các O(log n)
chữ số không?
Một trong những bài tập trong SICP là về điều này, có câu trả lời được mô tả ở đây.
Theo phong cách mệnh lệnh, chương trình sẽ trông giống như
Hàm Fib ( đếm ) a ← 1 b ← 0 p ← 0 q ← 1 Trong khi đếm > 0 Làm Nếu Chẵn ( đếm ) Sau đó p ← p ² + q ² q ← 2 pq + q ² đếm ← đếm ÷ 2 Khác a ← bq + aq + ap b ← bp + aq đếm ← đếm - 1 Kết thúc Nếu Kết thúc trong khi Trả về b Kết thúc Chức năng
twisted
khuôn khổ).
if even(count)
là đúng. Dãy số bắt đầu bằng số 0 (số Fibonacci thứ 0 là số 0): 0,1,1,2,3,5,8,13, ...
Bạn cũng có thể làm điều đó bằng cách lũy thừa ma trận các số nguyên. Nếu bạn có ma trận
/ 1 1 \
M = | |
\ 1 0 /
thì (M^n)[1, 2]
sẽ bằng với n
số Fibonacci thứ, nếu []
là chỉ số con của ma trận và ^
là lũy thừa ma trận. Đối với ma trận có kích thước cố định, phép lũy thừa thành lũy thừa dương có thể được thực hiện trong thời gian O (log n) theo cách tương tự như với số thực.
CHỈNH SỬA: Tất nhiên, tùy thuộc vào loại câu trả lời bạn muốn, bạn có thể sử dụng thuật toán thời gian không đổi. Giống như các công thức khác cho thấy, n
số Fibonacci thứ tăng theo cấp số nhân với n
. Ngay cả với các số nguyên không dấu 64 bit, bạn sẽ chỉ cần một bảng tra cứu 94 mục nhập để bao gồm toàn bộ phạm vi.
CHỈNH SỬA THỨ HAI: Làm ma trận theo cấp số nhân với một phân tích riêng trước tiên chính xác tương đương với giải pháp của JDunkerly bên dưới. Các giá trị riêng của ma trận này là (1 + sqrt(5))/2
và (1 - sqrt(5))/2
.
Wikipedia có một giải pháp dạng đóng http://en.wikipedia.org/wiki/Fibonacci_number
Hoặc trong c #:
public static int Fibonacci(int N)
{
double sqrt5 = Math.Sqrt(5);
double phi = (1 + sqrt5) / 2.0;
double fn = (Math.Pow(phi, N) - Math.Pow(1 - phi, N)) / sqrt5;
return (int)fn;
}
|1 - phi|^n / sqrt(5) < 1/2
khi nào n
là một số nguyên không âm.
Đối với những cái thực sự lớn, hàm đệ quy này hoạt động. Nó sử dụng các phương trình sau:
F(2n-1) = F(n-1)^2 + F(n)^2
F(2n) = (2*F(n-1) + F(n)) * F(n)
Bạn cần một thư viện cho phép bạn làm việc với các số nguyên lớn. Tôi sử dụng thư viện BigInteger từ https://mattmccutchen.net/bigint/ .
Bắt đầu với một mảng các số fibonacci. Sử dụng fibs [0] = 0, fibs [1] = 1, fibs [2] = 1, fibs [3] = 2, fibs [4] = 3, v.v. Trong ví dụ này, tôi sử dụng một mảng gồm 501 đầu tiên (đếm 0). Bạn có thể tìm thấy 500 số Fibonacci khác 0 đầu tiên tại đây: http://home.hiwaay.net/~jalison/Fib500.html . Phải chỉnh sửa một chút để đưa nó vào đúng định dạng, nhưng điều đó không quá khó.
Sau đó, bạn có thể tìm thấy bất kỳ số Fibonacci nào bằng cách sử dụng hàm này (trong C):
BigUnsigned GetFib(int numfib)
{
int n;
BigUnsigned x, y, fib;
if (numfib < 501) // Just get the Fibonacci number from the fibs array
{
fib=(stringToBigUnsigned(fibs[numfib]));
}
else if (numfib%2) // numfib is odd
{
n=(numfib+1)/2;
x=GetFib(n-1);
y=GetFib(n);
fib=((x*x)+(y*y));
}
else // numfib is even
{
n=numfib/2;
x=GetFib(n-1);
y=GetFib(n);
fib=(((big2*x)+y)*y);
}
return(fib);
}
Tôi đã kiểm tra điều này cho số Fibonacci thứ 25.000 và những thứ tương tự.
Đây là phiên bản đệ quy của tôi đệ quy log (n) lần. Tôi nghĩ rằng nó dễ đọc nhất ở dạng đệ quy:
def my_fib(x):
if x < 2:
return x
else:
return my_fib_helper(x)[0]
def my_fib_helper(x):
if x == 1:
return (1, 0)
if x % 2 == 1:
(p,q) = my_fib_helper(x-1)
return (p+q,p)
else:
(p,q) = my_fib_helper(x/2)
return (p*p+2*p*q,p*p+q*q)
Nó hoạt động vì bạn có thể tính toán fib(n),fib(n-1)
bằng cách sử dụng fib(n-1),fib(n-2)
nếu n lẻ và nếu n chẵn, bạn có thể tính fib(n),fib(n-1)
bằng cách sử dụng fib(n/2),fib(n/2-1)
.
Trường hợp cơ sở và trường hợp lẻ là đơn giản. Để suy ra trường hợp chẵn, hãy bắt đầu với a, b, c là các giá trị fibonacci liên tiếp (ví dụ: 8,5,3) và viết chúng trong một ma trận, với a = b + c. Để ý:
[1 1] * [a b] = [a+b a]
[1 0] [b c] [a b]
Từ đó, chúng ta thấy rằng ma trận của ba số fibonacci đầu tiên, nhân với ma trận của ba số fibonacci liên tiếp bất kỳ, bằng với số tiếp theo. Vì vậy, chúng tôi biết rằng:
n
[1 1] = [fib(n+1) fib(n) ]
[1 0] [fib(n) fib(n-1)]
Vì thế:
2n 2
[1 1] = [fib(n+1) fib(n) ]
[1 0] [fib(n) fib(n-1)]
Đơn giản hóa bên tay phải dẫn đến trường hợp đồng đều.
sử dụng R
l1 <- (1+sqrt(5))/2
l2 <- (1-sqrt(5))/2
P <- matrix(c(0,1,1,0),nrow=2) #permutation matrix
S <- matrix(c(l1,1,l2,1),nrow=2)
L <- matrix(c(l1,0,0,l2),nrow=2)
C <- c(-1/(l2-l1),1/(l2-l1))
k<-20 ; (S %*% L^k %*% C)[2]
[1] 6765
Số học điểm cố định không chính xác. Mã C # của Jason đưa ra câu trả lời không chính xác cho n = 71 (308061521170130 thay vì 308061521170129) và hơn thế nữa.
Để có câu trả lời đúng, hãy sử dụng một hệ thống đại số tính toán. Sympy là một thư viện như vậy cho Python. Có một bảng điều khiển tương tác tại http://live.sympy.org/ . Sao chép và dán chức năng này
phi = (1 + sqrt(5)) / 2
def f(n):
return floor(phi**n / sqrt(5) + 1/2)
Sau đó tính toán
>>> f(10)
55
>>> f(71)
308061521170129
Bạn có thể muốn thử kiểm tra phi
.
Ngoài việc tinh chỉnh bằng các phương pháp toán học, một trong những giải pháp tối ưu tốt nhất (tôi tin rằng) là sử dụng từ điển để tránh các phép tính lặp lại.
import time
_dict = {1:1, 2:1}
def F(n, _dict):
if n in _dict.keys():
return _dict[n]
else:
result = F(n-1, _dict) + F(n-2, _dict)
_dict.update({n:result})
return result
start = time.time()
for n in range(1,100000):
result = F(n, _dict)
finish = time.time()
print(str(finish - start))
Chúng tôi bắt đầu với từ điển tầm thường (hai giá trị đầu tiên của dãy Fibonacci) và liên tục thêm các giá trị Fibonacci vào từ điển.
Mất khoảng 0,7 giây cho 100000 giá trị Fibonacci đầu tiên (CPU Intel Xeon E5-2680 @ 2,70 GHz, RAM 16 GB, HĐH Windows 10-64 bit)
xem thuật toán chia và chinh phục tại đây
Liên kết có mã giả cho phép lũy thừa ma trận được đề cập trong một số câu trả lời khác cho câu hỏi này.
Bạn có thể sử dụng phương trình căn bậc hai kỳ lạ để có câu trả lời chính xác. Lý do là $ \ sqrt (5) $ bị rơi ra ở cuối, bạn chỉ cần theo dõi các hệ số với định dạng nhân của riêng bạn.
def rootiply(a1,b1,a2,b2,c):
''' multipy a1+b1*sqrt(c) and a2+b2*sqrt(c)... return a,b'''
return a1*a2 + b1*b2*c, a1*b2 + a2*b1
def rootipower(a,b,c,n):
''' raise a + b * sqrt(c) to the nth power... returns the new a,b and c of the result in the same format'''
ar,br = 1,0
while n != 0:
if n%2:
ar,br = rootiply(ar,br,a,b,c)
a,b = rootiply(a,b,a,b,c)
n /= 2
return ar,br
def fib(k):
''' the kth fibonacci number'''
a1,b1 = rootipower(1,1,5,k)
a2,b2 = rootipower(1,-1,5,k)
a = a1-a2
b = b1-b2
a,b = rootiply(0,1,a,b,5)
# b should be 0!
assert b == 0
return a/2**k/5
if __name__ == "__main__":
assert rootipower(1,2,3,3) == (37,30) # 1+2sqrt(3) **3 => 13 + 4sqrt(3) => 39 + 30sqrt(3)
assert fib(10)==55
Dưới đây là một lớp lót tính F (n), sử dụng các số nguyên có kích thước O (n), trong các phép toán số học O (log n):
for i in range(1, 50):
print(i, pow(2<<i, i, (4<<2*i)-(2<<i)-1)//(2<<i))
Sử dụng số nguyên có kích thước O (n) là hợp lý, vì nó có thể so sánh với kích thước của câu trả lời.
Để hiểu điều này, hãy gọi phi là tỷ lệ vàng (nghiệm lớn nhất của x ^ 2 = x + 1) và F (n) là số Fibonacci thứ n, trong đó F (0) = 0, F (1) = F (2) = 1
Bây giờ, phi ^ n = F (n-1) + F (n) phi.
Chứng minh bằng quy nạp: phi ^ 1 = 0 + 1 * phi = F (0) + F (1) phi. Và nếu phi ^ n = F (n-1) + F (n) phi, thì phi ^ (n + 1) = F (n-1) phi + F (n) phi ^ 2 = F (n-1) phi + F (n) (phi + 1) = F (n) + (F (n) + F (n-1)) phi = F (n) + F (n + 1) phi. Bước khó duy nhất trong phép tính này là bước thay thế phi ^ 2 bằng (1 + phi), theo sau vì phi là tỷ lệ vàng.
Ngoài ra các số có dạng (a + b * phi), trong đó a, b là các số nguyên được đóng dưới phép nhân.
Chứng minh: (p0 + p1 * phi) (q0 + q1 * phi) = p0q0 + (p0q1 + q1p0) phi + p1q1 * phi ^ 2 = p0q0 + (p0q1 + q1p0) phi + p1q1 * (phi + 1) = ( p0q0 + p1q1) + (p0q1 + q1p0 + p1q1) * phi.
Sử dụng cách biểu diễn này, người ta có thể tính toán phi ^ n trong các phép toán số nguyên O (log n) bằng cách sử dụng lũy thừa bằng bình phương. Kết quả sẽ là F (n-1) + F (n) phi, từ đó người ta có thể đọc ra số Fibonacci thứ n.
def mul(p, q):
return p[0]*q[0]+p[1]*q[1], p[0]*q[1]+p[1]*q[0]+p[1]*q[1]
def pow(p, n):
r=1,0
while n:
if n&1: r=mul(r, p)
p=mul(p, p)
n=n>>1
return r
for i in range(1, 50):
print(i, pow((0, 1), i)[1])
Lưu ý rằng phần lớn mã này là một hàm lũy thừa chuẩn theo bình phương.
Để đi đến lớp lót bắt đầu câu trả lời này, người ta có thể lưu ý rằng biểu diễn phi bằng một số nguyên đủ lớn X
, người ta có thể thực hiện (a+b*phi)(c+d*phi)
như phép toán số nguyên (a+bX)(c+dX) modulo (X^2-X-1)
. Sau đó, pow
hàm có thể được thay thế bằng pow
hàm Python chuẩn (thuận tiện bao gồm đối số thứ ba z
để tính toán mô-đun kết quả z
. X
Lựa chọn là 2<<i
.
Tôi đã xem qua một số phương pháp để tính Fibonacci với độ phức tạp thời gian hiệu quả, sau đây là một số phương pháp:
Phương pháp 1 - Lập trình động Bây giờ ở đây cấu trúc con thường được biết đến do đó tôi sẽ chuyển ngay đến giải pháp -
static int fib(int n)
{
int f[] = new int[n+2]; // 1 extra to handle case, n = 0
int i;
f[0] = 0;
f[1] = 1;
for (i = 2; i <= n; i++)
{
f[i] = f[i-1] + f[i-2];
}
return f[n];
}
Phiên bản tối ưu hóa không gian ở trên có thể được thực hiện như sau:
static int fib(int n)
{
int a = 0, b = 1, c;
if (n == 0)
return a;
for (int i = 2; i <= n; i++)
{
c = a + b;
a = b;
b = c;
}
return b;
}
Phương pháp 2- (Sử dụng lũy thừa của ma trận {{1,1}, {1,0}})
Đây là một O (n) dựa trên thực tế là nếu chúng ta nhân n lần ma trận M = {{1,1}, {1,0}} với chính nó (nói cách khác là tính lũy thừa (M, n)), thì chúng ta nhận được số Fibonacci thứ (n + 1) làm phần tử ở hàng và cột (0, 0) trong ma trận kết quả. Giải pháp này sẽ có O (n) thời gian.
Biểu diễn ma trận đưa ra biểu thức đóng sau cho các số Fibonacci: fibonaccimatrix
static int fib(int n)
{
int F[][] = new int[][]{{1,1},{1,0}};
if (n == 0)
return 0;
power(F, n-1);
return F[0][0];
}
/*multiplies 2 matrices F and M of size 2*2, and
puts the multiplication result back to F[][] */
static void multiply(int F[][], int M[][])
{
int x = F[0][0]*M[0][0] + F[0][1]*M[1][0];
int y = F[0][0]*M[0][1] + F[0][1]*M[1][1];
int z = F[1][0]*M[0][0] + F[1][1]*M[1][0];
int w = F[1][0]*M[0][1] + F[1][1]*M[1][1];
F[0][0] = x;
F[0][1] = y;
F[1][0] = z;
F[1][1] = w;
}
/*function that calculates F[][] raise to the power n and puts the
result in F[][]*/
static void power(int F[][], int n)
{
int i;
int M[][] = new int[][]{{1,1},{1,0}};
// n - 1 times multiply the matrix to {{1,0},{0,1}}
for (i = 2; i <= n; i++)
multiply(F, M);
}
Điều này có thể được tối ưu hóa để làm việc trong thời gian phức tạp O (Logn). Chúng ta có thể thực hiện phép nhân đệ quy để lấy lũy thừa (M, n) trong phương pháp trước.
static int fib(int n)
{
int F[][] = new int[][]{{1,1},{1,0}};
if (n == 0)
return 0;
power(F, n-1);
return F[0][0];
}
static void multiply(int F[][], int M[][])
{
int x = F[0][0]*M[0][0] + F[0][1]*M[1][0];
int y = F[0][0]*M[0][1] + F[0][1]*M[1][1];
int z = F[1][0]*M[0][0] + F[1][1]*M[1][0];
int w = F[1][0]*M[0][1] + F[1][1]*M[1][1];
F[0][0] = x;
F[0][1] = y;
F[1][0] = z;
F[1][1] = w;
}
static void power(int F[][], int n)
{
if( n == 0 || n == 1)
return;
int M[][] = new int[][]{{1,1},{1,0}};
power(F, n/2);
multiply(F, F);
if (n%2 != 0)
multiply(F, M);
}
Phương pháp 3 (O (log n) Time) Dưới đây là một công thức lặp lại thú vị có thể được sử dụng để tìm Số Fibonacci thứ n trong thời gian O (log n).
Nếu n chẵn thì k = n / 2: F (n) = [2 * F (k-1) + F (k)] * F (k)
Nếu n lẻ thì k = (n + 1) / 2 F (n) = F (k) * F (k) + F (k-1) * F (k-1) Công thức này hoạt động như thế nào? Công thức có thể được suy ra từ phương trình ma trận trên. fibonaccimatrix
Lấy định thức cả hai vế, ta được (-1) n = Fn + 1Fn-1 - Fn2 Hơn nữa, vì AnAm = An + m đối với ma trận vuông A bất kỳ, các đồng dạng sau có thể được suy ra (chúng nhận được từ hai hệ số khác nhau của sản phẩm ma trận)
FmFn + Fm-1Fn-1 = Fm + n-1
Bằng cách đặt n = n + 1,
FmFn + 1 + Fm-1Fn = Fm + n
Đặt m = n
F2n-1 = Fn2 + Fn-12
F2n = (Fn-1 + Fn + 1) Fn = (2Fn-1 + Fn) Fn (Nguồn: Wiki)
Để công thức được chứng minh, chúng ta chỉ cần làm như sau Nếu n chẵn thì đặt k = n / 2 Nếu n lẻ thì đặt k = (n + 1) / 2
public static int fib(int n)
{
if (n == 0)
return 0;
if (n == 1 || n == 2)
return (f[n] = 1);
// If fib(n) is already computed
if (f[n] != 0)
return f[n];
int k = (n & 1) == 1? (n + 1) / 2
: n / 2;
// Applyting above formula [See value
// n&1 is 1 if n is odd, else 0.
f[n] = (n & 1) == 1? (fib(k) * fib(k) +
fib(k - 1) * fib(k - 1))
: (2 * fib(k - 1) + fib(k))
* fib(k);
return f[n];
}
Phương pháp 4 - Sử dụng công thức Trong phương pháp này, chúng ta thực hiện trực tiếp công thức cho số hạng thứ n trong chuỗi Fibonacci. Thời gian O (1) Không gian O (1) Fn = {[(√5 + 1) / 2] ^ n} / √5
static int fib(int n) {
double phi = (1 + Math.sqrt(5)) / 2;
return (int) Math.round(Math.pow(phi, n)
/ Math.sqrt(5));
}
Tham khảo: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html
Trước tiên, chúng ta cần lưu ý rằng các số Fibonacci (F(n))
phát triển rất nhanh n
và không thể được biểu diễn bằng 64-bit cho n
lớn hơn 93. Vì vậy, một chương trình để tính toán chúng cho cácn
nhu cầu sử dụng các cơ chế bổ sung để hoạt động trên những số lớn này. Bây giờ, chỉ xét đến số lượng các phép toán (số lượng lớn), thuật toán để tính toán tuần tự chúng sẽ yêu cầu số lượng các phép toán tuyến tính.
Chúng tôi có thể hưởng lợi từ nhận dạng dưới đây về số Fibonacci:
F(2m) = 2*F(m)*F(m+1) − (F(m))^2
F(2m+1) = (F(m))^2 + (F(m+1))^2
(một ký hiệu như A ^ 2 biểu thị hình vuông của A).
Vì vậy, nếu chúng ta biết F(m)
và F(m+1)
, chúng ta có thể trực tiếp tính toán F(2m)
và F(2m+1)
.
Hãy xem xét biểu diễn nhị phân của n
. Quan sát rằng bắt đầu với x = 1
, chúng ta có thể thực hiện x = n
bằng cách nhân đôi lặp đi lặp lại và có thể thêm 1 vào x
. Điều này có thể được thực hiện bằng cách lặp lại các bit củan
và kiểm tra xem nó là 0 hay 1.
Ý tưởng là, chúng tôi có thể duy trì sự F(x)
đồng bộ với x
. Trong mỗi lần lặp như vậy, khi chúng ta nhân đôi x
và có thể thêm 1 vào x
, chúng ta cũng có thể tính giá trị mới F(x)
bằng cách sử dụng giá trị trước đó của F(x)
và F(x+1)
, với các phương trình trên.
Vì số lần lặp sẽ tính theo logarit n
, nên tổng (số lớn) các phép toán cũng theo logarit n
.
Để biết thêm chi tiết, vui lòng tham khảo phần "Thuật toán cải tiến" của bài viết này .