Hiện nay, các bài toán liên quan đến clique rất nhiều nhưng trong đồ án này chỉ tập trung giải quyết là tìm clique cực đại.. Đặc biệt thuật toán tìm kiếm Clique lớn nhất có nhiều ứng dụn
Trang 1TRƯỜNG ĐẠI HỌC CÔNG NGHỆ THÔNG TIN
ĐỒ ÁN MẠNG XÃ HỘI TÌM HIỂU VÀ TRIỂN KHAI GIẢI THUẬT
TÌM CLIQUE LỚN NHẤT
Lớp: IS353.G12 Giảng viên hướng dẫn: TS Đỗ Phúc Sinh viên thực hiện: 13520966 - Nguyễn Minh Trường
TP Hồ Chí Minh, 30 tháng 12 năm 2015
Trang 2LỜI MỞ ĐẦU
Nội dung của của đồ án tập trung vào việc đưa ra giải thuật tìm clique bậc k trong đồ thị Hiện nay, các bài toán liên quan đến clique rất nhiều nhưng trong đồ án này chỉ tập trung giải quyết là tìm clique cực đại Do thời gian có hạn và khả năng nhóm chúng em còn một số hạn chế, nên không tránh khỏi những thiếu sót trong báo cáo này Chúng em xin chân thành cảm ơn
Nhóm cũng xin trân thành cảm ơn đến các cá nhân, các nhóm khác đã hỗ trợ nhóm hoàn thành đồ án này
Trang 3Mục lục
A BÀI TOÁN TÌM CLIQUE
I Tổng quan đề tài 4
II Clique và bài toán tìm clique cực đại 4
III Giải thuật 6
IV Phân tích độ phức tạp của thuật toán 9
B TRIỂN KHAI GIẢI THUẬT 10
C TÀI LIỆU THAM KHẢO 16
Trang 4A BÀI TOÁN TÌM CLIQUE
I Tổng quan đề tài
Trong thực tế, Clique thường được ứng dụng trong đồ thị có số đỉnh lớn và mật độ phức tạp Đặc biệt thuật toán tìm kiếm Clique lớn nhất có nhiều ứng dụng trong khoa học máy tính như mô tả và giải quyết bài toán lập lịch, lý thuyết mã, thị giác máy tính và nhận dạng mẫu…
Vì vậy, trong đề tài này nhóm chú trọng tìm hiểu và đưa ra giải pháp thực hiện thuật toán “Tìm clique cực đại bậc k”
II Clique và bài toán Tìm clique cực đại bậc k:
1 Clique: Trong lý thuyết đồ thị, một Clique trong đồ thị vô hướng G là tập các đỉnh V (V là tập con của tập các đỉnh của G) thỏa mãn điều kiện: với mỗi cặp đỉnh thuộc V luôn tồn tại một cạnh của G nối chúng Do vậy một đồ thị con được tạo ra từ V sẽ là một đồ thị đầy đủ Kích thước của một clique là số đỉnh của nó
Trong giới hạn đồ án này, nhóm chỉ xét đồ thị đơn G với n đỉnh, không bao gồm vòng và đa cạnh (nhiều cạnh nối cùng 1 cặp đỉnh) Sau đây là một số khái niệm, kí hiệu được dùng trong bài:
Q clique của đồ thị G là một tập hợp các đỉnh sao cho các cặp đỉnh bất kì
trong Q là một cạnh
ρ(Q) là số đỉnh có thể thêm vào (adjoinable vertices) của clique Q Maximal clique không thể thêm bất kì đỉnh nào vô được nữa Maximum clique là clique có số đỉnh nhiều nhất
Trang 5Figure 1- Đồ thị đầy đủ và Clique
2 Áp dụng thuật toán Polynomial – time vào bài toán tìm clique cực đại 2.1 Giới thiệu hướng tiếp cận giải quyết
Năm 1972, Karp, đã giới thiệu danh sách 21 bài toán NP-complete, một trong số đó là bài toán tìm clique cực đại trên đồ thị Thực tế, các bài toán ứng dụng Clique thường với số đỉnh rất lớn, khi đó bài toán sẽ có thời gian chạy rất lớn Để giải quyết vấn đề này, nhóm chọn giải thuật tìm clique cực đại Polynomial – time.
2.2 Các thủ tục sử dụng trong giải thuật polynomial – time
a Procedure 1:
Ta có đồ thị G có n đỉnh và Q là clique của G, nếu Q không thể thêm đỉnh nữa thì xuất Q Ngược lại, với mỗi đỉnh v có thể thêm vào Q, ta tiếp tục tìm ρ(Q) – số đỉnh có thể thêm vào Q Giải sử vmax là một đỉnh có thể thêm vào được thì ρ(Q∪{vmax}) là cực đại (maximum) và ta có clique là Q∪{vmax} Ta lập lại trình tự này cho đến khi không thể tìm thấy đỉnh có thể thêm vào Q
b Procedure 2:
Ta có đồ thị G có n đỉnh và Q là maximal clique, nếu không có đỉnh v nào nằm ngoài Q mà có duy nhất 1 đỉnh w trong tập Q mà không phải là hàng xóm của v, xuất ra Q Ngược lại, tìm đỉnh v nằm ngoài Q mà có chính xác 1 đỉnh w
Trang 6trong Q không phải là hàng xóm của v Định nghĩa Qv,w được tạo ra bằng cách
thêm đỉnh v đó vào Q và loại bỏ đỉnh w khỏi Q Thực hiện thủ tục 1 trên Qv,w và xuất ra clique
III Giải thuật:
Cho đầu vào là đồ thị G có n đỉnh đánh số từ 1 đến n, tìm clique có kích thước tối thiểu là k Ở mỗi giai đoạn, nếu clique đạt được kích thước tối thiểu là k thì dừng
lại
Procedure 1, dùng vòng lặp For i=1, 2,…,n
Khởi tạo clique Qi = {i}
Thực hiện thử tục 1 với Qi
Dùng vòng lặp For r=1, 2,…, k, ta thực hiện thủ tục 2 lặp lại r lần
Kết quả Qi là maximal clique Procedure 2 với mỗi cặp maxiamal clique Qi, Qj tìm được ở phần 1
Khởi tạo clique Qi, j = Qi∩Qj
Thực hiện thủ tục 1 với clique Qi, j
Dùng vòng lặp For r = 1, 2, , k thực hiện thủ tục 2 r lần
Kết quả maximal clique là Qi, j Ví dụ, ta lấy ma trận sau để demo thuật toán với số đỉnh n = 11
Trang 7Với I = 1, ta bắt đầu khởi tạo Q có đỉnh là 1
Đỉnh có thể thêm vào Q1 Các đỉnh có thể thêm vào tập Q1∪{v} ρ(Q1∪{v})
Trang 8Ta chọn ρ lớn nhất, thêm đỉnh 9 vào Q thì ta có Q={1, 7, 9} Tới đây thì Q đã đạt
mức tối đa
Tiếp theo, ta tính i = 2, ta bắt đầu khởi tạo Q có đỉnh là 2
Đỉnh có thể thêm vào Q1 Các đỉnh có thể thêm vào tập Q1∪{v} ρ(Q1∪{v})
Trang 9Ta chọn ρ lớn nhất là 1, thêm đỉnh 3 vào Q thì ta có Q={2, 3, 4, 5} Đỉnh có thể thêm vào Q1 Các đỉnh có thể thêm vào tập Q1∪{v} ρ(Q1∪{v})
Ta chọn ρ lớn nhất, thêm đỉnh 6 vào Q thì ta có Q={2, 3, 4, 5, 6} Với i = 2
thì ta đã có clique Q đạt mức tối đa đồng thời cũng là clique cực đại Tương tự, ta tiếp tục thực hiện lần lượt giải thuật với từng đỉnh cho đến hết Nếu Q mới được tìm thấy mà lớn hơn Q của i = 2 thì ta sẽ lấy Q đó làm cực đại
IV Phân tích độ phức tạp của thuật toán
Độ phức tạp của thuật toán thời gian đa thức, bằng cách xác định một đa thức theo n số đỉnh của đồ thị đầu vào, đó là một giới hạn trên tổng số bước tính toán được thực hiện bởi các thuật toán Lưu ý chúng ta xem xét: Kiểm tra xem một cặp đỉnh cho trước được kết nối bởi các cạnh trong G, và so sánh giữa một số nguyên cho trước và một số nguyên cho trước khác Các thao tác này được xem là các bước tính toán cơ bản
Mệnh đề A: Cho một đồ thị đơn G với n đỉnh và một Clique Q,
Procedure 1 tốn ít nhất n5 bước Chứng minh: Kiểm tra một đỉnh đặc trưng có khả năng kề với đỉnh khác hay không thì tốn ít nhất là n2 bước Mỗi clique đặc biệt, tìm số đỉnh đặc trưng p có khả năng kề với các đỉnh khác thì tốn nhiều nhất là n3 = nn2
bước Cho mỗi clique đặc trưng, tìm một đỉnh mà có p là maximum thì tốn n4 = nn3 bước Procudure 1 xác định khi nhiều nhất n đỉnh là được kết nối, vì vậy tổng chi phí tốn nhiều nhất n5 = nn4 bước
Trang 10 Mệnh đề B: Cho một đồ thị đơn giản G với n đỉnh và một clique cực
đại Q, Procedure 2 tốn nhiều nhất là n5 + n2 + 1 bước Chứng minh: Tìm một đỉnh v nằm ngoài Q có chính xác một đỉnh w nằm trong Q mà nó không là hàng xóm của nhau thì tốn ít nhất n2 bước Nếu một đỉnh v đã tìm thấy, nó tốn một bước để trao đổi v và w Theo mệnh đề A, nó tốn chi phí nhiều nhất n5 bước để thực hiện Procedure 1 trên clique
kết quả Vì thế, Procedure 2 tốn nhiều nhất n2+1+n5 bước Mệnh đề C: Cho một đồ thị đơn giảng G với n đỉnh, Part I của thuật
toán tốn nhiều nhất n7+n6+n4+n2 bước Chứng minh: Tại mỗi lần lặp, Procedure 1 tốn nhiều nhất là n5 bước Sau đó, Procedure 2 được thực hiện ít nhất n lần, vì vậy, tốn chi phí nhiều
nhất là n(n5+n2+1) = n6+n3+n bước Vì thế, tại mỗi lần lặp, có nhiều nhất
n5+n6+n3+n bước thực thi Do có n lần lặp, Part I thực hiện nhiều nhất
n(n5+n6+n3+n) = n6+n7+n4+n2bước Mệnh đề D: Cho một đồ thị đơn giản G với n đỉnh, thuật toán tốn ít
hơn n8+2n7+n6+n5+n4+n3+n2bước để kết thúc.Chứng minh: Có ít hơn n2 cặp clique cực đại khác nhau được tìm thấy trong Part I Trong Part II tốn chi phí nhỏ hơn n2(n5+n6+n3+n) = n7+n8+n5+n3 Vì vậy, tổng chi phí của thuật toán trong Part I và Part II là
(n7+n6+n4+n2)+(n8+n7+n5+n3) = n8+2n7+n6+n5+n4+n3+n2 bước để kết thúc thuật toán
Vậy thuật toán này có độ phức tạp là một đa thức bậc 8
B TRIỂN KHAI THUẬT TOÁN
Trang 11Do thời gian thực hiện đồ án tương đối ngắn nên thay vì tự viết hoàn toàn giải thuật thì nhóm sẽ tham khảo chương trình có sẵn từ tài liệu tham khảo (được viết dưới ngôn ngữ C++) Ngoài ra, nhóm cũng chỉnh sửa lại code để phù hợp với mục đích sử dụng của thuật toán phục vụ cho môn học Sau đây là đoạn code kèm theo chú thích chức năng của của từng hàm
//Hàm kiểm tra từng hàng xóm với từng đỉnh trong Q, nếu một hàng xóm của đỉnh đó bằng 0 thì trả về False
public static bool removable(List<int> neighbor, List<int> cover) {
bool check = true; for (int i = 0; i < neighbor.Count; i++) {
if (cover[neighbor[i]] == 0) {
check = false; break; }
} return check; }
//Hàm kiểm tra các hàng xóm chung của các cặp đỉnh
public static int max_removable(List<List<int>> neighbors, List<int> cover) {
int r = -1;
Trang 12int max = -1;
//Duyệt từng đỉnh trong cover for (int i = 0; i < cover.Count; i++) {
if (cover[i] == 1 && removable(new List<int>(neighbors[i]), new List<int>(cover)) == true) //Nếu đỉnh có hàng xóm
{ List<int> temp_cover = new List<int>(cover); temp_cover[i] = 0;
int sum = 0; for (int j = 0; j < temp_cover.Count; j++) {
if (temp_cover[j] == 1 && removable(new List<int>(neighbors[j]), new List<int>(temp_cover)) == true)
{ sum++; }
} if (sum > max) {
if (r == -1) {
max = sum; r = i; }
//So sánh số lượng hàng xóm của 2 đỉnh r va i, nếu r lớn hơn i thì ta lấy giá trị của i
Trang 13else if (neighbors[r].Count >= neighbors[i].Count)
{
max = sum; r = i; }
}
}
}
return r; }
//Procedure 1: trả về kết quả là Clique không thể thêm đỉnh nữa //Đầu vào là mảng các hàng xóm, dãy các đỉnh cần xét public static List<int> procedure_1(List<List<int>> neighbors, List<int> cover) {
List<int> temp_cover = new List<int>(cover); int r = 0; while (r != -1) {
r = max_removable(new List<List<int>>(neighbors), new List<int>(temp_cover)); if (r != -1) //Nếu r tìm thấy {
temp_cover[r] = 0; }
Trang 14} return temp_cover; }
//Kiểm tra các node hàng xóm với cụm clique đang có để loại bỏ các node trong clique giả định ban đầu không thể thuộc clique cực đại
public static List<int> procedure_2(List<List<int>> neighbors, List<int> cover, int k) {
int count = 0; List<int> temp_cover = new List<int>(cover); for (int i = 0; i < temp_cover.Count; i++) {
if (temp_cover[i] == 1) {
int sum = 0; int index = 0; for (int j = 0; j < neighbors[i].Count; j++) {
if (temp_cover[neighbors[i][j]] == 0) {
index = j; sum++; }
} if (sum == 1 && cover[neighbors[i][index]] == 0) {
temp_cover[neighbors[i][index]] = 1;
Trang 15temp_cover[i] = 0; temp_cover = new List<int>(procedure_1(new List<List<int>>(neighbors), new List<int>(temp_cover)));
count++; }
if (count > k) break; }
} return temp_cover; }
public static int cover_size(List<int> cover) {
int count = 0; for (int i = 0; i < cover.Count; i++) {
if (cover[i] == 1) {
count++; }
} return count; }
//Hàm kiểm tra kết nối giữa 2 nút với nhau để đưa vào List public static int getEdge(int locate, String a)
{
Trang 16char[] c = a.ToCharArray(); //Chuyển chuỗi a thành mảng kí tự int result = 0;
int dem = 0; for (int i = 0; i < c.Length; i++) {
char tmp = c[i]; if (tmp != ' ') {
result = int.Parse(tmp.ToString()); //Parse chuyển kiểu kí tự sang kiểu số if (dem == locate)
{ if (result != 0) return 1; else return 0; }
dem++; }
} if (result != 0) return 1; else return 0; }
Trang 17{ this.lb_result_clique.Items.Add("Clique Algorithm."); int n, i, j, k, K, p, q, r, s, min, edge, counter = 0; n = file.Graph.Vertices.Count;
//Đưa dữ liệu từ file vào List List<List<int>> graph = new List<List<int>>(); for (i = 0; i < n; i++)
{ List<int> row = new List<int>(); String temp = "";
for (int a = 0; a < file.Graph.Vertices.Count; a++) {
if (cost[i, a] == MAX_DISTANCE) {
temp += "0"; }
else { temp += cost[i, a].ToString(); }
} for (j = 0; j < n; j++) {
edge = getEdge(j, temp); if (edge == 0)
Trang 18{ row.Add(1); }
else { row.Add(0); }
} graph.Add(row); }
//Tìm nút láng giềng nếu i và j bằng 1 thì thêm j vào hàng xóm
//Kết quả là List<int> là các hàng xóm của mỗi đỉnh trong List lớn
List<List<int>> neighbors = new List<List<int>>(); for (i = 0; i < graph.Count; i++)
{ List<int> neighbor = new List<int>(); for (j = 0; j < graph[i].Count; j++) {
if (graph[i][j] == 1) {
neighbor.Add(j); }
}
Trang 19}
this.lb_result_clique.Items.Add("Graph has n = " + n.ToString() + " vertices.");
//K là số đỉnh tối thiểu ngdùng nhập vào k là số đỉnh tiếp theo mà ta xét
K = Convert.ToInt32(this.tb_clique.Text); k = n - K;
bool found = false; this.lb_result_clique.Items.Add("Finding Cliques "); min = n + 1;
//Covers là clique ta khởi tạo
List<List<int>> covers = new List<List<int>>(); List<int> allcover = new List<int>();
for (i = 0; i < graph.Count; i++) {
allcover.Add(1); }
for (i = 0; i < allcover.Count; i++) {
if (found) //Found ban đầu là False nên break vong for break;
counter++; String writer = "";
Trang 20writer += counter.ToString() + ".";
List<int> cover = new List<int>(allcover); cover[i] = 0;
cover = new List<int>(procedure_1(new List<List<int>>(neighbors), new List<int>(cover))); s = cover_size(new List<int>(cover));
if (s < min) {
min = s; }
if (s <= k) {
writer += "Clique (" + (n - s) + "): "; for (j = 0; j < cover.Count; j++) {
if (cover[j] == 0) {
writer += j + 1 + " "; }
} this.lb_result_clique.Items.Add(writer); covers.Add(cover);
found = true; break; }
Trang 21{ cover = new List<int>(procedure_2(new List<List<int>>(neighbors), new List<int>(cover), j));
}
s = cover_size(new List<int>(cover)); if (s < min)
{ min = s; }
writer = ""; writer += "Clique (" + (n - s) + "): "; for (j = 0; j < cover.Count; j++) {
if (cover[j] == 0) {
writer += (j + 1) + " "; }
} this.lb_result_clique.Items.Add(writer); covers.Add(cover);
if (s <= k) {
found = true; break; }