I2 Ứng dụng của giải thuật tìm kiếm chu trình Euler.... Nhà toán học Thụy sĩ Leonhard Euler đã giải bài toán này và có thê coi đây là ứng dụng đầu tiên của Lý thuyết đồ thị, ông đã mô hì
Trang 1HỌC VIỆN CÔNG NGHỆ BƯU CHÍNH VIÊN THÔNG
KHOA VIÊN THÔNG 1
BAO CAO TIỂU LUẬN CẦU TRÚC DỮ LIEU VA GIẢI THUẬT
Họ và tên Mã sinh viên
Phan Bá Thực : B2IDCVT417 Bùi Tùng Lâm : B2IDCVT265
Hà Nội — 2024
Trang 2Mục lục
I.Giới thiệu chung c 2 20 eee EE EEE EEE EEE EEE EEE cede E bed
1.1 Lich sử hình thành giải thuật - Bài toán 7 cái cầu
I2 Ứng dụng của giải thuật tìm kiếm chu trình Euler -.- c2 c s2
H.1 Định nghĩa cc C2222 02 nee te bee cette ten ee hs hy Kế Hs
IIL Thuật toán tìm kiếm chu trình Euler s 2 22 2225 nh He Ha
Giải thuật Euler CÂycÏ€ ee ene ee 2n n2 211 vn nh n EHn nh khe hy nh y cáo
Lưu đồ thuật toán cà cà cà cọ ett nh nh nh nh TH nh TY KH TH nh Ty Ki th ki tớ
Mã giả thuật toán ccecccece vce vecceec ces tereveveseeeseeeeeeceecetcttseeesevetesereuttteeees
Cài đặt thuật toán cọ 2n cọc nnn cee een tne tenets see stneesineses anes seaeeeeeees
Thuật toàn FÏ€ury các các 2n 1 nh TH n nnn Ty nnn nh nnn kh Tà Ekn ky ke cán khay
Luu náo sa
Mã giả thuật toán c Q.2 c2 200022 02 211 vn Ty ch nh Tà g Tp hy kh xà ky cv
Đánh giá thuật toán Lọ cọc Bì nh nh nh nen khe nh TE Đi BE kh hy
Tài liệu tham khảo - - c co QC ĐH n nn nn n knn kh tk sa
L Giới thiệu chung
Trang 3I1 Lịch sử hình thành giải thuật - Bài toán 7 cái cầu
Thành phố Konigsberg thuộc Phổ (nay là Kaliningrad thuộc Cộng hoà Nga), được chia làm 4 vùng bằng các nhánh sông Pregel Các vùng này gồm 2 vùng bên bờ sông (B, C), dao Kneiphof (A) và một miền năm giữa hai nhánh sông Pregel (D) Vào thé ky XVIII người ta đã xây 7 chiếc cầu nối những vùng này với nhan Người dân ở đây tự hỏi: Liệu có cách nào xuất phát tại một địa điểm trong thành phố, đi qua 7 chiếc cầu, mỗi chiếc đúng 1 lần rồi quay trở về nơi xuất phát không ?
Nhà toán học Thụy sĩ Leonhard Euler đã giải bài toán này và có thê coi đây là ứng dụng đầu tiên của
Lý thuyết đồ thị, ông đã mô hình hoá sơ đồ 7 cái cầu bằng một đa đô thị, bốn vùng được biểu diễn băng 4 đỉnh, các cầu là các cạnh Bài toán tìm đường qua 7 cầu, mỗi cầu đúng một lần có thê tổng quát hoá bằng bài toán: Có tồn tại chu trình đơn trong đa đồ thị chứa tất cả các cạnh ?
Bai vi Leonhard Euler là người đã giải bài toán này, nên dạng chu trình đi qua tất cả các cạnh, mỗi cạnh dung moklan duoc goi la chu trinh Euler, nhu m6 kt cach dé tri an nha bac hoc vi dai cua lich sử loài người
L2 Ứng dụng của giải thuật tìm kiếm chu trình Euler
Thuật toán tìm kiếm chu trình Euler có nhiều ứng dụng trong thực tế, đặc biệt là trong lĩnh vực mạng
lưới và van tai, ly thuyết đồ thị, trò chơi và các ứng dụng trong khoa học máy tính Dưới đây là một số
ứng dụng phô biên của thuật toán này:
Thuật toán tìm chu trình Euler có nhiều ứng dụng quan trọng và lợi ích trong nhiều lĩnh vực khác nhau
như:
1 Mạng lưới và vận tải: Tối ưu hóa hé thong mang lưới vận tải và điện, giúp giảm thiêu chi phí và tăng
cường sự Ôn định
2 Mạng máy tính và giao thức định tuyến: Tối ưu hóa việc định tuyến dữ liệu, cải thiện hiệu suất và
bảo mật của hệ thống mạng
3 Tính toán hóa học và sinh học: Mô hình hóa các quá trình hóa học và sinh học, cung cấp hiểu biết sâu
sắc về câu trúc và hoạt động của các hệ thông sinh học
Trang 44 Trò chơi và giải đồ: Thiết kế và giải quyết các bài toán có tính logic cao trong trò chơi và giải đồ
logic
5 Mạng xã hội và phân tích dữ liệu: Phát hiện mẫu và quy luật tự nhiên trong dữ liệu mạng xã hội, đưa
ra dự đoán và giải pháp cho các vẫn để xã hội và kinh doanh
Bằng cách áp dụng thuật toán tìm chu trình Euler, chúng ta có thê tạo ra những ứng dụng ý nghĩa và giải quyết các vấn đề phức tạp trong thực tế một cách hiệu quả
IL Cơ cở lý thuyết
H.1 Định nghĩa
Duong di Euler trên 1 đồ thị (bất kê là vô hướng hay có hướng, đơn hay đa đồ thị) là đường đi qua tất
cả các cạnh của đồ thị, mỗi cạnh đúng 1 lần
Chu trình Euler trén 1 đồ thị là đường đi Euler trên đồ thị đó thỏa mãn điều kiện đường đi bắt đầu và
kêt thúc tại cùng l đỉnh Điêu này tương đương với chu trình Euler là 1 đường đi euler
Đồ thị tồn tại chu trình Enler được gọi là đồ thị Euler
Chu Trinh Euler : 1-2-3-4-5-6-3-1
IIL2 Định lý
1 đồ thị vô hướng liên thông là đồ thị Euler khi và chỉ khi:
e _ Bậc của mọi đỉnh là số chan
© Tat cả các đỉnh có bậc lớn hơn 0 thuộc cùng 1 thành phan liên thông
II Thuật toán tìm kiếm chu trình Euler
Giả thiết 1 đồ thị vô hướng đã thỏa mãn điều kiện tồn tại chu trinh Euler
Giải thuật Euler Cycle :
Ý tưởng thuật toán:
- _ Bất đầu từ 1 đỉnh bất kỳ trong đồ thị: chọn 1 dinh bất kì cho việc duyệt
Trang 5- _ Duyệt qua các cạnh: Tại mỗi bước chọn cạnh kẻ với đỉnh hiện tại, đây đỉnh kể đó vào stack và
loại bỏ cạnh đã đi qua Lặp lại quá trình này cho đên khi không còn đỉnh kê nảo
- Quay lại: Khi không còn đỉnh kề nào đây đỉnh đó ra khỏi stack và thêm vào chu trình
- Lap lai cho đến khi hoàn thành chu trình Euler
Luu dé thudt todn:
S$ = get(stack)
Ke(s)>
E=E\(s,t)
Mã giả thuật toán:
put t onto stack
®©———Yss
t= <Ðinh đầu tiên trong
G<Vv,E>
y stack = ©
EC = Ø
v=x put v onto stack
-
0 Reverse EC
jo,
Hiến thị EC
s = pop(stack) put s onto CE +
Trang 6Thuật toan Euler-Cycle(u):
Bước I (Khởi tạo) : stack=@; //Khởi tạo một stack bắt dau la @
CE =Ớ; //Khoi tao mang CE bat dau la @ Push (stack, u) ; //Dua dinh u vào ngăn xép Bước 2 (Lap ):
while (stack#@ ) do { //Lap cho dén khi stack rong
s = get(stack); //Lay dinh ở đầu ngăn xếp
if ( Ke(s) #@ ) then { // Nếu danh sách Ke(s) chưa rỗng
t =< Dinh dau tién trong Ke(s)>:;
Push(stack, t)’ //Dua t vao stack;
E =E \(s,t); // Loai bo canh (s,t);
}
else { //Trudng hgp Ke(s}=@
s = Pop(stack);// Đưa s ra khỏi ngăn xếp
s >CE; //Dua s sang CE
I }
Bước 3 (Trả lại kết quả) :
<Lật ngược lại các định trong CE ta được chu trình Euler> ;
Cài đặt thuật toán:
#include<bits/stde++.h>
using namespace std;
using I] = long long;
int n, m;
set<int> adj[1001];
void nhap(){
cin >>n>>m;
for(int 1 = 0; i <m,; i++){
int X, y;
cin>> xX >> y;
adj[x].insert(y);
adj[y].insert(x);
}
}
Trang 7void EulerCycle(int v){
stack<int> st;
vector<int> EC;
st.push(v);
while(!st.empty())
t
int s = sttopQ);
if(adj[s].sizeQ !=0){
int y = *adj[s].beginQ);
st.push(y);
adj[s].erase(y);
adj[y].erase(s);
}
else
t
st.pop();
EC push(x);
}
}
reverse(EC.begin(Q),EC.endQ);
for(int x: EC) cout<<x<<" ":
}
int main(){
nhap();
EulerCycle(1);
}
Đánh giá :
Ưu điểm:
- _ Dễ cài đặt và triển khai
- Hoạt động có hiệu suât tôt với đồ thị nhỏ
- _ Không cần sử dụng đệ quy, tránh được rủi ro bộ nhớ
Trang 8- Độ phức tạp của bài toán là O(/V| + [El)— E và V là số cạnh và số đỉnh của đồ thị ,nên sẽ không hiệu quả với đồ thị có số lượng cạnh lớn vì độ phức tạp tăng lên đáng kê
- Kết quả của thuật toán phụ thuộc vào thứ tự cạnh, một thứ tự không tốt có thê làm cho thuật
toán trở nên chậm hơn
Thuật toán Fleury:
Ý tưởng của giải thuật:
Xuất phát từ một đỉnh, ta chọn một cạnh liên thuộc với nó đê đi tiếp theo hai nguyên tắc sau:
s Xoá bỏ cạnh đã đi qua
« Chỉ đi qua cầu khi không còn cạnh nào khác đề chọn
Và ta cứ chọn cạnh đi một cách thoải mái như vậy cho tới khi không đi tiếp được nữa, đường đi tìm được là chu trình Enler
Lưu đồ thuật toán:
Start Fleurys ” j
Initialize an empty list circuit
| Choose a starting vertex v in G
oe
2 “Ấn there unexplored `
<< a -
i
| Let u be the next
.vertex adjacent to v
có eo : Add v to the beginning of circuit
Remove the edge between v and u from G |
⁄
sa
xế Arethereother `» | No
x adjacent to v?_
‹ sgl
Z
Yes
Insertv betweenvandu "TT
Remove the edge between v and u from G
v<:Uu
—— TẾ
bogkaseae-eesde End |
Trang 9Algorithm 1: Fleury’s Algorithm
Data: An Eulerian graph G
Result: A Eulerian circuit or Eulerian path in G Initialize an empty list circuit:
Choose a starting vertex v in G;
while There are unexplored edges in G incident to v do Let u be the next vertex adjacent to 0;
if G has no other edges adjacent to v then Add v to the beginning of circuit;
Remove the edge between v and œ from G;
| ve U;
else Insert v between v and uw in circuit:
Remove the edge between v and u from G;
Vve-U;
end end
Cai dat thuat toan :
#include <bits/stde++.h>
#define task "Euler."
using namespace std;
typedef int arr[1001];
typedef int arr2[1001][1001];
// Nhâk dữ liêkcho đô thị Thao tác này có thê dé dang điều chỉnh
// khi đề bài thay đôi cách nhâk liêk
void enter(int &n, arr2 adj, arr deg)
{
ci >> n;
int u, v, k;
while (cin >> u >> v >> k)
adj[u][v] = adj[v][u] = k:;
deg[u] += k;
deg[v] += k;
}
void dfs(int n, int u, int cnt_comps, arr2 adj, arr number) // Dém so TPLT cua do thi
{
number[u] = cnt_comps;
for (int v = 1; v =n; ++v)
if (‘number[v] && adj[u][v]) dfs(n, v, cnt_comps, adj, number);
}
/ Kiểm tra đồ thị có phải đồ thi Euler không
bool check euler_graph(int n, arr2 adj, arr deg)
{
Trang 10// Đêm số thành phân liên thông của đồ thị
int cnt_comps = 0;
arr number;
for (int u = 1; u<=n; +4)
if (‘number[u]) +rent comps;
dfs(n, u, cnt_comps, adj, number);
} / Nếu đồ thị không liên thông thì nó không phải đồ thị Euler
if (cnt_comps > 1)
return false;
/ Nếu tồn tại dinh cé bakleé thi cing khéng phai dé thi Euler
for (int u = 1; u<=n; +4n)
if (deg[u] % 2 == 1) return false;
return true;
// Kiêm tra xem nêu xóa cạnh (u, v) thì có thê đi ngược từ v về u không
bool can go baek(int n, arr2 ad], Int u, int v)
{
// Thử xóa cạnh (u, v), tức là số cạnh nỗi giảm di 1
¬adj[u][v];
adj[v][ul;
// Sau khi x6a canh (u, v), thir bfs tr v toi u xem có đi được nữa không
bool is_free[n + 1];
fill(is free + 1, 1s free +n + 1, true);
queue < int > path;
path push(v);
while (!path.empty())
{
int cur = path.front();
path.pop();
if (cur =u) break;
for (int next_v = 1; next_v <=n; ++next_v)
if (is free[next_v] && adj[cur][next_v])
{
is free[next_v] = false;
path.push(next_v);
} }
/ Nỗi lại cạnh (u, v) do lúc nãy đã thử xóa cạnh này di
+radj[u][v];
++adj[v][ul;
return !is fee[u];
}
/ Giải thuật Fleury tìm chu trình Euler trén đồ thị
void fleury(int n, arr2 adj, arr deg)
{
/ Đồ thị không phải đồ thi Euler -> In ra 0
10
Trang 11{
cout << 0;
return;
}
int cur_vertex = 1, next_v =0;
vector < int > circuit;
step.push_back(cur_vertex);
do
{
next_v =0;
// Lần lượt chọn các cạnh liên thuộc với đỉnh hiêằktại // trong chu trình đang xét
for Gnt v = 1; v<=n; ++v)
if (adj[cur_vertex][v])
next _v=Vv;
/ Ưu tiên chọn cạnh không phải là cạnh "môki không trở lại"
If(can øo baek(n, adJ, cur vertex, next_v)) break;
// Tìm được L cạnh nối chưa bị xóa có thé đi được
if (next_v !=0)
// Xóa cạnh nối đó đi
adj[cur_vertex][next_v];
adj[next_v][cur_vertex];
step.push_back(next_v); // Dua dinh nay vao danh sach cac dinh trén chu trinh
cur_vertex = next_v;
} }
while (next_v != 0);
In ra chu trình tìm được
for (int u: circuit)
cout <<u <<'';
}
Đánh giá thuật toán:
1 Don gian va dé hiéu va dé triển khai: Thuật toán Fleury khá đơn giản và dé hiểu Nó chỉ sử dụng một số phép kiểm tra cơ bản trên các cạnh của đồ thị để tạo ra một đường đi Enler
2 Hiệu suất thời gian tốt: Trong trường hợp tốt nhất, thuật toán Fleury có độ phức tạp thời gian là
O(El), trong đó |E| là sô lượng cạnh của đô thị Điêu này làm cho nó trở thành một lựa chọn
hiệu quả cho việc tìm kiểm chu trình Euler trong các đồ thị lớn
3 Hiệu suất không gian: Fleury không đòi hỏi bộ nhớ phụ quá lớn Thông thường, nó chỉ cần lưu
trữ các danh sách kể của đỉnh và một số biến đếm, làm cho hiệu suất không gian của nó là O(| VỊ), trong đó [VỊ là số lượng đỉnh của đồ thị
4 Nếu đồ thị có số cạnh không quá lớn việc sử dụng thuật toán Fleury có thê có hiệu suất không tốt bằng các thuật toán khác
11
Trang 12- Slide bài giảng Cấu trúc đữ liệu và giải thuật - HVCN Bưu chính viễn thông Hà Nội
- Bai giang ly thuyết đồ thị - Thầy Lê Minh Hoàng
- Bài giảng cấu trúc dữ liệu và giải thuật — Đại học Bách khoa Hà Nội
- Fleury Algorthm — geek for geeks
12