Bài toán thực hiện dãy phép nhân ma trậnBài toán tìm đường đi ngắn nhất - thuật toán Floy QUY HOẠCH ĐỘNG Bài toán dãy con lớn nhất 2.2.3... 3.1 Thuật toán quy hoạch động tổng quát Để
Trang 1TRƯỜNG CAO ĐẲNG CNTT HỮU NGHỊ ViỆT - HÀN
KHOA KHOA HỌC MÁY TÍNH
-*** -THUẬT TOÁN
(Algorithms)
Trang 3QUY HOẠCH ĐỘNG
Chia để trị là thiết kế thuật toán theo kiểu từ trên xuống (top-down)
Quy hoạch động là quá trình tiếp cận thuật toán
theo quá trình ngược lại, đó là thiết kế theo kiểu từ dưới lên (bottom-up).
Điểm khác cơ bản của quy hoạch động với phương pháp chia để trị đó là:
các bài toán con không độc lập với nhau,
nghĩa là các bài toán con cùng có chung các bài toán con nhỏ hơn
Trong tình huống đó, phương pháp chia để trị sẽ tỏ ra
không hiệu quả, khi nó phải lặp đi lặp lại việc giải các bài toán con chung đó
Quy hoạch động sẽ giải một bài toán con một lần và lời giải
Trang 4QUY HOẠCH ĐỘNG
động là một phương pháp giảm thời gian
chạy của các thuật toán thể hiện các tính
chất của các bài toán con gối nhau
(overlapping subproblem) và cấu trúc con tối
ưu (optimal substructure).
phương pháp quy hoạch động vào năm
1953.
Trang 5Bài toán thực hiện dãy phép nhân ma trận
Bài toán tìm đường đi ngắn nhất - thuật toán Floy
QUY HOẠCH ĐỘNG
Bài toán dãy con lớn nhất
2.2.3
Trang 63.1 Thuật toán quy hoạch động tổng quát
Để giải một bài toán bằng quy hoạch động, chúng
ta cần tiến hành những công việc sau:
Tìm nghiệm của các bài toán con (các trường hợp riêng) đơn giản nhất.
Tìm ra các công thức (hoặc quy tắc) xây dựng
nghiệm của bài toán con thông qua nghiệm của các bài toán con cỡ nhỏ hơn.
Tạo ra một bảng để lưu giữ các nghiệm của các bài toán con Sau đó tính nghiệm của các bài toán con theo các công thức đã tìm ra và lưu vào bảng.
Từ bảng đã làm đầy, tìm cách xây dựng nghiệm của bài toán đã cho
Trang 73.1 Thuật toán quy hoạch động tổng quát
Việc phát triển giải thuật dựa trên quy hoạch động có
thể chia làm 3 giai đoạn:
Phân rã: Chia bài toán cần giải thành những bài toán con nhỏ hơn
có cùng dạng với bài toán ban đầu sao cho bài toán con kích thước nhỏ nhất có thể giải một cách trực tiếp Bản thân bài toán xuất
phát có thể coi là bài toán con có kích thước lớn nhất trong họ các bài toán con này.
Ghi nhận lời giải: Lưu trữ lời giải của các bài toán con vào một
bảng Việc làm này là cần thiết vì lời giải của các bài toán con
thường được sử dụng lại rất nhiều lần, và điều đó nâng cao hiệu quả của giải thuật do không phải giải lặp lại cùng một bài toán
nhiều lần.
Tổng hợp lời giải: Lần lượt từ lời giải của các bài toán con kích
thước nhỏ hơn tìm cách xây dựng lời giải của bài toán kích thước lớn hơn, cho đến khi thu được lời giải của bài toán xuất phát (là bài
Trang 83.1 Thuật toán quy hoạch động tổng quát
Có hai tính chất quan trọng mà một bài toán tối ưu cần phải thoả mãn để có thể áp dụng quy hoạch
động để giải nó là:
Cấu trúc con tối ưu: Để giải được bài toán đặt ra một cách
tối ưu, mỗi bài toán con cũng phải được giải một cách tối ưu
Số lượng các bài toán con phải không quá lớn Rất nhiều
các bài toán NP (xem [1] trang 234) – khó có thể giải được nhờ quy hoạch động, nhưng việc làm này là không hiệu quả
do số lượng các bài toán con tăng theo hàm mũ Một đòi hỏi quan trọng đối với quy hoạch động là tổng số các bài toán con cần giải là không quá lớn, cùng lắm phải bị chặn bởi một
Trang 9Bài toán thực hiện dãy phép nhân ma trận
Bài toán tìm đường đi ngắn nhất - thuật toán Floy
QUY HOẠCH ĐỘNG
Bài toán dãy con lớn nhất
2.2.3
Trang 103.2.2
Bài toán thực hiện dãy phép nhân ma trận
Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Trang 113.2.1 Bài toán thực hiện dãy phép nhân ma trận
Tích của ma trận A = (aik) kích thước p x q với ma trận B = (bkj) kích thước q x r là ma trận C = (cij) kích thước p x r với các phần
tử của C được tính theo công thức:
Trang 123.2.1 Bài toán thực hiện dãy phép nhân ma trận
Chúng ta có thể sử dụng đoạn chương trình sau đây
Rõ ràng, đoạn chương trình trên đòi hỏi thực hiện tất
cả p.q.r phép nhân vô hướng để tính tích của hai ma trận.
Trang 133.2.1 Bài toán thực hiện dãy phép nhân ma trận
sau:1×7 + 2×2 + 3×6 = 29 nên nó có 3 phép nhân
vô hướng.
35 nên cũng có 3 phép nhân vô hướng,…
trận A và B là: 2×3×4 = 24 phép nhân.
Trang 143.2.1 Bài toán thực hiện dãy phép nhân ma trận
trước lần lượt như sau:
A × B × C × D (*)
[20×2] [2×30] [30×12] [12×8]
ma trận ở trên, đòi hỏi chúng phải tương thích với
nhau Tức là số cột của A phải đúng bằng số dòng của
B, số cột của B phải đúng bằng số dòng của C,…
Trang 153.2.1 Bài toán thực hiện dãy phép nhân ma trận
nhưng lại có tính chất kết hợp nên tích của 4 ma trận trên có thể được tính bằng nhiều cách như sau:
Trang 163.2.1 Bài toán thực hiện dãy phép nhân ma trận
hưởng lớn tới phí tổn để thực hiện dãy phép nhân của các ma trận Bài toán đặt ra là tính số phí tổn ít nhất
có thể được, khi thực hiện dãy phép nhân của n ma trận.
Trang 173.2.1 Bài toán thực hiện dãy phép nhân ma trận
Áp dụng phương pháp quy hoạch động chúng ta sẽ giải quyết bài toán theo kiểu “bottom-up”:
Gọi F[i, j] là số phép nhân tối thiểu cần thực hiện để nhân đoạn
ma trận liên tiếp: M i *M i+1 *…*M j (1 <= i <= j <= n)
Khi đó F[i, i] = 0 với mọi i.
Để tính M i *M i+1 *…*M j ta có thể có nhiều cách kết hợp: M i *M i+1 *…
*M j = (M i *M i+1 *…*M k )*(M k+1 *…*M j-1 *M j ) với i<= k <j Với mỗi
cách kết hợp (phụ thuộc vào cách chọn vị trí k), chi phí tối thiểu phải thực hiện bằng:
Chi phí thực hiện phép nhân: M i *M i+1 *…*M k = F[i, k]
Cộng với chi phí thực hiện phép nhân: M k+1 *…*M j-1 *M j = F[k+1, j]
Trang 183.2.1 Bài toán thực hiện dãy phép nhân ma trận
nhân Mk+1*…*Mj-1*Mj có kích thước ak+1×aj+1, vậy chi phí này là : ai×ak+1×aj+1.
chọn cách kết hợp để có chi phí ít nhất nên ta sẽ cực tiểu hóa F[i, j] theo công thức:
F[i, j] = min(F[i, k] + F[k+1,j] + ai*ak+1*aj+1) mọi
i<= k <j (3.1)
Trang 193.2.1 Bài toán thực hiện dãy phép nhân ma trận
Trang 203.2.1 Bài toán thực hiện dãy phép nhân ma trận
ra cách kết hợp tối ưu để nhân đoạn Mi*Mi+1*…*Mk
và cách kết hợp tối ưu để nhân đoạn
Mk+1*…*Mj-1*Mj (có kèm theo dấu mở ngoặc) đồng thời viết
thêm dấu “*” vào giữa hai biểu thức đó.
Trang 213.2.1 Bài toán thực hiện dãy phép nhân ma trận
Ta có hàm:
int Minmult(int n, const int a[], index P[][]) //a[] kích thước của các ma trận
{ //P[][] là ma trận lưu vị trí kết hơp k tối ưu
F[i][j] = min(F[i][k] + F[k+1][j] + ai*ak+1*aj+1);
P[i][j] = k sao cho F[i][j] dat min
}
return M[1][n];
Trang 223.2.1 Bài toán thực hiện dãy phép nhân ma trận
Trang 233.2.1 Bài toán thực hiện dãy phép nhân ma trận
Trang 243.2.1 Bài toán thực hiện dãy phép nhân ma trận
cách đặt dấu ngoặc tách đầu tiên cho giá trị F[i][j]
Cùng với việc tính các giá trị F[i][j], ta sẽ tính P[i][j] theo quy tắc:
d = 1: F[i][i+1] = ai×ak+1×aj+1
P[i][i+1] = i+1
1< d < n: F[i][i+d] = min(F[i][k] + F[k+1][i+d] + a i a k a i+d )
P[i][i+d] = k
Trang 253.2.1 Bài toán thực hiện dãy phép nhân ma trận
Thí dụ 3: Các giá trị của P[i, j] theo (*) được cho
trong bảng dưới đây:
Trang 263.2.1 Bài toán thực hiện dãy phép nhân ma trận
Ta có số phép nhân cần thực hiện là F[1,4] = 1232 Dấu ngoặc đầu tiên cần đặt sau vị trí P[1,4] = 1, tức là M = A(BCD) Ta tìm cách đặt dấu ngoặc đầu tiên để có F[2,4] tương ứng với tích
BCD Ta có P[2,4] = 2, tức là tích BCD được tính tối ưu theo
cách: BCD = (BC)D Từ đó suy ra, lời giải tối ưu là: M =
A((BC)D).
Bây giờ, ta tính số phép toán cần thực hiện theo thuật toán vừa trình bày Với mỗi d > 0, có n – d phần tử trên đường chéo cần tính, để tính mỗi phần tử đó ta cần so sánh d giá trị số tương ứng với các giá trị có thể của k Từ đó suy ra số phép toán cần thực hiện theo thuật toán là cỡ
6 / ) (
6 / ) 1 2 )(
1 ( 2 / ) 1 (
) (
3 2
1 1
1 1
1 1 2
n n
n n
n n
n
d d
n d d n
n d
n s
n d
Trang 273.2.1 Bài toán thực hiện dãy phép nhân ma trận
sau:
/* F[i,j] - chi phí tối ưu thực hiện nhân dãy Mi Mj;
P[i,j] - ghi nhận vị trí đặt dấu ngoặc đầu tiên trong cách thực hiện nhân dãy Mi Mj */
{for (i = 1;i<= n;i++) F[i,i] = 0; //khởi tạo
for (d = 1 ;d<= n;d++) // d = chỉ số của đường chéo for (i = 1;i<= n – d;i++)
{ j = i + d - 1; F[i,j] = +;
for (k = i;k<= j – 1;k++)
{ q = F[i,k] + F[k+1,j] + a[i]*a[k+1]*a[j+1]; if(q <F[i,j]) then
{ F[i,j] = q; P[i,j] = k; }
Trang 283.2.1 Bài toán thực hiện dãy phép nhân ma trận
đưa ra trình tự nhân tối ưu
Trang 293.2.1 Bài toán thực hiện dãy phép nhân ma trận
Như đã trình bày ở trên một trong những điều kiện để áp dụng được quy hoạch động để giải bài toán tối ưu là số lượng các bài toán con phải không quá lớn, nghĩa là thuật toán đệ quy để giải bài toán phải giải đi, giải lại cùng một bài toán con chứ không
phải luôn giải các bài toán con mới Khi một thuật toán đệ quy phải giải đi, giải lại cùng một bài toán con ta sẽ nói là bài toán tối
ưu có các bài toán con trùng lặp Để minh họa cho tính chất này,
ta sẽ tìm hiểu bài toán nhân dãy ma trận
Xét thuật toán đệ quy sau đây để tính F[i,j] là số phép nhân ít
nhất cần thực hiện để tính tích dãy ma trận MiMi+1 … Mj
RMat(a,i,j);
{ If( i == j) return 0;
F[i,j] = ;
for (k = I;k<= j – 1;k++)
{ q = RMat(a,i,k) + RMat(a, k+1,j) + d[i]*d[k+1]*d[j+1];
if q < F[i,j] then F[i,j]: = q;
}
Trang 303.2.1 Bài toán thực hiện dãy phép nhân ma trận
Trang 313.2.1 Bài toán thực hiện dãy phép nhân ma trận
Hình vẽ dưới đây cho thấy cây đệ quy thực hiện lệnh gọi RMat(a,1,4) Mỗi đỉnh của cây được đánh dấu bởi giá trị của hai tham số i, j.
Trang 323.2.1 Bài toán thực hiện dãy phép nhân ma trận
RMat(a,1,n) thực hiện thủ tục đệ quy trên để tính m[1,n] tăng theo hàm mũ của n Thật vậy, ta có:
) 1 ) (
) ((
1 ) (
1 ) 1 (
n k
k n T k
n T T
)(2
1
)1)(
)((1
)(
n
n i
n k
n k
i T n
i T k
k n T k
T n
T
Trang 333.2.2
Bài toán thực hiện dãy phép nhân ma trận
Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Trang 343.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
nhắc lại sơ lược về lý thuyết đồ thị Hình 3.2 ở dưới là một đồ thị
Trang 353.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
ví dụ trong hình 3.2 để đi từ v1 đến v3 ta có 3 đơn
đường đi là: [v1, v2, v3], [v1, v4, v3], [v1, v2, v4, v3] Vì thế độ dài của các đường đi này là:
Trang 363.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Để tìm đường đi ngắn nhất từ đỉnh u đến đỉnh v, ta liệt kê tất cả các đường đi từ u đến v (có thể có) Sau
đó chọn đường đi ngắn Tuy nhiên thuật toán này là không khả thi vì có độ phức tạp lớn hơn là thời gian hàm mũ
Nếu gọi T(n) là thời gian thực hiện thuật toán trên thì
ta có T(n) = (n-2)! nên suy ra T(n) = O(n!), điều này
là tồi hơn thời gian hàm mũ Mục đích của chúng ta là tìm ra một thuật toán có hiệu quả hơn.
Trang 373.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
toán tìm đường đi ngắn nhất, chúng ta có thể thực hiện như sau:
0 , nếu i = j
W[i][j]= trọng số trên cạnh , nếu có cung từ vi đến vj
∞ , nếu không có cung từ vi đến vj
Bảng 3.3 dưới đây là ma trận trọng số của đồ thị ở hình 3.2
Trang 383.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Trang 393.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Bảng 3.4 Ma trận D chứa đường đi ngắn nhất giữa các cặp đỉnh trên đồ thị hình
Trang 403.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
sẽ tính một vài giá trị mẫu của D(k)[i][j] cho đồ thị ở hình 3.2
D3[2][5] = D2[2][5] = 14 (vì không có đường đi từ v3 đến v5)
D4[2][5] = min(D3[2][5], length[v2, v4 ,v5]) = min(14, 5) = 5
Và cuối cùng giá trị tính toán D5[2][5] = 5 là chiều dài của
đường đi ngắn nhất từ v2 đến v5 đã đi qua các đỉnh trung gian Điều này có nghĩa nó là chiều dài của đường đi ngắn nhất
Như vậy D(n)[i][j] là chiều dài của đường đi ngắn nhất từ vi đến
vj vượt qua các đỉnh trung gian, và D(0)[i][j] là chiều dài của
đường đi ngắn nhất không vượt qua các đỉnh còn lại, nó là trọng
số từ vi đến vj chúng ta đã thiết lập D(0) = W và D(n) = D
Trang 413.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Sử dụng phương pháp quy hoạch động ta có thể thực hiện như sau:
Cho k chạy từ 1 đến n, tính D(k) thông qua D(k-1) Như vậy ta đã tao ra một dãy
Trang 423.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
[3] = 3 bởi vì khi chúng ta chọn đỉnh v5 làm đỉnh trung gian trên
đường đi chỉ còn [v1, v4, v3]
trung gian trong [v1, v2, , vk] và sử dụng vk trong trường hợp này thì đường đi ngắn nhất là:
D(k)[i][j] = D(k-1)[i][k] + D(k-1)[k][j] (3.2)
D(2)[5][3] = 7 = 4 + 3 = D(1)[5][2] + D(1)[2][3]
của D(k)[i][j] là min của các giá trị bên phải của biểu thức 3.3 và 3.4 điều này có nghĩa là chúng ta xác định D(k) từ D(k-1) theo công thức sau:
D(k)[i][j] = min(D(k-1)[i][j], D(k-1)[i][k] + D(k-1)[k][j])
Trang 433.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Ví dụ:
ở bảng 3.3, một vài tính toán đơn giản như sau (với D(0) = W):
Trang 443.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Thuật toán Floyd tìm đường đi ngắn nhất
thị có trọng số đến các đỉnh còn lại (trọng số không âm)
Trang 453.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Phân tích thuật toán:
Vòng lặp bên trong j chạy từ 1 đến n, có 3 vòng for lồng nhau nên:
T(n)=n×n×n=O(n3).
Để lưu lại các đường đi ta đưa vào mảng P[][]
P[i][j] = một đỉnh trung gian trên đường đi ngắn
nhất, hoặc bằng 0 nếu không có đỉnh trung gian
Trang 463.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Trang 473.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Dưới đây là mảng P lưu lại vết các đường đi của bảng 3.4
Trang 483.2.2 Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Để in đường đi ngắn nhất ta có thuật toán sau:
void path (index q, r)
Trang 493.2.2
Bài toán thực hiện dãy phép nhân ma trận
Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Trang 503.2.3 Bài toán dãy con lớn nhất
Trong chương 2 ta đã trình bày thuật toán chia để trị
để giả bài toán dãy con lớn nhất với thời gian tính
Trang 513.2.3 Bài toán dãy con lớn nhất
Phân rã Gọi si là tổng thứ i của dãy con lớn nhất
Tổng hợp lời giải Trước hết, ta có sn = a1 Bây
giờ giả sử i > 1 và ta đã tính được si-1 Ở bước thứ i
để tính si là tổng của dãy con lớn nhất của dãy a1,
a2, …, ai-1, ai.
Trang 523.2.3 Bài toán dãy con lớn nhất
Rõ ràng dãy con lớn nhất của dãy này hoặc là có chứa
phần tử ai hoặc là không chứa phần tử ai, vì thế chỉ
có thể là một trong hai dãy sau đây:
· Dãy con lớn nhất của dãy a1, a2, …, ai-1.
· Dãy con lớn nhất của dãy a1, a2, …, ai kết thúc tại ai.
Từ đó suy ra Si = max {si-1, ei },
Trong đó ei là tổng của dãy con lớn nhất của dãy a1,
a2, …, ai kết thúc tại ai.
Trang 533.2.3 Bài toán dãy con lớn nhất
Lưu ý rằng để tính ei, i = 1, 2, …, n, ta cũng có thể sử dụng công thức đệ quy sau:
e1 = a1;
ei = max {ai, ei-1 + ai }, i > 1.
Ví dụ: cho dãy a = (3, 2, -7, 3, -5, 5, 3) thì dãy con lớn nhất là dãy b = (3,-5,5,3)
Từ đó, ta có thuật toán sau để giải bài toán đặt ra:
Trang 543.2.3 Bài toán dãy con lớn nhất
{
smax = a[1]; // smax - tổng của dãy con lớn nhất
maxendhere = a[1];
s=1; //vị trí đầu của dãy
imax = 1; // imax - vị trí kết thúc của dãy con lớn nhất for( i = 2;i<= n;i++)
Trang 553.2.2
Bài toán thực hiện dãy phép nhân ma trận
Bài toán tìm đường đi ngắn nhất - thuật toán Floy
Trang 563.2.4 Bài toán dãy con chung dài nhất
Trang 573.2.4 Bài toán dãy con chung dài nhất
Thiết kế thuật toán:
Thuật toán đơn giản:
Duyệt tất cả các dãy con của dãy a và
kiểm tra mỗi dãy như vậy có phải là dãy con của b, và giữ lại dãy con dài nhất
Mỗi dãy con của a tương ứng với dãy chỉ số <i1, i2, …,
ik> là tập con k phần tử của tập chỉ số {1, 2, …, m},
vì thế có tất cả 2m dãy con của a