Tìm hiểu dãy số Fibonacci Võ Công Chương Qua tìm hiểu tổng hợp nghiên cứu tác giả dãy số Fibonacci từ internet, thấy có nhiều thông tin mà theo hấp dẫn đời số Fibonacci, nhà toánhọc Fibonacci ai, giải thuật để tính số Fibonacci Tôi xin chia sẻ bạn đọc Định nghĩa dãy số Fibonacci Trong Toán học, số Fibonacci dãy số, kí hiệu F(n) (n số nguyên không âm), định nghĩa cách đệ quy theo công thức sau: Dãy hai số F(0)=0 F(1)=1 Như vậy, số dãy Fibonacci là: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765 Sự đời số Fibonacci Xuất phát từ toán gia tăng số lượng đàn thỏ, gọi toán đàn thỏ: Bắt đầu từ cặp thỏ con, sau 12 tháng, ta có cặp thỏ đàn giả sử: - Tháng đầu tiên, có cặp thỏ vừa sinh (nói vui thỏ Adam Eve), - Các cặp thỏ vừa sinh trở thành cặp có khả sinh sản sau hai tháng, - Mỗi tháng, cặp thỏ hai tháng tuổi sinh cặp mới, - Những thỏ không chết Điều giả sử cuối vô lí làm cho toán trở nên đơn giản hơn, có số tài liệu thay đổi giả sử thành 'các cặp thỏ chết sau k tháng đó', chất toán không thay đổi nhiều Một số giả sử khác hiển nhiên nên không nêu thỏ không chạy trốn khỏi đàn không bị vô sinh Theo giả sử, cặp thỏ hai tháng tuổi cho đời cặp thỏ tháng, điều có nghĩa rằng, tất 'á cặp thỏ sống tháng thứ n cho đời 'á cặp thỏ khác tháng thứ n+2 Điều lí giải ta tính số cặp thỏ tháng thứ n+2 số cặp thỏ tháng thứ n+1 (có 'b' cặp) cộng thêm số cặp thỏ tháng thứ n (có 'á cặp) Ta biểu diễn số cặp thỏ đàn hàm theo thời gian, F(n) (n số tháng): F(1) = - ta bắt đầu với cặp thỏ sinh, F(2) = - chúng nhỏ nên chưa thể có sau tháng, F(3) = - đến tháng thứ ba, chúng sinh cặp thỏ F(4) = - đến tháng thứ tư, chúng lại sinh cặp thỏ nữa, F(5) = - đến tháng này, cặp thỏ con, chúng có thêm cặp thỏ cháu Tổng quát, F(n) = F(n - 1) + F(n - 2), tức tất cặp thỏ sống tháng trước F(n - 1) cộng thêm cặp thỏ cặp thỏ sống tháng thứ n-2, tức F(n - 2) Rõ ràng, công thức tính F(n) áp dụng cho toán đàn thỏ Câu trả lời toán tạo dãy số: 1, 2, 3, 5, 8, 13, 21 mà sau ta gọi dãy số Fibonacci Đến đây, ta đặt câu hỏi dãy số lại có tên dãy Fibonacci? Leonardo người phát minh dãy số Fibonacci? Theo giáo sư D.E.Knuth (trường đại học Stanford, http://www-cs-faculty.stanford.edu) dẫn chứng tập sách tiếng The Art of Computer Programming, Volume 1, Fundamental Algorithms , dãy số lần tranh luận nhà Toánhọc ấn Độ, có Gopala Hemachandra, người mô tả cách xác dãy số 1, 2, 3, 5, 8, 13, 21 Còn Tây Âu, dãy số lần nghiên cứu cách đầy đủ nhà Toánhọc Leonardo of Pisa , người có biệt danh Fibonacci, từ mà dãy số có tên dãy Fibonacci Trong sách Liber Abaci, Fibonacci chủ yếu nghiên cứu số (dạng hình tượng) việc tính toán số học người ấn Độ sử dụng nhiều quốc gia khắp Địa Trung Hải Vì vậy, toán đàn thỏ trình bày sách hoàn toàn đơn liên hệ ông phát minh toán hay dãy số Fibonacci Các giải thuật tính số Fibonacci 4.1 Giải thuật 1: Đệ quy nhị phân Từ định nghĩa số Fibonacci, cách tự nhiên, ta có hàm Fib(n) để tính số Fibonacci thứ n dãy sau: Function Fib(n); Begin If (n=1) or (n=0) then Fib:=n Else Fib:=Fib(n-1)+Fib(n-2); End; Giải thuật đệ quy rõ ràng chậm, lẽ tính toán lặp lặp lại nhiều lần giá trị Độ phức tạp thời gian giải thuật theo hàm số mũ, cụ thể O(2n) Với n = 45, cần tỉ bước tính Vì mà giải thuật thường dùng mục đích giảng dạy, đặc biệt để minh họa cho việc phân tích hiệu giải thuật Nó cách để đánh giá hiệu suất chương trình dịch 4.2 Giải thuật 2: Độ phức tạp tuyến tính Giải thuật tính toán F(n) lần việc lưu giữ lại hai số Fibonacci kế trước lần tính Các giải thuật sau có độ phức tạp tương đương nhau, O(n): Giải thuật 2A Mảng chiều Ở đây, số Fibonacci tính trước lưu giữ mảng chiều Trên thực tế, theo cách này, liệu lưu giữ vượt nhu cầu (vì lưu giữ tất số Fibonacci trước không hai số kế trước) Vì vậy, tốn nhớ Tuy nhiên, chương trình cần nhiều số Fibonacci gọi hàm lại nhiều lần việc lưu giữ thứ vừa tính toán giúp giảm bớt thời gian lần gọi sau: (1) Xây dựng mảng F[1 n] với F[0]=0 F[1]=1; (2) Tính phần tử F[i], với i=2 n theo công thức F[i]=F[i -1]+F[i-2]; (3).Trả giá trị F[n] Đoạn mã lệnh tương ứng: Function Fib(n); Begin F[0]:=0; F[1]:=1; For i:=2 to n F[i]:=F[i-1]+F[i-2]; Fib:=F[n]; End; Giải thuật 2B Đệ quy đơn Trong giải thuật này, hai số Fibonacci trước lưu giữ đối số cho hàm đệ quy λ(n1,n2, n): (1) Ta định nghĩa hàm đệ quy λ(n1, n2,n): (1.1) λ(n1, n2,n) nhận giá trị n1 n1; (2) Hàm Fib(n) nhận giá trị trả λ(0,1,n) Đoạn mã lệnh tương ứng: Function lamda (n1,n2,n); Begin If n=0 then lamda:=n1 Else lamda:=lamda (n1+n2,n1,n-1); End; Function Fib(n); Begin Fib:=lamda (0,1,n); End; Giải thuật 2C Lặp không đệ quy Cũng bắt đầu tính số Fibonacci từ lên, hai biến lưu hai giá trị lặp lặp lại n lần việc thay số thứ tổng hai số số thứ hai giá trị cũ số thứ nhất: Function Fib(n); Begin n1:=0; n2:=1; For i:=1 to n Begin t:=n1; n1:=n1+n2; n2:=t; End; Fib:=n1; End; Giải thuật có độ phức tạp O(n) thực tế, chậm giải thuật 2A chút phép hoán đổi, bù lại chiếm nhớ Rõ ràng, ba giải thuật 2A, 2B 2C nhanh khoảng 10 triệu lần so với giải thuật 4.3 Giải thuật 3: Độ phức tạp logarit Tất giải thuật sau tương đương độ phức tạp thời gian, O(log2n) Giải thuật 3A: Phương trình ma trận Dưới công thức toánhọc tinh tế để tính số Fibonacci: Ta biết công thức nhân hai ma trận đối xứng 2x2 là: Ta chứng minh công thức (*) quy nạp Cho , quy nạp, giả sử rằng, công thức với n đó, nhân hai vế với A (và sử dụng công thức nhân ma trận trên) kiểm tra lại để thấy số hạng ta có tương tự công thức định nghĩa số Fibonacci Nên nhớ rằng, kĩ thuật bình phương, việc tính lũy thừa n cần O(log2 n) phép nhân Giải thuật sau khởi tạo ma trận M ma trận đơn vị lặp lại n lần việc nhân M với A Từ công thức trên, F(n) phần tử góc trái ma trận thu A[0,0]:=1; A[0,1]:=1; A[1,0]:=1; A[1,1]:=0; Function M_Pow(M, n); {lũy thừa n ma trận M} Begin If (n>1) then Begin T:=M_Pow(M,n div 2); T:=M_Mult(T,T); {nhân ma trận} End; If (n mod 2)=1 then T:=M_Mult(T,A); M_Pow:=T; End; Function Fib(n); Begin If n2 n số lẻ, gọi λ(f1,f2,(n-1)/2) gán n1=F1*(f1+f2) + f1*f2; n2=F1*f1 + f2*f2 (2) Hàm Fib( n) nhận giá trị phần tử việc gọi λ( n1, n2, n) Lưu ý giải thuật nhanh chút so với giải thuật 3A không lặp lại việc tính số (góc phải góc trái ma trận giải thuật 3A nhau) Sau đoạn mã lệnh cài đặt giải thuật: Procedure lamda (n1,n2,n); Begin If n1) then Begin M:=Pow(x,n div 2); M:=M*M; End; If ođ(n) then M:=M*x; End; Function Fib(n); Begin If (n