1.5. THUẬT TOÁN ĐỆ QUY.
1.5.1. Khái niệm đệ quy:
Đôi khi chúng ta có thể quy việc giải bài toán với tập các dữ liệu đầu vào xác định về việc giải cùng bài toán đó nhưng với các giá trị đầu vào nhỏ hơn. Ch ẳng hạn, bài toán tìm UCLN c ủa hai số a, b với a > b có thể rút gọn về bài toán tìm ƯCLN của hai số nhỏ hơn, a mod b và b. Khi việc rút gọn như v ậy thực hiện được thì lời giải bài toán ban đầu có thể tìm được bằng một dãy các phép r út gọn cho tới những trường hợp mà ta có thể dễ dàng nhận được lời giải của bài toán. Ta sẽ thấy rằng các thuật toán rút gọn liên tiếp b ài toán ban đầu tới b ài toán có dữ liệu đầu vào nhỏ hơn, được áp dụng trong một lớp rất rộng các bài toán.
Định nghĩa: Một thuật toán được gọi là đệ quy nếu nó giải bài toán bằng cách rút gọn liên tiếp bài toán ban đầu tới bài toán cũng nh ư vậy nhưng có dữ liệu đầu vào nhỏ hơn.
Thí dụ 10: Tìm thuật toán đệ quy tính giá trị an với a là số thực khác không và n là số nguyên không âm.
Ta xây dựng thuật toán đệ quy nhờ định nghĩa đệ quy của an, đó là an+1=a.an với n>0 và khi n=0 thì a0=1. Vậy để tính an ta quy về các trường hợp có số mũ n nhỏ hơn, cho tới khi n=0.
Để tìm x trong dãy tìm kiếm a1,a2,...,an trong bước thứ i của thuật toán ta so sánhx với ai. Nếu x bằng ai thì i là vị trí cần tìm, ngược lại thì việc tìm kiế m được quy về dãy có số phần tử ít hơn, cụ thể là dãy ai+1,...,an. Thuật toán tìm kiếm có dạng thủ tục đệ quy như sau.
Cho search (i,j,x) là thủ tục t ìm số x trong dãy ai, ai+1,..., aj. Dữ liệu đầu vào là bộ ba (1,n,x). Thủ tục sẽ dừng khi số hạng đầu tiê n của dãy còn lại là x hoặc là khi dãy còn lại chỉ có một phần tử khác x. Nếu x không là số hạng đầu tiên và còn có các số hạng khác thì lại áp dụng thủ tục này, nhưng dãy tìm kiếm ít hơn một phần tử nhận được bằng cách xóa đi phần tử đầu tiên của dãy tìm kiếm ở bước vừa qua.
Giả sử ta muốn định vị x trong dãy a1, a2, ..., an bằng tìm ki ếm nhị phân. Trước tiên ta so sánh x với số hạng giữa a[(n+1)/2]. Nếu chúng bằng nhau thì thu ật toán kết thúc, nếu không ta chuyển sang tìm kiếm trong dãy ngắn hơn, nửa đầu của dãy nếu x nhỏ hơn giá trị giữa của của dãy xuất phát, nửa sau nếu ngược lại. Như vậy ta rút gọn việc giải bài toán tìm kiếm về việc giải cũng bài toán đó nhưng trong dãy tìm kiếm có độ dài lần lượt giảm đi một nửa.
Thí dụ 14. Thủ tục đệ quy sau đây cho ta giá trị của n! với n là số nguyên dương.
Bây giờ ta sẽ tính các phép toán cần dùng để tính fn khi s ử dụng phương pháp lặp. Thủ tục này khởi tạo x là f0 = 0 và y là f1= 1. Khi vòng lặp được duyệt qua tổng của x và y được gán cho biến phụ z. Sau đó x được gán giá trị của y và y được gán giá trị của z. Vậy sau khi đi qua vòng lặp lần 1, ta có x= f1 và y = f0 + f1 = f2. Khi qua vòng lặp lần n-1 thì x = fn-1. Như v ậy chỉ có n – 1 phép cộng được dùng để tìm fn khi n > 1.
1.5.1. Khái niệm đệ quy:
Đôi khi chúng ta có thể quy việc giải bài toán với tập các dữ liệu đầu vào xác định về việc giải cùng bài toán đó nhưng với các giá trị đầu vào nhỏ hơn. Ch ẳng hạn, bài toán tìm UCLN c ủa hai số a, b với a > b có thể rút gọn về bài toán tìm ƯCLN của hai số nhỏ hơn, a mod b và b. Khi việc rút gọn như v ậy thực hiện được thì lời giải bài toán ban đầu có thể tìm được bằng một dãy các phép r út gọn cho tới những trường hợp mà ta có thể dễ dàng nhận được lời giải của bài toán. Ta sẽ thấy rằng các thuật toán rút gọn liên tiếp b ài toán ban đầu tới b ài toán có dữ liệu đầu vào nhỏ hơn, được áp dụng trong một lớp rất rộng các bài toán.
Định nghĩa: Một thuật toán được gọi là đệ quy nếu nó giải bài toán bằng cách rút gọn liên tiếp bài toán ban đầu tới bài toán cũng nh ư vậy nhưng có dữ liệu đầu vào nhỏ hơn.
Thí dụ 10: Tìm thuật toán đệ quy tính giá trị an với a là số thực khác không và n là số nguyên không âm.
Ta xây dựng thuật toán đệ quy nhờ định nghĩa đệ quy của an, đó là an+1=a.an với n>0 và khi n=0 thì a0=1. Vậy để tính an ta quy về các trường hợp có số mũ n nhỏ hơn, cho tới khi n=0.
procedure power (a: số thực khác không; n: số nguyên không âm)Thí dụ 11: Tìm thuật toán đệ quy tính UCLN của hai số nguyên a,b không âm và a > b.
if n = 0 then power(a,n) := 1
else power(a,n) := a * power(a,n-1)
procedure UCLN (a,b: các số nguy ên không âm, a > b)Thí dụ 12: Hãy biểu diễn thuật toán tìm kiếm tuyến tính như một thủ tục đệ quy.
if b = 0 then UCLN (a,b) := a
else UCLN (a,b) := UCLN (a mod b, b)
Để tìm x trong dãy tìm kiếm a1,a2,...,an trong bước thứ i của thuật toán ta so sánhx với ai. Nếu x bằng ai thì i là vị trí cần tìm, ngược lại thì việc tìm kiế m được quy về dãy có số phần tử ít hơn, cụ thể là dãy ai+1,...,an. Thuật toán tìm kiếm có dạng thủ tục đệ quy như sau.
Cho search (i,j,x) là thủ tục t ìm số x trong dãy ai, ai+1,..., aj. Dữ liệu đầu vào là bộ ba (1,n,x). Thủ tục sẽ dừng khi số hạng đầu tiê n của dãy còn lại là x hoặc là khi dãy còn lại chỉ có một phần tử khác x. Nếu x không là số hạng đầu tiên và còn có các số hạng khác thì lại áp dụng thủ tục này, nhưng dãy tìm kiếm ít hơn một phần tử nhận được bằng cách xóa đi phần tử đầu tiên của dãy tìm kiếm ở bước vừa qua.
procedure search (i,j,x)Thí dụ 13: Hãy xây dựng phi ên b ản đệ quy của thuật toán tìm kiếm nhị phân.
if ai= x then loacation := i
else if i = j then loacation := 0
else search (i+1,j,x)
Giả sử ta muốn định vị x trong dãy a1, a2, ..., an bằng tìm ki ếm nhị phân. Trước tiên ta so sánh x với số hạng giữa a[(n+1)/2]. Nếu chúng bằng nhau thì thu ật toán kết thúc, nếu không ta chuyển sang tìm kiếm trong dãy ngắn hơn, nửa đầu của dãy nếu x nhỏ hơn giá trị giữa của của dãy xuất phát, nửa sau nếu ngược lại. Như vậy ta rút gọn việc giải bài toán tìm kiếm về việc giải cũng bài toán đó nhưng trong dãy tìm kiếm có độ dài lần lượt giảm đi một nửa.
procedure binary search (x,i,j)1.5.2. Đệ quy và lặp:
m := [(i+j)/2]
if x = am
then loacation := m
else if (x < am and i < m) then binary search (x,i,m-1)
else if (x > am and j > m) then binary search (x,m+1,j)
else loacation := 0
Thí dụ 14. Thủ tục đệ quy sau đây cho ta giá trị của n! với n là số nguyên dương.
procedure factorial (n: positive integer)Có cách khác tính hàm giai thừa của một số nguyên từ định nghĩa đệ quy của nó. Thay cho việc lần lượt rút gọn việc tính toán cho các giá trị nhỏ hơn, ta có thể xuất phát từ giá trị của hàm t ại 1và lần lượt áp dụng định nghĩa đệ quy để tìm giá trị của h àm tại các số nguyên lớn dần. Đó là thủ tục lặp.
if n = 1 then factorial(n) := 1
else factorial(n) := n * factorial(n-1)
procedure iterative factorial (n: positive integer)Thông thường để tính một dãy các giá trị đư ợc định nghĩa bằng đệ quy, nếu dùng phương pháp lặp thì số các phép tính sẽ ít hơn là dùng thuật toán đệ quy (trừ khi dùng các máy đệ quy chuyên dụng). Ta sẽ xem xét bài toán tính số hạng thứ n của dãy Fibonacci.
x := 1
for i := 1 to n
x := i * x
{x là n!}
procedure fibonacci (n: nguyên không âm)Theo thuật toán này, để tìm fn ta biểu diễn fn= fn-1+ fn-2. Sau đó thay thế cả hai số này bằng tổng của hai số Fibonacci bậc thấp hơn, cứ tiếp tục như vậy cho tới khi f0 và f1 xuất hiện thì được thay bằng các giá trị của chúng theo định nghĩa. Do đó để tính fn cần fn+1-1 phép cộng.
if n = 0 the fibonacci(n) := 0
else if n = 1 then fibonacci(n) := 1
else fibonacci(n) := fibonacci(n - 1) + fibonacci(n - 2)
Bây giờ ta sẽ tính các phép toán cần dùng để tính fn khi s ử dụng phương pháp lặp. Thủ tục này khởi tạo x là f0 = 0 và y là f1= 1. Khi vòng lặp được duyệt qua tổng của x và y được gán cho biến phụ z. Sau đó x được gán giá trị của y và y được gán giá trị của z. Vậy sau khi đi qua vòng lặp lần 1, ta có x= f1 và y = f0 + f1 = f2. Khi qua vòng lặp lần n-1 thì x = fn-1. Như v ậy chỉ có n – 1 phép cộng được dùng để tìm fn khi n > 1.
procedure Iterative fibonacci (n: nguyên không âm)Ta đã chỉ ra rằng số các phép toán dùng trong thuật toán đệ quy nhiều hơn khi dùng phương pháp lặp. Tuy nhiên đôi khi người ta vẫn thích dùng thủ tục đệ quy hơn ngay cả khi nó tỏ ra kém hiệu quả so với thủ tục lặp. Đặc biệt, có nh ững bài toán chỉ có thể giải bằng thủ tục đệ quy mà không th ể giải bằng thủ tục lặp.if n = 0 then y := 0{y là số Fibonacci thứ n}
else
beginx := 0 ; y := 1end
for i := 1 to n - 1
beginz := x + yend
x := y ; y := z