Đồ thị mẫu ban đầu: Tại mức 20
: gồm các đỉnh đơn .
Tại mức 21: gồm cạnh 8-7 tạo thành thành phần 1. Chúng ta chọn đỉnh có số thứ tự nhỏ hơn làm gốc tại đây sẽ là 7
72
Tại mức 22: thêm cạnh 7-6, 9-3, tiến hành gộp đỉnh 6 vào thành phần 1 và thêm thành phần 2 có 2 đỉnh là 9 - 3 .
Tại mức 23: thêm cạnh 1-2, 8-9,3-4,3-6,... tiến hành gộp thành phần 2, đỉnh 4 vào thành phần 1 tạo thành thành phần 4 với gốc là 4, gộp 2 đỉnh 1 và 2 thành thành phần mới là 3. Tiếp tục nhƣ vậy cho đến khi hoàn tất cây phân tầng.
Chúng ta bỏ qua các nút , do đó sẽ không có một nút nào chỉ có một con và số lƣợng các nút sẽ ≤ 2 – 1. Hình 4. 10. Ví dụ về cây phân tầng Hình 4. 11. Ví dụ về cây phân tầng 4.4.3. Ví dụ về Bucket B
Thuật toán Thorup quản lý khoảng cách tạm thời
của tất cả các đỉnh nhƣ thuật toán Dijkstra. Chúng ta sẽ gán
= 0, với mọi . Chúng ta xếp các giá trị con của thành phần
vào bucket của
. Sử dụng giá trị nhỏ nhất trong của các con nhƣ là chỉ mục:
với là cha của trong T. j = 32 khi là gốc của cây. Số lƣợng Bucket sẽ là (tổng số cạnh có trong thành phần / 2i) + 1. Với ví dụ tại 4.3.2: Giả sử ta xét nguồn là đỉnh 1,
= 0; = 9; = 4.
73 - Với thành phần 3: = =0; (Do là nhỏ nhất). - Với thành phần 4: = 9; (Do la nhỏ nhất). - Với thành phần 5 là gốc tại :
= 0/23 = 0; vậy vị trí của thành phần 3 là bucket 0;
=9/23=1 - vậy vị trí thành phần 4 là bucket 1; Tƣơng tự, chúng ta tiếp tục sắp xếp khoảng cách tạm thời nhƣ của thuật toán Dijkstra (Hình minh họa cho hoạt động của Bucket).
Hình 4. 12. Ví dụ về Bucket.
4.4.4. Ví dụ về Unvisited Structure (U)
Với lần đầu tiên cấu trúc của U chỉ có [1]4 đƣợc quản lý và phân đoạn trong U là [1, 2, 3, 9, 6, 7, 8, 4, 5]
]. Khi chúng ta tiến hành thăm từ gốc cho đến các thành phần của cây phân tầng T, chúng ta chia phân đoạn trong U và tiến hành tính toán giá trị nhỏ nhất D của phân đoạn mới hình thành trongU của các thành phần chƣa thăm trong dãy Bucket B.
Khi tiến hành thăm [1]4, chúng ta chia [1]4 để đƣa các con của nó vào bucket. Kết quả có 3 phân đoạn: [1]3, [3]3, [5]0 (tƣơng đƣơng với phân đoạn
). Tiếp tục tiến hành thăm từ theo từng phân đoạn chúng ta sẽ có các phân đoạn mới đƣợc hình thành.
74
Cấu trúc của U [1]4
[1, 2, 8, 7, 6, 3, 9, 4, 5]
Cấu trúc của U khi thăm gốc [1]4
[1]3 , [3]3, [5]0 [1, 2] [8, 7, 6, 3, 9, 4] [5]
Cấu trúc của U khi thăm gốc [1]3
[1]0, [2]0, [3]3, [5]0
[1] [2] [8, 7, 6, 3, 9, 4] [5]
Cấu trúc của U khi thăm gốc [3]3
[1]0, [2]0, [6]2, [3]2, [4]0, [5]0 [1] [2] [8, 7, 6] [3,9] [4] [5]
75
4.4.5. Ví dụ về thuật toán thăm các đỉnh của giải thuật Thorup
Áp dụng theo đồ thị mẫu ở trên: Chọn gốc s là đỉnh 1 -
. Chúng ta sẽ thăm đỉnh [1]4 thuộc cây phân tầng T. bucket của [1]4 đang rỗng. Khởi tạo bucket của [1]4.
Lúc này, tập các đỉnh chƣa thăm U bao gồm [1]3, [3]3, [5]0. Chỉ có [1]3 có chứa giá trị đếm đƣợc là . Ta có, . Xếp [1]3 vào bucket 0 và gán chỉ mục . Tiếp tục thăm đỉnh [1]3 , chia U ra làm các phân đoạn [1]0, [2]0, [3]3, [5]0. Khởi tạo bucket [1]3.
Chúng ta có
, [1]0 đƣợc xếp vào bucket 0 của [1]3, và gán giá trị k = = 0. Tiếp tục thăm [1]0 là đỉnh 1 của đồ thị hay là lá của cây phân tầng T. Tiến hành cập nhật giá trị
theo phƣơng pháp của giải thuật Dijkstra. Do [1]0 không phải là gốc, không chứa con nào nên xóa [1]0 ra khỏi bucket 0 của [1]3. Đồng thời
là giá trị nhỏ nhất trong điều đó có nghĩa
và đỉnh 1 đƣợc gán giá trị vĩnh viễn thêm đỉnh 1 vào tập S. Lúc này Bucket 0 rỗng, Tăng Chỉ mục
lên 1.
Lúc này [2]0, [3]3 trong U đã có giá trị tƣơng ứng là
,
,chúng ta xếp vào các bucket tƣơng ứng với giá trị đó tại bucket 1 của [1]3 và [1]4.
Do vẫn còn một phần tử và
, tiếp tục thăm với chỉ mục tăng thêm 1 tƣơng ứng với bucket 1. Chúng ta sẽ thăm [2]0, tiến hành cập nhật giá trị
theo phƣơng pháp của giải thuật Dijkstra nhƣng
nên giá trị nhỏ nhất không thay đổi
. Lúc này [2]0 không phải là gốc và không chứa con nào nên xóa [2]0 khỏi bucket 1 của [1]3 và làm tƣơng tự nhƣ đỉnh 1. Tất cả các con của [1]3 đƣợc xét qua nên xóa [1]3 khỏi bucket 0 của [1]4 và tăng
lên 1. (Trong trƣờng hợp nếu điều đó đƣợc chứng tỏ là các con của [1]3 vẫn chƣa là nhỏ nhất thì chuyển [1]3 sang bucket 1 của [1]4).
Tiếp tục nhƣ vậy cho tới khi tất cả các đỉnh đều đƣợc thăm. (Chúng ta có thể xem trong hình minh họa bên dƣới).
76
Hình 4. 13. Ví dụ về phép thăm
77
CHƯƠNG 5. ĐÁNH GIÁ THỰC NGHIỆM CÁC THUẬT TOÁN
5.1. Mục đích thực nghiệm và các thuật toán được lựa chọn 5.1.1. Mục đích
Mục đích của thực nghiệm là kiểm tra khả năng xử lý thực tế của các thuật toán với các bộ dữ liệu đầu vào từ Testcase 1- Testcase 7 khác nhau. Qua đó thống kê, phân loại đƣợc, vẽ đồ thị thể hiện thời gian chạy của mỗi thuật toán thực nghiệm trên cùng một bộ dữ liệu đầu vào.Qua thời gian chạy thực nghiệm của các thuật toán khác nhau trên cùng một bộ dữ liệu thực nghiệm ta có thể so sánh, đánh giá đƣợc thời gian chạy của các thuật toán đƣợc lựa chọn thực nghiệm đƣợc thể hiện trên đồ thị. Qua đó ta có thể phân tích, đánh giá đƣợc khả năng xử lý của từng thuật toán trong từng bộ dữ liệu đầu vào hay các trƣờng hợp đồ thị khác nhau, thuật toán SSSP nào cho kết quả tốt nhất với các trƣờng hợp đồ thị thực tế. Do thuật toán Thorup chỉ có thể chạy trên đồ thị vô hƣớng nên tất cả các đồ thị dữ liệu đều là vô hƣớng.
Ngoài ra, thuật toán A* đƣợc chúng tôi đề cập phần trên chỉ phù hợp với việc tìm kiếm khoảng cách ngắn nhất của từng cặp đỉnh đơn với nhau. Tuy nhiên, mục tiêu của luận văn hƣớng tới các giải thuật SSSP nên chúng tôi không tiến hành thực nghiệm thuật toán A*.
5.1.2. Các thuật toán
Các thuật toán đƣợc lựa chọn trong luận văn bao gồm:
TT Thuật toán Thời gian chạy theo lý thuyết 1 Dijkstra Binary Heap (DIJB)
2 Dijkstra Fibonacci Heap (DIJF)
3 Thorup (TR)
) 4 Thorup -Wei Yusi (TRWY)
)
78
Thuật toán Thorup trên lý thuyết thời gian xử lý sẽ là
. Tuy vây, cấu trúc đống mà Thorup sử dụng là đống atomic đã nêu ở trên không phù hợp với máy tính hiện đại, vì vậy chúng tôi sử dụng thuật toán Kruskal kết hợp với Tarjan union- findvà Gabow thay thế điều này đẩy thời gian xử lý của thuật toán lên
).
5.2. Ngôn ngữ lập trình, môi trường thực nghiệm và Bộ dữ liệu thực nghiệm
5.2.1. Ngôn ngữ lập trình và môi trường thực nghiệm
Thuật toán Thorup và các thuật toán khác đƣợc mô tả trong luận văn này đƣợc thực hiện cài đặt trên ngôn ngữ Java.
CPU Intel ® Core i5 2.5 GHZ
RAM 4 GB
OS Window 10 - 64 bit
IDE Bộ lập trình Netbeans - Oracle
Thông số Run build -Xms2024m -Xss60m -Xmx3248m
5.2.2. Bộ dữ liệu thực nghiệm
Dữ liệu thực nghiệm dựa trên các đồ thị dữ liệu đã có sẵn và xây dựng ngẫu nhiên. Các đồ thị dữ liệu có sẵn có thể tải về từ trang web:
http://www.dis.uniroma1.it/challenge9/download.shtml
Dữ liệu bao gồm 3 dạng đồ thị Distance graph, Travel time graph, Coordinates (trong đó Coordinates là dạng đồ thị tọa độ). Để thống nhất với các đồ thị sinh ngẫu nhiên, chúng ta lựa chọn dạng đồ thị Distance graph. Một số đồ thị trong dữ liệu thực nghiệm có sẵn có một số cạnh trùng nhau nên số cạnh khi đƣa vào chƣơng trình sẽ ít hơn số cạnh đƣợc khai báo. Đối với các đồ thị xây dựng ngẫu nhiên: Xây dựng ngẫu nhiên một đơn đồ thị liên thông với đỉnh và
79 1. Tạo một đồ thị
với đỉnh đơn và chƣa có cạnh nối. Tiến hành nối các đỉnh đƣợc sắp xếp theo thứ tự từ nhỏ đến lớn nhƣ 1 và 2, 2 và 3,... cho đến n đỉnh. Bƣớc này đảm bảo đồ thị ngẫu nhiên là liên thông.
2. Đối với tập cạnh còn lại
: Lấy ngẫu nhiên hai đỉnh bất kỳ mà chƣa tồn tại cạnh nối giữa 2 đỉnh, kết nối 2 đỉnh đó. Lặp lại quá trình cho đến khi đủ cạnh.
3. Tất cả các cạnh đƣợc tạo ra với trọng số đƣợc lấy ngẫu nhiên thuộc khoảng
Xây dựng Bộ dữ liệu thực nghiệm
Chọn là số cạnh trong đồ thị đầy đủ đó là
. Chúng tôi tiến hành thực nghiệm đối với các thuật toán trên đồ thị ngẫu nhiên với các bộ Testcase sau:
Testcase1: Dữ liệu đầu vào sẵn có - DIMACS
321270 1070376 1207945 1524453 1890815 2758119 3598623 6262104 800172 2712798 2840208 3897636 4657742 6885658 8778114 15248146 Bảng 5.2. Dữ liệu đồ thị có sẵn. Testcase2: Mật độ đồ thị 275320 321271 551625 1070376 1500000 2097152 3000000 825960 963814 1654876 3211128 4500000 6291456 9000000 Bảng 5.3. Dữ liệu đồ thị với mật độ m=3n Testcase3: Mật độ đồ thị 150000 321270 635666 800000 1000000 1207945 1890815 900000 1927620 3813396 4800000 6000000 7247670 11340890 Bảng 5. 4. Dữ liệu đồ thị với mật độ m=6n Testcase4: Mật độ đồ thị 300000 400000 500000 600000 700000 800000 1000000 3000000 4000000 5000000 6000000 7000000 8000000 10000000 Bảng 5. 5. Dữ liệu đồ thị với mật độ m=10n
80
Testcase5: Mật độ đồ thị
264436 435666 520000 700000 1088092 1379917 1965206 1057744 1742664 2080000 2800000 4352368 5519668 7860824
Bảng 5. 6. Dữ liệu đồ thị với mật độ m=nloglogn
Testcase6: Mật độ đồ thị
50000 100000 250000 350000 500000 626210 800000 780000 1660964 4482892 6445948 9465784 12058479 15687712
Bảng 5.7. Dữ liệu đồ thị với mật độ m=nlogn.
Testcase7: Mật độ đồ thị
256 512 1024 1536 2048 2560 3000
65280 261632 1047552 2357760 4192256 6551040 8997000
Bảng 5.8. Dữ liệu đồ thị với mật độ m=n*(n-1)/2.
Kết cấu của File dữ liệu đầu vào và File kết quả đầu ra
Các giá trị và đƣợc lựa chọn tƣơng đối dựa trên cấu hình của máy tính thực nghiệm nhằm tránh hiện tƣợng tràn bộ nhớ Ram trong quá trình chạy các thuật toán. Sau khi sinh ngẫu nhiên các đơn đồ thị liên thông theo số đỉnh , số cạnh đƣợc chọn trong bảng 4.3-4.8, chúng tôi tiến hành cho chạy các thuật toán với dữ liệu đầu vào là các đơn đồ thị đƣợc sinh ngẫu nhiên theo số đỉnh và số cạnh để tìm SSSP.
Đối với File dữ liệu đầu vào:
Từ các giá trị đƣợc chọn lựa qua các bảng dữ liệu chúng tôi sinh ra các đơn đồ thị ngẫu nhiên và lƣu dƣới dạng File text (đuôi .txt) với các thông số nhƣ sau:
1. Tên File đồ thị bao gồm số đỉnh và số cạnh :
.
2. Kết cấu bên trong đƣợc thể hiện thống nhất theo cấu trúc File của Bộ dữ liệu có sẵn: Trong đó và là số đỉnh và số cạnh của đồ thị, và tƣơng ứng với đỉnh nguồn, đỉnh đích và trọng số của cạnh với
81
Đối với File dữ liệu đầu ra:
Đối với mỗi thuật Với File dữ liệu đầu ra của mỗi thuật toán gồm 2 File: 1. File lƣu lại thời gian chạy của mỗi thuật toán khi tiến hành tìm kiếm ứng với đồ thị tƣơng ứng. File sẽ đƣợc lƣu dƣới dạng text và tên File theo cấu trúc:
Tên thuật toán - số lần tiến hành truy vấn - số đỉnh - số cạnh . Cấu tạo File lƣu thời gian chạy:
Result - timequery -
(Với
và là số lần tìm kiếm và thời gian tìm kiếm)
2. File lƣu lại kết quả đƣờng đi ngắn nhất từ nguồn đến 2 đỉnh trong đồ thị nhằm tiện so sánh (trong đó, 1 đỉnh ngẫu nhiên và 1 đỉnh đích chung cho tất cả các thuật toán).
Cấu tạo File kết quả đƣờng đi :
( là đỉnh nguồn và đỉnh đích, …. là các đỉnh nằm trên đƣờng đi ngắn nhất từ ).
5.3. Mô tả cài đặt các thuật toán
Chƣơng trình bao gồm các thƣ mục:
1. Các thuật toán đƣợc lƣu trong Thƣ mục src/de/unikiel/npr/thorup/algs: - Dijkstra.java: Mô tả phƣơng thức tìm SSSP trên các bộ dữ liệu thử nghiệm của thuật toán Dijkstra.
- Kruskal.java: Thuật toán tìm cây khung nhỏ nhất phục vụ cho việc xây dựng cây phân tầng cho thuật toán Thorup.
- Thorup.java: Mô tả phƣơng thức tìm SSSP trên các bộ dữ liệu thử nghiệm của thuật toán Thorup.
- Thorup2.java: Mô tả phƣơng thức tìm SSSP trên các bộ dữ liệu thử nghiệm của thuật toán Thorup đƣợc cải tiến bởi Wei Yusi.
82
2. Các cấu trúc dữ liệu sử dụng trong thuật toán đƣợc lƣu trong thƣ mục src/de/unikiel/npr/thorup/ds:
- AckermannTable.java: Lấy giá trị ngƣợc của hàm Ackermann. - BinaryHeap.java: Mô tả thực hiện đống Binary.
- UnionFindStructureTarjan.java: Hàm tìm nhóm danh sách các đỉnh có cùng chung đỉnh kết nối của Tarjan.
- SplitFindminStructureGabow.java: Mô tả phƣơng thức tìm phần tử nhỏ nhất theo cấu trúc dữ liệu của Gabow.
- Cấu trúc đống Fibonacci Heap đƣợc lƣu riêng trong thƣ mục src/de/unikiel/npr/thorup/ds/Heap.
3. Các hàm thực hiện và thông tin của đồ thị đƣợc lƣu trong thƣ mục src/de/unikiel/npr/thorup/graph:
- AdjacencyListweightedDirectedGraph.java: hàm lƣu trữ các thông tin về đồ thị đầu vào:
+ Mảng danh sách các đỉnh.
+ Mảng danh sách các cạnh tƣơng ứng của các đỉnh trong đồ thị.
- WeightedEdge.java: Tƣơng ứng với 1 cạnh của đồ thị, bao gồm các thông tin 2 đỉnh của cạnh, trọng số của cạnh.
4. Các hàm đọc các dữ liệu đầu vào và sinh đồ thị ngẫu nhiên đƣợc lƣu trong thƣ mục src/de/unikiel/npr/thorup/util:
- DIMACSGraphParser.java: Hàm đọc dữ liệu đồ thị đầu vào. - RandomGraphGenerator.java: Hàm sinh đồ thị ngẫu nhiên. 5. Hàm Main của chƣơng trình đƣợc lƣu trong thƣ mục src/thorup: - ThorupMain.java: Hàm chạy chƣơng trình.
- WriteGraph.java: Hàm ghi kết quả đầu ra.
- Node.java: Tƣơng ứng với một đỉnh của đồ thị, lƣu các thông tin tên của đỉnh, bậc của đỉnh .
83
5.4. Kết quả thực nghiệm
Kết quả thời gian chạy của các thuật toán cho mỗi đồ thị ngẫu nhiên có
đỉnh, cạnh đƣợc thể hiện trong các ô tƣơng ứng các bảng 5R1- 5R7 (đƣợc tính theo mili giây). Với dòng là sô các đỉnh tăng dần, dòng là số cạnh tăng dần tƣơng ứng, Thời gian
của các thuật toán tƣơng ứng và . Các ô đƣợc bỏ trống trong các bảng kết quả do máy tính thực nghiệm không đủ bộ nhớ Ram để chạy thuật toán tƣơng ứng nên kết quả không đƣợc thể hiện.
Các hình vẽ từ 5.1- 5.7 là các đồ thị tƣơng ứng thể hiện thời gian chạy của các thuật toán thực nghiệm theo các bảng 5R1- 5R7, với trục Ox thể hiện số cạnh (
, và trục Oy thể hiện thời gian
, là một đƣờng nối giao của các trị trên trục Ox và trên trục Oy, mỗi thuật toán đƣợc thể hiện bằng một đƣờng khác nhau.
Thời gian thể hiện ở hai thuật giải Thorup và Thorup - WeiYusi đã tính cả thời gian tìm kiếm cây khung nhỏ nhất, xây dựng cây phân tầng T và cấu trúc tập đỉnh chƣa thăm U.
84
Bảng 5.R1. Dữ liệu đầu vào sẵn có - DIMACS
321270 1070376 1207945 1524453 1890815 2758119 3598623 6262104 800172 2712798 2840208 3897636 4657742 6885658 8778114 15248146 DIJB 218.8 556.2 650 975 1071.8 1590.6 2547 6556.2 DIJF 300 990.6 1140.8 1615.6 2115.6 4196.8 6603.2 16481.4 TR 1753.2 20846.6 31847.2 60313 70747.6 TRWY 378.2 1137.6 1400 1784.4 2778 4987.6 11003.2