BÀI 4: PHƯƠNG PHÁP CHIA ĐỂ TRỊ

Một phần của tài liệu Giáo trình phân tích thiết kế thuật toán (nghề lập trình máy tính) (Trang 110 - 114)

Mã bài : ITPRG3_12.4

Giới thiệu

Mặc dù không tồn tại một phương pháp vạn năng nào có thể giúp chúng ta thiết kế được thuật toán giải quyết mọi vấn đề, nhưng các nhà nghiên cứu đã tìm ra một số phương pháp thiết kế cơ bản, các phưong pháp này còn được gọi là các chiến lược thiết kế thuật toán. Mỗi phương pháp này có thể áp dụng để giải quyết một phạm vi khá rộng các bài toán. Trong tài liệu này chúng tôi sẽ trình bày các phương pháp thiết kế thuật toán như : chia để trị (divide and conquer), phương pháp tham lam (greeding method), quay lui (backtracking), quy hoạch động (dynamic programming), nhánh cận (branch and bound). Trong mỗi chiến lược, chúng tôi sẽ trình bày phương pháp chung, sau đó sẽ đưa ra một số ví dụ minh họạ

Cần lưu ý rằng, các phương pháp thiết kế thuật toán mà chúng ta xem xét chỉ là các chiến lược có tính định hướng sự tìm tòi thuật toán. Trong bài này chúng ta sẽ xét một phương pháp thường được sử dụng đó là phương pháp chia để trị.

Mục tiêu thực hiện

Học xong bài này học viên sẽ có khả năng:

 Nắm bắt được ý tưởng của phương pháp chia để trị.

 Sử dụng phương pháp chia để trị để giải quyết các bài toán tìm kiếm, chọn các phân từ, sắp xếp, nhân ma trận.

 Áp dụng phương pháp chia để trị để giải quyết một số bài toán trong thực tế.

 Thấy được lợi thế của phương pháp chia để trị trong việc xây dựng một số thuật toán.

.13. Phương pháp tổng quát

Ý tưởng chính của phương pháp này là phân bài toán cần giải thành các bài toán con. Các bài toán con lại được tiếp tục phân thành các bài toán con nhỏ hơn, cứ thế tiếp tục cho tới khi chúng ta nhận được các bài toán con hoặc là đã có thuật giải, hoặc là có thể dễ dàng đưa ra thuật giảị Sau đó ta tìm cách kết hợp các nghiệm của các bài toán con để nhận được nghiệm của bài toán

con nhận được trong quá trình phân chia là cùng dạng với bài toán ban đầu, chỉ có cỡ của chúng là nhỏ hơn. Trong các trường hợp như thế, thuật toán tìm ra được có thể biểu diễn một cách tự nhiên bởi thủ tục đệ quỵ

Sau đây là lược đồ phương pháp chia để trị. procedure DivideConquer(A,x);

Tìm nghiệm x của bài toán A

begin

if A đủ nhỏ then solve(A) else

begin

Phân A thành các bài toán A1, A2,....Am; for i:=1 to m do DivideConquer (A1, x1);

Kết hợp các nghiệm x1(i=1, 2,...,m)của bài toán con A1 để nhận được nghiệm x của bài toán A

end; end;

Trong thủ tục trên, Solve là thuật giải bài toán A trong trường hợp A có cỡ đủ nhỏ.

Thuật toán tìm kiếm nhị phân mà chúng ta đã biết là thuật toán được thiết kế dựa trên chiến lược chia để trị. Cho mảng A1...n được sắp xếp theo thứ tự tăng dần, A1 2... An. Với x cho trước, ta cần xác định xem x có chứa trong mảng A1...n hay không, tức là có hay không chỉ số 1  i  n, sao cho Ai = x. Phương pháp chia để trị gợi ý ta chia mảng A1...n thành 3 mảng con A1...k-1 , mảng con gồm một thành phần duy nhất Ak và mảng con Ak+1...n, (k là chỉ số nằm giữa 1 và n). Với mảng con chỉ gồm một thành phần ta biết ngay được nó có chứa x hay không. Nếu x = Ak  thì mảng A chứa x và i = k. Nếu x < Ak thì ta chỉ cần tìm trên mảng con A1..k-1, còn nếu x > Ak ta chỉ cần tìm kiếm trên mảng con Ak + 1...n. Để tìm kiếm x trên mảng con A1...k - 1 hoặc Ak +1... n ta lại áp dụng cách phân chia như đã làm với mảng A1...n.

Thuật toán sắp xếp nhanh (QuickSort) cũng được thiết kế bởi kỹ thuật chia để trị. Sau đây chúng ta sẽ đưa ra một số ví dụ minh họa cho kỹ thuật chia để trị.

.14. Tìm max và min

Cho mảng A1...n, chúng ta cần tìm thành phần nhỏ nhất và lớn nhất của mảng nàỵ Đây là bài toán rất đơn giản có thể giải bằng các thuật toán khác nhau, trong đó có thuật giải bằng kỹ thuật chia để trị.

Ý tưởng của thuật toán là đầu tiên ta lấy max, min là thành phần đầu A1] của mảng. Sau đó so sánh max, min với các thành phần A[i] với 2in, và cập nhật max, min một cách thích ứng.

Thuật toán được mô tả bởi thủ tục sau : Procedure maxmin(A[ị.n], max, min); Begin

max:=a[i]; min:=A[1]; for i:=2 to n do

if max < A[i] then max := A[i] else if min > A[i] then min := A[i]; End;

Thời gian thực hiện thuật toán này được xác định bởi số phép toán so sánh. Từ vòng lặp for, ta thấy số phép toán so sánh cần thực hiện trong trường hợp xấu nhất (trường hợp mảng A[1..n] được sắp theo thứ tự giảm dần) là 2(n-1).

Áp dụng kỹ thuật chia để trị, ta chia mảng A[1..n] thành hai mảng con A[1..k] và A[k+1..n] (k=n/2). Nếu tìm được max và min trên các mảng con A[1..k] và A[k+1..n], ta sẽ dễ dàng xác định được max, min trên toàn mảng A[1..n]. Để tìm max, min trên mảng con A[1..k] và A[k+1..n] ta lại tiếp tục chia đôi chúng. Quá trình sẽ dừng lại khi ta nhận được các mảng con chỉ có một hoặc hai phần tử. Trong các trường hợp đơn giản này max, min được xác định dễ dàng. Từ phương pháp đã trình bày trên, ta có thể đưa ra thủ tục đệ qui MaxMin(i, j, fmax, fmin). Thủ tục này cần tìm max và min trên mảng A[ị.n], ta chỉ cần gọi thủ tục này với i=1, j=n.

Procedure MaxMin(i, j, fmax, fmin);

{fmax, fmin ghi lại phần tử lớn nhất, nhỏ nhất của mảng A[ị.j]} Begin if i=j then begin fmax := A[i]; fmin := A[i]; end

else if j=i+1 then

if A[i] < A[j] then begin fmax := A[j]; fmin := A[i]; end else begin fmax := A[i];

fmin := A[j]; end

else begin

mid := i + j div 2;

MaxMin(i, mid, gmax, gmin); MaxMin(mid+1, j hmax, hmin); if gmax < hmax then fmax := hmax else fmax := gmax;

if gmin < hmin then fmin := gmin else fmin := hmin;

end; End;

Bây giờ ta đánh giá thời gian thực hiện thuật toán này trên mảng n phần tử A[1..n]. Gọi T(n) là số phép toán so sánh cần thực hiện. Từ thủ tục trên, ta xác định được quan hệ đệ quy sau đây :

 0 nếu n = 1

 T(n) = 1 nếu n = 2

 T(n/2) + (n/2) + 2 nếu n > 2

Giả sử n = 2, với k là số nguyên dương nào đó. Bằng phương pháp thế, ta tính được T(n) như sau: T(n) = T(2k) = 2T(22k-1) + 2 =22T(2k-2)+22+2 =23T(2k-3)+23+22+2 .. .. ... =2k-1T(2) + =2k-1+2k-2 =n/2+n-2 =3n/2-2

Như vậy với n =2k , thuật toán MaxMin cần 3n/2 – 2 phép so sánh, so với thuật toán trước, nó tiết kiệm được khoảng 25% phép so sánh. Tuy nhiên MaxMin là thuật toán đệ quy, nó tiêu tốn nhiều bộ nhớ hơn thuật toán trước.

.15. Trao đổi hai phần của một mảng

Một phần của tài liệu Giáo trình phân tích thiết kế thuật toán (nghề lập trình máy tính) (Trang 110 - 114)

Tải bản đầy đủ (PDF)

(186 trang)