1. Trang chủ
  2. » Công Nghệ Thông Tin

Thuật toán dijkstra fibonacci heap thuật toán aco tìm đường đi tối ưu và ứng dụng

73 423 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 73
Dung lượng 790,99 KB

Nội dung

 Phạm vi nghiên cứu của đề tài Các khái niệm cơ bản về đồ thị, các thuật toán tìm đường đi tối ưu trên đồ thị, cấu trúc dữ liệu Fibonacci heap, ứng dụng cấu trúc dữ liệu này vào việc c

Trang 1

LỜI CAM ĐOAN

Tôi xin cam đoan luận văn này là kết quả nghiên cứu của riêng tôi Các thông tin trích dẫn trong luận văn lấy từ các nguồn đã được công khai hoặc

đã được sự đồng ý của tác giả Các kết quả nêu trong luận văn là kết quả nghiên cứu riêng của tác giả luận văn, chưa có ai công bố trong các công trình khác

Thái Nguyên, ngày 10 tháng 4 năm 2015

Học viên

Nghiêm Quang Khải

Trang 2

LỜI CẢM ƠN

Được sự phân công của trường Đại Học Công Nghệ Thông Tin Và Truyền Thông - Đại Học Thái Nguyên và sự đồng ý của thầy giáo hướng dẫn PGS - TS Đoàn Văn Ban, tôi đã thực hiện đề tài “Thuật toán Dijkstra Fibonacci heap, thuật toán ACO tìm đường đi tối ưu và ứng dụng”

Để hoàn thành được đề tài này, tôi đã nhận được sự hướng dẫn tận tình chu đáo của thầy hướng dẫn PGS – TS Đoàn Văn Ban, qua đây cho phép tôi được bày tỏ lòng biết ơn chân thành tới Thầy và gia đình Thầy

Tôi cũng xin được tỏ lòng cảm ơn đối với các thầy các cô đã tận tình hướng dẫn, giảng dạy lớp cao học 12G trong suốt hai năm qua, cám ơn những tri thức các thầy cô đã truyền thụ, cảm ơn những tình cảm chân thành các thầy cô đã dành cho lớp

Xin chân thành cám ơn những ý kiến đóng góp quý báu của các thầy cô giáo và các bạn đồng nghiệp đối với đề tài này

Chắc chắn đề tài này sẽ không tránh khỏi những thiếu sót, rất mong nhận được các ý kiến đóng góp của các thầy cô, các bạn đồng nghiệp và các bạn độc giả, tôi xin chân thành cảm ơn

Thái Nguyên, ngày 10 tháng 4 năm 2015

Học viên

Nghiêm Quang Khải

Trang 3

MỤC LỤC

LỜI CAM ĐOAN i

LỜI CẢM ƠN ii

MỞ ĐẦU 1

CHƯƠNG 1 4

CÁC THUẬT TOÁN TÌM ĐƯỜNG ĐI TỐI ƯU TRÊN ĐỒ THỊ 4

1.1 Các khái niệm cơ bản của lý thuyết đồ thị 4

1.1.1 Định nghĩa đồ thị 4

1.1.2 Các thuật ngữ cơ bản 5

1.1.3 Đường đi, chu trình, đồ thị liên thông 6

1.1.4 Đồ thị có trọng số 7

1.2 Cây 8

1.3 Bài toán đường đi tối ưu trên đồ thị 8

1.4 Thuật toán Dijkstra 10

1.4.1 Phát biểu bài toán 10

1.4.2.Mô tả thuật toán 10

1.5 Thuật toán Dijkstra kết hợp với Fibonacci heap 11

1.5.1 Hàng đợi ưu tiên 11

1.5.2 Fibonacci heap 14

1.5.3 Sơ đồ thuật toán Dijkstra kết hợp với Fibonacci Heap 30

1.6 Kết luận chương 32

CHƯƠNG 2 33

THUẬT TOÁN ĐÀN KIẾN GIẢI BÀI TOÁN TÌM ĐƯỜNG ĐI TỐI ƯU 33

2.1 Từ kiến tự nhiên đến kiến nhân tạo 33

2.1.1 Kiến tự nhiên 33

2.1.2 Kiến nhân tạo 36

2.2 Thuật toán ACO tổng quát giải bài toán ngươi chào hàng 37

2.2.2 Thuật toán ACO tổng quát giải bài toán TSP 38

2.3 Các thuật toán ACO giải bài toán TSP 39

2.3.1 Thuật toán AS 40

2.3.2 Thuật toán ACS 42

2.3.3 Thuật toán Max-Min (MMAS) 44

2.4 Một số vấn đề trong việc áp dụng ACO tìm đường đi tối ưu 46

Trang 4

2.4.1 ACO kết hợp với tìm kiếm cục bộ 46

2.4.2 Thông tin heuristic 47

2.4.3 Số lượng kiến 47

2.4.4 Tham số bay hơi 48

2.4.5 Một số đề xuất cải tiến 48

2.5 Kết luận chương 49

CHƯƠNG 3 50

ỨNG DỤNG THUẬT TOÁN DIJKSTRA FIBONACCI HEAP, THUẬT TOÁN ACO GIẢI CÁC BÀI TOÁN TÌM ĐƯỜNG ĐI TRÊN MẠNG GIAO THÔNG 50

3.1 Ứng dụng Dijkstra Fibonacci heap 50

13.1.1 Phát biểu bài toán 1 50

3.1.2 Mô hình hoá bài toán 50

3.1.3 Mô tả input, output 50

3.1.4 Một số kiểu dữ liệu và các biến trong chương trình 51

3.1.5 Một số hàm và thủ tục trong chương trình 52

3.1.6.Sơ đồ thuật toán 55

3.1.7 Các kết quả thực nghiệm giải bài toán 1 56

3.2 Ứng dụng Dijkstra Fibonacci heap, ACO giải bài toán TSP mở rộng 58

3.2.1 Phát biểu bài toán 2 58

3.2.2 Mô hình hoá bài toán 58

3.2.3 Mô tả input, output 58

3.2.4 Thuật toán tổng quát giải bài toán 2 59

3.2.5 Một số hàm và thủ tục trong chương trình 59

3.2.6 Sơ đồ tổng quát của thuật toán giải bài toán 2 62

3.2.7 Các kết quả thực nghiệm giải bài toán 2 63

3.3 Kết luận chương 65

KẾT LUẬN 67

TÀI LIỆU THAM KHẢO 69

Trang 5

MỞ ĐẦU

Thuật toán tìm đường đi tối ưu có nhiều ứng dụng trong thực tế, nếu xây dựng được các thuật toán tốt sẽ giúp tiết kiệm được rất nhiều tiền bạc, thời gian, công sức của con người Một số bài toán thực tế điển hình cần phải

sử dụng thuật toán tìm đường đi tối ưu như:

- Tìm đường đi từ địa điểm A đến địa điểm B sao cho độ dài đường đi

là tối ưu hoặc nhanh nhất hoặc giá cước là nhỏ nhất

- Tìm đường đi ngắn nhất xuất từ một điểm cho trước, đi qua một số địa điểm cố định cho trước rồi quay trở về điểm xuất phát

- Tương tự ta cũng có bài toán tìm đường đi cho gói tin được gửi từ nút

A đến nút B trên mạng máy tính sao cho giá cước là nhỏ nhất hoặc nhanh nhất

- Tìm đường đi tối ưu cho robot, cho tên lửa hành trình, máy bay, phi thuyền v.v cũng là những bài toán đang được quan tâm

Đã có nhiều công trình nghiên cứu về lĩnh vực này và có nhiều thuật toán nổi tiếng đã được phát minh như: Thuật toán Bellman – Ford, thuật toán Dijkstra, thuật toán Floyd, thuật toán Johnson…

Tuy nhiên việc nghiên cứu cải tiến nâng cao hiệu quả của các thuật toán tìm đường đi tối ưu luôn nhận được sự quan tâm của nhiều người, nhiều tổ chức, cơ quan Vì lý do nói trên và được sự gợi ý của PGS – TS Đoàn Văn Ban, tác giả đã chọn đề tài này để nghiên cứu trong luận văn tốt nghiệp thạc sĩ của mình

 Phạm vi nghiên cứu của đề tài

Các khái niệm cơ bản về đồ thị, các thuật toán tìm đường đi tối ưu trên

đồ thị, cấu trúc dữ liệu Fibonacci heap, ứng dụng cấu trúc dữ liệu này vào việc cải tiến nâng cao hiệu quả của thuật toán tìm đường đi tối ưu trên đồ thị

Trang 6

Ứng dụng các thuật toán tìm đường đi tối ưu trên đồ thị đã nghiên cứu để giải quyết một số bài toán tìm đường đi tối ưu trong mạng giao thông

 Hướng nghiên cứu của đề tài

- Nghiên cứu thuật toán Dijkstra tìm đường đi tối ưu trên đồ thị, nghiên cứu về Fibonacci heap và ứng dụng cấu trúc dữ liệu này để cải tiến thuật toán Dijkstra

- Nghiên cứu về thuật toán tối ưu đàn kiến, ứng dụng thuật toán này để giải quyết bài toán tìm đường đi tối ưu trên đồ thị

- Ứng dụng hai thuật toán trên giải quyết một số bài toán tìm đường đi tối ưu trên mạng giao thông

 Đề tài gồm có 3 chương:

Chương 1: Trình bày một số khái niệm cơ bản về đồ thị, một số dạng

bài toán tìm đường đi tối ưu trên đồ thị, phần chủ yếu của chương này là trình bày về Fibonacci heap và dùng cấu trúc dữ liệu này để cải tiến nâng cao hiệu quả thuật toán Dijkstra

Chương 2: Trình bày về thuật toán tối ưu đàn kiến và thuật toán ACO

giải bài toán tìm đường đi tối ưu Thuật toán đàn kiến là một thuật toán tương đối mới và khả năng ứng dụng thực tế cao

Chương 3: Ứng dụng thuật toán Dijkstra đã cải tiến và thuật toán đàn

kiến vào việc giải một số bài toán tìm đường đi tối ưu trên mạng giao thông

 Ý nghĩa khoa học của đề tài:

- Thuật toán Dijkstra Fibonacci heap là thuật toán mạnh, nó có thể được ứng dụng để giải quyết các bài toán cả trong nghiên cứu lý thuyết và trong thực tiễn Hiện tại thuật toán này chưa phổ biến ở Việt Nam, vì thế đề tài này

có thể sẽ có ích cho những người quan tâm đến lĩnh vực này Đề tài cũng có

Trang 7

thể giúp cho các em học sinh Chuyên Tin có thêm một công cụ mạnh để giải quyết một số bài toán có liên quan trong lập trình

- Thuật toán ACO là thuật toán gần đúng, tuy nhiên nó rất hiệu quả trong việc giải quyết các bài toán thực tiễn Đề tài đã ứng dụng thành công hai thuật toán nói trên vào việc giải quyết một số bài toán mà thực tiễn đang đặt ra

Trang 8

CHƯƠNG 1 CÁC THUẬT TOÁN TÌM ĐƯỜNG ĐI TỐI ƯU TRÊN ĐỒ THỊ

1.1 Các khái niệm cơ bản của lý thuyết đồ thị

1.1.1 Định nghĩa đồ thị

Đồ thị là một cấu trúc rời rạc bao gồm các đỉnh và các cạnh nối các đỉnh này Chúng ta phân biệt các loại đồ thị khác nhau bởi kiểu và số lượng cạnh nối hai đỉnh nào đó của đồ thị

Định nghĩa 1.1 Đơn đồ thị vô hướng G = (V,E) bao gồm V là tập các

đỉnh, và 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 [3]

Định nghĩa 1.2 Đa đồ thị vô hướng G = (V,E) bao gồm V là tập các

đỉnh, và E là họ 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 e 1 và e 2 được gọi là cạnh lặp nếu chúng cùng tương ứng với một cặp đỉnh [3]

Định nghĩa 1.3 Đơn đồ thị có hướng G = (V,E) bao gồm V là tập các

đỉnh, và E là tập các cặp có thứ tự gồm hai phần tử khác nhau của V gọi là các cung [3]

Định nghĩa 1.4 Đa đồ thị có hướng G = (V,E) bao gồm V là tập các

đỉnh, và E là họ các cặp có thứ tự gồm hai phần tử khác nhau của V gọi là các cung Hai cung e 1 và e 2 được gọi là cung lặp nếu chúng cùng tương ứng với một cặp đỉnh [3]

Trang 9

Hình 1.1.Hai loại đồ thị cơ bản:

a) Đồ thị vô hướng (6 đỉnh, 9 cạnh) b) Đồ thị có hướng (5 đỉnh, 7 cung)

1.1.2 Các thuật ngữ cơ bản

Định nghĩa 1.5 Hai đỉnh u và v của đồ thị vô hướng G được gọi là kề

nhau nếu (u,v) là cạnh của đồ thị G Nếu e = (u,v) là cạnh của đồ thị thì chúng ta nói cạnh này là liên thuộc với hai đỉnh u và v, hoặc cũng nói là cạnh

e là nối đỉnh u và đỉnh v, đồng thời các đỉnh u và v sẽ được gọi là các đỉnh đầu của cạnh (u,v) [3]

Để có thể biết được bao nhiêu cạnh liên thuộc với một đỉnh, chúng ta đưa vào định nghĩa sau:

Định nghĩa 1.6 Gọi bậc của đỉnh v trong đồ thị vô hướng là số cạnh

liên thuộc với nó và sẽ kí hiệu là deg(v)

Định lý 1.1 Giả sử G = (V, E) là đồ thị vô hướng với m cạnh Khi đó

Định nghĩa 1.7 Nếu e = (u,v) là cung của đồ thị có hướng G thì chúng

ta nói hai đỉnh u và v là kề nhau, và nói cung (u,v) nối đỉnh u và đỉnh v hoặc

Trang 10

cũng nói cung này là đi ra khỏi đỉnh u và đi vào đỉnh v Đỉnh u(v) sẽ được gọi

là đỉnh đầu (cuối) của cung (u,v) [3]

Định nghĩa 1.8 Chúng ta gọi bán bậc ra (bán bậc vào) của đỉnh v

trong đồ thị có hướng là số cung của đồ thị đi ra khỏi nó (đi vào nó) và ký hiệu là: deg+(v) (deg-(v)) [3]

Định lý 1.2 Giả sử G= (V, A) là đồ thị có hướng Khi đó

1.1.3 Đường đi, chu trình, đồ thị liên thông

Định nghĩa 1.9 Đường đi độ dài n từ đỉnh u đến đỉnh v, trong đó n là

số nguyên dương, trên đồ thị vô hướng G = (V, E) là dãy x0, x1, , xn-1, xn trong

đó u = x0 , v = xn, (xi, xi+1) E, i = 0, 1, 2,…, n-1 Đường đi nói trên còn có thể biểu diễn dưới dạng dãy các cạnh: (x0, x1), (x1, x2), , (xn-1, xn)

Đỉnh u gọi là đỉnh đầu, còn đỉnh v gọi là đỉnh cuối của đường đi

Đường đi có đỉnh đầu trùng với đỉnh cuối (tức là u = v) được gọi là chu trình

Đường đi hay chu trình được gọi là đơn nếu như không có cạnh nào bị lặp lại [3]

Định nghĩa 1.10 Đường đi độ dài n từ đỉnh u đến đỉnh v, trong đó n là

số nguyên dương, trên đồ thị có hướng G = (V, A) là dãy x 0 , x 1 , , x n-1, x n trong

đó u = x 0 , v = x n , (x i , x i+1 ) A, i = 0, 1, 2 , , n-1 Đường đi nói trên còn

có thể biểu diễn dưới dạng dãy các cung: (x 0 , x 1 ), (x 1 , x 2 ), , (x n-1 , x n )

Đỉnh u gọi là đỉnh đầu, còn đỉnh v gọi là đỉnh cuối của đường đi Đường

đi có đỉnh đầu trùng với đỉnh cuối (tức là u = v) được gọi là chu trình Đường đi

hay chu trình được gọi là đơn nếu như không có cung nào bị lặp lại [3]

Định nghĩa 1.11 Đồ 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 nó

Trang 11

Định nghĩa 1.12 Chúng ta gọi đồ thị con của đồ thị G = (V, E) là đồ

thị H = (W, F), trong đó W V và F E

Trong trường hợp đồ thị là không liên thông, nó sẽ rã ra thành một số

đồ thị con liên thông đôi một không có đỉnh chung Những đồ thị con liên thông như vậy chúng ta sẽ gọi là các thành phần liên thông của đồ thị [3]

1.1.4 Đồ thị có trọng số

Đồ thị được sử dụng để giải các bài toán trong nhiều lĩnh vực khác nhau Chẳng hạn, đồ thị được 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ể xác định xem hai máy tính trong mạng

có thể trao đổi thông tin với nhau được hay không Khi đó, đồ thị được sử dụng để biễu diễn mạng truyền thông với các đỉnh là các nút mạng, các cạnh, các cung là các đường truyền dữ liệu giữa các nút mạng Đồ thị có thể dùng

để biễu diễn các đường đi trong một vùng: Các đỉnh tương ứng với các ngã 3, ngã 4, còn các cạnh, các cung tương ứng là các đường đi 2 chiều và đường đi

1 chiều Để cấu trúc đồ thị có thể biễu diễn được các bài toán thực tế người ta đưa vào khái niệm đồ thị có trọng số, trên mỗi cạnh hay mỗi cung được gán một trọng số thể hiện chi phí cho việc thực hiện một mục đích nào đó trên cạnh hay trên cung đó

Trang 12

1.2 Cây

Định nghĩa 1.14: Ta gọi cây là một đồ thị vô hướng liên thông không

có chu trình Đồ thị không có chu trình gọi là rừng

Định lý 1.3: Giả sử G = (V, E) là đồ thị vô hướng n đỉnh, khi đó các

mệnh đề sau đây là tương đương:

(1) G là cây

(2) G không chứa chu trình và có n-1 cạnh

(3) G liên thông và có n-1 cạnh

(4) G liên thông và mỗi cạnh của nó đều là cầu

(5) Hai đỉnh bất kỳ của G được nối với nhau bởi đúng một đường

đi đơn

(6) G không chứa chu trình nhưng nếu cứ thêm vào nó một cạnh ta thu được đúng một chu trình [3]

1.3 Bài toán đường đi tối ưu trên đồ thị

Như đã nói ở phần mở đầu, bài toán tìm đường đi tối ưu trên đồ thị có một ý nghĩa thực tế vô cùng to lớn Trong chương một này chỉ tập trung

Trang 13

nghiên cứu đường đi tối ưu trên đồ thị có hướng G = (V, E), |V|=n, |E|= m với mỗi cung được gán một trọng số, nghĩa là mỗi cung (u,v) E được đặt tương

ứng với một số thực a(u,v) gọi là trọng số của nó Trong ứng dụng cụ thể cho từng bài toán thì trọng số của một cung có thể là độ dài cung (u,v), có thể là chi phí đi từ u đến v cũng có thể là thời gian đi từ u đến v Nếu (u, v) E ta

sẽ đặt a(u, v) = Nếu dãy v 0 , v 1 , , v p là một đường đi trên G thì độ dài của

Bài toán tìm đường đi tối ưu trên đồ thị có thể phát biểu dạng tổng

quát như sau: Tìm đường đi có độ dài nhỏ nhất xuất phát từ đỉnh s V (s là đỉnh xuất phát) đến đỉnh t V (t là đỉnh đích)

Đường đi như vậy ta gọi là đường đi tối ưu từ đỉnh s đến đỉnh t, độ dài của đường đi này ta gọi là khoảng cách từ s đến t và ký hiệu là d(s, t) Nếu không tồn tại đường đi từ s đến t ta đặt d(s, t) = Trong một số trường hợp

đường đi tối ưu từ s đến t còn bị ràng buộc thêm một số điều kiện khác nữa, ví

dụ phải đi qua một số đỉnh cố định cho trước hoặc phải quay lại đỉnh xuất phát

Dễ thấy rằng nếu trong G không tồn tại chu trình có độ dài âm (gọi tắt

là chu trình âm) thì đường đi tối ưu từ s đến t không có đỉnh nào bị lặp lại

Đường đi không có đỉnh lặp lại gọi là đường đi cơ bản hoặc đường đi đơn

Trong trường hợp trong G có chu trình âm thì khoảng cách giữa hai điểm s và

t có thể không xác định, bởi vì bằng cách đi vòng theo chu trình âm một số

lần đủ lớn nào đó thì d(s,t) có thể nhỏ hơn bất kỳ một số thực nào đó cho

trước [3] Khi đó ta có thể đặt vấn đề tìm đường đi cơ bản tối ưu, tuy nhiên

Trang 14

bài toán sẽ rất phức tạp, vì vậy trong luận văn này chúng ta sẽ giả thiết trong

G các cung đều có trọng số không âm Trường hợp trong đồ thị G trọng số các

cung không âm, có nhiều thuật toán tìm đường đi tối ưu nổi tiếng như thuật toán For_Bellman, thuật toán Dijkstra, thuật toán Floyd Trong phần còn lại của chương này của luận văn chúng ta sẽ nghiên cứu về thuật toán Dijkstra và

sử dụng Fibonacci heap để cải tiến thuật toán Dijkstra

1.4 Thuật toán Dijkstra

1.4.1 Phát biểu bài toán

“Cho đồ thị có hướng có trọng số G = (V, E), hãy tìm một đường đi tối

ưu xuất phát từ đỉnh s thuộc V, đến một đỉnh t cũng thuộc V.”

Bài toán có thể được tìm thấy rất nhiều trong thực tế, chẳng hạn trong mạng lưới giao thông đường bộ, đường thủy, đường không, trong truyền tải

dữ liệu của một mạng máy tính

1.4.2.Mô tả thuật toán

Trong trường hợp trọng số trên cung không âm, bài toán trên có thể giải quyết hiệu quả bằng thuật toán Dijkstra mô tả như sau [3] :

Bước 1: Khởi tạo

Mỗi đỉnh v thuộc V, gọi d[v] là khoảng cách từ s đến v Ban đầu

d[s]:=0, d[v≠s] := ∞, ban đầu các đỉnh được coi là chưa cố định (mỗi đỉnh có

một trong 2 trạng thái là tự do hoặc cố định, tự do nghĩa là d[v] còn có thể tối

ưu hơn nữa, cố định tức là d[v] đã bằng độ dài đường đi tối ưu từ s đến t,

không tối ưu được nữa)

Bước 2: Lặp cho đến khi t trở thành đỉnh cố định

Bước lặp bao gồm 2 thao tác :

Thao tác 1: Cố định nhãn

Trang 15

Chọn đỉnh u tự do có nhãn d[u] nhỏ nhất và cố định nhãn cho nó

Thao tác 2: Sửa nhãn

Đối với mỗi đỉnh v tự do, kề với u Nếu d[v] > d[u]+c[u,v] thì ta sẽ sửa nhãn cho v: d[v] := d[u]+c[u,v] và lưu u là đỉnh kề trước v trên đường đi tối ưu (c[u,v] là trọng số của cung (u, v))

Bước 3: Xuất kết quả

Trả lại d[t] là độ dài đường đi tối ưu, kết hợp truy vết để tìm đường đi

Mô hình :

Độ phức tạp của FindMin() là O(n), của Repair(u) là O(n) Số lần lặp của bước 2 sẽ là số cung trong đường đi tối ưu, tức là khoảng O(n) Thuật toán Dijkstra cài đặt như trên sẽ có độ phức tạp O(n 2 ), kết quả này là không

khả thi cho đồ thị có số đỉnh n lớn

1.5 Thuật toán Dijkstra kết hợp với Fibonacci heap

Do độ phức tạp của thuật toán Dijkstra là O(n 2 ) nên khi số đỉnh của đồ

thị lớn, chương trình chạy rất chậm, trong phần 1.5 này chúng ta sẽ sử dụng Fibonacci heap để cải tiến thuật toán này

1.5.1 Hàng đợi ưu tiên

1.5.1.1 Khái niệm hàng đợi, hàng đợi ưu tiên

Hàng đợi (queue): Là một kiểu danh sách mà việc bổ sung một phần tử

được thực hiện ở cuối danh sách còn việc loại bỏ một phần tử được thực hiện

ở đầu danh sách Có thể hình dung hàng đợi như một hàng người xếp hàng

Trang 16

mua vé: Người xếp hàng trước sẽ được mua vé trước, người đứng đầu tiên mua vé xong đi ra thì người thứ hai tiến lên thay vị trí người đứng đầu, còn người mới đến sẽ đứng vào cuối hàng Vì nguyên tắc vào trước ra trước nên hàng đợi còn được gọi là danh sách kiểu FIFO (First in first out) Có 6 thao tác cơ bản đối với hàng đợi [2]:

Init: Tạo một ngăn xếp rỗng

isEmpty: Cho biết hàng đợi có rỗng hay không

isFull: Cho biết hàng đợi có đầy không

Get: Đọc giá trị của phần tử ở đầu hàng đợi

Push: Đẩy (bổ sung) một phần tử vào hàng đợi

Pop: Lấy một phần tử ra khỏi hàng đợi

Ta có thể biểu diễn hàng đợi bằng mảng hoặc dang sách móc nối [2]

Hàng đợi ưu tiên: Hàng đợi có độ ưu tiên, gọi tắt là hàng đợi ưu tiên

(priority queue) là một cấu trúc dữ liệu quan trọng dùng trong việc cài đặt

nhiều thuật toán Hàng đợi ưu tiên là một kiểu danh sách chứa các phần tử của

một tập hữu hạn S nào đó, mỗi phần tử của S được gán cho một mức độ ưu tiên nào đó Ta đánh số các phần tử của S lần lượt từ 1 đến n và đồng nhất mỗi phần tử với chỉ số của nó, khi đó độ ưu tiên của phần tử i là một số thực

p[i] ( i = 1, 2, , n)

Với một hàng đợi ưu tiên có các thao tác chính sau đây [2]:

+ Insert(i): Đẩy phần tử i vào hàng đợi ưu tiên nếu nó chưa có trong

hàng đợi

+ Find min(Find max): Trả về phần tử có độ ưu tiên nhỏ nhất (lớn nhất)

trong hàng đợi ưu tiên

+ Extract: Trả về phần tử có độ ưu tiên nhỏ nhất (lớn nhất) trong hàng

Trang 17

đợi ưu tiên, và loại bỏ nó khỏi hàng đợi ưu tiên

+ Update(i, new(p)): Cập nhật độ ưu tiên của phần tử i thành new(p)

Hàng đợi ưu tiên là một biến thể của hàng đợi, nó khác ở chỗ là hàng

đợi thông thường hoạt động theo kiểu vào trước ra trước còn trong hàng đợi

ưu tiên thủ tục Extract luôn lấy ra phần tử có độ ưu tiên nhỏ nhất (lớn nhất)

1.5.1.2 Cấu trúc dữ liệu heap

Ta có thể dùng mảng hoặc danh sách móc nối để biểu diễn một hàng

đợi ưu tiên, khi đó các thao tác Insert và Update có thể thực hiện với độ phức tạp là O(1), tuy nhiên các thao tác Find min (Find max) và Extract lại có độ phức tạp là O(n) Vì vậy, trong thực tế người ta hay dùng cấu trúc dữ liệu trừu

tượng heap (đống) để biểu diễn hàng đợi ưu tiên

Heap là một cấu trúc dữ liệu trừu tượng, bao gồm một tập n phần tử,

mỗi phần tử có một giá trị khóa xác định Các phép toán trên một heap được

mô tả trong bảng dưới đây :

Make_ heap Trả về một heap mới rỗng

Insert (x,h) Chèn một giá phần tử x mới,có khóa xác định vào heap

Find_ min Trả về phần tử có khóa nhỏ nhất, không làm thay đổi heap

Extract_ min Trả về phần tử có khóa nhỏ nhất và xóa nó ra khỏi heap

Trong một số bài toán còn có thêm các phép toán sau :

Union(h1,h2) Hợp nhất hai heap h1, h2 thành heap mới, đồng thời xóa h1,

h2

Decrease( ,x,h) Giảm khóa của phần tử x một lượng trong heap

Delete(xh) Xóa phần tử X ra khỏi heap

Trang 18

Heap còn có thể gọi là hàng đợi có độ ưu tiên (priority queue) hay các đống khả trộn (mergeable heaps)

Một số loại heaps, và thời gian thao tác các phép toán được trình bày trong bảng dưới đây [6]:

Heaps

Thao tác Linked

list Binary Bionimal Fibonacci Relax

Trong phần tiếp theo chúng ta sẽ nghiên cứu kỹ về Fibonacci heap

1.5.2 Fibonacci heap

1.5.2.1 Giới thiệu Fibonacci heap (Đống Fibonacci)

Cấu trúc dữ liệu Fibonacci heap (Đống Fibonacci) được hai giáo sư Fredman và Tarjan đưa ra vào năm 1986, nhằm áp dụng vào các bài toán tối

ưu trên đồ thị, độ phức tạp của các thuật toán giải một số bài toán điển hình khi sử dụng Fibonacci heap được thống kê dưới đây [6]:

O(nlogn + m): Cho bài toán tìm đường đi tối ưu xuất phát từ một đỉnh O(n 2 logn + nm): Cho bài toán tìm đường đi tối ưu giữa mọi cặp đỉnh O(n 2 logn + nm): Cho bài toán so khớp hai nhánh có trọng số

Trang 19

Trước khi định nghĩa Fibonacci heap, ta đưa ra một số khái niệm:

Cây có gốc: Là một cây tự do mà ở đó có một trong các đỉnh được phân

biệt với các đỉnh còn lại và được gọi là gốc

Cây có thứ tự: Là cây có gốc, mà các nút con trực thuộc một nút cha

được sắp xếp theo một thứ tự xác định

Cây sắp xếp theo đống: Là cây có gốc, và nếu nút x là nút bất kỳ thì nó

có giá trị khóa lớn hơn hoặc bằng (nhỏ hơn hoặc bằng) khóa của cha nó Từ

đó nút gốc là nút có khóa nhỏ nhất (lớn nhất)

Định nghĩa : Fibonacci heap là một tập hợp các cây được sắp xếp theo

đống Các cây trong Fibonacci heap không bị ràng buộc về thứ tự[6]

Hình1.3: Fibonacci heap gồm 5 cây sắp xếp theo đống với 14 nút [4]

1.5.2.2 Cấu trúc Fibonacci heap

Hình 1.4 mô tả cách biểu diễn cấu trúc heap Mỗi nút x chứa một biến trỏ p[x] trỏ đến cha của nó và một biến trỏ child[x] trỏ đến một con bất kỳ của

nó Các con của x được nối kết theo một danh sách nối kết đôi theo vòng tròn

mà ta gọi là danh sách con của x Mỗi nút y trong danh sách con có các biến trỏ left[y] và right[y] trỏ đến anh em ruột trái và phải của nó Nếu nút y là duy nhất thì left[y] = right[y] = y Thứ tự xuất hiện các nút trong danh sách con là

tùy ý

Việc thể hiện danh sách con bằng danh sách nối đôi vòng tròn có 2 ưu

Trang 20

điểm: Thứ nhất, có thể gỡ bỏ một nút ra khỏi danh sách với độ phức tạp là

O(1) Thứ hai, có thể ghép nối 2 danh sách với độ phức tạp tính toán là O(1)

Ngoài ra mỗi nút x còn có :

degree[x]: Lưu số nút trong danh sách con của x

bool mark[x]: Kiểm tra x đã mất một con hay chưa kể từ lần cuối cùng

x trở thành con của một nút khác

Các gốc của tất cả các cây trong Fibonacci heap được nối kết với nhau bằng các biến trỏ left, right của chúng tạo thành một danh sách nối kết đôi vòng tròn gọi là danh sách gốc Biến trỏ min[H] trỏ đến nút có khóa nhỏ nhất trong danh sách gốc, từ đây ta sẽ coi min[H] là đại diện của H nói cách khác

là minH quản lý H Số lượng các nút trong H sẽ được lưu trong biến nH

Hình 1.4 Mô phỏng cấu trúc Fibonacci heap [4]

Cấu trúc dữ liệu miêu tả một nút trong Fibonacci heap[4]:

end;

Trang 21

Hàm tiềm năng

Để phân tích đánh giá độ phức tạp trong các phép toán đối với Fibonacci heap ta sử dụng phương pháp phân tích tiềm năng, khi đó hao phí khấu trừ của một thuật toán được xem là độ phức tạp của thuật toán đó [4] Đối với Fibonacci heap, ta sử dụng hàm tiềm năng Φ(H) = t(H) + 2m(H), trong đó: t(H) là số lượng nút trong danh sách gốc, m(H) là số lượng các nút đánh dấu Ta mặc nhận rằng ở trạng thái ban đầu Φ(H) = 0

Ở ví dụ trên : Φ(H) = 5 + 2  3 = 11

1.5.2.3 Các thao tác trên Fibonacci heap

Ý tưởng chính trong các thao tác trên Fibonacci heap đó là trì hoãn các công việc chưa cần thiết nếu có thể, chờ đến lúc buộc phải thực hiện thì thực hiện cùng một lúc nhiều công việc, điều đó giúp giảm bớt các phép tính toán Nếu số lượng các cây trong một Fibonacci heap không lớn, ta có thể nhanh

chóng xác định nút cực tiểu mới bằng thủ tục EXTRACT_ MIN Ta sẽ không

gắng thống nhất các cây trong Fibonacci heap khi chèn một nút mới hoặc hợp nhất hai đống Việc thống nhất các cây trong đống chỉ phải làm khi gặp thủ

tục EXTRACT_MIN, là thủ tục tìm nút cực tiểu và xóa nó khỏi đống

(1) Tạo một Fibonacci heap mới

Để tạo một Fibonacci heap rỗng ta dùng thủ tục FIB_HEAP_ MAKE,

thủ tục này phân bổ và trả về đối tượng Fibonacci heap, trong đó n[H] = 0,

min[H] = NIL, ngay sau lời gọi thủ tục này thì chưa có cây nào trong H

Vì thế t(H) = 0, m(H) = 0, nên Φ(H) = 0 Như vậy, mức hao phí khấu

trừ (độ phức tạp tính toán) của FIB_HEAP_MAKE bằng với mức hao phí thực

tế O(1) của nó

(2) Chèn một nút mới

Thủ tục dưới đây chèn nút x với khóa là key[x] vào Fibonacci heap H

được quản lý bởi biến con trỏ min[H]:

Trang 22

Procedure FIB_HEAP_INSERT (var x, min[H] : tro); [4]

// tro là một kiểu con trỏ, trỏ vào các node: tro = ^node;

7 ghép nối danh sách gốc chứa x với danh sách gốc H;

8 if min[H] = NIL hoặc x^.key < minH^.key

H Như vậy nút x trở thành một cây nút đơn được sắp xếp theo đống trong

Fibonacci heap Nó không có cây con và không được đánh dấu Các dòng 8 -

9 cập nhật min[H] Dòng 10 tăng số lượng nút trong H

Có thể nhận ra rằng FIB_HEAP_INSERT(c,min[H]) sẽ không gắng thống nhất các cây trong đống Nếu k phép toán FIB_HEAP_INSERT thực hiện thì sẽ có k cây mới được bổ sung vào danh sách gốc

Hình 1.5: Minh họa chèn thêm nút có key 21 vào Fibonacci heap[4]

Trang 23

Để xác định mức hao phí khấu trừ của FIB_HEAP_INSERT, gọi H là Fibonacci heap ban đầu và H’ là Fibonacci heap kết quả, thì t(H’) = t(H)+1, m(H’) = m(H), sự gia tăng trong hàm tiềm năng là : Φ(H’) - Φ(H) = 1 Bởi mức hao phí thực tế là O(1), nên mức hao phí khấu trừ là O(1) + 1 = O(1)

Mã nguồn thao tác FIB_HEAP_INSERT:

procedure FIB_HEAP_INSERT(var x, minH: tro); [4]

Ở đây nút mới x đã được chèn vào bên trái minH, thực tế trong lập trình

ta có thể chèn x vào bên phải minH thì kết quả vẫn giống nhau

(3) Tìm nút cực tiểu

Nút cực tiểu của Fibonacci heap được trỏ bởi min[H], do đó ta có thể tìm nút cực tiểu với độ phức tạp tính toán thực tế (hao phí thực tế) là O(1) Do

Trang 24

tiềm năng của H không thay đổi, nên mức hao phí khấu trừ của phép toán này bằng với mức hao phí thực tế của nó là O(1)

(4) Hợp nhất hai Fibonacci heap [6]

Thủ tục dưới đây hợp nhất 2 Fibonacci heap H 1 và H 2 thành H, đồng thời hủy H 1 , H 2

procedure FIB_HEAP_UNION(var min[H 1 ], min[H 2 ], minH : tro);

begin

1 n[H] := 0;

2 min[H] := min[H 1 ];

3 ghép nối danh sách gốc của H 2 với danh sách gốc của H;

4 if (min[H 1 ] = NIL) hoặc (min[H 2 ]^.key < min[H 1 ]^.key) then

5 min[H] := min[H 2 ];

6 n[H] := n[H 1 ] + n[H 2 ];

7 giải phóng các đối tượng H 1 và H 2 ;

end;

Các dòng 1-3 ghép nối các danh sách gốc của H 1 và H 2 thành một danh

sách gốc mới H Các dòng 2, 4, 5 ấn định nút cực tiểu của H, dòng 6 cập nhật lại số nút của H Ta có thể thấy trong thủ tục FIB-HEAP-UNION(H 1 , H 2) không cần thực hiện thống nhất cây

Sự thay đổi tiềm năng : Φ(H) – (Φ(H 1 ) + Φ(H 2)) = 0 Do đó mức hao

phí khấu trừ của FIB_HEAP_UNION bằng với mức hao phí thực tế O(1)

(5) Trích nút cực tiểu[4]

Đây là thủ tục phức tạp nhất của Fibonacci heap Thủ tục này thực hiện

xóa nút cực tiểu ra khỏi H, đồng thời thực hiện việc thống nhất các cây đã bị

trì hoãn ở các phép toán khác

Trang 25

Đoạn mã giả dưới đây thực hiện trích nút cực tiểu, có sử dụng thủ tục

3 for x thuộc con của z do

4 begin cộng x vào danh sách gốc của H

5 x.parent := NIL;

6 end;

7 Gỡ bỏ z ra khỏi danh sách gốc của H;

8 if z = z^.right

9 then min[H] := NIL

10 else min[H] := z^.right;

Dòng 1 dùng biến trỏ z, cho z trỏ vào nút cực tiểu, biến trỏ này được trả lại ở cuối Nếu z=NIL, thì Fibonacci heap đã rỗng và ta đã hoàn tất công việc Ngược lại, ta xóa nút z ra khỏi H bằng cách khiến tất các con của z thành các

Trang 26

gốc của H, trong các dòng 4 - 9 Nếu z= right[z] hay danh sách chỉ có một nút duy nhất, thì ta chỉ việc biến H thành rỗng, ngược lại, tạm thời cho biến trỏ gốc min[H] vào một nút bất kì trong danh sách gốc, ở đây là right[z], sau đó

rút gọn các cây trong Fibonacci heap gọi là thống nhất Fibonacci heap Điều này được thực hiện bằng thủ tục CONSOLIDATE

Nguyên tắc làm việc của thủ tục CONSOLIDATE:

(1) Tìm hai gốc x, y có cùng số nút con trong danh sách con, và key[x]

≤ key[y]

(2) Gỡ bỏ y ra khỏi danh sách gốc, cho y làm con của x Việc này sẽ

được thực hiện bởi thủ tục FIB_HEAP_LINK

Thủ tục CONSOLIDATE sử dụng thêm một mảng phụ A[0 D(H)], với D(H) = log2N [4], với ý nghĩa A[i] = y thì y là một gốc có degree[y]=i

procedure COSONLIDATE(var minH : tro); [4]

begin

1 For i = 0 to D(H) do A[i] := NIL;

2 For mỗi nút w trong danh sách gốc của H do

Trang 27

19 Begin

20 Cộng A[i] vào danh sách gốc của H;

21 If (min[H]=NIL) hoặc ([A[i]]^.key < min[H]^.key)

22 then Min[H] := A[i];

w Nếu A[d]=NIL thì ta gán A[d]= w Ngược lại, dòng 6-13: ta chọn cây có

gốc nhỏ hơn trong hai cây w và A[d] làm gốc, và ghép cây còn lại vào danh sách con của cây kia, kết quả trả về cây trỏ bởi x Cây x sẽ là cây có số con là

d + 1, tiếp tục xét với cây trỏ bởi A[d+1] và x, nếu A[d+1]=NIL thì dừng lại

Kết thúc quá trình này ta sẽ được một danh sách gốc mới, trong đó mỗi gốc có số lượng nút con là khác nhau Công việc còn lại chỉ là thực hiện cập

nhật nút minH được thực hiện trong các dòng từ 16 – 23

Hình 16 dưới đây mô tả hoạt động của FIB_HEAP_EXTRACT_MIN,

quá trình biến đổi của Fibonacci heap được giải thích như sau:

(a) Một Fibonacci heap

(b) Trạng thái Fibonacci heap khi gỡ bỏ nút cực tiểu z ra khỏi danh sách

gốc và bổ sung con của nó vào danh sách gốc

Trang 28

(c) - (e) Mảng A và các cây trong 3 lần lặp đầu tiên của vòng lặp for

trong thủ tục CONSOLIDATE Danh sách gốc ban đầu tại nút cực tiểu tạm thời (sau khi bỏ nút cực tiểu mới), tiếp theo tại biến trỏ right

(f) - (h) Tình huống đầu tiên khi đi qua vòng lặp while, nút cây có gốc

23 được nối vào cây gốc 7 tạo cây mới có degree =1, do đã tồn tại cây có

degree =1 (a[1]) nên tiếp tục ghép cây trỏ bởi a[1] có gốc là 17 vào cây gốc

7 Tương tự đến bước (h) cây có gốc 24 tiếp tục được nối vào cây gốc 7 tạo cây có degree = 3, và được A[3] trỏ đến

(i) - (l) Tình huống trong 4 lần lặp tiếp theo của vòng lặp for

(m) Trạng thái Fibonacci heap sau khi kết thúc, với biến trỏ mới min [H]

Trang 29

Hình 1.6 Mô tả hoạt động của FIB_HEAP_EXTRACT_MIN [4]

Mức hao phí của phép toán trích nút cực tiểu bao gồm: O(D(H)) của

phép toán duyệt các nút con của nút cực tiểu thực hiện bởi vòng for trong thủ

Trang 30

tục FIB_HEAP_EXTRACT_MIN, D(H) trong việc khởi tạo mảng A trong thủ tục CONSOLIDATE, D(H) trong vòng for thực hiện cập nhật lại min[H]

(dòng 17 - 23 của thủ tục CONSOLIDATE), cuối cùng là vòng for dòng 2 - 15

Để đánh giá mức hao phí của vòng for này ta xét: Kích cỡ của danh sách gốc

vào lúc gọi CONSOLIDATE tối đa là D(H) + t(H)-1, bởi nó bao gồm t(H)

nút trong danh sách gốc ban đầu, trừ cho nút gốc đã trích cộng với các con

của nút đã trích mà tối đa là D(H) Mỗi lần qua vòng lặp while của các dòng 6 -

12, một gốc sẽ được nối với gốc khác, và như vậy tổng lượng công việc được

thực hiện trong vòng lặp for tối đa tỉ lệ với D(H) + t(H) Vậy tổng hao phí thực tế là O(D(H) + t(H))[4]

Giá trị hàm tiềm năng trước khi trích nút cực tiểu là t(H) + 2m(H) và giá trị hàm tiềm năng sau đó tối đa là (D(H) +1) + 2m(H), bởi số lượng nút trong danh sách gốc sau khi trích nút tối đa là D(H) + 1 và số lượng nút đánh

dấu không thay đổi

Mức hao phí khấu trừ: O(D(H) + t(H)) + ((D(H) + 1) + 2m(H)) -(t(H)

+ 2m(H)) = O(D(H)) = O(logn).

(6) Giảm một khóa và xóa một nút

Giảm khóa một nút

Mã giả dưới đây thực hiện phép toán giảm một khóa [4]

procedure FIB_HEAP_DECREASE_KEY(var minH, x: tro; k: longint);

Trang 32

Thủ tục FIB_HEAP_DECREASE_KEY làm việc như sau: Các dòng

1-3 đảm bảo khóa mới không lớn hơn khóa hiện tại, và sau đó gán khóa mới

cho x Nếu x là một gốc hoặc key[x] ≥ key[y] với y là cha của x thì không cần

thay đổi cấu trúc của Fibonacci heap - các dòng 4 - 5 kiểm tra điều kiện này

Ngược lại, thứ tự đống bị vi phạm, ta cần phải tổ chức lại Ta bắt đầu

bằng cách cắt nối kết giữa x và cha y của nó, biến x thành một gốc

Ta dùng trường mark để được cận thời gian tốt hơn Xét một nút x đã từng bị chuyển thành con của nút khác Ta đánh dấu mark[x] = true khi nó mất con thứ nhất, khi x mất con thứ 2 ta bỏ đánh dấu nó đưa nó vào danh sách gốc Bởi khi gỡ một nút x và ghép vào danh sách gốc, thì có thể tác động đến

trường mark của các nút cha, ông, cụ của nó, cho nên ta sử dụng một thao tác đệ quy cắt liên hoàn CASCADING_CUT để thực hiện kiểm tra đối với các nút cha, ông…

Sau khi xảy ra xong tất cả tác vụ cắt liên hoàn, các dòng 8, 9 của

FIB_DECREASE_KEY hoàn tất bằng cách cập nhất lại nút minH

Ta sẽ chứng tỏ mức hao phí khấu trừ củaFIB_HEAP_DECREASE_KEY

chỉ là O(1)

Thủ tục FIB_HEAP_DECREASE_KEY có độ phức tạp là O(1) (các lệnh từ

1 - 4) cộng với việc thực hiện các tác vụ cắt liên hoàn Giả sử thủ tục

CASCADING_CUT được gọi đệ qui c lần, mỗi lệnh CASCADING_CUT có độ

phức tạp là O(1) Như vậy mức hao phí thực tế của thủ tục FIB_HEAP_ DECREASE_KEY là O(c)

Tiếp theo ta sẽ tính sự thay đổi tiềm năng cho H là Fibonaci heap ngay

trước phép toán FIB_HEAP_DECREASE_KEY Mỗi lệnh gọi đệ quy của

CASCADING_CUT, tăng tối đa c cây ở gốc, giảm tối đa c-2 nút đánh dấu, từ

đó suy ra sự thay đổi về tiềm năng là:

((t(H) + c) + 2(m(H) – c + 2)) - (t(H) + 2m(H)) = 4 - c

Trang 33

Như vậy chi phí khấu trừ của FIB_HEAP_DECREASE_KEY là :

O(c) + 4 - c = O(1)

Như vậy, ta có thể hiểu được tại sao hàm tiềm năng lại bao gồm 2 lần

số lượng nút đánh dấu: Khi một nút đánh dấu y được cắt bởi một tác vụ cắt liên hoàn, nó được xóa đánh dấu, do đó tiềm năng được rút gọn đi 2 gồm: một thanh toán cho thao tác cắt và xóa đánh dấu, hai là bù cho đơn vị gia tăng trong tiềm năng do nút y trở thành gốc

Hình 1.7 Mô tả thao tác FIB_HEAP_DECREASE_KEY [4]

(a) Fibonacci heap ban đầu

(b) Nút có khóa 46 giảm xuống 15, trở thành một gốc, cha của nó 24 được đánh dấu

(c)-(e) Nút có khóa 35 giảm xuống thành 5, các nút cha 26, 24 được cắt

liên hoàn Cuối cùng cập nhật lại nút minH

Trang 34

Xóa một nút

Ta có thể dễ dàng xóa một nút ra khỏi Fibonacci heap với độ phức tạp

là O(logn), như trong mã giả dưới đây Mặc nhận rằng không có giá trị khóa

nào là -∞ trong Fibonacci heap

procedure FIB_HEAP_DELETE(var minH, x : tro); [4]

begin

1 FIB_HEAP_DECREASE_KEY(minH, x, - ∞);

2 FIB_HEAP_EXTRACT_MIN(minH);

end;

FIB_HEAP_DELETE(H, x) biến nút x thành nút cực tiểu bằng cách

gán cho nó khóa - ∞ thông qua thủ tục FIB_HEAP_DECREASE_KEY, sau đó loại nó ra khỏi Fibonacci heap bằng thủ tục FIB_HEAP_EXTRACT_MIN

Chi phí khấu trừ của FIB_HEAP_DELETE là tổng chi phí khấu trừ O(1)

của FIB_HEAP_DECREASE_KEY và O(logn) của FIB_HEAP_EXTRACT_MIN.

1.5.3 Sơ đồ thuật toán Dijkstra kết hợp với Fibonacci Heap

Ở mục 1.4.2 chúng ta đã đưa ra sơ đồ thuật toán Dijkstra nguyên thủy

Trang 35

Bây giờ chúng ta sẽ tổ chức mảng nhãn d theo cấu trúc Fibonacci heap,

khi đó mô hình thuật toán Dijkstra có thể viết lại như sau:

u := p^ u; // u là một trường mới của các nút trong Fibonacci heap để lưu nhãn

//của đinh (tên đỉnh) mà nút đại diện

if u = t then exit;

Đánh dấu u đã cố định ;

for (v thuộc ke(u)) do

if (d[v] > d[u] + c[u,v] ) then

Thao tác FIB_HEAP_DECREASE_KEY(H, ptr[ v], d[v]) có độ phức

Trang 36

tạp là O(1), số lần lặp trong một vòng for chỉ bằng số lượng đỉnh kề u, vì vậy

nếu chúng ta sử dụng danh sách liên kết để tổ chức các cạnh thì độ phức tạp

của tất cả các vòng lặp for trong vòng lặp repeat sẽ là O(m)

Từ hai nhận xét trên suy ra thuật toán Dijkstra Fibonacci heap có độ

phức tạp là cỡ O(nlogn +m), với m là số cạnh của đồ thị Với những đồ thị

thưa thì đây sẽ là một thuật toán rất tốt

1.6 Kết luận chương

Ở chương 1 này chúng ta đã sử dụng Fibonacci heap để cải tiến, nâng cao hiệu quả của thuật toán Dijkstra Hiệu quả của thuật toán Dijkstra cải tiến

so với thuật toán nguyên thủy đã được minh chứng trong mục 1.5 Trong thực

tế nếu dùng thuật toán Dijkstra nguyên thủy, chúng ta chỉ có thể áp dụng trên

những đồ thị mà số đỉnh N cỡ vài ba chục nghìn trở xuống, còn nếu dùng Dijkstra Fibonacci heap chúng ta có thể áp dụng cho đồ thị có số đỉnh N lớn hơn nhiều lần, thậm chí N có thể lên đến hàng triệu Ngoài việc ứng dụng để

cải tiến Thuật toán Dijkstra ta còn có thể ứng dụng Fibonacci heap vào việc giải một số bài toán khác như bài toán sắp xếp, bài toán tìm cây khung cực tiểu, bài toán so khớp hai nhánh có trọng số…

Tuy có ưu thế về tốc độ (độ phức tạp tính toán) đối với hầu hết các thao tác với heap nhưng Fibonacci heap vẫn chưa được nhiều người lập trình sử dụng, lý do là vì để cài đặt Fibonacci heap chúng ta phải dùng danh sách móc nối và nói chung là việc cài đặt phức tạp hơn Tuy nhiên trong trường hợp không bị giới hạn về thời gian lập trình và cần xử lý những đồ thị thưa, ta có thể dùng Fibonacci heap để tăng thêm hiệu quả của thuật toán

Ngày đăng: 15/04/2017, 20:53

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w