Báo cáo thực tập cơ sở thuật toán Floyd
Trang 1LỜ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ụnghiệ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ủathế 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ẳnghạ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ứcphâ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 xemhai 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àitoá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ữngthuậ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ướngdẫ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ôngthể 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 2MỤ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 3DANH 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 4DANH 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 5Chươ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.2 Nhà toán học Thụy Sĩ
Leonhard Euler
(April 1707 – September 1783)
Trang 6Ví 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 71.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.
New York Chicago
Washington Detroit
San Francisco
Denver Los Angeles
New York Chicago
Washington Detroit
Trang 8Bả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à
New York Chicago
Washington Detroit
Trang 9Cho đồ 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
f
Trang 10Hệ 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 v0e1v1e2…vk-1ekvk sao cho: v0=u ,vk= v, ei=vi-1vi , 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
Đồ 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 11Hì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ừ utớ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]:= +∞;
Trang 12if d[v] > d[u] + c[u,v] then
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 quakhô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ừ stớ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ànhlậ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á icạ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 quakhô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 đingắ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 - 1bướ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ấtdướ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 trongtrườ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 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 13Ford-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ềulầ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 usử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ãncủ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 ưuhơ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ênkhô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ôngthứ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ắnnhấ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 mangnhã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áivớ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ố địnhnhã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(n2), đó là đồ thị không có
Trang 14chu 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
/* Đầ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] */
Trang 15Thuậ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àobằ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ừ v2thì 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ũngkhô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ếptụ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
Trang 161.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(n4) (nếudùng tt Ford-Bellman) hoặc O(n3) đố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ầnkhô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ánO(n3) : 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
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(n3)
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