Duyệt đồ thị theo chiều sâu DFS-Depth First Search:DFS là thuật toán được sử dụng để tìm kiếm hay duyệt qua tất cả các đỉnh thuộc các thành phần liên thông của đồ thị một cách có hệ thốn
Trang 1BỘ CÔNG THƯƠNG TRƯỜNG ĐẠI HỌC CÔNG THƯƠNG TP.HỒ CHÍ MINH
Đồ án học phần: CẤU TRÚC RỜI RẠC
Tên đề tài: Mô phỏng chức năng đón trả khách của ứng dụng xe ôm
công nghệ dựa trên thuật toán Dijkstra
Danh sách thành viên:
1 Trần Nguyễn Như Huỳnh 2001221771 100%
GV hướng dẫn: Đinh Thị Hồng Loan
Thành phố Hồ Chí Minh, tháng 12 năm 2023
Trang 2MỤC LỤC
1 Lý thuyết đồ thị và ứng dụng: 1
1.1 Khái niệm đồ thị: 1
1.2 Phân loại: 1
1.2.1 Đơn đồ thị vô hướng: 1
1.2.2 Đa đồ thị vô hướng: 2
1.2.3 Giả đồ thị: 2
1.2.4 Đơn đồ thị có hướng: 3
1.2.5 Đa đồ thị có hướng: 3
1.3 Các bài toán về đồ thị và ứng dụng: 4
1.3.1 Duyệt đồ thị: 4
1.3.2 Đường đi Euler – Chu trình Euler: 5
1.3.3 Đường đi Hamilton – Chu trình Hamilton: 6
1.3.4 Bài toán tìm cây khung ngắn nhất: 7
1.3.5 Bài toán tìm đường đi ngắn nhất: 9
1.4 Ứng dụng: 11
2 Bài toán thực tế: 11
2.1 Mô tả bài toán và giải pháp: 11
2.2 Cấu trúc dữ liệu và giải thuật: 12
2.2.1 Cấu trúc dữ liệu: 12
2.2.2 Giải thuật: 14
3 Kết quả cài đặt và thử nghiệm: 20
3.1 Nền tảng, ngôn ngữ cài đăt: 20
3.2 Các giao diện của chương trình: 20
3.3 Thử nghiệm: 21
3.3.1 Test case : 21
3.3.2 Input: 22
3.3.3 Output: 23
4 Kết luận: 26
Trang 31.2.1 Đơn đồ thị vô hướng:
Đơn đồ thị vô hướng G = (V,E) bao gồm:
Trang 41.2.2 Đa đồ thị vô hướng:
Đa đồ thị vô hướng G= (V, E) bao gồm:
• E: là tập các cặp không có thứ tự gồm hai phần tử khác nhau của V gọi là các cạnh Hai cạnh e1 và e2 được gọi là cạnh lặp(bội hay song song) nếu chúng cùng tương ứng với một cặp đỉnh
Hình 2 Sơ đồ mạng máy tính đa kênh thoại.
Mỗi đơn đồ thị là đa đồ thị, nhưng không phải đa đồ thị nào cũng
là đơn đồ thị, vì trong đa đồ thị có thể có hai (hoặc nhiều hơn) cạnhnối một cặp đỉnh nào đó
Trang 61.3.1.1 Duyệt đồ thị theo chiều sâu (DFS-Depth First Search):
DFS là thuật toán được sử dụng để tìm kiếm hay duyệt qua tất cả các đỉnh thuộc các thành phần liên thông của đồ thị một cách có hệ thống với kỹ thuật gọi là độ ưu tiên sâu Ứng dụng giải các bài toán về liên thông, cây khung, đường đi…
* Ý tưởng:
‐ Xuất phát từ một đỉnh v cho trước chưa thăm
‐ Thăm đỉnh v và tìm đỉnh u nào đó chưa thăm kề với v, thăm u và lặp lại quá trình này cho đến khi tất cả các đỉnh đều được thăm
‐ Nếu tại một đỉnh u nào đó mà không còn đỉnh kề chưa thăm thì quay trở lại đỉnh trước của u (có đỉnh kề là u
Trang 7trong đường đi đến u) và tìm các đỉnh kề chưa thăm củađỉnh này.
1.3.1.2 Duyệt đồ thị theo chiều rộng (BFS-Breadth First Search):
Thuật toán BFS dùng để duyệt và tìm kiếm trên đồ thi cũng tương tự như thuật toán DFS nhưng thay vì dùng cấu trúc stack thì BFS dùng cấu trúc hàng đợi để thực hiện, kỹ thuật này gọi là tìm kiếm ưu tiên chiều rộng
‐ Lặp lại cho đến khi hàng đợi rỗng
1.3.2 Đường đi Euler – Chu trình Euler:
1.3.2.1 Bài toán Euler:
‐ 1736 Euler (1707-1783) công bố lời giải “bài toán về các cầu ở Konigsberg”
‐ Bài toán: Thị trấn Konigsberg chia thành 4 phần bởi cácnhánh của dòng sông Pregel Bốn phần này được kết nốibởi 7 cây câu
Trang 8Bài toán tìm đường đi qua tất cả các cầu, mỗi cầu chỉqua một lần có thể được phát biểu lại bằng mô hìnhnhư sau:
1.3.2.2 Đường đi Euler:
‐ Đường đi qua mỗi cạnh của đồ thị đúng một lần được gọi là đường đi Euler
‐ Chu trình qua mỗi cạnh của đồ thị đúng một lần được gọi là chu trình Euler
‐ Đồ thị được gọi là đồ thị Euler nếu nó có chu trình Euler, và gọi là đồ thị nửa Euler nếu nó có đường đi Euler
Nhận xét: Mọi đồ thị Euler luôn là nửa Euler,nhưng điều
ngược lại không luôn đúng
1.3.2.3 Thuật toán Fleury tìm chu trình Euler:
Xuất phát từ một đỉnh u nào đó của G ta đi theo các cạnh của
nó một cách tuỳ ý chỉ cần tuân thủ 2 qui tắc sau:
(1) Xoá bỏ cạnh đã đi qua đồng thời xoá bỏ đỉnh cô lập tạo thành
(2) Ở mỗi bước ta chỉ đi qua cầu khi không còn cách lựa chọn nào khác
1.3.3 Đường đi Hamilton – Chu trình Hamilton:
Trang 91.3.3.1 Định nghĩa:
Xét G liên thông có hơn 1 đỉnh
• Đường đi Hamilton: (nếu có) là đường đi mà đi qua tất
cả các đỉnh của G mỗi đỉnh đúng 1 lần
• Chu trình Hamilton: là một chu trình xuất phát từ 1 đỉnh, đi qua tất cả các đỉnh của đồ thị G mỗi đỉnh đúngmột lần, cuối cùng quay lại đỉnh xuất phát
• Đồ thị được gọi là đồ thị Hamilton nếu nó có chu trìnhHamilton Đồ thị được gọi là đồ thị nửa Hamilton nếu
nó có đường đi Hamilton
→ Nếu G có chu trình Hamilton thì G cũng có đường đi Hamilton Điều ngược lại không đúng
1.3.3.2 Qui tắc tìm chu trình Hamilton:
(1) Nếu tồn tại 1 đỉnh của G có bậc <= 1 thì G không có chu
(4) Trong quá trình xây dựng chu trình Hamilton, sau khi đã lấy
2 cạnh tới đỉnh v đặt vào chu trình H rồi thì không thể lấy thêm cạnh nào tới v nữa, do đó có thể xóa mọi cạnh còn lại tới v
1.3.3.3 Các định lý:
• Định lý 1 (Dirak 1952): Đơn đồ thị vô hướng G với n>2 đỉnh, mỗi đỉnh có bậc không nhỏ hơn n/2 là đồ thị Hamilton
• Định lý 2 : Nếu G là đồ thị phân đôi với hai tập đỉnh là V1, V2
có số đỉnh cùng bằng n (n ≥ 2) và bậc của mỗi đỉnh lớn hơn n/2 thì G là một đồ thị Hamilton
• Định lý 3 : Giả sử G là đồ có hướng liên thông với n đỉnh Nếu deg+(v) ≥ n/2, deg–(v) ≥ n/2, ∀v thì G là Hamilton
1.3.4 Bài toán tìm cây khung ngắn nhất:
Trang 10* Định nghĩa cây khung ngắn nhất:
Cho một đồ thị vô hướng G, trên mỗi cạnh của G được gán một trọng số c(i,j) Cây khung T của G được gọi là cây khung ngắn nhất nếu tổng trọng số các cạnh của T là nhỏ nhất
* Bài toán tìm cây khung ngắn nhất:
Bài toán đặt ra là trong số tất cả các cây khung của đồ thị G, tìm cây khung có độ dài nhỏ nhất
Hai mô hình thực tế tiêu biểu:
‐ Bài toán xây dựng hệ thống đường sắt
‐ Bài toán nối mạng máy tính
1.3.4.1 Thuật toán Kruskal:
Bước 3: Tiếp tục thực hiện bước 2 cho đến khi T có n-1
cạnh thì dừng
1.3.4.2 Thuật toán Prim:
Trang 11*Ý tưởng:
S là tập chứa các đỉnh của cây khung; S={x} (với x là đỉnh bất kỳ thuộc đồ thị)
số bé nhất trong w(s)
T = T∪ (x,y); S =S ∪ {y}
Bước 3: Lặp bước 2 đến khi T có n-1 cạnh thì dừng.
1.3.5 Bài toán tìm đường đi ngắn nhất:
Bài toán:
• Tìm đường đi ngắn nhất từ một đỉnh s ∈ V (đỉnh nguồn) đến đỉnh cuối t ∈V (đỉnh đích) Độ dài của đường đi từ s đến t là d(s,t) bằng tổng độ dài các cạnh mà đường đi qua
• Ma trận trọng số (khoảng cách) Cho đồ thị G=(V, E), V
1.3.5.1 Thuật toán Dijkstra:
‐ Thuật toán này do E.Dijkstra, nhà toán học người Hà Lan,
đề xuất năm 1959
- Thuật toán tìm đường đi ngắn nhất từ đỉnh s đến các đỉnh còn lại được Dijkstra đề nghị áp dụng cho trường hợp đồ thị có hướng với trọng số không âm
- Thuật toán duyệt qua các đỉnh theo thứ tự tăng dần của khoảng cách từ đỉnh nguồn và cập nhật khoảng cách ngắnnhất đến mỗi đỉnh khi tìm thấy một đường đi tốt hơn
Trang 12- Thuật toán thường được sử dụng trong định tuyến với một chương trình con trong các thuật toán đồ thị hay trong công nghệ Hệ thống định vị toàn cầu (GPS).
Bài toán: Cho đơn đồ thị liên thông G =(V,E) có trọng số
dương (w(uv) ∀ u≠v) Tìm đường đi ngắn nhất từ u đến v và tính khoảng cách d(u,v)
*Ý tưởng: Xác định tuần tự các đỉnh có khoảng cách đến u từ
nhỏ đến lớn
• Trước tiên đỉnh có khoảng cách nhỏ nhất đến u là u
• Trong V\{u} tìm đỉnh có khoảng cách đến u nhỏ nhất (đỉnh này phải là một trong số các đỉnh kề với u) giả sử
u1
• Trong V\{u, u1} tìm đỉnh có khoảng cách đến u nhỏ nhất (đỉnh này phải là một trong số các đỉnh kề với u hoặc với u1) giả sử là u2
• Tiếp tục như trên cho đến bao giờ tìm được khoảng cách từ u đến mọi đỉnh
Nếu G có n đỉnh thì:
0 = d(u,v) < d(u, u1) ≤ d(u, u2) ≤… ≤ d(u,un-1)
1.3.5.2 Thuật toán Floyd:
Ý tưởng thuật toán
Ý tưởng chính của thuật toán Floyd-Warshall là sử dụng phương pháp lặp để cập nhật ma trận khoảng cách ngắn nhất.Thuật toán bắt đầu với ma trận khoảng cách ban đầu, trong
đó khoảng cách giữa các đỉnh được biết trước (trực tiếp kề nhau) được gán giá trị là trọng số của cạnh nối chúng Sau
Trang 13đó, thuật toán lặp qua tất cả các cặp đỉnh và cố gắng cải thiệnkhoảng cách ngắn nhất bằng cách kiểm tra xem có đường đi nào ngắn hơn thông qua một đỉnh trung gian Nếu có, thuật toán sẽ cập nhật khoảng cách ngắn nhất và lưu trữ đỉnh trunggian đó.
1.4 Ứng dụng:
• Có tiềm năng ứng dụng trong nhiều lĩnh vực (Đồ thị có thể dùng để biểu diễn các quan hệ Nghiên cứu quan hệ giữa các đối tượng là mục tiêu của nhiều lĩnh vực khác nhau)
• Ứng dụng trong mạng máy tính, mạng giao thông, mạng cung cấp nước, mạng điện,…) lập lịch, tối ưu hoá luồng, thiết kế mạch, quy hoạch phát riển
• Các ứng dụng khác: Phân tích gen, trò chơi máy tính, chương trình dịch, thiết kế hướng đối tượng,
2 Bài toán thực tế:
2.1 Mô tả bài toán và giải pháp:
‐ Bài toán: Viết một chương trình mô phỏng lại tính năng đón trả khách dành cho tài xế của ứng dụng xe ôm công nghệ dựa trên thuật toán Dijkstra
‐ Giải pháp: Sử dụng thuật toán Dijkstra để tìm đường đi ngắn nhấtgiữa hai điểm trên bản đồ, dựa trên khoảng cách giữa các điểm Chương trình sẽ có các bước như sau:
Nhập vào danh sách thông tin khách hàng cần book chuyến (thông tin khách hàng, điểm đón, điểm trả, giá cước,…) v à vị trí hiện tại của tài xế
Hiển thị danh sách các yêu cầu đón khách để tài xế lựa chọn
Cho phép tài xế lựa chọn chuyến đi mong muốn
Sử dụng thuật toán Dijkstra để tìm đường đi ngắn nhất từ
vị trí của xe ôm công nghệ đến vị trí của khách hàng, và
từ vị trí của khách hàng đến điểm trả khách
Trang 14 Kết thúc chuyến đi khi xe ôm công nghệ đưa khách hàng đến điểm đến mong muốn và cho phép tài xế lựa chọn việc có tiếp tục đón khách hay không Nếu có tiếp tục thực hiện lại các bước trên, nếu không chương trình kết thúc.
2.2 Cấu trúc dữ liệu và giải thuật:
2.2.1 Cấu trúc dữ liệu:
2.2.1.1 Tổ chức dữ liệu:
‐ Định nghĩa một số cấu trúc dữ liệu cần thiết:
Cấu trúc của mỗi điểm trong đồ thị, bao gồm:
Tên của điểm
Mảng 1 chiều chứa các khoảng cách từ điểm đó đến các điểm còn lại
Cấu trúc của mỗi kết quả đường đi ngắn nhất từ điểmnguồn đến điểm đích, bao gồm:
Tổng độ dài đường đi
Tên điểm kề trước điểm đích
Cấu trúc thông tin của mỗi khách hàng book chuyến, bao gồm:
Tên khách hàng
Số điện thoại liên lạc
Điểm đón khách
Điểm trả khách
Trang 15 Cước phí của chuyến đi
2.2.1.2 Lưu trữ và truy xuất dữ liệu:
- Sử dụng mảng một chiều để lưu danh sách các điểm, danhsách các kết quả đường đi ngắn nhất và danh sách các khách hàng book chuyến
- Mỗi hàng của ma trận khoảng cách trong đồ thị được lưu vào trong mảng distance của mỗi phần tử dot
- Kết quả đường đi ngắn nhất từ điểm nguồn đến các điểm còn lại được lưu lần lượt vào mảng dijkstra Điểm nguồn được mặc định là phần tử đầu tiên của mảng dot, các điểm còn lại là điểm đích Vì vậy phần
tử đầu tiên của mảng dijkstra luôn bằng 0 do khoảng cách từ đỉnh nguồn đến đỉnh nguồn là 0 Các kết quả trong mảng dijkstra được lưu theo chỉ số tương ứng của đỉnh đích trong mảng Dot
Vd: Thông tin của điểm B được lưu trong mảng Dot ở phần tử thứ 2 thì kết quả đường đi ngắn nhất từ đỉnh
Trang 16nguồn đến đỉnh B cũng được lưu ở phần tử thứ 2 trongmảng dijkstra.
- Thông tin của mỗi khách hàng book chuyến cũng được lưu trong mảng p theo thứ tự dữ liệu nhập vào
2.2.2 Giải thuật:
2.2.2.1 Thuật toán Dijkstra:
- Bước 1: Khởi tạo
(1) Tổng độ dài đường đi của phần tử đầu tiên trong mảng dijkstra là 0 và điểm kề trước nó làchính nó
(2) Tất cả các phần tử của mảng 1 chiều visiteddot (mảng dùng để đánh dấu các điểm
đã đi qua trên đồ thị, có 2 giá trị là 0 và -1, nếuphần tử ở vị trí thứ i có giá trị là -1 tức điểm ở
vị trí đó đã được đi qua và ngược lại)(3) Bộ đếm k = n (n là số lượng điểm trong đồ thị)
- Bước 2: Tìm khoảng cách từ điểm nguồn đến các điểm
liền kề nó(1) Duyệt các phần tử trong mảng distance của điểm đầu tiên:
+ Nếu khoảng cách từ điểm đầu tiên đến điểm thứ i là khác 0, thì :
Cập nhật kết quả đường đi ngắn nhất
từ điểm đầu tiên đến phần tử thứ i trong mảng dijkstra là khoảng cách thứ i trong mảng distance
Cập nhật điểm kề trước phần tử thứ i
đó là điểm đầu tiên
+ Ngược lại, cập nhật kết quả đường đi ngắnnhất ở các vị trí đó là vô hạn
Trang 17(2) Cập nhật giá trị phần tử đầu tiên của mảng visiteddot là -1.
Gán minindex bằng vị trí của phần tử có giá trị min
(3) Cập nhật lại giá trị của phần tử thứ minindex trong mảng visiteddot thành -1 (đánh dấu đã điqua, hiện là đỉnh nguồn tạm thời)
(4) Duyệt lại lần lượt các giá trị trong mảng dijkstra, cập nhật lại giá trị tổng đường đi ngắnnhất ở các đỉnh chưa được đi qua:
Nếu nếu min cộng với khoảng cách từ đỉnh nguồn tạm thời đến đích thứ i nhỏ hơn kết quả đường đi ngắn nhất thứ i trong mảng dijkstra, thì :
Cập nhật lại giá trị của phần tử thứ i trong mảng dijkstra thành giá trị mincộng với khoảng cách từ điểm nguồntạm thời đến điểm đích thứ i
Cập nhật điểm kề trước phần tử thứ itrong mảng dijkstra là điểm thứ minindex
Ngược lại, không cập nhật
(5) Giảm k
(6) Lặp lại từ (1) cho đến khi k=0
Trang 182.2.2.2 Đổi đỉnh nguồn:
‐ Thuật toán Dijkstra như ở trên được mặc định đỉnh nguồn
là điểm đầu tiên trong mảng dot Do vậy nếu muốn lặp đi lặp lại việc tìm đường từ điểm này đến điểm khác ta cần phải đổi đỉnh nguồn để có thể tiếp tục tìm đường
‐ Thực tế, mỗi hàng trong ma trận khoảng cách của đồ thị được lưu vào các mảng distance trong mỗi dot Vậy nên, việc đổi đỉnh nguồn không thể chỉ là di chuyển phần tử
Trang 19thứ i trong mảng dot lên đầu mà còn là việc thay đổi giá trị của các hàng và cột trong trận khoảng cách của đồ thị.
‐ Giải pháp để giải quyết vấn đề này là ghi ra một ma trận mới vào file txt và đọc lại nó để cập nhật ma trận mới Các bước của giải thuật như sau:
Bước 1: Ghi file
(1) Dòng đầu tiên là số lượng điểm trong đồ thị(2) N dòng tiếp theo là tên của các điểm, tên của điểm nguồn mới sẽ được ghi trên cùng(3) N dòng tiếp theo là ma trận khoảng cách của đồ thị, ưu tiên ghi các dòng và cột có index của điểm nguồn mới đầu tiên, sau đó đến các hàng và cột theo thứ tự từ bé đến lớn
Trang 20 Bước 2: Đọc file txt vứa ghi để cập nhật lại dữ liệu cho ma trận.
Trang 212.2.2.3 Thuật toán hướng dẫn chỉ đường:
(1) Mảng số nguyên 1 chiều direct (dùng để lưu chỉ
số của các điểm trên đường đi)(2) Bộ đếm j=0 (đếm số điểm đi qua trên đường đi)(3) des = destination (biến tạm lưu chỉ số các điểm
đi qua, khởi tạo bằng chỉ số điểm đích)
(1) Duyệt mảng dot tìm điểm có tên trùng với tên của điểm kề trước ở phần tử thứ des trong mảng dijkstra
Nếu tên trùng nhau, thì:
Lưu vị trí của điểm có tên đó vào mảng direct;
Cập nhật lại des bằng vị trí của điểm vừa tìm được
Tăng j(2) Tiếp tục bước 2.(1) cho đến khi des =0;
Trang 22‐ Bước 3: In ngược mảng direct để lấy kết quả chỉ đường từ
đỉnh nguồn đến đỉnh đích
3 Kết quả cài đặt và thử nghiệm:
3.1 Nền tảng, ngôn ngữ cài đăt:
‐ Nền tảng : Visual Studio 2022
‐ Ngôn ngữ cài đặt : C
3.2 Các giao diện của chương trình:
‐ Giao diện khi bắt đầu chương trình:
Trang 23‐ Giao diện khi kết thúc chương trình:
3.3 Thử nghiệm:
3.3.1 Test case :
Trang 243.3.2 Input:
‐ File input:
Dòng đầu tiên là số lượng điểm trên đồ thị
N dòng tiếp theo là tên các điểm
N dòng tiếp theo là ma trận khoảng cách của đồ thị
Trang 25‐ File thông tin khách cần book chuyến:
Dòng đầu tiên là số lượng khách
N dòng tiếp theo là thông tin khách hàng
3.3.3 Output:
‐ Output 1:
Trang 26- Output 2:
Trang 27- Output 3:
Trang 28- Output 4:
4 Kết luận:
Trong đồ án này, chúng em đã khám phá và giới thiệu một số khái niệm cơ bản về đồ thị và các thuật toán quan trọng liên quan Dijkstra và Floyd-Warshall là hai phương pháp quan trọng để tìm đường đi ngắn nhất trong đồ thị Cùng với đó, Kruskal và Prim giúp tìm cây khung ngắn nhất trong đồ thị
DFS và BFS là hai thuật toán duyệt đồ thị quan trọng, với ứng dụng rộng rãi trong việc khám phá cấu trúc của đồ thị Nói về đường đi và chu trình Euler, ta đã tìm hiểu về điều kiện cần để có chu trình Euler và cách tìm chu trình này Cũng như đường đi Euler, thuật toán Hamilton cũng đã được
đề cập
Tóm lại, các thuật toán trên mang lại những kiến thức quan trọng và công cụ mạnh mẽ để giải quyết nhiều vấn đề thực tế trong nhiều lĩnh vực khác nhau Sự hiểu biết vững về các thuật toán như Dijkstra, Kruskal, DFS
và BFS sẽ hỗ trợ đắc lực trong việc phân tích và giải quyết các thách thức liên quan đến mạng lưới và tối ưu hóa