Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 34

Chương 3: Tìm đường đi ngắn nhất

Môn học: Lý thuyết đồ thị

PGS.TS. Dương Tuấn Anh

1
Nội dung

 1. Giải thuật Warshall và Giải thuật Floyd


 2. Hàng đợi có thứ tự ưu tiên
 3. Giải thuật Dijkstra
 4. Giải thuật Bellman-Ford

2
1. Giải thuật Warshall và giải thuật Floyd

Tính bao đóng truyền (transitive closure)

Trong đồ thị có hướng, chúng ta quan tâm đến tập đỉnh


mà đến được từ một đỉnh nào đó bằng cách duyệt các
cạnh trong đồ thị theo một hướng đã được ấn định.

Một tác vụ mà ta muốn thực hiện là “thêm một cạnh từ x


đến y nếu tồn tại một cách nào đó để đi từ x đến y”
Đồ thị tạo ra bằng cách thêm tất cả các cạnh có tính
chất trên được gọi là bao đóng truyền của đồ thị.

Vì đồ thị bao đóng truyền thì thường là đồ thị dày, do đó


ta nên dùng cách biểu diễn ma trận kế cận.

3
Giải thuật Warshall
Có một giải thuật đơn giản để tính bao đóng truyền của
một đồ thị được biểu diễn bằng ma trận kế cận.
for y : = 1 to V do
for x : = 1 to V do
if a[x, y] then
for j: = 1 to V do
if a[y, j] then a[x, j]: = true;

S. Warshall đề ra giải thuật này năm 1962, dựa trên một


quan sát đơn giản: “Nếu tồn tại một cách để đi từ nút x đến
nút y và cách để đi từ nút y đến nút j, thì sẽ có cách để đi từ
nút x đến nút j.”
Giải thuật Warshall gồm 3 vòng lặp lồng nhau.
4
Một thí dụ tính bao đóng truyền

Hình 3.1 (a) Đồ thị có hướng (b) Ma trận


kề (c) Ma trận bao đóng truyền

Qui ước: Các phần tử trên đường chéo chính của ma trận kề bằng 0.

5
Giải thích giải thuật Warshall
 Giải thuật Warshall lặp V bước trên ma trận kế cận a, tạo ra
một loạt những ma trận:
a(0),.., a(y-1),a(y),…,a(V) (3.1)
 Ý tưởng chính của giải thuật là ta có thể tính tất cả các phần
tử trong mỗi ma trận a(y) từ ma trận đi trước nó a(y-1) trong loạt
ma trận (3.1)
 Sau bước lặp thứ y, a[x, j] sẽ bằng 1 nếu và chỉ nếu có bất kỳ
lối đi nào từ đỉnh x đến đỉnh j với những đỉnh trung gian mang
chỉ số không lớn hơn y. Nghĩa là, x và j có thể là bất kỳ đỉnh
nào nhưng những đỉnh trung gian trên lối đi phải có chỉ số
nhỏ hơn hay bằng y.

 Tại bước lặp thứ y, ta tính các phần tử của ma trận a bằng
công thức sau:
ay[x,j] = ay-1[x,j] or (ay-1[x, y] and ay-1[y, j]) (3.2)
Chỉ số y chỉ trị của một phần tử trong ma trận a sau bước lặp
thứ y.
6
a b c d
a 0 1 0 0
Thí dụ : A =
(0)
b 0 0 0 1
c 0 0 0 0
d 1 0 1 0
a b c d
a b a 0 1 0 0
A =
(1)
b 0 0 0 1
c 0 0 0 0
d 1 1 1 0
a b c d
a 0 1 0 1
c d A =
(2)
b 0 0 0 1
c 0 0 0 0
d 1 1 1 1
a b c d
a 0 1 0 1
Hình 3.2 Đồ thị có hướng và A =
(3)
b 0 0 0 1
c 0 0 0 0
diễn tiến của giải thuật Warshall d 1 1 1 1

Giải thuật Warshall thể hiện tinh thần của a b c d


a 1 1 1 1
giải thuật quy hoạch động (là một giải A =
(4)
b 1 1 1 1
thuật lặp mà có lưu các kết quả trung gian c 0 0 0 0
d 1 1 1 1
tại các bảng)
7
Giải thuật Floyd cho bài toán các lối đi ngắn nhất

Đối với đồ thị có trọng số (có hướng hoặc không) ta có thể


muốn xây dựng một ma trận cho phép người ta tìm được lối
đi ngắn nhất từ x đến y đối với mọi cặp đỉnh. Đấy là bài toán
những lối đi ngắn nhất cho mọi cặp đỉnh (all-pairs shortest
path problem).
A 4 2
3
1 H I
B 1 C G
1 1
Hình 3.3 Đồ thị có 2
hướng và trọng số 2
1
1

1 D E J K
5 2
F 3
2
L M
1

8
Giải thuật Floyd

Có thể dùng một phương pháp tương tự như phương pháp


Warshall, mà được đưa ra bởi R. W. Floyd:

for y : = 1 to V do
for x : = 1 to V do
if a [x, y] > 0 then
for j: = 1 to V do
if a [y, j] > 0 then
if (a[x, j] = 0) or (a[x, y] + a[y, j] < a[x, j])
then
a[x, j] = a[x, y] + a[y, j];

9
Một thí dụ dùng giải thuật Floyd (cho đồ thị hình 5.7)

Ma trận trọng số
khởi đầu của
a b c d giải thuật Floyd
a 0  3
b 2 0  2

c  7 0 1 a b
d 6  0 7
3 6

c d
Hình 3.4 Đồ thị có hướng và 1
trọng số và ma trận trọng số

Qui ước: Các phần tử trên đường chéo chính của ma trận trọng số
bằng 0.
10
a b c d Ma trận trọng số
a 10 10 3 4 ứng với bước cuối
R(4) = b 2 12 5 6 của giải thuật Floyd
c 7 7 10 1
d 6 16 9 10 2

a b

7
3 6

Tính chất 3.2 Giải thuật Floyd để giải c d


bài toán những lối đi ngắn nhất giữa 1

những cặp có độ phức tạp tính toán


O(V3).
Giải thuật Floyd gồm 3 vòng lặp lồng
nhau
11
Giải thích giải thuật Floyd
Giải thuật Floyd lặp V bước trên ma trận trọng số a, tạo ra một
loạt những ma trận:
a(0), …,a(y-1),a(y),…,a(V) (3.3)
Ý tưởng chính của giải thuật là ta có thể tính tất cả các phần tử
trong mỗi ma trận a(y) từ ma trận đi trước nó, a(y-1) trong loạt ma
trận (3.3).
Sau bước lặp thứ y, a[x, j] sẽ chứa chiều dài nhỏ nhất của bất
kỳ lối đi nào từ đỉnh x đến đỉnh j mà đi qua những đỉnh trung
gian không mang chỉ số lớn hơn y. Nghĩa là, x và j có thể có là
bất kỳ đỉnh nào nhưng những đỉnh trung gian trên lối đi phải có
chỉ số nhỏ hơn hay bằng y.
Tại bước lặp thứ y, ta tính các phần tử của ma trận a bằng công
thức sau:
ay[x,j] = min( ay-1[x,j], ay-1[x, y] + ay-1[y, j]) (3.4)
Chỉ số y chỉ trị của một phần tử trong ma trận a sau bước lặp
thứ y.
12
a b c d
Thí dụ: R(0) =
a 0  3
b 2 0
2 c 7 0 1
d 6 0
a b
a b c d
7 a 0 3
3 6
R(1) = b 2 0 5
c  7 0 1
c d d 6 9 0
1
a b c d
a 0 3
Hình 3.5 Đồ thị có hướng và trọng số
R(2) = b 2 0 5
và diễn tiến của giải thuật Floyd c 9 7 12 1
d 6 9 0

Giải thuật Floyd thể hiện tinh thần a b c d


của giải thuật quy hoạch động (là a 12 10 3 4
một giải thuật lặp mà có lưu các kết R(3) = b 2 12 5 6
c 9 7 12 1
quả trung gian tại các bảng) d 6 16 9 10

a b c d 13
a 10 10 3 4
Cải tiến giải thuật Floyd
 Ta thường muốn biết lối đi ngắn nhất từ một đỉnh đến một
đỉnh khác bao gồm những đỉnh trung gian nào.
 Một cách để thực hiện điều này là dùng thêm một ma trận P,
với P[i,j] chứa đỉnh k mà khiến giải thuật Floyd tìm được giá
trị nhỏ nhất cho a[i,j].
 Giải thuật Floyd cải tiến sẽ như sau:

for i := 1 to V do
for j:= 1 to V do
P[i,j] := 0;
for i := 1 to V do
a[i,i]:= 0;

14
for y : = 1 to V do
for x : = 1 to V do
if a [x, y] > 0 then
for j: = 1 to V do
if a [y, j] > 0 then
if (a[x, j] = 0) or (a[x, y] + a[y, j] < a [x, j]) then
begin
a[x, j] = a[x, y] + a[y, j];
P[x,j] := y;
end

Để in ra những đỉnh trung gian procedure path(x, j: int)


trên lối đi ngắn nhất từ đỉnh x var k : int;
đến đỉnh j, ta gọi thủ tục begin
path(x,j) với path là một thủ k := P[x,j];
tục đệ quy được cho ở hình if k = 0 then return;
bên. path(x,k); writeln(k); path(k,j);
end

15
a b c d
Thí dụ: R =
(0)
a
b
3
2
0000
P = 0000
(0)

c  7 1 0000
2 d 6 0000
a b a b c d
a  3 0000
R =
(1)
b 2 5 P =00a0
(1)
7
3 6 c 7  1 0000
d 6 9 00a0
a b c d
c d a  3 0000
1
R =
(2)
b 2 5 P =00a0
(2)

c 9 7 12 1 b0b0
d 6 9 00a
0
a b c d
a 12 10 3 4 cc0c
R(3) = b 2 12 5 6 P = 0 c a c
(3)

c 9 7 12 1 b0b0
Hình 3.6 Đồ thị có hướng và d 6 16 9 10 0cac
trọng số và diễn tiến của giải a b c d
thuật Floyd cải tiến a 10 10 3 4 dc0
c
R =
(4)
b 2 12 5 6 P(4)= 0 c a c
c 7 7 10 1 d0d
0
d 6 16 9 10 0ca
c 16
2. Hàng đợi có độ ưu tiên
Hàng đợi có độ ưu tiên (a priority-queue) là cấu
trúc dữ liệu mà hỗ trợ ít nhất hai tác vụ:
 thêm một phần tử mới vào cấu trúc
 Tìm phần tử có độ ưu tiên lớn nhất và xóa bỏ phần tử
có độ ưu tiên lớn nhất

Hàng đợi có độ ưu tiên khác với hàng đợi thông


thường ở điểm khi lấy phần tử ra khỏi hàng đợi
thì đó không phải là phần tử cũ nhất trong hàng
đợi mà là phần tử có độ ưu tiên lớn nhất trong
hàng đợi.

17
Thi công hàng đợi có độ ưu tiên

Hàng đợi có độ ưu tiên như đã mô tả là một ví dụ về kiểu


dữ liệu trừu tượng. Có hai cách để thi công hàng đợi có
độ ưu tiên:
 1. Dùng mảng để thi công hàng đợi có độ ưu tiên (Cách

này thì đơn giản khi thêm vào một phần tử mới nhưng
khi xóa bỏ phần tử có độ ưu tiên lớn nhất ra khỏi hàng
đợi thì độ phức tạp sẽ cao.)
 2. Dùng cấu trúc dữ liệu heap. (Xem Phụ Lục C)

18
3. Giải thuật Dijkstra
 Bây giờ chúng ta xem xét bài toán các lối đi ngắn nhất từ
một đỉnh nguồn (single source shortest paths problem). Cho
một đồ thị có hướng có trọng số với một đỉnh được xem là
đỉnh nguồn, tìm tất cả những lối đi ngắn nhất từ đỉnh nguồn
đến tất cả những đỉnh còn lại.
 Giải thuật nổi tiếng để giải bài toán này là giải thuật Dijkstra.

 Giải thuật này có thể áp dụng cho đồ thị có hướng lẫn vô

hướng với trọng số không âm.


 Giải thuật Dijkstra tìm những lối đi ngắn nhất đến các đỉnh

trong đồ thị dựa vào thứ tự của khoảng cách giữa các đỉnh
này với đỉnh nguồn.
Lưu ý: Giải thuật Dijkstra áp dụng cho đồ thị có hướng có
trọng số và cả đồ thị vô hướng có trọng số.

19
Ý tưởng của giải thuật Dijkstra
 Trước tiên, giải thuật tìm lối đi ngắn nhất từ đỉnh nguồn đến một
đỉnh gần với nó nhất, rồi đến đỉnh gần thứ nhì với nó, và cứ thế.
 Trong trường hợp tổng quát, trước khi lượt lặp thứ i bắt đầu,
giải thuật đã xác định được những lối đi ngắn nhất từ đỉnh
nguồn đến i - 1 đỉnh gần với đỉnh nguồn nhất. Những đỉnh này,
đỉnh nguồn và những cạnh làm thành những lối đi ngắn nhất
đến chúng tạo thành một cây con Ti trong đồ thị đã cho.
 Vì tất cả các cạnh trong đồ thị đều có trọng số không âm nên
đỉnh kế tiếp gần nhất đối với đỉnh nguồn sẽ nằm trong số
những đỉnh kế cận với những đỉnh trong cây Ti.
 Tập hợp những đỉnh kế cận với những đỉnh trong cây Ti được
gọi là những đỉnh vùng biên (fringe vertex) làm thành một tập
đỉnh ứng viên để giải thuật chọn đỉnh kế tiếp gần nhất với đỉnh
nguồn.

20
Ý tưởng của giải thuật Dijkstra (tt.)

Để nhận dạng đỉnh gần nhất thứ i, với mỗi đỉnh vùng biên y, giải
thuật tính tổng của khoảng cách từ đỉnh y đến đỉnh x gần nhất
với nó mà thuộc về cây Ti (cho bởi trọng số cạnh (x, y)) và chiều
dài p1 của lối đi ngắn nhất từ đỉnh nguồn đến đỉnh x và chọn ra
đỉnh nào có tổng này nhỏ nhất. Việc so sánh chiều dài của
những lối đi như vậy là trọng tâm của giải thuật Dijkstra.

S: tập đỉnh đã
được xử lý, làm
thành cây con Ti

Hình 3.11 Minh họa ý tưởng của giải thuật Dijkstra.


21
Mã giả của giải thuật Dijkstra được cho như sau:
procedure dijkstra(G, w, s);
/* G là đồ thị, w là hàm trọng số và s là đỉnh xuất phát*/
begin
for each v  V[G] do /* khởi tạo */
begin
d[v] := ; p[v] := NIL
end;
d[s] := 0;
Q := V[G] /* tạo hàng đợi có thứ tự ưu tiên Q từ tập đỉnh của đồ thị */
while Q is not empty do
begin
u := EXTRACT-MIN(Q);
for each v  Adj [u] do
if d[v] > d[u] + w(u, v) then
begin
d[v] := d[u] + w(u, v); p[v] := u
end
end
end
22
Thí dụ 1
 Ghi chú: Với mỗi đỉnh v trong đồ thị, chúng ta có d[v] = min
(giá trị ước lượng chiều dài lối đi ngắn nhất từ s đến v) và
p[v] là tên của đỉnh cha của v trong lối đi.
 Cho một đồ thị có hướng có trọng số như trong hình 5.16. Giả
sử đỉnh nguồn là V1. Diễn tiến từng bước của giải thuật
Dijkstra sẽ được trình bày như sau:
Hình 3.12

23
Q: hàng đợi có thứ tự ưu tiên
Ban đầu các mảng d và q như sau:
Q V1 V2 V3 V4 V5 V6 V7
d 0      
p nil nil nil nil nil nil nil
Chọn V1 và các mảng d và p trở thành:
Q V2 V3 V4 V5 V6 V7
d 2  1   
p V1 nil V1 nil nil nil
Chọn V4 và các mảng d và p trở thành:
Q V2 V3 V5 V6 V7
d 2 3 3 9 5
p V1 V4 V4 V4 V4
Chọn V2 và các mảng d và p trở thành:
Q V3 V5 V6 V7
d 3 3 9 5
p V4 V4 V4 V4 24
Chọn V3 và các mảng d và p trở
thành:
Q V5 V6 V7
d 3 8 5
p V4 V3 V4
Chọn V5 và các mảng d và p trở
thành:
Q V6 V7
d 8 5
p V3 V4
Chọn V7 và các mảng d và p trở
thành: Tóm lại: lối đi ngắn nhất từ V1 đến V2 = 2 (V1 – V2);
Q V6 từ V1 đến V3 = 3 (V1 - V4 - V3) ; từ V1 đến V4 = 1 (V1 – V4);
d 6 từ V1 đến V5 = 3 (V1 - V4 - V5);
p V7 từ V1 đến V6 = 6 (V1 - V4 - V7 -V6);
từ V1 đến V7 = 5 (V1 - V4 - V7).
Chọn V6
25
4. Giải thuật Bellman-Ford
 Giải thuật Bellman-Ford giải được bài toán tìm những đường
đi ngắn nhất từ một đỉnh nguồn mà trong đó trọng số của các
cạnh có thể âm.
 Cho một đồ thị có hướng có trọng số G = (V, E) với đỉnh
nguồn s và hàm trọng số w: E  R, giải thuật Bellman-Ford
sẽ trả về một giá trị bool cho biết có tồn tại một chu trình với
tổng trọng số âm mà đến được từ đỉnh nguồn không. Nếu
tồn tại một chu trình như vậy, giải thuật thông báo rằng bài
toán vô nghiệm. Nếu không tồn tại một chu trình như vậy, giải
thuật cung cấp lời giải của bài toán.
 Cũng giống như giải thuật Dijkstra, giải thuật Bellman-Ford sử
dụng hai mảng d và p.
 Giải thuật Bellman-Ford được mô tả như sau:

26
Mã giả của giải thuật Bellman-Ford được cho như sau:
procedure Bellman-Ford(G, w, s);
/* G là đồ thị, w là hàm trọng số và s là đỉnh nguồn */
begin
for each v  V[G] do /* khởi tạo */
begin
d[v] := ; p[v] := NIL
end;
d[s] := 0;
for i:= 1 to |V|-1 do /* thực hiện |V|-1 chuyến
for each edge (u,v ) E do
if d[v] > d[u] + w(u, v) then
begin
d[v] := d[u] + w(u, v); p[v] := u
end
/* kiểm tra có tồn tại chu trình với tổng trọng số âm
for each edge (u,v ) E do
if d[v] > d[u] + w(u, v) then return FALSE
return TRUE
end
27
Giải thích giải thuật Bellman-Ford
 Sau khi thực hiện việc khởi tạo hai mảng d và p, giải thuật
thực hiện |V|-1 chuyến duyệt qua tất cả các cạnh trong đồ thị.
Một lần duyệt qua các đỉnh trong đồ thị, hai mảng d và p sẽ
được điều chỉnh giá trị.
 Sau khi hoàn tất |V|-1 chuyến duyệt qua tất cả các cạnh, giải
thuật kiểm tra xem có tồn tại chu trình với tổng trọng số âm
hay không bằng vòng lặp cuối cùng trong giải thuật và trả về
giá trị bool phù hợp.
 Giải thuật Bellman-Ford có độ phức tạp O(VE) vì lý do sau
đây. Với vòng lặp khởi tạo có độ phức tạp O(V) và |V|-1
chuyến duyệt qua tất cả các cạnh có độ phức tạp (|V|-1).E
tức là O(VE). Tóm lại: O(V) + O(VE)  O(VE).

28
Thí dụ Hình 3.14a

Cho đồ thị có hướng có trọng số ở Hình 3.14. Sau khi khởi tạo, các
mảng d và p có giá trị như sau:
s t x y z
d 0    
p nil nil nil nil
Trạng thái của đồ thị sau bước khởi tạo được mô tả ở Hình 3.14a.
Trong 4 lượt lặp tiếp theo, thứ tự để xét các cạnh trong đồ thị:
(s, t), (s, y), (t, x), (t, y), (t,z), (y,x), (y, z), (x,t), (z, x), (z, s),
29
Chuyến 1
Xét (s,t): s t x y z
Thứ tự để xét các cạnh :
d 0 6    (s, t), (s, y), (t, x), (t, y), (t,z),
p s nil nil nil (y,x), (y, z), (x,t), (z, x), (z, s)
Xét (s,y): s t x y z
d 0 6  7 
p s nil s nil

Sau chuyến 1, tình trạng


của đồ thị như trong hình
bên

Hình 3.14b

30
Chuyến 2
Xét (t,x): s t x y z
Thứ tự để xét các cạnh :
d 0 6 11 7  (s, t), (s, y), (t, x), (t, y), (t,z),
p s t s nil (y,x), (y, z), (x,t), (z, x), (z, s)
Xét (t,z): s t x y z
d 0 6 11 7 2
p s t s t
Xét (y,x): s t x y z
d 0 6 4 7 2
p s y s t

Sau chuyến 2, tình trạng


của đồ thị như trong hình
bên

Hình 3.14c

31
Chuyến 3
Thứ tự để xét các cạnh :
(s, t), (s, y), (t, x), (t, y), (t,z),
Xét (x, t): s t x y z (y,x), (y, z), (x,t), (z, x), (z, s)
d 0 2 4 7 2
p x y s t

Sau chuyến 3, tình trạng


của đồ thị như trong hình
bên

Hình 3.14d

32
Chuyến 4
Xét (t, z): s t x y z
d 0 2 4 7 -2 Sau chuyến 4, tình trạng
p x y s t của đồ thị như trong Hình
3.14e. Đến đây, giải thuật
kết thúc.
Tóm lại:
lối đi ngắn nhất từ s đến t = 2
(s - y – x - t);
từ s đến x = 4 (s - y - x) ;
từ s đến y = 7 (s – y);
từ s đến z = -2 (s - y – x - t – z);

Qua thí dụ trên, ta thấy giải thuật


Bellman-Ford thể hiện tinh thần
của giải thuật quy hoạch động (là
một giải thuật lặp mà có lưu các
kết quả trung gian tại các bảng) Hình 3.14e

33
Kết luận
 Giải thuật Warshall và giải thuật Floyd thể hiện một chiến
lược thiết kế giải thuật đặc biệt: các kết quả trung gian
được tính và lưu vào một bảng và sự tính toán bảng tại
một lượt lặp dựa trên trạng thái của bảng tại lượt lặp
trước. Quy hoạch động.
 Giải thuật Dijkstra thể hiện tinh thần của giải thuật tham
lam và có sử dụng hàng đợi có thứ tự ưu tiên như là một
cấu trúc dữ liệu hỗ trợ.
 Giải thuật Bellman-Ford thể hiện tinh thần của giải thuật
quy hoạch động.

34

You might also like