Tải bản đầy đủ (.docx) (32 trang)

Báo cáo thực tập cơ sở thuật toán Floyd

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (801.71 KB, 32 trang )

LỜI NÓI ĐẦU
Lý thuyết đồ thị là một lĩnh vực nghiên cứu đã có từ lâu đời và có nhiều ứng dụng
hiện đại. Những tư tưởng cơ bản của lý thuyết đồ thị đươc đề xuất từ những năm đầu của
thế kỷ 18 bởi nhà toán học lỗi lạc người Thụy Sĩ Leonhard Euler. Chính ông là người đã
sử dụng đồ thị để giải bài toán nổi tiếng về các cái cầu ở thàng phố Konigsberg.
Đồ thị được sử dụng để giải quyết các bài toán trong nhiều lĩnh vực khác nhau. Chẳng
hạn, đồ thị có thể sử dụng để xác định các mạch vòng trong vấn đề giải tích mạch điện.
Chúng ta có thể phân biệt các hợp chất hoá học hữu cơ khác nhau với cùng công thức
phân tử nhưng khác nhau về cấu trúc phân tử nhờ đồ thị. Chúng ta có thể xác định xem
hai máy tính trong mạng có thể trao đổi thông tin được với nhau hay không nhờ mô hình
đồ thị của mạng máy tính. Đồ thị có trọng số trên các cạnh có thể sử dụng để giải các bài
toán như: tìm đường đi ngắn nhất giữa hai thành phố trong cùng một mạng giao thông.
Chúng ta còn sử dụng đồ thị để giải các bài toán về lập lịch, thời khoá biểu, và phân bố
tần số cho các trạm phát thanh và truyền hình
Mục đích của đề tài là tìm hiểu là nhằm giới thiệu các khái niệm cơ bản, các bài toán
ứng dụng quan trọng của lý thuyết đồ thị như bài toán tìm đường đi ngắn nhất và những
thuật toán để giải quyết chúng đã được trình bày chi tiết cùng với việc phân tích và hướng
dẫn cài đặt chương trình trên máy tính.
Trong thời gian làm đề tài em được giúp đỡ và trực tiếp chỉ bảo tận tình của cô giáo
DƯƠNG THỊ MAI THƯƠNG. Em xin chân thành cảm ơn cô đã giúp đỡ em hoàn thành
báo cáo này.
Mặc dù đã cố gắng hoàn thiện đề tài với tất cả sự nỗ lực của bản thân, nhưng không
thể tránh khỏi những thiếu sót. Kính mong quý Thầy Cô tận tình chỉ bảo.
Em xin chân thành cảm ơn!
Thái Nguyên, tháng 04 năm 2015
Sinh viên
- Trang 1 -
MỤC LỤC
CHƯƠNG 1 : LÝ THUYẾT VỀ THUẬT TOÁN TÌM ĐƯỜNG ĐI NGẮN NHẤT
1.1 Các khái niệm cơ bản của lý thuyết đồ thị 5
1.1.1 Giới thiệu 5


1.1.2 Định nghĩa đồ thị 6
1.1.3 Phân loại đồ thị 7
1.1.4 Các thuật ngữ 8
1.1.5 Định lý về bậc của đỉnh 9
1.1.6 Đường đi, chu trình, đồ thị liên thông 10
1.2 Các thuật toán tìm đường đi ngắn nhất 11
1.2.1 Thuật toán Ford-Bellman 11
1.2.2 Thuật toán Dijkstra 12
1.2.3 Đường đi trong đồ thị không có chu trình 13
1.2.4 Đường đi ngắn nhất giữa tất cả các cặp đỉnh 16
CHƯƠNG 2 : THUẬT TOÁN FLOYD
2.1 Nội dung thuật toán Floyd 18
2.2 Ứng dụng của thuật toán Floyd-Warshall 23
CHƯƠNG 3 : CHƯƠNG TRÌNH MÔ TẢ THUẬT TOÁN
3.1 Giao diện chính của chương trình 24
3.2 Các bước thực hiện chương trình 24
3.3 Các chức năng chính của chương trình 25
3.4 Cài đặt – thử nghiệm 26
KẾT LUẬN 30
TÀI LIỆU THAM KHẢO 31
- Trang 2 -
DANH MỤC HÌNH VẼ
Hình 1.1 Cầu ở Konigsberg 5
Hình 1.2 Nhà toán học Thụy Sĩ Leonhard Euler 5
Hình 1.3 Đồ thị vô hướng 6
Hình 1.4 Đồ thị con 6
Hình 1.5 Các loại đồ thị 7
Hình 1.6 Đơn đồ thị 7
Hình 1.7 Đa đồ thị 7
Hình 1.8 Giả đồ thị 8

Hình 1.9 Ví dụ bậc của đỉnh 9
Hình 1.10 Bậc vào và bậc ra 9
Hình 1.11 Đồ thị G và các thành phần liên thông G1, G2, G3 10
Hình 1.12 Liên thông mạnh và liên thông yếu 11
Hình 1.13 Khớp và cầu 11
Hình 1.14 Thí dụ 1 12
Hình 1.15 Đồ thị không có chu trình 14
Hình 2.1 Đồ thị ví dụ 20
Hình 3.1 Giao diện chương trình 24
Hình 3.2 Công cụ vẽ 25
Hình 3.3 Công cụ chỉnh sửa và hỗ trợ vẽ đồ thị 25
Hình 3.4 Truy xuất kết quả 25
Hình 3.5 Hiển thị kết quả của chương trình 26
Hình 3.6 Bài toán thử nghiệm 27
Hình 3.7 Kết quả chạy chương trình 28
Hình 3.8 Dòng thông báo 29
- Trang 3 -
DANH MỤC BẢNG BIỂU
Bảng 1.1 So sánh các loại đồ thị…………………………….……………………………8
Bảng 1.2 Kết quả tính toán theo thuật toán Dijkstra…………………………………….13
Bảng 2.1 Kết quả lần lặp đầu tiên……………………………………… ………………20
Bảng 2.2 Kết quả lần lặp thứ 1…………………………………………… ……………21
Bảng 2.3 Kết quả lần lặp thứ 2………………………………………………………… 21
Bảng 2.4 Kết quả lần lặp thứ 3……………………………………….………………….21
Bảng 2.5 Kết quả lần lặp thứ 4……………………………………… …………………22
Bảng 2.6 Kết quả lần lặp thứ 5…………………………………… ……………………22
Bảng 2.7 Kết quả lần lặp thứ 6………………………………………….……………….22
Bảng 3.1 Ma trận trọng số…………………………………………….…………………28
- Trang 4 -
Chương I : LÝ THUYẾT VỀ THUẬT TOÁN TÌM ĐƯỜNG ĐI NGẮN

NHẤT
I.1 Các khái niệm cơ bản của lý thuyết đồ thị
1.1.1 Giới thiệu
- Bài toán về các cây cầu ở Konigsberg:
Có cách nào để đi dạo qua tất cả bảy cây cầu, mà mỗi cây cầu chỉ đi qua một lần ?
Hình 1.1 Cầu ở Konigsberg
- Năm 1736, là năm khai sinh lý thuyết đồ thị, qua việc công bố lời giải bài toán về
các cây cầu ở Konigsberg của nhà toán học Euler.

- Năm 1736, là năm khai sinh lý thuyết đồ
thị, qua việc công bố lời giải bài toán về các cây
cầu ở Konigsberg của nhà toán học Euler.

Hình 1.2 Nhà toán học Thụy Sĩ
Leonhard Euler
(April 1707 – September 1783)
- Trang 5 -
G
H
1.1.2 Định nghĩa đồ thị
Khái niệm đồ thị là một mô hình toán học dùng để giải quyết rất nhiều bài toán và các
vấn đề toán học.
Một đồ thị có thể hiểu một cách đơn giản là một hệ thống các đỉnh và các cạnh nối
các đỉnh này với nhau.
Ví dụ: Một bản đồ giao thông là một đồ thị với hệ thống đỉnh là các ngã ba, ngã tư.
Các đường đi là các cạnh của đồ thị.
Định nghĩa 1: Đồ thị G được xác định bởi (V, E) gồm:
- V là tập hợp hữu hạn khác rỗng các phần tử gọi là đỉnh (hay nút) của đồ thị;
- E là tập hợp các cặp đỉnh. Mỗi phần tử của E được gọi là một cạnh.


Hình 1.3 Đồ thị vô hướng
Định nghĩa 2: Cho hai đồ thị G = (V,E) và G’ = (V’,E’)
- G’ được gọi là đồ thị con của G, ký hiệu G’≤ G nếu V’ ⊆ V và E’ ⊆ E
- Nếu V’ = V và E’ ⊆ E thì G’ được gọi là đồ thị con khung của G.
Hình 1.4 Đồ thị con
- Trang 6 -
San Francisco
Denver
Los Angeles
New York
Chicago
Washington
Detroit
San Francisco
Denver
Los Angeles
New York
Chicago
Washington
Detroit
1.1.3 Phân loại đồ thị
Đồ thị G được phân loại theo đặc tính và số lượng của tập các cạnh E:
Hình 1.5 Các loại đồ thị
Đồ thị G được phân loại theo đặc tính và số lượng của tập các cạnh E:
- G được gọi là đơn đồ thị nếu giữa hai đỉnh u, v thuộc V chỉ có nhiều nhất là 1 cạnh.
Hình 1.6 đơn đồ thị
- G được gọi là đa đồ thị nếu giữa hai đỉnh u, v thuộc V có nhiều hơn 1 cạnh.
Hình 1.7 Đa đồ thị
- Đa đồ thị G được gọi là giả đồ thị nếu có khuyên. Khuyên là cạnh có hai đầu mút
trùng nhau, dạng (u,u).

- Trang 7 -
San Francisco
Denver
Los Angeles
New York
Chicago
Washington
Detroit
Hình 1.8 Giả đồ thị
- G là đồ thị vô hướng nếu các cạnh trong E là không định hướng. Tập E gồm các cặp
(u,v) không sắp thứ tự (u,v)≡ (v,u)
- G là đồ thị có hướng nếu các cạnh trong E là có định hướng. Trong đồ thị có hướng,
hai điểm u và v, có thể được nối bởi hai cung (u,v) và (v,u).
Loại đồ thị Cạnh Có cạnh
bội
Có khuyên
Đơn đồ thị vô hướng Vô hướng Không Không
Đa đồ thị vô hướng Vô hướng Có Không
Giả đồ thị vô hướng Vô hướng Có Có
Đồ thị có hướng Có hướng Không Có
Đa đồ thị có hướng Có hướng Có Có
Bảng 1.1 So sánh các loại đồ thị
1.1.4 Các thuật ngữ
Cạnh uv nối u với v, cạnh uv được gọi là cạnh liên thuộc với u,v; đỉnh u được gọi là
kề với đỉnh v.
Hai cạnh nối cùng một cặp đỉnh gọi là cạnh song song.
Cạnh uv nối u với v, cạnh uv được gọi là cạnh liên thuộc với u,v; đỉnh u được gọi là
kề với đỉnh v.
Cho đồ thị vô hướng G = (V,E). Bậc của đỉnh v, ký hiệu deg(v), là số cạnh liên thuộc
với v. Trong đó một khuyên tại một đỉnh được đếm hai lần cho bậc của đỉnh ấy.

- Đỉnh có bậc bằng 0 gọi là đỉnh cô lập
- Đỉnh có bậc bằng 1 gọi là đỉnh treo
- Trang 8 -
a
b
d
c
e
f
a
b
c
d
e
f
Ví dụ 1:
Hình 1.9 Ví dụ bậc của đỉnh
deg(a)=2; deg(b)=4; deg(f)= 3
deg(d)=0; deg(c)=1
Đỉnh d là đỉnh cô lập
Đỉnh c là đỉnh treo
Cho đồ thị có hướng G* = (V,E).
- bậc ra của đỉnh v, ký hiệu deg
+
(v), là số cung đi ra khỏi đỉnh,
- bậc vào của đỉnh v, ký hiệu deg
-
(v), là số cung đi vào đỉnh.
Hình 1.10 Bậc vào và bậc ra
1.1.5 Định lý về bậc của đỉnh

Định lý:
Với G là đồ thị vô hướng, với m cạnh, khi đó tổng số bậc của đỉnh là 2m.
Hệ quả:
Trong đồ thị vô hướng, tổng số đỉnh bậc lẻ là một số chẵn.
Định lý:
Với G là đồ thị vô hướng, với m cạnh, khi đó tổng số bậc của đỉnh là 2m.
- Trang 9 -
Hệ quả:
Trong đồ thị vô hướng, tổng số đỉnh bậc lẻ là một số chẵn.
Định lý:
Với G* là đồ thị có hướng, với m cung, khi đó chúng ta có công thức:
1.1.6 Đường đi, chu trình, đồ thị liên thông
Định nghĩa:
Cho G = (V,E) là đồ thị vô hướng u,v∈V
a) Đường đi (dây chuyền) độ dài k nối hai đỉnh u,v là dãy đỉnh và cạnh liên tiếp
nhau v
0
e
1
v
1
e
2
…v
k-1
e
k
v
k
sao cho: v

0
=u ,v
k
= v, e
i
=v
i-1
v
i
, i=1,2,…,k
b) Đường đi không có cạnh nào xuất hiện quá một lần gọi là đường đi đơn
c) Đường đi không có đỉnh nào xuất hiện quá một lần gọi là đường đi sơ cấp
Định nghĩa:
Đường đi được gọi là chu trình nếu bắt đầu và kết thúc tại cùng một đỉnh
Đường đi đơn có đỉnh bắt đầu và đỉnh kết thúc trùng nhau tạo ra chu trình đơn.
Định nghĩa:
Đồ thị vô hướng G = (V,E) được gọi là liên thông nếu luôn tìm được đường đi giữa
hai đỉnh bất kỳ của đồ thị.
Với đồ thị G không liên thông, G được phân rã thành một số đồ thị con liên thông.
Mỗi đồ thị con này được gọi là thành phần liên thông.
Hình 1.11 Đồ thị G và các thành phần liên thông G1, G2, G3
Định nghĩa:
Đồ thị có hướng G* = (V,E) tính liên thông được xác định theo hướng của cung.
Đồ thị G* là liên thông mạnh nếu luôn tìm được đường đi giữa hai đỉnh bất kỳ của đồ
thị.
Đồ thị G* là liên thông yếu nếu chỉ tồn tại đồ thị vô hướng nền của nó là liên thông.
- Trang 10 -
Hình 1.12 Liên thông mạnh và liên thông yếu
Định nghĩa:
Cho G = (V,E) là đồ thị vô hướng liên thông

a) Đỉnh v được gọi là đỉnh khớp nếu G\v không liên thông (G\v là đồ thị con của G
có được bằng cách xoá v và các cạnh kề với v)
b) Cạnh e được gọi là cầu nếu G\e không liên thông( G\e là đồ thị con của G có được
bằng cách xoá cạnh e).
Hình 1.13 Khớp và cầu
1.2 Các thuật toán tìm đường đi ngắn nhất
1.2.1 Thuật toán Ford-Bellman
Thuật toán Ford-Bellman có thể phát biểu rất đơn giản:
Với đỉnh xuất phát s. Gọi d[v] là khoảng cách từ s tới v với các giá trị khởi tạo là:
+ d[s] := 0
+ d[v] := +∞ nếu v ≠ s
Sau đó ta tối ưu hoá dần các d[v] như sau: Xét mọi cặp đỉnh u, v của đồ thị, nếu có
một cặp đỉnh u, v mà d[v] > d[u]+ c[u, v] thì ta đặt lại d[v] := d[u] + c[u, v]. Tức là nếu độ
dài đường đi từ s tới v lại lớn hơn tổng độ dài đường đi từ s tới u cộng với chi phí đi từ u
tới v thì ta sẽ huỷ bỏ đường đi từ s tới v đang có và coi đường đi từ s tới v chính là đường
đi từ s tới u sau đó đi tiếp từ u tới v. Chú ý rằng ta đặt c[u, v] = +∞ nếu (u, v) không là
cung. Thuật toán sẽ kết thúc khi không thể tối ưu thêm bất kỳ một nhãn d[v] nào nữa.
for (∀v ∈ V) do d[v]:= +∞;
d[s]:= 0;
repeat
Stop:= True;
for (∀u ∈ V) do
for (∀v ∈ V:(u,v) ∈ E) do
- Trang 11 -
if d[v] > d[u] + c[u,v] then
begin
d[v] := d[u] + c[u,v];
Stop := False;
end;
until Stop;

Tính đúng của thuật toán:
Tại bước khởi tạo thì mỗi d[v] chính là độ dài ngắn nhất của đường đi từ s tới v qua
không quá 0 cạnh.
Giả sử khi bắt đầu bước lặp thứ i (i ≥ 1), d[v] đã bằng độ dài đường đi ngắn nhất từ s
tới v qua không quá i - 1 cạnh. Bởi đường đi từ s tới v qua không quá i cạnh sẽ phải thành
lập bằng cách: lấy một đường đi từ s tới một đỉnh u nào đó qua không quá i - 1 cạnh, rồi
đi tiếp tới v bằng cung (u, v), nên độ dài đường đi ngắn nhất từ s tới v qua không quá i
cạnh sẽ được tính bằng giá trị nhỏ nhất trong các giá trị (Nguyên lý tối ưu Bellman):
- Độ dài đường đi ngắn nhất từ s tới v qua không quá i - 1 cạnh
- Độ dài đường đi ngắn nhất từ s tới u qua không quá i - 1 cạnh cộng với trọng số
cạnh (u, v)
(∀u)
Vì vậy, sau bước lặp tối ưu các d[v] bằng công thức:
d[v]
bước i
= min(d[v]
bước i-1
, d[u]
bước i-1
+ c[u, v]) (∀u)
thì các d[v] sẽ bằng độ dài đường đi ngắn nhất từ s tới v qua không quá i cạnh.
Sau bước lặp tối ưu thứ n - 1, ta có d[v] = độ dài đường đi ngắn nhất từ s tới v qua
không quá n - 1 cạnh. Vì đồ thị không có chu trình âm nên sẽ có một đường đi ngắn nhất
từ s tới v là đường đi cơ bản (qua không quá n - 1 cạnh). Tức là d[v] sẽ là độ dài đường đi
ngắn nhất từ s tới v.
Vậy thì số bước lặp tối ưu hoá sẽ không quá n - 1 bước.
Trong khi cài đặt chương trình, nếu mỗi bước lặp được mô tả dưới dạng:
for u := 1 to n do
for v := 1 to n do
d[v] := min(d[v], d[u] + c[u, v]);

Sự tối ưu bắc cầu (dùng d[u] tối ưu d[v] rồi lại có thể dùng d[v] tối ưu d[w] nữa…)
chỉ làm tốc độ tối ưu các nhãn d[.] tăng nhanh hơn nên số bước lặp vẫn sẽ không quá n - 1
bước.
1.2.2 Thuật toán Dijkstra
Trong trường hợp trọng số trên các cung không âm, thuật toán do Dijkstra đề xuất
dưới đây hoạt động hiệu quả hơn nhiều so với thuật toán Ford-Bellman. Ta hãy xem trong
trường hợp này, thuật toán Ford-Bellman thiếu hiệu quả ở chỗ nào:
Với đỉnh v ∈ V, Gọi d[v] là độ dài đường đi ngắn nhất từ s tới v. Thuật toán Ford-
Bellman khởi gán d[s] = 0 và d[v] = +∞ với ∀v ≠ s, sau đó tối ưu hoá dần các nhãn d[v]
bằng cách sửanhãn theo công thức: d[v] := min(d[v], d[u] + c[u, v]) với ∀u, v ∈ V. Như
vậy nếu như ta dùng đỉnh u sửa nhãn đỉnh v, sau đó nếu ta lại tối ưu được d[u] thêm nữa
- Trang 12 -
thì ta cũng phải sửa lại nhãn d[v] dẫn tới việc d[v] có thể phải chỉnh đi chỉnh lại rất nhiều
lần. Vậy nên chăng, tại mỗi bước không phải ta xét mọi cặp đỉnh (u, v) để dùng đỉnh u
sửa nhãn đỉnh v mà sẽ chọn đỉnh u là đỉnh mà không thể tối ưu nhãn d[u] thêm được nữa.
Thuật toán Dijkstra (E.Dijkstra - 1959) có thể mô tả như sau:
Bước 1: Khởi tạo
Với đỉnh v ∈ V, gọi nhãn d[v] là độ dài đường đi ngắn nhất từ s tới v. Ban đầu d[v]
được khởi gán như trong thuật toán Ford-Bellman (d[s] = 0 và d[v] = ∞ với ∀v ≠ s). Nhãn
của mỗi đỉnh có hai trạng thái tự do hay cố định, nhãn tự do có nghĩa là có thể còn tối ưu
hơn được nữa và nhãn cố định tức là d[v] đã bằng độ dài đường đi ngắn nhất từ s tới v nên
không thể tối ưu thêm. Để làm điều này ta có thể sử dụng kỹ thuật đánh dấu: Free[v] =
TRUE hay FALSE tuỳ theo d[v] tự do hay cố định. Ban đầu các nhãn đều tự do.
Bước 2: Lặp
Bước lặp gồm có hai thao tác:
- Cố định nhãn: Chọn trong các đỉnh có nhãn tự do, lấy ra đỉnh u là đỉnh có d[u] nhỏ
nhất, và cố định nhãn đỉnh u.
- Sửa nhãn: Dùng đỉnh u, xét tất cả những đỉnh v và sửa lại các d[v] theo công
thức:
d[v]:= min(d[v],d[u]+c[u,v])

Bước lặp sẽ kết thúc khi mà đỉnh đích f được cố định nhãn (tìm được đường đi ngắn
nhất từ s tới f); hoặc tại thao tác cố định nhãn, tất cả các đỉnh tự do đều có nhãn là +∞
(không tồn tại đường đi). Có thể đặt câu hỏi, ở thao tác 1, tại sao đỉnh u như vậy được cố
định nhãn, giả sử d[u] còn có thể tối ưu thêm được nữa thì tất phải có một đỉnh t mang
nhãn tự do sao cho d[u] > d[t] + c[t, u]. Do trọng số c[t, u] không âm nên d[u] > d[t], trái
với cách chọn d[u] là nhỏ nhất. Tất nhiên trong lần lặp đầu tiên thì s là đỉnh được cố định
nhãn do d[s] = 0.
Bước 3: Kết hợp với việc lưu vết đường đi trên từng bước sửa nhãn, thông báo đường
đi ngắn nhất tìm được hoặc cho biết không tồn tại đường đi (d[f] = +∞).
for (∀v ∈ V) do d[v] := +∞;
d[s] := 0;
repeat
u := arg min(d[v]|∀v ∈ V); {Lấy u là đỉnh có nhãn d[u] nhỏ nhất}
if (u = f) or (d[u] = +∞) then Break; {Hoặc tìm ra đường đi ngắn
nhất từ s tới f, hoặc kết luận không có đường}
for (∀v ∈ V: (u, v) ∈ E) do {Dùng u tối ưu nhãn những đỉnh v kề
với u}
d[v] := min (d[v], d[u] + c[u, v]);
until False;
1.2.3 Đường đi trong đồ thị không có chu trình.
Bây giờ ta xét trường hợp riêng thứ hai của bài toán tìm đường đi ngắn nhất, mà để
giải nó có thể xây dựng thuật toán với độ phức tạp tính toán O(n
2
), đó là đồ thị không có
- Trang 13 -
chu trình( còn trọng số trên các cung có thể là các số thực tuỳ ý). Trước hết ta chứng minh
định lý sau:
Định lý: Giả sử G là đồ thị không có chu trình. Khi đó các đỉnh của nó có thể đánh
số sao cho mỗi cung của đồ thị chỉ hướng từ đỉnh có chỉ số nhỏ hơn đến đỉnh có chỉ số
lớn hơn , nghĩa là mỗi cung của nó có thể biểu diễn dưới dạng (v[i],v[j]), trong đó i<j .

Thí dụ: Đồ thị trong hình sau có các đỉnh được đánh số thỏa mãn điều kiện nêu trong
định lý.

Hình 1.15 Đồ thị không có chu trình
Để chứng minh định lý ta mô tả thuật toán sau, cho phép tìm ra cách đánh số thỏa
mãn điều kiện định lý.
Procedure Numbering;
/* Đầu vào : Đồ thị có hướng G=(V,E) với n đỉnh không chứa chu trình được cho
bởi danh sách kề Ke(v),v V
Đầu ra: Với mỗi đỉnh v V chỉ số NR[u] < NR[v]. */
Begin
For v V do Vao[v]:=0;
/* tinh Vao[v]=deg-(v) */
For u V do
For v Ke(u) do Vao[v]:=Vao[v] + 1;
QUEUE:= ;
For v V do
If Vao[v]=0 then QUEUE v ;
Num :=0;
While QUEUE do
Begin u QUEUE;
Num :=num +1; NR[u] :=num;
For v Ke(u) do
Begin
- Trang 14 -
Vao[v]:=Vao[v] - 1;
If Vao[v]=0 then QUEUE v ;
End;
End;
End;

Thuật toán được xây dựng dựa trên ý tưởng rất đơn giản sau:
Rõ rang trong đồ thị không có chu trình bao giờ cũng tìm được đỉnh có bán bậc vào
bằng 0 ( không có cung đi vào ). Thực vậy, bắt đầu từ đỉnh v1 nếu có cung đi vào nó từ v2
thì ta lại chuyển sang xét đỉnh v2. Nếu có cung v3 đi vào v2, thì ta chuyển sang xét v3
Do đồ thị là không có chu trình nên sau một số hữu hạn lần chuyển như vậy ta phải đi
đến đỉnh không có cung đi vào. Thoạt tiên, tìm các đỉnh như vậy của đồ thị. Rõ ràng ta
có thể đáng số chúng theo một thứ tự tuỳ ý bắt đầu từ 1. Tiếp theo, loại bỏ khỏi đồ thị
những đỉnh đã được đánh số cùng các cung đi ra khỏi chúng, ta thu được đồ thị mới cũng
không có chu trình, và thủ tục được lặp lại với đồ thị mới này. Quá trình đó sẽ được tiếp
tục cho đến khi tất cả các đinỉh của đồ thị được đánh số.
Do có thuật toán đánh số trên, nên khi xét đồ thị không có chu trình ta có thể giả thiết
là các đỉnh của nó được đánh số sao cho mỗi cung chỉ đi từ đỉnh có chỉ số nhỏ đến đỉnh
có chỉ số lớn hơn. Thuật toán tìm đường đi ngắn nhất trên đồ thị không có chu trình được
mô tả trong sơ đồ sau đây :
Procedure Critical_Path;
/* Tìm đường đi ngắn nhất từ đỉnh nguồn đến tất cả các đỉnh còn lại trên đồ thị
không có chu trình
Đầu vào: Đồ thị G=(V,E) trong đó V= { v[1], v[2], , v[n] }
Đối với mỗi cung (v[i],v[j]) E ta có i<j.
Đồ thị được cho bởi danh sách kề Ke(v),v V.
Đầu ra: Khoảng cách từ v[1] đến tất cả các đỉnh còn lại được ghi trong
mảng d[v[i] ], i=1,2, ,n */
Begin
d[v[1]]:=0;
for j:=2 to n do d[v[j]]:=a[v[1]],v[j]];
fo j:=2 to n do
for v Ke [v[j]] do
d [v]:=min (d[v], d[v[j]] + a[v[j]], v );
end;
Độ phức tạp của thuật toán là O(m)., do mỗi cung của đồ thị phải xét qua đúng một

lần.
Các thuật toán mô tả ở trên thường được ứng dụng vào việc xây dựng những phương
pháp giải bài toán điều khiển việc thực hiện những dự án lớn, gọi tắt là PERT (Project
Evaluation and Review Technique ) hay CMD ( Critical path method).
- Trang 15 -
1.2.3 Đường đi ngắn nhất giữa tất cả các cặp đỉnh
Rõ ràng ta có thể giải bài toán tìm đường đi ngắn nhất giữa tất cả các cặp đỉnh của đồ
thị bằng cách sử dụng n lần thuật toán mô tả ở mục trước, trong đó ta sẽ chọn s lần lượt là
các đỉnh của đồ thị. Rõ ràng, khi đó ta thu được thuật toán với độ phức tạp là O(n
4
) (nếu
dùng tt Ford-Bellman) hoặc O(n
3
) đối với trường hợp trọng số không âm hoặc đồ thị
không có chu trình. Trong trường hợp tổng quát, sử dụng thuật toán Ford-Bellman n lần
không phải là cách làm tốt nhất. Ở đây ta sẽ mô tả thuật toán với độ phức tạp tính toán
O(n
3
) : thuật toán Floyd được mô tả như sau:
Procedure Floyd;
/* Tìm đường đi ngắn nhất giữa tất cả các cặp đỉnh
Đầu vào : Đồ thị cho bởi ma trận trọng số a[i,j], i,j=1,2, ,n
Đầu ra : Ma trận đường đi ngắn nhất giữa các cặp đỉnh
d[i,j] i,j =1,2, ,n
trong đó d[i,j] cho độ dài đường di ngắn nhất từ i đến j.
Ma trận ghi nhận đường đi
p[i,j], i, j=1,2, ,n.
trong đó p[i,j] ghi nhận đỉnh đi trước j trong đường đi ngắn nhất từ i đến j.
*/
Begin

/* Khởi tạo */
For i:=1 to n do
For j:=1 to n do
Begin
d[i,j]:=a[i,j];
p[i,j]:=i;
end;
/* Bước lặp */
For k:=1 to n do
For i:=1 to n do
For j:=1 to n do
If d[i,j]>d[i,k] + d[k,j] then
Begin
d[i,j]:= d[i,k] + d[k,j];
p [i,j]:= p [k,j];
end;
end;
Rõ ràng độ phức tạp của thuật toán là O(n
3
).
Khác biệt rõ ràng của thuật toán Floyd là khi cần tìm đường đi ngắn nhất giữa một
cặp đỉnh khác, chương trình chỉ việc in kết quả chứ không phải thực hiện lại thuật toán
Floyd nữa.
- Trang 16 -
NHẬN XÉT:
Bài toán đường đi dài nhất trên đồ thị trong một số trường hợp có thể giải quyết bằng
cách đổi dấu trọng số tất cả các cung rồi tìm đường đi ngắn nhất, nhưng hãy cẩn thận, có
thể xảy ra trường hợp có chu trình âm.
Trong tất cả các cài đặt trên, vì sử dụng ma trận trọng số chứ không sử dụng danh
sách cạnh hay danh sách kề có trọng số, nên ta đều đưa về đồ thị đầy đủ và đem trọng số

+∞ gán cho những cạnh không có trong đồ thị ban đầu. Trên máy tính thì không có khái
niệm trừu tượng +∞ nên ta sẽ phải chọn một số dương đủ lớn để thay. Như thế nào là đủ
lớn? số đó phải đủ lớn hơn tất cả trọng số của các đường đi cơ bản để cho dù đường đi
thật có tồi tệ đến đâu vẫn tốt hơn đường đi trực tiếp theo cạnh tưởng tượng ra đó.
Xét về độ phức tạp tính toán, nếu cài đặt như trên, thuật toán Ford-Bellman có độ
phức tạp là O(n3), thuật toán Dijkstra là O(n2), thuật toán Floyd là O(n3).
Khác với một bài toán đại số hay hình học có nhiều cách giải thì chỉ cần nắm vững
một cách cũng có thể coi là đạt yêu cầu, những thuật toán tìm đường đi ngắn nhất bộc lộ
rất rõ ưu, nhược điểm trong từng trường hợp cụ thể (Ví dụ như số đỉnh của đồ thị quá
lớn làm cho không thể biểu diễn bằng ma trận trọng số thì thuật toán Floyd sẽ gặp khó
khăn, hay thuật toán Ford-Bellman làm việc khá chậm). Vì vậy yêu cầu trước tiên là phải
hiểu bản chất và thành thạo trong việc cài đặt tất cả các thuật toán trên để có thể sử dụng
chúng một cách uyển chuyển trong từng trường hợp cụ thể. Những bài tập sau đây cho ta
thấy rõ điều đó.
- Trang 17 -
Chương II : THUẬT TOÁN FLOYD
2.1 Nội dung thuật toán Floyd:
Floyd là giải thuật tìm độ dài đường đi ngắn nhất giữa mọi cặp đỉnh trong đồ thị có
hướng liên thông có trọng số.
+ Đầu vào: Đồ thị liên thông G = (V,E), V= {1, 2, , n}, có trọng số w(i,j) >0 với
mọi cung (i,j).
+ Đầu ra: Ma trận D=[d(i,j)], trong đó d(i,j) là chiều dài đường đi ngắn nhất từ i đến j
với mọi cặp (i,j).
Khái niệm trung tâm của F-W Algo. Là các đỉnh trung gian.
Định nghĩa: Ký hiệu p=(x
1
, x
2
,…, x
k

) là đường đi từ x
1
đến x
k
thì mọi đỉnh x
2
,…,x
k

gọi là các đỉnh trung gian trên đường đi p từ x
1
đến x
k
.
Ý tưởng chính của F-W Algo. là: Cho V={1, 2,…, n} là tập đỉnh của đồ thị và tập
đỉnh U={1, 2,…, k}. Xét cặp đỉnh i,j và mọi đường đi có thể từ i đến j với các đỉnh trung
gian chỉ là các đỉnh thuộc tập U. Gọi p là đường đi ngắn nhất từ i đến j với các đỉnh trung
gian thuộc U. Khi đó ta có hai tình huống sau:
a. Nếu k không là đỉnh trung gian trên đường đi từ i đến j thì đường đi ngắn nhất từ i
đến j có các đỉnh trung gian là {1, 2,…, k-1} cũng là đường đi ngắn nhất từ i đến j với các
đỉnh trung gian là {1, 2,…, k}
b. Nếu k là một đỉnh trung gian trên đường đi từ i đến j thì ta tách đường đi p thành
hai đoạn con là p
1
đi từ i đến k và p
2
đi từ k đến j. Các đoạn đường con p
1
, p
2

là các đường
đi ngắn nhất với các đỉnh trung gian là các đỉnh {1, 2,…, k-1}. Từ đó suy ra cách xác định
độ dài của đường đi từ i đến j nhờ hệ thức sau:
Trong đó d
ij
là độ dài đường đi ngắn nhất từ i đến j; w
ij
là trọng số trên đường đi ij.
Từ hệ thức trên ta thấy: để xác định độ dài đường đi ngắn nhất w
ij
{1 k} từ i đến j qua các
đỉnh
tập {1, 2,…, k} ta chỉ cần dựa vào d
ij
{1 k-1} (đường đi ngắn nhất từ i đến j qua tập đỉnh
1… k-1).

- Trang 18 -
Sau đây là các bước của thuật toán Floyd:
Bước 1: Khởi tạo:
Ký hiệu D
0
là ma trận xuất phát
D
0
= [d
0
(i,j)]
trong đó:
d

0
(i,j) = w(i,j) nếu tồn tại cung (i,j)
d
0
(i,j) = +

nếu không tồn tại cung (i,j)

(đặc biệt nếu không có khuyên tại i thì d
0
(i,i) = +

).
Gán k:=0
Bước 2: Kiểm tra kết thúc:
Nếu k = n, kết thúc. D = D
n
là ma trận độ dài đường đi ngắn nhất. Ngược lại:
k:=k+1
→ sang bước 3.
Bước 3: Tính ma trận D
k
theo D
k-1

Với mọi cặp (i,j), i=1 n, j=1 n thực hiện:
Nếu d
k-1
(i,j) > d
k-1

(i,k) + d
k-1
(k,j) thì đặt
d
k
(i,j) := d
k-1
(i,k) + d
k-1
(k,j)
ngược lại đặt
d
k
(i,j) := d
k-1
(i,j)
Quay lại bước 2.
Độ phức tạp của thuật toán Floyd là: O(n
3
).
Định lý : Thuật toán Floyd là đúng.
Hệ quả :
(i) Nếu ma trận kết quả của thuật toán Floyd có phần tử hữu hạn trên đường chéo
chính thì đồ thị chứa chu trình.
(ii) Nếu ma trận kết quả chứa phần tử +∞ ngoài đường chéo chính thì đồ thị không
liên thông mạnh.
- Trang 19 -
VÍ DỤ:
Xét đồ thị sau :


Hình 2.1
Áp dụng thuật toán Floyd:
Ma trận khoảng cách xuất phát D
0
là (các ô trống là ∞):
Bảng 2.1
- Trang 20 -
Bảng 2.2
Bảng 2.3
Bảng 2.4
- Trang 21 -
Bảng 2.5
Bảng 2.6
Cuối cùng, D là ma trận khoảng cách ngắn nhất giữa các đỉnh. Theo hệ quả ta thấy đồ
thị liên thông mạnh và chứa chu trình:
Bảng 2.7
- Trang 22 -
2.2 Ứng dụng của thuật toán Floyd-Warshall
Trong các ứng dụng thực tế, bài toán tìm đường đi ngắn nhất giữa hai đỉnh của một
đồ thị liên thông có một ý nghĩa to lớn.
Ví dụ, bài toán chọn một hành trình tiết kiệm nhất (theo tiêu chuẩn hoặc khoảng cách
hoặc thời gian hoặc chi phí) trên một mạng giao thông đường bộ, đường thủy hoặc đường
không; bài toán chọn một phương pháp tiết kiệm nhất để đưa ra một hệ thống động lực từ
trạng thái xuất phát đến trạng một trạng thái đích, bài toán lập lịch thi công các công các
công đoạn trong một công trình thi công lớn, bài toán lựa chọn đường truyền tin với chi
phí nhỏ nhất trong mạng thông tin, v.v…
Các thuật toán Floyd-Warshall có thể được sử dụng để giải quyết các vấn đề sau đây:
- Tìm đường đi ngắn nhất giữa mọi cặp đỉnh trong đồ thị có hướng.
- Bài toán tìm bao đóng chuyển tiếp.
- Tìm một biểu thức chính quy biểu thị các ngôn ngữ thông thường được chấp nhận bởi

một automaton hữu hạn.
- Nghịch đảo của các ma trận thực.
- Tối ưu định tuyến (Tìm kiếm các con đường với lưu lượng tối đa giữa hai đỉnh).
- Kiểm tra đồ thị hai phía.
- Tối đa băng thông

- Trang 23 -
Chương III : CHƯƠNG TRÌNH MÔ TẢ THUẬT TOÁN
3.1 Giao diện chính của chương trình
Hình 3.1 Giao diện chương trình
Chương trình được thiết kế nhằm mục đích thực hiện công việc mô tả thuật toán tìm
đường đi ngắn nhất giữa mọi cặp đỉnh Floyd, nó được viết bằng công cụ lập trình Visual
studio C# và gồm có một số chức năng chính sau: Nhập thông tin vào cho đồ thị cần xét
bằng cách sử dụng công cụ thiết kế, các tùy chọn nút để chỉnh sửa đỉnh của đồ thị, và một
số chức năng khác như: chọn điểm bắt đầu và điểm kết thúc, xóa cạnh mới nhất, xóa tất
cả cạnh và xóa cả đồ thị.
3.2 Các bước thực hiện chương trình
Bước 1: Ban đầu ta cần nhập thông tin vào cho chương trình bằng cách sử dụng công
cụ vẽ nằm phía dưới của chương trình để vẽ đồ thị, các thông tin về trọng số của 2 đỉnh sẽ
không được nhập trực tiếp mà ta sẽ thực hiện bằng cách di chuyển các đỉnh ra xa nhau,
chương trình sẽ tự động tăng trọng số nếu ta kéo 2 đỉnh ra xa (và ngược lại).
Bước 2: Sử dụng một số chức năng của chương trình để sửa, xóa, đổi mầu đỉnh đồ
thị muốn xét.

- Trang 24 -
Ngay khi đưa dữ liệu vào thì chương trình tự động tính toán và đưa ra kết quả là ma
trận có trọng số là đường đi ngắn nhất giữa các đỉnh và hiện trên chương trình cho ta thấy.
Bước 3: Chọn đỉnh ban đầu và đỉnh kết thúc để chương trình đưa ra kết quả là đường
đi ngắn nhất giữa 2 đỉnh đó. Đường đi sẽ được hiện màu xanh đoạn gạch đứt và có 1
chấm màu xanh đậm di chuyển từ điểm đầu đến điểm cuối.

3.3 Các chức năng chính của chương trình
Công cụ vẽ đồ thị:
+ Di chuyển: Dùng để di chuyển
các đỉnh của đồ thị để chỉnh trọng số
của các đỉnh.
Hình 3.2 Công cụ vẽ
+ Vẽ đỉnh: Tạo các đỉnh cho đồ thị, chương trình sẽ tự động tạo đỉnh với tên theo thứ
thự alphabet. Do vậy ta không cần phải đặt tên cho từng đỉnh.
+ Vẽ cạnh: Tạo các cạnh cho đồ thị.
+ Xóa: Xóa 1 đỉnh nào đó nếu ta nháy chuột vào.
Công cụ chỉnh sửa và hỗ trợ vẽ đồ thị:
+ Xóa cạnh mới nhất: Xóa cạnh vừa mới tạo.
+ Xóa cạnh: Xóa toàn bộ cạnh của đồ thị.
+ Xóa tất cả: Xóa toàn bộ đồ thị.
+ Đồ thị vô hướng: Khi ta tích vào thì đồ thị chuyển thành đồ
thị vô hướng. Các cạnh của đồ thị hiện bên cạnh sẽ không còn mũi
tên chỉ chiều nữa.
+ Xóa đỉnh: Xóa đỉnh hiện tại đang được chọn.
+ Đổi màu: Đổi màu cho đỉnh đang được chọn. Đổi màu để
đánh dấu các đỉnh, tùy vào mục đích của người dùng chương trình.
Hình 3.3
Truy xuất kết quả:
+ Từ: Đỉnh ban đầu.
+ Đến: Đỉnh kết thúc.
+ Bắt đầu: Chương trình sẽ thực hiện tìm đường đi ngắn nhất
giữa đỉnh ban đầu và đỉnh kết thúc, sau đó đưa ra kết quả.
Hình 3.4
- Trang 25 -

×