Các thuật toán tìm kiếm trên cây khung

Một phần của tài liệu Mô phỏng một số thuật toán trên đồ thị (Trang 26)

6. Ba bài toán trên mô hình đồ thị được đưa vào giảng dạy trong trường Trung

6.4. Các thuật toán tìm kiếm trên cây khung

6.4.1. Bài toán cây khung

Khái niệm cây khung: Cho đồ thị G = (V, E) vô hướng, liên thông và T = (V,E’) là một đồ thị con của G (E’  E). Khi đó, T được gọi là cây khung (cây bao trùm) nếu T liên thông và không có chu trình đơn

Cho G = (V, E, w) là đồ thị vô hướng liên thông có trọng số, với một cây khung T của G, ta gọi trọng số của cây T là tổng trọng số các cạnh trong T.

Yêu cầu: Trong số các cây khung của G, chỉ ra cây khung có trọng số nhỏ nhất.

Cây khung như vậy được gọi là cây khung nhỏ nhất của đồ thị, và bài toán đó gọi là bài toán xây dựng cây khung nhỏ nhất. Dưới đây ta sẽ xét một trong hai thuật toán thông dụng để giải bài toán cây khung nhỏ nhất của đơn đồ thị vô hướng có trọng số.

6.4.2. Giới thiệu thuật toán Prim

Một trong hai thuật toán quan trọng để giải bài toán tìm cây khung nhỏ nhất là thuật toán Prim. Thuật toán đó có thể phát biểu hình thức như sau:

Đơn đồ thị vô hướng G = (V, E,w). Xét cây T trong G và một đỉnh v, gọi khoảng cách từ v tới T là trọng số nhỏ nhất trong số các cạnh nối v với một đỉnh nào đó trong T:

d[v] = min{w[u, v]  uT}

Ban đầu khởi tạo cây T chỉ gồm có mỗi đỉnh {1}. Sau đó cứ chọn trong số các đỉnh ngoài T ra một đỉnh gần T nhất, kết nạp đỉnh đó vào T đồng thời kết nạp luôn cả cạnh tạo ra khoảng cách gần nhất đó. Cứ làm như vậy cho tới khi:

Hoặc đã kết nạp được tất cả n đỉnh thì ta có T là cây khung nhỏ nhất

Hoặc chưa kết nạp được hết n đỉnh nhưng mọi đỉnh ngoài T đều có khoảng cách tới T là +. Khi đó đồ thị đã cho không liên thông, ta thông báo việc tìm cây khung thất bại.

Về mặt kỹ thuật cài đặt, ta có thể làm như sau:

Sử dụng mảng đánh dấu Free. Free[v] = TRUE nếu như đỉnh v chưa bị kết nạp vào T.

Gọi d[v] là khoảng cách từ v tới T. Ban đầu khởi tạo d[1] = 0 còn d[2] = d[3] = ... = d[n] = +. Tại mỗi bước chọn đỉnh đưa vào T, ta sẽ chọn đỉnh u nào ngoài T và có d[u] nhỏ nhất. Khi kết nạp u vào T rồi thì rõ ràng các nhãn d[v] sẽ thay đổi: d[v]mới := min(d[v]cũ, v[u, v]). Vấn đề chỉ có vậy (chương trình rất giống thuật toán Dijkstra, chỉ khác ở công thức tối ưu nhãn).

Có thể mô tả thuật toán Prim bằng đoạn giả mã sau:

Bước 1: Khởi tạo: T = {s} d[s] = 0, u = s (s - đỉnh xuất phát) d[v]=+∞(v ∉ T) Bước 2: Lặp N-1 lần (N số đỉnh của đồ thị): 2.1 Cập nhật các đỉnh kề với u ở ngoài T Nếu d[v] > w[u,v] trace[v] = u, d[v] = w[u,v] 2.2 Chọn v (v ∉ T) mà d[v] nhỏ nhất Nếu d[v] = +∞ đến bước 3 2.3 Kết nạp v vào T, u = v

Bước 3: In ra cây khung hoặc thông báo vô nghiệm.

6.4.3. Giới thiệu thuật toán Kruskal

Thuật toán Kruskal dựa trên mô hình xây dựng cây khung bằng thuật toán hợp nhất [xxx], chỉ có điều thuật toán không phải xét các cạnh với thứ tự tuỳ ý mà xét các cạnh theo thứ tự đã sắp xếp: Với đồ thị vô hướng G = (V, E) có n đỉnh. Khởi tạo cây T ban đầu không có cạnh nào. Xét tất cả các cạnh của đồ thị từ cạnh có trọng số nhỏ đến cạnh có trọng số lớn, nếu việc thêm cạnh đó vào T không tạo thành chu trình đơn trong T thì kết nạp thêm cạnh đó vào T. Cứ làm như vậy cho tới khi:

Hoặc đã kết nạp được n - 1 cạnh vào trong T thì ta được T là cây khung nhỏ nhất

Hoặc chưa kết nạp đủ n - 1 cạnh nhưng hễ cứ kết nạp thêm một cạnh bất kỳ trong số các cạnh còn lại thì sẽ tạo thành chu trình đơn. Trong trường hợp này đồ thị G là không liên thông, việc tìm kiếm cây khung thất bại.

Như vậy có hai vấn đề quan trọng khi cài đặt thuật toán Kruskal:

Thứ nhất, làm thế nào để xét được các cạnh từ cạnh có trọng số nhỏ tới cạnh có trọng số lớn. Ta có thể thực hiện bằng cách sắp xếp danh sách cạnh theo thứ tự không giảm của trọng số, sau đó duyệt từ đầu tới cuối danh sách cạnh.

Nên sử dụng các thuật toán sắp xếp hiệu quả để đạt được tốc độ nhanh trong trường hợp số cạnh lớn. Trong trường hợp tổng quát, thuật toán HeapSort là hiệu quả nhất bởi nó cho phép chọn lần lượt các cạnh từ cạnh trọng nhỏ nhất tới cạnh trọng số lớn nhất ra khỏi Heap và có thể xử lý (bỏ qua hay thêm vào cây) luôn.

Thứ hai, làm thế nào kiểm tra xem việc thêm một cạnh có tạo thành chu trình đơn trong T hay không. Để ý rằng các cạnh trong T ở các bước sẽ tạo thành một rừng (đồ thị không có chu trình đơn). Muốn thêm một cạnh (u, v) vào T mà không tạo thành chu trình đơn thì (u, v) phải nối hai cây khác nhau của rừng T, bởi nếu u, v thuộc cùng một cây thì sẽ tạo thành chu trình đơn trong cây đó. Ban đầu, ta khởi tạo rừng T gồm n cây, mỗi cây chỉ gồm đúng một đỉnh, sau đó, mỗi khi xét đến cạnh nối hai cây khác nhau của rừng T thì ta kết nạp cạnh đó vào T, đồng thời hợp nhất hai cây đó lại thành một cây.

6.4.5. Độ phức tạp

Xét về độ phức tạp tính toán, thuật toán Prim có độ phức tạp là O(n2

). Tương tự thuật toán Dijkstra, nếu kết hợp thuật toán Prim với cấu trúc Heap sẽ được một thuật toán với độ phức tạp O((m+n)logn).

Đối với thuật toán Kruskal, ta có thể chứng minh được rằng thao tác GetRoot có độ phức tạp là O(log2n), còn thao tác Union là O(1). Giả sử ta đã có danh sách cạnh đã sắp xếp rồi thì xét vòng lặp dựng cây khung, nó duyệt qua danh sách cạnh và với mỗi cạnh nó gọi 2 lần thao tác GetRoot, vậy thì độ phức tạp là O(mlog2n), nếu đồ thị có cây khung thì m  n-1 nên ta thấy chi phí thời gian chủ yếu sẽ nằm ở thao tác sắp xếp danh sách cạnh bởi độ phức tạp của HeapSort là O(mlog2m). Vậy độ phức tạp tính toán của thuật toán là O(mlog2m) trong trường hợp xấu nhất. Tuy nhiên, phải lưu ý rằng để xây dựng cây khung thì ít khi thuật toán phải duyệt toàn bộ danh sách cạnh mà chỉ một phần của danh sách cạnh mà thôi.

6.5.Bài toán tìm chu trình Hamilton qua tất cả các đỉnh của đồ thị 6.5.1. Phát biểu bài toán

Khái niệm chu trình Hamilton: Cho đồ thị G = (V, E) có n đỉnh

Chu trình (x1, x2, ..., xn, x1) được gọi là chu trình Hamilton nếu xi  xj với 1  i < j  n

Đường đi (x1, x2, ..., xn) được gọi là đường đi Hamilton nếu xi xj với 1  i < j  n

Có thể phát biểu một cách hình thức: Chu trình Hamilton là chu trình xuất phát từ 1 đỉnh, đi thăm tất cả những đỉnh còn lại mỗi đỉnh đúng 1 lần, cuối cùng quay trở lại đỉnh xuất phát. Đường đi Hamilton là đường đi qua tất cả các đỉnh của đồ thị, mỗi đỉnh đúng 1 lần.

Bài toán tìm chu trình Hamilton: Cho đồ thị G = (V, E) có n đỉnh, m cạnh. Yêu cầu: Hãy chỉ ra một chu trình Hamilton của đồ thị đã cho.

Để tìm chu trình Hamilton, ta quan tâm đến các định lý sau:

Định lý 1: Đồ thị vô hướng G, trong đó tồn tại k đỉnh sao cho nếu xoá đi k đỉnh này cùng với những cạnh liên thuộc của chúng thì đồ thị nhận được sẽ có nhiều hơn k thành phần liên thông. Thì khẳng định là G không có chu trình Hamilton. Mệnh đề phản đảo của định lý này cho ta điều kiện cần để một đồ thị có chu trình Hamilton

Định lý 2 (Định lý Dirac (1952)): Đồ thị vô hướng G có n đỉnh (n  3). Khi đó nếu mọi đỉnh v của G đều có deg(v)  n/2 thì G có chu trình Hamilton. Đây là một điều kiện đủ để một đồ thị có chu trình Hamilton

Định lý 3: Đồ thị có hướng G liên thông mạnh và có n đỉnh. Nếu

2 ) ( deg vnvà 2 ) (

6.5.2. Giới thiệu thuật toán tìm chu trình Hamilton:

Dưới đây ta sẽ cài đặt một chương trình liệt kê tất cả các chu trình Hamilton xuất phát từ đỉnh 1, các chu trình Hamilton khác có thể có được bằng cách hoán vị vòng quanh. Lưu ý rằng cho tới nay, người ta vẫn chƣa tìm ra một phương pháp nào thực sự hiệu quả hơn phương pháp quay lui để tìm dù chỉ một chu trình Hamilton cũng như đường đi Hamilton trong trường hợp đồ thị tổng quát.

Bước 1. Khởi tạo: free[u] = true với u thuộc G Bước 2. Visit(i): Thăm đỉnh ở bước thứ i

2.1 u = đỉnh vừa thăm xong

Xét các đỉnh v kề u và free[v] = true 2.2 Ghi nhận đỉnh ở bước thứ i

2.3 Nếu (i =n) và (u kề với 1) đến Bước 3

2.4 Đánh dấu thăm v và Vitsit(i+1); Bước 3. Truy vết tìm đường đi (nếu có)

Chương 2 MÔ PHỎNG THUẬT TOÁN

1. Khái niệm và chức năng của mô phỏng

Mô phỏng thuật toán là quá trình mô tả cấu trúc dữ liệu, các thao tác của một chương trình bằng đồ hoạ [Stasko 1990]. Mô phỏng thuật toán được thiết kế nhằm giúp người dùng hiểu thuật toán, đánh giá thuật toán và gỡ lỗi khi cài đặt chương trình [xem 12]

Một chương trình lập trình trên máy tính sẽ chứa các cấu trúc dữ liệu của thuật toán mà nó sẽ thực hiện. Trong khi thực hiện chương trình đó, giá trị thực của cấu trúc dữ liệu thay đổi dựa trên từng bước hoạt động của thuật toán. Mô phỏng thuật toán sử dụng đồ hoạ để biểu diễn sự thay đổi trạng thái của cấu trúc dữ liệu cho từng bước hoạt động của nó một cách trực quan, khoa học. Trong suốt quá trình biểu diễn, người dùng có thể quan sát việc thực thi thuật toán theo từng bước để có thể biết chi tiết về thuật toán cũng như hiểu một cách tường tận về thuật toán đó.

Ví dụ: Một chương trình mô phỏng thuật toán phân tích LR phân tích trên 1 chuỗi kí tự. Chương trình chứa ba cấu trúc: bảng chuyển, một ngăn xếp (stack) và 1 xâu kí tự. Khi thực hiện, chương trình minh hoạ bằng việc đối sánh các trạng thái trong bảng chuyển và và thực hiện từng thao tác trên ngăn xếp để tách xâu kí tự. Các chỉ dẫn, kí hiệu trên xâu vào và các phần tử trong ngăn xếp ở mỗi trạng thái sẽ lần lượt thay đổi trong khi thực hiện thuật toán. Đây là một công việc khá khó khăn để nắm bắt nếu một học sinh chỉ sử dụng hình vẽ và các xâu kí tự. Mô phỏng thuật toán có thể chỉ ra tất cả các thông tin và sự thay đổi đồng thời ở từng bước bằng đồ hoạ. Ta có thể quan sát bằng hình vẽ dưới đây:

Hình. 1 Mô phỏng một bước hoạt động của thuật toán

Bên cạnh việc giúp người dùng hiểu rõ thuật toán, mô phỏng thuật toán cũng có thể dùng để gỡ lỗi chương trình một cách dễ dàng hơn. Để sử dụng mô phỏng thuật toán trong việc gỡ lỗi, người dùng ghi chú các trạng thái thực trên chương trình của họ để đưa ra các lệnh mô phỏng đầu ra và những bước làm cho thuật toán không cho ra kết quả như mong muốn.

2. Lịch sử của mô phỏng thuật toán

Năm 1981 Ronald Baecker tại đại học Toronto đã thiết kế đoạn video “Sorting Out Sorting”. Đây là mốc đánh dấu sự phát triển của lĩnh vực mô phỏng. Từ đó, các giảng viên đã sử dụng hệ thống mô phỏng để giảng dạy các môn thuật toán. Từ những năm 1980 đến đầu những năm 90 của thế kỉ XX, có 2 hệ thống được phát triển đã gây ảnh hưởng đến tất cả những hệ thống về sau đó là BALSA-I (Brown ALgorithm Simulator and Animator) [Brown 1984] và TANGO (Transition-based Animation GeneratiOn) [Stasko 1990].

BALSA – I là hệ thống mô phỏng thuật toán nổi tiếng được biết đến đầu tiên, được viết bởi giáo sư Marc Brown và Robert Sedgewick (Đại học Brown – Mỹ). BALSA – I là chương trình mô phỏng thuật toán tương tác. Nó đưa ra

nhiều khung nhìn về cấu trúc dữ liệu và có thể thực hiện nhiều thuật toán một cách đồng thời. Sự phát triển của nó là động lực thúc đẩy các nghiên cứu khác trong lĩnh vực này.

Một hệ thống khác, TANGO, do John Stasko (Đại học Brown – Mỹ) viết vào đầu những năm 90. Điểm nổi bật của hệ thống này là giới thiệu mô hình chuyển cho thiết kế mô phỏng và framework cho hệ thống mô phỏng thuật toán. Nó giới thiệu một khái niệm mới về framework (sau này có rất nhiều hệ thống sử dụng nó như một cấu trúc cơ bản)

Từ khi hai hệ thống này được công bố, các hệ thống con của nó cũng được phát triển. BALSA – II (1988). TANGO có rất nhiều phiên bản. Một trong số đó là XTANGO và POLKA. POLKA được thiết kế cho các thuật toán chạy song song. Nó là hệ thống mô phỏng thuật toán 2D hướng đối tượng và mở rộng trong 3D, POLKA 3D. POLKA 3D cung cấp cách nhìn 3D và các cơ sở như góc, hình cầu, hình khối…. Người dùng không yêu cầu phải biết kiến thức về đồ họa 3D để sử dụng POLKA 3D. SAMBA là hệ thống mô phỏng tương tác thông dịch, nó đọc các lệnh ASCII và thực hiện các thao tác mô phỏng tương ứng. Phiên bản của SAMBA cài bằng Java và có thể xem chi tiết tại trang web http://www.cc.gatech.due/gvu/softviz/parviz/samba.html.

Một số hệ thống mô phỏng nổi tiếng khác là Zeus, Leonardo, CATAI, Mocha. Zeus (Brown 1991) được phát triển tại đại học Brown cùng với hệ thống BALSA và BALSA – II. Nó được coi là phần mềm tương tác trực quan hóa. Nó hỗ trợ việc đồng bộ các khung nhìn và cho phép người dung chỉnh sửa các khung nhìn này và thay đổi biểu diễn của dữ liệu. Zeus được thực thi trong môi trường đa xử lý và đa tuyến, nó cũng cho phép thực thi các thuật toán song song. Leonardo là hệ thống mô phỏng các chương trình C. Nó tích hợp phát triển với mô phỏng của các chương trình C vào cùng một môi trường. CATAI mô phỏng các chương trình C++. Nó dựa trên kĩ thuật đối tượng phân phối và cho phép nhiều người dùng chia sẻ cùng một mô phỏng thông qua “lớp” ảo. Việc giao tiếp

và đồng bộ giữa các trạm và thuật toán được mô phỏng được đảm bảo bởi server có sử dụng kĩ thuật CORBA. Mocha là mô hình phân phối với kiến trúc client – server, nó phân đoạn một cách tối ưu các thành phần của hệ thống mô phỏng thông thường. Nó vượt qua các tồn tại của X - window và mô hình Java. Trong mô hình Mocha chỉ có mã giao diện được xuất tới máy của người dùng. Khi thuật toán chay trên server thì đoạn mã giao diện lại chạy trên máy người dùng.

Hiện tại, một số lượng lớn các chương trình mô phỏng đã được viết ra. Hầu hết trong số chúng rất phổ biến và sử dụng phức tạp. Các phần mềm này được thiết kế nhằm mục đích giáo dục hoặc các nghiên cứu. Một vài chương trình có cấu trúc phức tạp và yêu cầu hỗ trợ kĩ thuật cao. Rõ ràng, điều này ít thực tế, chúng ta mong muốn một phần mềm nhỏ gọn và linh hoạt, có cấu trúc đơn giản.

3. Hiệu quả của mô phỏng thuật toán trong giảng dạy

Trên thế giới, hệ thống mô phỏng đã được sử dụng rộng rãi trong việc dạy học ngành khoa học máy tính. Đã có một số lượng các sinh viên được đào tạo bằng các chương trình mô phỏng để thấy được hiệu quả của nó trong giảng dạy nhưng kết quả rất khác nhau. Có thể đưa ra một vài ví dụ như sau:

Giáo sư Brown (Đại học Brown) năm 1984 đã sử dụng phần mềm BALSA – I để dạy môn học lập trình và môn cấu trúc dữ liệu và giải thuật. Hệ thống này sử dụng để trực quan hóa chương trình dạy học. Ông viết trong bản báo cáo rằng việc sử dụng các đoạn mô phỏng kèm vào các bài giảng là một chứng minh cho việc “tốc độ hiểu bài của học sinh tăng lên” so với cách giảng dạy truyền thống.

Stasko (Đại học Brown) năm 1997 sử dụng SAMBA – chương trình mô phỏng của hệ thống XTANGO để dạy môn thuật toán. Ông yêu cầu các sinh viên sử dụng hệ thống để thêm vào các mô phỏng của chính bài tập của mình. Kết quả chỉ ra rằng sinh viên rất có hứng thú với các chương trình mô phỏng và

chính chương trình mô phỏng đã khuyến khích sự sáng tạo của học sinh. Rõ ràng, nhờ có chương trình mô phỏng mà các sinh viên hiểu các thuật toán nhanh

Một phần của tài liệu Mô phỏng một số thuật toán trên đồ thị (Trang 26)

Tải bản đầy đủ (PDF)

(84 trang)