Đồ thị cũng được dùng để giải nhiều bài toán thuộc những lĩnh vực rất khác nhau như: người ta có thể dùng đồ thị để biểu diễn sự cạnh tranh các loài trong môi trường sinh thái, dùng đồ t
Trang 1MỤC LỤC
Lời mở đầu………
Chương 1: Cơ sở lý thuyết ………
1.1 Một số khái niệm cơ bản ………
1.1.1 Khái niệm đồ thị ………
1.1.2 Các loại đồ thị ………
1.1.3 Biểu diễn đồ thị………
1.2 đồ thị hai phía
1.2.1 Định nghĩa
1.2.2 ứng dụng………
Chuơng 2: bài toán tìm bộ ghép cực đại trên đồ thị và các thuật toán ………
2.1 bài toán tìm bộ ghép cực đại trên đồ thị hai phía………
2.1.1 Bài toán ghép đôi không trọng và các khái niệm ………
2.1.2 Thuật toán đường mở ………
2.2 Bài toán tìm bộ ghép cực đại với tổng trọng số cực đại hoặc cực tiểu trên đồ thị hai phía …
2.2.1 Bài toán phân công ……….………
2.2.2 Thuật toán tìm cặp ghép với tổng trọng số trên các cạnh là lớn nhất hoặc nhỏ nhất ………
2.2.3 Thuật toán Hung-ga-ri ………
2.2.4 Phương pháp đối ngẫu Kuhn-Munkres …………
2.2.5 Nâng cấp
2.3 bài toán tìm bộ ghép cực đại trên đồ thị tổng quát
2.3.1 Các khái niệm………
2.3.2 Thuật toán Edmonds ………
2.3.3 Thuật toán Lawler ………
Chương 3: Giới thiệu ngôn ngữ delphi ………
6
8
8
8
8
8
10
10
10
11
11
11
12
12
12
13
14
16
18
20
20
21
24
27
Trang 23.1 Khái quát về ngôn ngữ Delphi ………
3.1.1 Delphi là gì?
3.1.2 Cấu trúc chương trình Delphi và Unit………
3.2 Form và các thành phần giao diện………
3.2.1 Xây dựng ứng dụng từ những thành phần công cụ VCL… 3.2.2 Form………
3.2.3 Các thành phần điều khiển của Windows………
3.3 Ngôn ngữ Object Pascal………
3.3.1 Các kiểu dữ liệu đơn giản………
3.3.2 Các kiểu dữ liệu có cấu trúc ………
3.3.3 Các câu lệnh cấu trúc………
3.4 Lập trình ứng dụng cơ sở dữ liệu………
3.4.1 Các cách kết nối với cơ sở dữ liệu………
3.4.2 Hạt nhân BDE………
3.4.3 Giới thiệu thành phần VCL Component dùng phát triển ứng dụng cơ sở dữ liệu………
3.4.4 Sử dụng và truy vấn bảng dữ liệu………
Chương 4: các bài toán ứng dụng ………
4.1 Bài toán điều hành Taxi………
4.1.1 Phát biểu bài toán ………
4.1.2 Phân tích bài toán
4.1.3 Chương trình
4 2 Bài toán xếp lớp học theo học chế tín chỉ………
4.2.1 Tìm hiểu về mô hình đào tạo theo học chế tín chỉ
4.2.2 Phát biểu bài toán
4.2.3 Phân tích bài toán………
4 2.4 Chương trình
27
27
29
30
30
31
32
36
36
38
39
41
41
43
43
44
46
46
46
46
49
62
62
62
63
66
Trang 3KÕt luËn Tµi liÖu tham kh¶o ………
75
76
Trang 4Lời mở đầu
Lý thuyết đồ thị là ngành khoa học được phát triển từ rất lâu nhưng lại có nhiều ứng dụng hiện đại Những ý tưởng cơ bản của lý thuyết đồ thị được nhà toán học Thuỵ Sỹ tên là Leonhard Euler đưa ra từ thế kỷ 18 Ông đã dùng lý thuyết đồ thị để giải quyết bài toán cầu Konigsberg nổi tiếng
Đồ thị cũng được dùng để giải nhiều bài toán thuộc những lĩnh vực rất khác nhau như: người ta có thể dùng đồ thị để biểu diễn sự cạnh tranh các loài trong môi trường sinh thái, dùng đồ thị ai có ảnh hưởng lên ai trong một tổ chức nào đó và cũng có thể dùng đồ thị để biểu diễn các kết cục của cuộc thi đấu thể thao, hay cũng có thể dùng đồ thị để giải các bài toán như bài toán tính số các tổ hợp khác nhau của các chuyến xe giữa hai thành phố trong một mạng giao thông, bài toán đi tham quan tất cả các phố của một thành phố sao cho mỗi phố đi qua
đúng một lần, hay bài toán tìm số các màu cần thiết để tô các vùng khác nhau của một bản đồ Đồ thị với các trọng số được gán cho các cạnh của nó có thể dùng
để giải các bài toán như bài toán tìm đường đi ngắn nhất giữa hai thành phố trong một mạng giao thông, bài toán phân công lao động sao cho tổng lợi nhuận thu
được là lớn nhất
Chính vì đồ thị có thể được sử dụng để giải quyết nhiều bài toán thuộc nhiều lĩnh vực khác nhau một cách dễ dàng và phổ biến như vậy nên đồ thị nắm giữ một vai trò hết sức quan trọng trong cuộc sống, đặc biệt là trong lĩnh vực Công nghệ Thông tin, dựa vào đồ thị và các thuật toán trên đồ thị người ta có thể xây dựng nên các phần mềm hữu ích phục vụ cho các lĩnh vực khác có được kết quả bài toán cần giải quyết một cách nhanh chóng và chính xác
Hiểu được tầm quan trọng của đồ thị trong ngành học mà mình theo đuổi
và say mê, được sự quan tâm, tận tình chỉ bảo, động viên của cô giáo Ths
Trương Hà Hải, em đã chọn nghiên cứu đề tài ứng dụng lý thuyết đồ thị giải
một số bài toán trong thực tế với mong muốn sau khi hoàn thành đề tài này em
sẽ khám phá được nhiều hơn các ứng dụng của đồ thị trong thực tế và một số thuật toán đặc biệt hiệu quả ứng dụng trên đồ thị để giải quyết các bài toán phục
vụ nhu cầu thực tế
Trang 5Em xin gửi lời cảm ơn chân thành tới cô giáo Ths Trương Hà Hải cùng
các thầy giáo, cô giáo khác đã tận tình chỉ bảo để em hoàn thành đề tài này Em cũng xin gửi lời cảm ơn tới các bạn sinh viên lớp K1B đã có những ý kiến đóng góp để chương trình của em được hoàn thiện hơn
Mặc dù đã hết sức cố gắng nhưng chắc chắn đề tài của em không tránh khỏi những thiếu sót Em rất mong nhận được sự góp ý của các thầy cô giáo và các bạn để đề tài của em được hoàn thiện hơn
Em xin chân thành cảm ơn !
Trang 6
Chương 1 Cơ sở lý thuyết 1.1 Một số khái niệm cơ bản
1.1.1 Khái niệm đồ thị
Đồ thị là một cấu trúc rời rạc gồm các đỉnh và các cạnh nối các đỉnh đó
Đồ thị được ký hiệu là G = (V, E), trong đó V là tập đỉnh và E là tập cạnh
Trang 7b Ma trận liền kề
Giả sử G = (V, E) là một đồ thị đơn trong đó |V| = n và các đỉnh được liệt
kê tuỳ ý v1, , vn Ma trận liền kề A của G ứng với danh sách các đỉnh này là ma
trận không – một cấp n*n có phần tử hàng i, cột j bằng 1 nếu vi và vj liền kề nhau,
và bằng 0 nếu chúng không được nối với nhau
c Ma trận liên thuộc
Giả sử G = (V, E) là một đồ thị vô hướng, v1, v2, , vn là tập các đỉnh còn
e1, e2, , em là tập các cạnh của nó Khi đó ma trận liên thuộc theo thứ tự trên của
V và E là ma trận M = [mij] trong đó:
mij = 1 nếu cạnh ej nối với đỉnh vi
mij = 0 nếu cạnh ej không nối với đỉnh vi
Trang 8Một đơn đồ thị vô hướng G = (V, E) được gọi là đồ thị hai phía nếu tập các
đỉnh V có thể phân thành hai tập con không rỗng, rời nhau X và Y sao cho mỗi
cạnh của đồ thị nối một đỉnh của X với một đỉnh của Y
Khi đó, người ta còn ký hiệu G là ( X U Y, E) và gọi một tập (giả sử là tập
X) là tập các đỉnh trái và tập còn lại là tập các đỉnh phải của đồ thị hai phía G
Các đỉnh thuộc X còn gọi là các X_đỉnh, các đỉnh thuộc Y gọi là các Y_đỉnh
Trang 9Chương 2 Bài toán tìm bộ ghép cực đại trên đồ thị
Và các thuật toán 2.1 bài toán tìm bộ ghép cực đại trên đồ thị hai phía
2.1.1 Bài toán ghép đôi không trọng và các khái niệm
Cho một đồ thị hai phía G = ( X U Y, E) ở đây X là tập các đỉnh trái và Y
là tập các đỉnh phải của G X = { x[1], x[2], , x[m] }, Y = { y[1], y[2], , y[n] }
Một bộ ghép của G là tập hợp các cạnh của G đôi một không có đỉnh chung Bài toán ghép đôi là tìm một bộ ghép lớn nhất (nghĩa là có số cạnh lớn nhất) của G
đường mở
Một cách dễ hiểu, có thể quan niệm như sau:
Ě Một đường pha là một đường đi đơn trong G bắt đầu bằng một X_đỉnh là đỉnh
nhạt, đi theo một cạnh nhạt sang Y rồi đến một cạnh đậm về X, rồi lại đến một cạnh nhạt sang Y cứ xen kẽ nhau như vậy
Ě Một đường mở là một đường pha Bắt đầu từ một X_đỉnh là đỉnh nhạt kết thúc bằng một Y_đỉnh là đỉnh nhạt
Ví dụ: Với đồ thị hai phía trong hình vẽ dưới đây và bộ ghép M = {(x[1], y[1]), (x[2], y[2])}
x[3] và y[3] là những đỉnh nhạt, các đỉnh khác là đỉnh đậm
Đường (x[3], y[2], x[2], y[1]) là đường pha
Đường (x[3], y[2], x[2], y[1], x[1], y[3]) là đường mở
Trang 10X Y
2.1.2 Thuật toán đường mở
Thuật toán đường mở để tìm một bộ ghép lớn nhất phát biểu như sau:
Bước 1:
Bắt đầu từ một bộ ghép bất kỳ M (thông thường bộ ghép được khởi gán
bằng bộ ghép rỗng hay được tìm bằng thuật toán tham lam)
Bước 2:
Tìm một đường mở
Bước 3:
• Nếu bước 2 tìm được đường mở thì mở rộng bộ ghép M: Trên đường mở,
loại bỏ những cạnh đã ghép (cạnh đậm) khỏi M và thêm vào M những cạnh chưa
ghép (cạnh nhạt) Sau đó lặp lại bước 2
• Nếu bước 2 không tìm được đường mở thì thuật toán kết thúc
Người ta đã chứng minh được chi phí thời gian thực hiện giải thuật này
trong trường hợp xấu nhất sẽ là O(n3) đối với đồ thị dày và O(n(n+m)logn) đối
với đồ thị thưa
2.2 bài toán tìm bộ ghép cực đại với tổng trọng số cực
đại hoặc cực tiểu trên đồ thị hai phía
2.2.1 Bài toán phân công
Đây là dạng bài toán phát biểu như sau: Một xí nghiệp có N công nhân và
dây chuyền sản xuất có M máy Nếu công nhân i đứng ở máy j thì sẽ tạo lợi
nhuận cho nhà máy là C(i,j) Hãy bố trí công nhân sao cho không quá một công
Trang 11nhân ở một máy và mỗi công nhân làm không quá một máy mà tổng lợi nhuận là lớn nhất
+ Tạo nhãn ban đầu chấp nhận được Fx và Fy theo quy tắc:
Fx[i] = Max( C[i,j], Vj: 1 <= j <= M) Fy[j] = 0, Vj: 1 <= j <= M
(FX, FY gọi là chấp nhận được nếu thoả mãn bất đẳng thức Fx[i] + Fy[j] >= C[i,j])
Nếu Fx[i] + Fy[j] = C[i,j] thì ta coi cạnh (i,j) là cạnh đậm (là đã ghép máy
i cho công nhân j), các cạnh còn lại là cạnh nhạt Hai đầu cạnh đậm là đỉnh đậm, các đỉnh còn lại là đỉnh nhạt
Vậy bằng cách tạo nhãn ban đầu như trên chúng ta có cặp ghép M ban đầu (ghép được một số máy với công nhân tạo lợi nhuận trên các máy đó)
Tìm dây chuyền (bằng logic hoặc đệ qui)
Nếu không có dây chuyền thì sửa nhãn
Ngược lại tăng cặp ghép trên dây chuyền này
Cho đến khi tìm được dây chuyền;
End;
Trang 12Trong thuật toán trên phải thực hiện các thao tác: tìm dây chuyền, sửa nhãn và tăng cặp ghép
Sửa nhãn: Phải thực hện khi dây chuyền không kết thúc được bằng đỉnh nhạt bên
Y mà kết thúc bằng đỉnh đậm bên X (ta gọi là dây chuyền “dở dang”) Trên các cung nối một đỉnh bên X đã nạp vào dây chuyền tới các đỉnh j thuộc Y chưa thuộc dây chuyền, chọn giá trị bé nhất trong các giá trị: Fx[i] + Fy[j] – C[i,j] Giá trị này được chọn làm lượng sửa nhãn (kí hiệu là m) Sửa nhãn theo cách như sau: Nhãn các đỉnh của X thuộc dây chuyền sẽ giảm đi một lượng là m, nhãn các đỉnh của Y thuộc dây chuyền sẽ tăng thêm một lượng là m, để đảm bảo Fx[i] + Fy[j] – C[i,j] >= 0 Sau khi các đỉnh thuộc dây chuyền “dở dang” đã được sửa nhãn thì nó
có khả năng mới kết hợp với các đỉnh j bên Y tạo nên một dây chuyền hoàn chỉnh (vì sẽ xuất hiện những cặp (i,j) mới mà Fx[i] + Fy[j] = C[i,j])
Tăng cặp ghép: Đổi màu các cung, bắt đầu từ cung nhạt cuối cùng của dây
chuyền đổi ngược dần về cung nhạt đầu tiên của dây chuyền
Tìm dây chuyền: Có thể tìm kiếm theo chiều sâu hoặc tìm kiếm theo chiều rộng
Yêu cầu dây chuyền xuất phát từ một đỉnh nhạt của X, kết thúc bằng một đỉnh nhạt của Y, đồng thời các cung nhạt và đậm liên tiếp xen kẽ nhau (do cung đầu
và cung cuối đều nhạt, nên số cung nhạt lớn hơn số cung đậm là 1)
* Nếu là bài toán tìm tổng nhỏ nhất thì đổi dấu C[i,j] và cuối cùng đổi dấu tổng hoặc khởi trị C[i,j] = vô cùng, nhãn ban đầu V i Є X: Fx[i] = Min { C[i,j] V j Є Y}, Fy[j] = 0, V j Є Y Lượng sửa nhãn m = Min { C[i,j] – Fx[i] – Fy[j] }
2.2.3 Thuật toán Hung-ga-ri
Bài toán: Có N người (số hiệu i = 1, 2, , N) và N công việc (số hiệu j = 1,
2, , N) Để giao việc j cho người i thực hiện cần chi phí là Cij không âm Vấn đề
là cần phân cho người nào làm việc gì (mỗi người chỉ làm một việc, mỗi việc chỉ
do một người làm) sao cho chi phí tổng cộng là nhỏ nhất
Một trong các phương pháp giải bài toán này là tìm cặp ghép có tổng trọng
số trên các cung đạt giá trị nhỏ nhất trên đồ thị hai phía Nhưng cũng có thể giải bằng phương pháp Hung-ga-ri được xây dựng trên cơ sở các định lý sau đây:
Trang 13Định lí 1: Nếu ma trận chi phí C(N,N) có N số 0 mà không có số 0 nào cùng
hàng hoặc cùng cột thì có phương án tối ưu là: Nếu số 0 ở (i,j) thì phân công người i làm việc j
Định lí 2: Nếu thay ma trận C(N,N) bởi ma trận C’(N,N) bằng cách trừ mỗi phần
tử trong một hàng (hoặc cột) cho phần tử nhỏ nhất của hàng đó (hoặc cột đó) thì phương án tối ưu không thay đổi khi tìm phương án này trên C’(N,N)
Thuật toán
Bước 1:
Thực hiện trên ma trận chi phí C: Trừ mỗi hàng cho phần tử nhỏ nhất trên hàng đó Trừ mỗi cột cho phần tử nhỏ nhất trên cột đó Kết quả thu được ma trận C’ có tính chất: trên mỗi hàng, trên mỗi cột có ít nhất một số 0 (ta kí hiệu những
Lần lượt từ hàng 1 đến hàng N, tìm hàng đầu tiên không chứa ô Đ (giả sử
đó là hàng i0) Tìm ô T trên hàng i0, chẳng hạn ở cột j0 Xuất phát từ ô T(i0,j0) này
ta xây dựng một dây chuyền các ô kế tiếp nhau theo cột rồi theo hàng xen kẽ các
ô T và Đ Giả sử đã có dây chuyền T(i0,j0), Đ( i1,j0), T(i1,j1), , ta tìm tiếp theo cách thức sau:
+ (3-A): Nếu đã tìm đến ô T(ik,jk) ta tìm ô T trong cột jk không cùng dòng với ô Đ nào đã có trong dây chuyền đang xét Nếu tìm thấy T ở dòng ik+1 thì gán lại kí hiệu T của ô này thành Đ được ô Đ(ik+1,jk) và thêm nó vào dây chuyền rồi chuyển tới bước (3-B), trái lại ta được một dây chuyền bắt đầu là ô T(i0,j0) và kết thúc tại ô T(ik,jk), ta đổi kí hiệu màu các ô trên dây chuyền (T thành Đ và ngược lại – nghĩa là tăng luồng trên dây chuyền) Nếu đủ N ô Đ thì dừng, trái lại trở về
đầu bước 3 tìm hàng tiếp theo không chứa ô Đ
Trang 14+ (3-B): Nếu đã đến ô Đ(ik+1,jk), ta tìm trên hàng ik+1 ô T không cùng cột với bất kì ô nào đã có mặt trong dây chuyền đang xét Nếu tìm được trên cột jk+1thì thêm ô T(ik+1,jk+1) vào dây chuyền rồi chuyển tới (3-A) Trong trường hợp ngược lại thì dây chuyền xuất phát từ T(i0,j0) và kết thúc tại Đ(ik+1,jk) và không thể tăng luồng trên dây chuyền này được (vì số ô T bằng số ô Đ) Ta sẽ loại khỏi dây chuyền các ô T(ik,jk) và Đ(ik+1,jk) đồng thời đánh dấu cột jk là cột “trở ngại”, lui về
ô Đ(ik,jk-1) tiếp tục mở rộng dây chuyền nếu trên hàng ik còn có ô T khác với ô T(ik,jk) không nằm trên cột “trở ngại” và các cột đã có mặt trong dây chuyền đang xét, nghĩa là ta lặp lại bước (3-B) với hàng ik
Nếu không tiếp tục mở rộng được dây chuyền theo cách trên nữa thì tìm ô
T trên dòng i0 ở cột j0 không là cột “trở ngại” Nếu ô T như thế tồn tại thì bắt đầu lặp lại (3-A), ngược lại chuyển sang bước 4
“trở ngại” với m
Quay về bước 2 với ma trận mới thu được là C’ đóng vai trò là C
2.2.4 Phương pháp đối ngẫu Kuhn-Munkres
Phương pháp đối ngẫu Kuhn-Munkres đi tìm hai dãy số Fx[1 k] và Fy[1 k] thoả mãn:
• C[i,j] – Fx[i] – Fy[j] >= 0
• Tập các cạnh (x[i], y[j]) thoả mãn C[i,j] – Fx[i] – Fy[j] = 0 chứa trọn một bộ ghép đầy đủ k cạnh, đây chính là bộ ghép cần tìm
Rõ ràng nếu tìm được hai dãy số thoả mãn trên thì ta chỉ việc thực hiện hai thao tác:
+ Với mỗi đỉnh x[i], trừ tất cả trọng số của những cạnh liên thuộc với x[i] đi một lượng Fx[i]
Trang 15+ Với mỗi đỉnh y[j], trừ tất cả trọng số của những cạnh liên thuộc với y[j] đi một lượng Fy[j]
(Hai thao tác này tương đương với việc trừ tất cả trọng số của các cạnh (x[i], y[j])
đi một lượng Fx[i] + Fy[j] tức là C[i,j] := C[i,j] – Fx[i] – Fy[j])
Thì dễ thấy đồ thị mới tạo thành sẽ gồm có các cạnh trọng số không âm và những cạnh có trọng số bằng 0 của đồ thị chứa trọn một bộ ghép đầy đủ
Vậy phương pháp đối ngẫu Kuhn-Munkres đưa việc biến đổi đồ thị G (biến đổi ma trận C) về việc biến đổi hai dãy số Fx và Fy Việc trừ một lượng delta vào trọng số tất cả các cạnh liên thuộc với x[i] tương đương với việc tăng Fx[i] lên một lượng delta Việc cộng một lượng delta vào trọng số tất cả các cạnh liên thuộc với y[j] tương đương với việc giảm Fy[j] đi một lượng delta Khi cần biết trọng số cạnh (x[i], y[j]) là bao nhiêu sau các bước biến đổi, thay vì viết C[i,j], ta viết C[i,j] – Fx[i] – Fy[j]
Sơ đồ cài đặt phương pháp đối ngẫu Kuhn-Munkres có thể viết như sau:
Bước 1: Khởi tạo:
• M := O;
• Việc khởi tạo các Fx, Fy có thể có nhiều cách miễn sao C[i,j] – Fx[i] – Fy[j] >=
0, đơn giản nhất có thể đặt tất cả các Fx[.], Fy[.] bằng 0
Bước 2: Với mọi đỉnh x* Є X, ta tìm cách ghép x* như sau:
Bắt đầu từ đỉnh x*, thử tìm đường mở bắt đầu ở x* bằng thuật toán tìm kiếm trên đồ thị (BFS hoặc DFS) Có hai khả năng xảy ra:
+ Hoặc tìm được đường mở thì dọc theo đường mở, ta loại bỏ những cạnh đã ghép khỏi M và thêm vào M những cạnh chưa ghép
+ Hoặc không tìm được đường mở thì xác định được:
VisitedX = { Tập những X_đỉnh có thể đến được từ x* bằng một đường pha}
VisitedY = { Tập những Y_đỉnh có thể đến được từ x* bằng một đường pha}
Đặt delta = min { C[i,j] – Fx[i] – Fy[j] | Vx[i] Є VisitedX, Vy[j] Є VisitedY}
Với Vx[i] Є VisitedX: Đặt Fx[i] := Fx[i] + delta;
Trang 16Với Vy[j] Є VisitedY: Đặt Fy[j] := Fy[j] - delta;
Lặp lại thủ tục tìm đường mở xuất phát tại x* cho tới khi tìm ra đường mở
ma trận chi phí C là không thể thực hiện được
2.2.5 Nâng cấp
Dựa vào sơ đồ cài đặt thuật toán Kuhn-Munkres ở trên, ta có thể đánh giá
độ phức tạp tính toán lý thuyết của cách cài đặt này:
Thuật toán tìm kiếm theo chiều rộng được sử dụng để tìm đường mở có độ phức tạp O(k2), mỗi lần xoay trọng số cạnh mất một chi phí thời gian cỡ O(k2) Vậy, mỗi lần tăng cặp cần tối đa k lần dò đường và k lần xoay trọng số cạnh, mất một chi phí thời gian cỡ O(k3) Thuật toán cần k lần tăng cặp nên độ phức tạp tính toán trên lý thuyết của phương pháp này cỡ O(k4) Có thể cải tiến mô hình cài đặt
để được một thuật toán với độ phức tạp O(k3) dựa trên những nhận xét sau:
Nhận xét 1:
Quá trình tìm kiếm theo chiều rộng bắt đầu từ một đỉnh x* chưa ghép cho
ta một cây pha gốc x* Nếu tìm được đường mở thì dừng lại và tăng cặp ngay, nếu không thì xoay trọng số cạnh và bắt đầu tìm kiếm lại để được một cây pha lớn hơn cây pha cũ
Nhận xét 2:
Việc xác định trọng số nhỏ nhất của cạnh nối một X_đỉnh trong cây pha với một Y_đỉnh ngoài cây pha có thể kết hợp ngay trong bước dựng cây pha mà không làm tăng cấp phức tạp tính toán Để thực hiện được điều này, ta sử dụng kỹ thuật sau:
Trang 17Với mọi y[j] Є Y, gọi d[j] là khoảng cách từ y[j] đến cây pha gốc x* Ban
đầu d[j] được khởi tạo bằng trọng số cạnh (x*,y[j]) (cây pha ban đầu chỉ có đúng một đỉnh x*)
Trong bước tìm đường bằng BFS, mỗi lần rút một đỉnh x[i] ra khỏi Queue,
ta xét những đỉnh y[j] Є Y chưa thăm và đặt lại d[j]mới := min(d[j]cũ, trọng số cạnh (x[i], y[j])) sau đó mới kiểm tra xem (x[i],y[j]) có phải là cạnh có trọng số bằng 0 hay không để tiếp tục các thao tác như trước Nếu quá trình BFS không tìm ra
đường mở thì giá trị xoay delta chính là giá trị nhỏ nhất trong các d[j] dương Ta bớt được một đoạn chương trình tìm giá trị xoay có độ phức tạp O(k2) Công việc tại mỗi bước xoay chỉ là tìm giá trị nhỏ nhất trong các d[j] dương và thực hiện phép cộng, trừ trên hai dãy đối ngẫu Fx và Fy, nó có độ phức tạp tính toán O(k) Tối đa có k lần xoay để tìm đường mở nên tổng chi phí thời gian thực hiện các lần xoay cho tới khi tìm ra đường mở cỡ O(k2) Lưu ý rằng đồ thị đang xét là đồ thị hai phía đầy đủ nên khi xoay các trọng số cạnh bằng giá trị xoay delta, tất cả các cạnh nối từ X_đỉnh trong cây pha tới Y_đỉnh ngoài cây pha đều bị giảm trọng số
đi delta, chính vì vậy sau mỗi bước xoay, ta phải trừ tất cả các d[j]> 0 đi delta để giữ được tính hợp lý của các d[j]
Nhận xét 4:
Thủ tục tăng cặp dựa trên đường mở có độ phức tạp O(k)
Trang 18Từ 3 nhận xét trên, phương pháp đối ngẫu Kuhn-Munkres có thể cài đặt bằng một chương trình có độ phức tạp tính toán O(k3) bởi nó có k lần tăng cặp và chi phí cho mỗi lần là O(k2)
2.3 Bài toán tìm bộ ghép cực đại trên đồ thị tổng quát 2.3.1 Các khái niệm
Xét đồ thị G = (V, E), một bộ ghép trên đồ thị G là một tập các cạnh đôi một không có đỉnh chung
Bài toán tìm bộ ghép cực đại trên đồ thị tổng quát phát biểu như sau: Cho một đồ thị G, phải tìm một bộ ghép cực đại trên G (bộ ghép có nhiều cạnh nhất)
Với một bộ ghép M của đồ thị ta gọi:
+ Những cạnh thuộc M được gọi là cạnh đã ghép hay cạnh đậm
+ Những cạnh không thuộc M được gọi là cạnh chưa ghép hay cạnh nhạt
+ Những đỉnh đầu mút của các cạnh đậm được gọi là đỉnh đã ghép, những đỉnh còn lại gọi là đỉnh chưa ghép
+ Một đường đi cơ bản (đường đi không có đỉnh lặp lại) được gọi là đường pha
nếu nó bắt đầu bằng một cạnh nhạt và tiếp theo là các cạnh đậm, nhạt nằm nối tiếp xen kẽ nhau
+ Một chu trình cơ bản (chu trình không có đỉnh trong lặp lại) được gọi là một
Blossom nếu nó đi qua ít nhất 3 đỉnh, bắt đầu và kết thúc bằng cạnh nhạt và dọc
trên chu trình, các cạnh đậm, nhạt nằm nối tiếp xen kẽ nhau Đỉnh xuất phát của
chu trình (cũng là đỉnh kết thúc) được gọi là đỉnh cơ sở của Blossom
+ Đường mở là một đường pha bắt đầu ở một đỉnh chưa ghép và kết thúc ở một
đỉnh chưa ghép
Ví dụ:
Với đồ thị G và bộ ghép M trong hình vẽ dưới đây
Đường (8, 1, 2, 5, 6, 4) là một đường pha
Chu trình (2, 3, 4, 6, 5, 2) là một Blossom
Đường (8, 1, 2, 3, 4, 6, 5, 7) là một đường mở
Trang 19Đường (8, 1, 2, 3, 4, 6, 5, 2, 1, 9) tuy có các cạnh đậm/nhạt xen kẽ nhưng
không phải đường pha (và tất nhiên không phải đường mở) vì đây không phải là
đường đi cơ bản
Ta dễ dàng suy ra được các tính chất sau:
+ Đường mở cũng như Blossom đều là đường đi độ dài lẻ với số cạnh nhạt nhiều
hơn số cạnh đậm đúng 1 cạnh
+ Trong mỗi Blossom, những đỉnh không phải là đỉnh cơ sở đều là đỉnh đã ghép
và đỉnh ghép với đỉnh đó cũng phải thuộc Blossom
+ Vì Blossom là một chu trình nên trong mỗi Blossom, những đỉnh không phải
đỉnh cơ sở đều tồn tại hai đường pha từ đỉnh cơ sở đi đến nó, một đường kết thúc
bằng cạnh đậm và một đường kết thúc bằng cạnh nhạt, hai đường pha này được
hình thành bằng cách đi dọc theo chu trình theo hai hướng ngược nhau Như ví dụ
trên, đỉnh 4 có hai đường pha từ đỉnh cở sở 2 đi tới: (2, 3, 4) là đường pha kết thúc
bằng cạnh đậm và (2, 5, 6, 4) là đường pha kết thúc bằng cạnh nhạt
2.3.2 Thuật toán Edmonds (1965)
Cơ sở của thuật toán là định lý (C.Berge): Một bộ ghép M của đồ thị G là
cực đại khi và chỉ khi không tồn tại đường mở đối với M
Thuật toán Edmonds:
Trang 20<Dọc trên đường mở: Loại bỏ những cạnh đậm khỏi M; Thêm vào
M những cạnh nhạt>;
<Trả về M là bộ ghép cực đại trên G>;
Điều khó nhất trong thuật toán Edmonds là phải xây dựng thuật toán tìm
đường mở xuất phát từ một đỉnh chưa ghép Thuật toán đó được xây dựng bằng cách kết hợp một thuật toán tìm kiếm trên đồ thị với phép chập Blossom
Xét những đường pha xuất phát từ một đỉnh x chưa ghép Những đỉnh có thể đến được từ x bằng một đường pha kết thúc là cạnh nhạt được gán nhãn
“nhạt” (gọi tắt là đỉnh nhạt), những đỉnh có thể đến được từ x bằng một đường pha kết thúc là cạnh đậm được gán nhãn “đậm” (gọi tắt là đỉnh đậm)
Với một Blossom, ta định nghĩa phép chập (shrink) là phép thay thế các
đỉnh trong Blossom bằng một đỉnh duy nhất Những cạnh nối giữa một đỉnh thuộc Blossom tới một đỉnh v nào đó không thuộc Blossom được thay thế bằng cạnh nối giữa đỉnh chập này với v và giữ nguyên tính đậm/nhạt Có thể kiểm chứng được nhận xét: sau mỗi phép chập, các cạnh đậm vẫn được đảm bảo là bộ ghép trên đồ thị mới
= đỉnh cơ sở của Blossom = đỉnh chập từ Blossom
Trang 21Thuật toán tìm đường mở xuất phát từ đỉnh x có thể phát biểu như sau: Trước hết đỉnh xuất phát x được gán nhãn đậm
Tiếp theo là thuật toán tìm kiếm trên đồ thị bắt đầu từ x, theo nguyên tắc: từ đỉnh
đậm chỉ được phép đi tiếp theo cạnh nhạt và từ đỉnh nhạt chỉ được đi tiếp theo cạnh đậm Mỗi khi thăm tới một đỉnh, ta gán nhãn đậm/nhạt cho đỉnh đó và tiếp tục thao tác tìm kiếm trên đồ thị như bình thường Cũng trong quá trình tìm kiếm, mỗi khi phát hiện thấy một cạnh nhạt nối hai đỉnh đậm, ta dừng lại ngay vì nếu gán nhãn tiếp sẽ gặp tình trạng một đỉnh có cả hai nhãn đậm/nhạt, trong trường hợp này, Blossom được phát hiện và bị chập thành một đỉnh, thuật toán được bắt
đầu lại với đồ thị mới cho tới khi trả lời được câu hỏi: “có tồn tại đường mở xuất phát từ x hay không?”
Nếu đường mở không đi qua đỉnh chập nào thì ta chỉ việc tăng cặp dọc theo đường mở Nếu đường mở có đi qua một đỉnh chập thì ta lại nở đỉnh chập đó
ra thành Blossom để thay đỉnh chập này trên đường mở bằng một đoạn đường xuyên qua Blossom
Hình vẽ: nở Blossom để dò đường xuyên qua Blossom
Lưu ý rằng không phải Blossom nào cũng bị chập, chỉ những Blossom ảnh hưởng tới quá trình tìm đường mở mới phải chập để đảm bảo rằng đường mở tìm
được là đường đi cơ bản
Trang 222.3.3 Thuật toán Lawler (1973)
Trong thuật toán Edmonds, sau khi chập mỗi Blossom thành một đỉnh thì
đỉnh đó hoàn toàn lại có thể nằm trên một Blossom mới và bị chập tiếp Thuật toán Lawler chỉ quan tâm tới đỉnh chập cuối cùng, đại diện cho Blossom ngoài nhất, đỉnh chập cuối cùng này được định danh bằng đỉnh cơ sở của Blossom ngoài nhất
Cũng chính vì thao tác chập/nở nói trên mà ta cần mở rộng khái niệm
Blossom, có thể coi một Blossom là một tập đỉnh nở ra từ một đỉnh chập chứ
không đơn thuần là một chu trình pha cơ bản nữa
Xét một Blossom B có đỉnh cơ sở là đỉnh r Với V v Є B, v = r, ta lưu lại hai đường pha từ r tới v, một đường kết thúc bằng cạnh đậm và một đường kết thúc bằng cạnh nhạt, như vậy có hai loại vết gán cho mỗi đỉnh v (hai vết này được cập nhật trong quá trình tìm đường):
+ S[v] là đỉnh liền trước v trên đường pha kết thúc bằng cạnh đậm, nếu không tồn tại đường pha loại này thì S[v] = 0
+ T[v] là đỉnh liền trước v trên đường pha kết thúc bằng cạnh nhạt, nếu không tồn tại đường pha loại này thì T[v] = 0
Bên cạnh hai nhãn S và T, mỗi đỉnh v còn có thêm:
+ Nhãn b[v] là đỉnh cơ sở của Blossom chứa v Hai đỉnh u và v thuộc cùng một Blossom b[u] = b[v]
+ Nhãn match[v] là đỉnh ghép với đỉnh v Nếu v chưa ghép thì match[v] = 0
Khi đó, thuật toán tìm đường mở bắt đầu từ đỉnh x chưa ghép có thể phát biểu như sau:
Trang 23Với mỗi đỉnh đậm u lấy ra từ Queue, xét những cạnh nhạt (u, v):
- Nếu v là đỉnh nhạt hoặc b[v] = b[u] ==> bỏ qua
- Nếu v là đỉnh đậm và b[v] = b[u] ta phát hiện được Blossom mới chứa
u và v, khi đó:
Phát hiện đỉnh cơ sở: Truy vết đường đi ngược từ hai đỉnh đậm u
và v theo hai đường pha về nút gốc, chọn lấy đỉnh a là đỉnh đậm chung gặp
đầu tiên trong quá trình truy vết ngược Khi đó Blossom mới phát hiện sẽ
có đỉnh cơ sở là a
Gán lại vết: Gọi (a = i[1], i[2], , i[p] = u) và (a = j[1], j[2], , j[q] =
v) lần lượt là hai đường pha dẫn từ a tới u và v Khi đó (a = i[1], i[2], , i[p]
= u, j[q] = v, j[q-1], , j[1] = a) là một chu trình pha đi từ a tới u và v rồi quay trở về a Bằng cách đi dọc theo chu trình này theo hai hướng ngược nhau, ta có thể gán lại tất cả các nhãn S và T của những đỉnh trên chu trình Lưu ý rằng không được gán lại các nhãn S và T cho những đỉnh k mà b[k] = a, và với những đỉnh k có b[k] = a thì bắt buộc phải gán lại nhãn S
và T theo chu trình này bất kể S[k] và T[k] trước đó đã có hay chưa
Chập Blossom: Xét những đỉnh v mà b[v] Є {b[i[1]], b[i[2]], ,
b[i[p]], b[j[1]], b[j[2]], , b[j[q]]}, gán lại b[v] = a Nếu v là đỉnh đậm (có nhãn S[v] = 0) mà chưa được duyệt tới (chưa bao giờ được đẩy vào Queue) thì đẩy v vào Queue chờ duyệt tiếp tại những bước sau
Bước 3:
Nếu bước 2 tìm được đường mở thì trả về đường mở, nếu bước 2 không tìm thấy đường mở và thoát ra do hàng đợi rỗng thì kết luận không tìm thấy đường
mở
Trang 24Tư tưởng chính của phương pháp Lawler là dùng các nhãn b[v] thay cho thao tác chập trực tiếp Blossom, dùng các nhãn S và T để truy vết tìm đường mở, tránh thao tác nở Blossom Phương pháp này dựa trên một nhận xét: Mỗi khi tìm
ra đường mở, nếu đường mở đó xuyên qua một Blossom ngoài nhất thì chắc chắn
nó phải đi vào Blossom này từ nút cơ sở và thoát ra ngoài bằng một cạnh nhạt
Trang 25Chương 3
Giới thiệu ngôn ngữ delphi
Delphi có tiền thân là ngôn ngữ Pascal Có thể nói Delphi là một trong “tứ đại thiên vương” của thế giới ngôn ngữ lập trình hiện nay đó là C/C++, Delphi, Visual Basic và Java Trong số đó chỉ có Delphi là có khả năng sánh ngang và vượt trội ngôn ngữ lập trình C/C++ Trong chương này chúng ta sẽ tìm hiểu một cách khái quát về ngôn ngữ lập trình Delphi
Những vấn đề sẽ được đề cập trong chương này:
Khái quát về ngôn ngữ Delphi
Form và các thành phần giao diện
3.1 Khái quát về ngôn ngữ Delphi
3.1.1 Delphi là gì?
Delphi có hạt nhân là trình biên dịch Pascal rất nổi tiếng và quen thuộc Delphi là một bước đột phá tiếp theo của trình biên dịch Pascal, được hãng Borland phát triển liên tục kể từ khi Anders Hejlsberg viết ra trình biên dịch Turbo Pascal đầu tiên cách đây 17 năm Kế thừa mọi đặc tính của Pascal, trình biên dịch của Delphi là sự tổng hợp tinh hoa về kinh nghiệm của Pascal qua hơn một thập kỷ, cộng với kỹ thuật biên dịch được tối ưu hoá dựa trên kiến trúc 32-bit của bộ xử lý Ngôn ngữ Delphi mang tính ổn định, sự uyển chuyển, độ tin cậy cao
Delphi là môi trường ứng dụng “tức thời” RAD (Rapid Application Divelopment) bao gồm ốac công cụ phát triển hệ thống và cơ sở dữ liệu dành cho Microsoft Windows 95/98 và Windows NT Delphi kết hợp sự tiện dụng của môi trường phát triển trực quan, tốc độ và sức mạnh của trình biên dịch 32-bit, khả năng quản lý cơ sở dữ liệu chặt chẽ với hạt nhân (thư viện BDE) uyển chuyển có thể truy suất nhiều loại cơ sở dữ liệu khác nhau Mặc dù bản thân các đặc tính trên không phải là đặc trưng riêng của Delphi nhưng nhìn chung Delphi đã tích hợp các công nghệ riêng rẽ để tạo nên một môi trường phát triển toàn diện, cần thiết và rất hữu ích cho ngành công nghiệp phần mềm hiện nay
Trang 26a Môi trường phát triển trực quan
Delphi cho phép xây dựng ứng dụng bằng cách chọn các thành phần công cụ
có sẵn từ bảng công cụ đặt chúng vào Form, lắp ghép và tạo nên một ứng dụng hoàn chỉnh Nét đẹp thực sự của môi trường phát triển trực quan là Delphi sẽ tự
động sinh mã khi kết nối các công cụ trên Form Mọi thành phần trong Delphi
được xây dựng trên nền tảng hướng đối tượng vững chắc
b Trình biên dịch 32-bit được tối ưu hoá
Điểm đặc biệt tách rời Delphi với các môi trường phát triển trực quan khác là khả năng biên dịch các chương trình thực thi exe độc lập 32-bit chạy rất nhanh
và hiệu quả Trình biên dịch Delphi cũng đưa ra những chỉ dẫn bổ ích giúp bạn quyết định sự tối ưu, loại bỏ những biến dư thừa không sử dụng đến, thông báo lỗi chính xác
Một ưu điểm nữa, nếu sử dụng chung các sản phẩm của Inprise như Borland C++ Builder chẳng hạn, trình biên dịch Delphi sẽ chia sẻ chung cơ chế biên dịch
và sinh mã, giúp ta dễ dàng trao đổi mã lệnh và thư viện của hai ngôn ngữ Object Pascal và C/C++ với nhau thông qua các tập tin OBJ
c Truy cập và xử lý cơ sở dữ liệu mềm dẻo
Delphi là công cụ lý tưởng để thiết kế các ứng dụng cơ sở dữ liệu ứng dụng Delphi có khả năng giao tiếp và truy xuất nhiều loại cơ sở dữ liệu khác nhau, có thể kết nối với các hệ cơ sở dữ liệu thông qua ODBC, các trình điều khiển dành riêng mà mỗi hệ cơ sở dữ liệu cung cấp, thông qua ADO hay OLE DB của Microsoft
d Ngôn ngữ và thư viện
Delphi sử dụng ngôn ngữ hướng đối tượng Object Pascal Object Pascal cung cấp một tập hợp ngôn ngữ mở rộng hiệu quả và uyển chuyển bao gồm:
- Xử lý lỗi ngoại lệ
- Kiểm tra kiểu dữ liệu thực thi
- Hỗ trợ từ khoá mới Interface
- Kiểu chuỗi không giới hạn
Trang 27- Kiểu dữ liệu tiền tệ
- Kiểu dữ liệu Variant
Thư viện các thành phần công cụ trực quan VCL (Visual Component Library)
là bộ khung làm việc hướng đối tượng chủ yếu của Delphi, nó bao bọc, che dấu
sự phức tạp của các hàm Windows API dựng để viết các ứng dụng Windows
3.1.2 Cấu trúc chương trình Delphi và Unit
Chương trình được xây dựng từ các đơn thể mã nguồn nhỏ gọi là Unit Mỗi Unit chứa trong một tập tin và được biên dịch độc lập Các Unit dùng để liên kết với nhau tạo thành ứng dụng
a Cấu trúc của chương trình chính và cú pháp
Một chương trình thường bao gồm 3 phần:
- Phần tiêu đề dành cho chương trình
- Mệnh đề Uses
- Các khai báo khối cũng như câu lệnh điều khiển
Tiêu đề của chương trình thường là tên của chương trình chính Mệnh đề
Uses liệt kê và khai báo các Unit mà chương trình sẽ sử dụng Khối được dựng để
chứa các khai báo và câu lệnh điều khiển tạo nên ứng dụng Môi trường phát triển tích hợp IDE của Delphi thường tạo ra tập tin dự án với tên DPR để lưu nội dung chương trình chính
Tiêu đề của chương trình bắt đầu bằng từ khoá Program, tiếp theo là một
định danh hợp lệ và kết thúc bằng dấu chấm phẩy (;), định danh của chương trình phải trùng vói tên của tập tin dự án
Mệnh đề Uses dùng để kết hợp một hay nhiều đơn thể Unit của chương trình
lại với nhau
Khối lệnh của chương trình sẽ bao gồm một hoặc nhiều câu lệnh có cấu trúc
Khối lệnh bắt đầu bằng từ khoá Begin và kết thúc bằng từ khoá End Một khối
lệnh thông thường kết thúc bằng dấu chấm phẩy Tuy nhiên, chương trình chính cũng được coi là một khối lệnh lớn bao trùm các khối lệnh con khác nhưng kết thúc bằng dấu chấm (.)
Trang 28b Cấu trúc Unit và cú pháp
Unit là một đơn thể độc lập của chương trình Unit bao gồm các khai báo kiểu, hằng, biến và các hàm, thủ tục Mã nguồn của mỗi Unit được đặt trong tập tin PAS
Một Unit bắt đầu bằng tiêu đề, tiếp đến là các phân đoạn Interface, implementation, initialization và finalization
Tiêu đề là phần tên của Unit, bắt đầu bằng từ khoá Unit, tiếp theo là tên định
danh, kết thúc bằng dấu chấm phẩy Tên của Unit phải trùng với tên tập tin chứa Unit và là duy nhất trong toàn bộ dự án
Phân đoạn interface bắt đầu bằng từ khoá interface và kết thúc khi gặp phân
đoạn implementation Phân đoạn này dùng để khai báo các hằng, biến, kiểu, thủ tục và hàm giành cho chương trình hay các Unit khác truy suất đến
Phân đoạn initialization bắt đầu bằng từ khoá initialization và kết thúc khi gặp
từ khoá finalization Mục đích của phân đoạn này là dùng để khởi tạo thư viện Phân đoạn này là tuỳ chọn
3.2 Form và các thành phần giao diện
3.2.1 Xây dựng ứng dụng từ những thành phần công cụ VCL
a Tình huống
Tình huống là những phương thức được gọi để phản hồi lại yêu cầu từ phía người dùng hay hệ thống Mỗi đối tượng VCL có một danh sách các tình huống như:
OnMounseMove Cho biết khi người sử dụng di chuyển chuột lên đối tượng
OnMounseDown Cho biết khi chuột được nhấn lên đối tượng
OnKeyPress Cho đối tượng biết hiện có một phím được gõ
…
Ngoài những tình huống giống nhau, mỗi đối tượng khác nhau cũng có những tình huống cần xử lý khác nhau tuỳ theo thuộc tính của mỗi đối tượng Ví dụ đối tượng Form sẽ có những tình huống sau đây mà đối tượng Button không có:
Trang 29OnClose Cho biết tình huống khi người dùng muốn đóng cửa sổ Form OnShow Cho biết tình huống khi Form được hiển thị
b Phương thức xử lý tình huống của đối tượng
Tất cả các phương thức xử lý tình huống trong Delphi đều có ít nhất một đối
số Sender được khai báo như sau: Sender : TObject Ngoài ra, tuỳ theo mỗi tình huống Delphi sẽ đặt thêm một số thông tin cần thiết vào phương thức xử lý để ta biết cách ứng xử
3.2.2 Form
a Form và tình huống OnCreate
Tình huống OnCreate của Form là tình huống thường sử dụng nhất Tình huống này được gọi khi lần đầu tiên Form được khởi tạo Ta có thể xây dựng phương thức xử lý tình huống OnCreate để gán các giá trị ban đầu cho biến, đặt giá trị thuộc tính của những đối tượng trên Form về trạng thái mặc định
b Form và tình huống OnClose
Tình huống OnClose phát sinh khi ta nhấn vào nút bên góc phải của cửa sổ hoặc khi ta gọi phương thức Close() của Form Ta có thể dùng tình huống này để nhận biết quá trình thao tác trên Form của người dùng chấm dứt
c Form và tình huống OnCloseQuery
Tình huống OnCloseQuery của Form được gọi trước tình huống OnClose Windows sẽ hỏi ta có cho phép đóng cửa sổ Form hay không? Nếu đồng ý ta đặt biến CanClose=true (CanClose là tham biến mà Delphi đặt trong tình huống này), Windows sẽ gọi tiếp đến tình huống OnClose Nếu CanClose được đặt giá trị False thì Windows sẽ trở lại với cửa sổ Form mà không làm gì cả
Trang 303.2.3 Các thành phần điều khiển của Windows
a Nút nhấn (Button)
Nút nhấn được Delphi đặt trong lớp đối tượng TButton, tình huống thường
được sử dụng nhất của nút nhấn là OnClick Khi người dùng Click chuột vào nút nhấn, tình huống này sẽ được gọi để phản ứng lại bằng một tác vụ nào đó
b Nhãn (Lable)
Nhãn là thành phần đơn giản nhất trong thư viện VCL Đối tượng nhãn chỉ dùng để trình bày một chuỗi văn bản thông thường, nhằm mục đích mô tả thêm thông tin cho các đối tượng khác Nhãn cũng có thể được dùng để đưa kết quả ra màn hình dưới dạng chuỗi
Trong Delphi, nhãn được đặt trong lớp đối tượng có tên là TLable Thuộc tính thường được sử dụng nhất của nhãn là:
- Caption thể hiện nội dung nhãn
- Font định font chữ, kiểu chữ cho nhãn
- Transparent cho phép màu nền của nhãn trùng với màu nền của Form
d Ô chọn (RadioButton)
Không như ô đánh dấu CheckBox được chọn riêng lẻ, các ô chọn (Radio) thường đi chung với nhau và mỗi lần chỉ được thể hiện trạng thái chọn cho một nút chọn
Ô chọn Radio được Delphi đặt trong lớp đối tượng có tên là TRadioButton
Trang 31Nếu Checked=True, ô chọn đang ở trạng thái được chọn Nếu Checked=False, người dùng dã bỏ chọn ở ô hiện hành để chuyển sang chọn ô khác Khi ô chọn Radio được kích chuột để yêu cầu thay đổi trạng thái, tình huống OnClick sẽ phát sinh
e Ô văn bản (EditBox)
Ô văn bản là thành phần nhập dữ liệu đơn giản nhất dùng giao diện đồ hoạ, nó cho phép nhập vào một chuỗi văn bản, chỉnh sửa, di chuyển con nháy để soạn thảo bằng các phím mũi tên Ô văn bản chỉ cho phép nhập một dòng văn bản duy nhất
Ô văn bản được Delphi đặt trong lớp đối tượng có tên là TEdit Thuộc tính thường được sử dụng nhất của ô văn bản là thuộc tính text Nó trả về một chuỗi, cho biết nội dung dữ liệu mà người dùng nhập vào Mỗi khi dữ liệu hay nội dung của thuộc tính text thay đổi, tình huống OnChange sẽ được gọi
f Vùng văn bản (Memo)
Vùng văn bản Memo cho phép nhập vào cùng lúc nhiều dòng dữ liệu Có thể dùng Memo để tạo chương trình soạn thảo văn bản hay dùng để xuất ra một chuỗi văn bản
Vùng văn bản được Delphi đặt trong lớp đối tượng có tên là TMemo Thuộc tính thường được sử dụng nhất của Memo là thuộc tính Lines Thuộc tính Lines
có kiểu String, được dùng để quản lý danh sách các dòng dã liệu mà Memo đang
có
Để đơn giản, Memo cung cấp thuộc tính Text là một chuỗi bao gồm tất cả các dòng trong Memo cộng lại, mỗi dòng cách nhau bởi ký tự 10 và 13 Tình huống thường được sử dụng nhất của Memo là tình huống OnChange, tình huống này
được gọi khi người dùng thay đổi hoặc thêm dữ liệu vào Memo
g Danh sách (ListBox)
Danh sách chứa một danh sách mục chọn, mỗi mục chọn thường được thể hiện là một chuỗi ListBox cho phép chọn mỗi lần một hay nhiều mục cùng lúc Danh sách được Delphi đặt trong lớp đối tượng có tên là TlistBox Thuộc tính thường được sử dụng nhất của danh sách là:
Trang 32- Items Dùng để thêm bớt các mục chọn vào danh sách
- ItemIndex Cho biết hoặc thiết lập mục chọn hiện hành
- Selected Kiểm tra xem một mục chọn trong danh sách có được chọn hay không
- MultiSelect Cho phép danh sách được chọn đồng thời nhiều mục
- Sorted Tự động sắp xếp các mục chọn theo thứ tự ABC
Các tình huống mà ListBox thường hay phát sinh là:
- OnClick Khi người dùng kích chuột vào mục chọn
- OnDblClick Khi người dùng kích đôi chuột vào một mục
- OnKeyPress Khi người dùng nhấn phím để chọn một mục
- OnKeyDown/ OnKeyUp Xử lý khi người dùng nhấn và nhả phím
- Text Nội dung của mục chọn hiện hành
- Items Danh sách các mục chọn
- ItemIndex Lấy về hoặc thiết lập mục chọn hiện hành cho ComboBox
- Sorted Cho phép sắp xếp các mục chọn trong danh sách theo thứ tự ABC
- Style Chọn các kiểu ComboBox mở rộng
Tình huống mà Litsbox thường phát sinh là tình huống OnChange, được gọi khi nội dung của mục chọn hiện hành bị thay đổi
i Thanh trượt (Scrollbar)
Thanh trượt gồm mũi tên ở hai đầu và một thanh trượt đặt giữa để xác định vị trí Khoảng cách giữa hai đầu thanh trượt và vị trí định vị được biểu hiện bằng các thuộc tính min, max, position Khi nhấn phím mũi tên, PageUp, PageDown hoặc kích chuột vào hai đầu thanh trượt sẽ làm cho vị trí nút trượt di chuyển dần đến hai biên
Thanh trượt được Delphi đặt trong lớp đối tượng có tên là TScrollbar
Trang 33Thuộc tính thường được sử dụng nhất của thanh trượt Scrollbar là:
- Min Thể hiện giới hạn dưới của thanh trượt
- Max Thể hiện giới hạn trên của thanh trượt
- Position Cho biết hoặc đặt lại vị trí hiện hành của nút trượt
- Kind Cho phép đặt thanh trượt đứng hoặc ngang
- SmallChange Bước nhảy cho thanh trượt khi phím mũi tên được nhấn
- PageSize Bước nhảy cho thanh trượt khi PageUp/PageDown được nhấn Tình huống mà thanh trượt Scrollbar thường phát sinh là OnChange, nó được gọi khi vị trí của nút trượt thay đổi
j Nhóm các ô chọn (RadioGroup)
RadioGroup là đối tượng giúp cho việc gom các nút chọn có cùng một chức năng lại với nhau thành một nhóm duy nhất Thành phần này được Delphi đặt trong lớp đối tượng có tên là TRadioGroup
Thuộc tính thường được sử dụng nhất của thành phần này là:
- Items Các ô chọn RadioButton gắn trong nhóm
- ItemIndex Nút Radio đang được chọn
- Caption Tiêu đề của nhóm ô chọn
Tương tự TRadioGroup, TGroupBox cũng dùng thuộc tính Caption để thể hiện tiêu đề cho nhóm TGroupBox cũng cung cấp các tình huống như OnClick, OnMouseUp, OnMouseDown … nhưng hầu như rất ít khi dùng đến chúng, nó thường chỉ được dùng với mục đích gom các đối tượng lại với nhau theo chủ đề
Nó đóng vai trò như một vật chứa
Trang 34l Bảng chứa (Panel)
Bảng chứa dùng để gom, nhóm các đối tượng con lại với nhau Thành phần bảng chứa Panel được Delphi đặt trong lớp đối tượng có tên là TPanel
Các thuộc tính thường được sử dụng nhất của Panel là:
- Align Canh lề cho bảng chứa
- BevelInner Đường viền trong
- BevelOuter Đường viền ngoài
- BevelWidth Độ dày của đường viền
- Visibled Cho phép bảng chứa hiển thị hoặc không
m Trình đơn chính (MainMenu)
Trình đơn chính MainMenu là một dãy các lựa chọn nằm phía trên cửa sổ
Đối tượng này được Delphi đặt tên là TMainMenu
n Trình đơn tắt (PopupMenu)
Trình đơn tắt thường hiển thị khi người dùng kích phím phải chuột lên Form hoặc lên một đối tượng nào đó Đối tượng này được Delphi đặt tên là TPopupMenu
3.3 Ngôn ngữ Object Pascal
3.3.1 Các kiểu dữ liệu đơn giản
Delphi phát triển thêm các kiểu dữ liệu sau:
a Kiểu nguyên
Type Range of Values Byte of
Memory Required
Trang 35d KiÓu Ký tù
Character Type Size in Byte What It Can Hold
ANSIChar 1 One Ansi
erminated
ShortString 255 ANSIChar No AnsiString up to ~3 GB ANSIChar Yes String either 255 or up to ~3GB ANSIChar Yes WideString up to ~1.5 GB WideChar Yes
Trang 363.3.2 Các kiểu dữ liệu có cấu trúc
a Mảng (Array)
Mảng dựng để biểu diễn một tập hợp các phần tử có cùng kiểu dữ liệu Mỗi phần tử của mảng được truy suất theo một chỉ số duy nhất
+ Cú pháp xây dựng mảng tĩnh:
Array [ indexType 1,…,indexType n ] Of baseType;
Trong đó: indexType là một tập các số có kiểu thứ tự dựng làm chỉ số mảng baseType là kiểu sẽ được áp dụng cho các phần tử mảng
+ Cú pháp xây dựng mảng động:
Array of array of …of baseType;
Trong khai báo mảng động, số phần tử của mảng chưa được chỉ định Muốn
sử dụng mảng cần phải chỉ định rõ cần chính xác bao nhiêu phần tử để chương trình cấp phát bằng lệnh sau:
Setlength(Name, Value_1, …, Value_n);
Trong đó, Name là tên mảng, Value là số lượng phần tử cần được cấp phát theo mỗi chiều của mảng
Để giải phóng vùng nhớ cấp phát cho marng động ta gọi lệnh gán như sau: Name:=nil;
Chỉ số của các phần tử trong mảng được đánh số bắt đầu từ 0
b Kiểu Record
Kiểu Record là một tập hợp gồm nhiều phần tử có các kiểu khác nhau hợp lại Mỗi phần tử trong cấu trúc bản ghi được gọi là trường
Cú pháp khai báo kiểu bản ghi:
Type recordTypeName = Record
c Kiểu miền con (Subrange Type)
Kiểu miền con của một kiểu rời rạc là một miền trị của kiểu rời rạc đó
Trang 37Cú pháp khai báo kiểu miền con:
d Kiểu tập hợp (Set type)
Kiểu tập hợp là một tập các giá trị có cùng kiểu thứ tự Mỗi biến kiểu tập hợp có thể chứa nhiều phần tử của tập hợp Tập hợp chỉ chứa được tối đa 256 phần tử, bao gồm cả tập hợp rỗng
Cú pháp khai báo: Set of baseType;
Trong đó baseType là kiểu thứ tự bất kỳ có số phần tử không quá 256
3.3.3 Các câu lệnh cấu trúc
a Câu lệnh IF
Dạng 1:
If expression then statement;
Trong đó, expression là một biểu thức logic Nếu expression trả về giá trị
đúng thì lệnh statement được thực hiện, nếu không chương trình sẽ chuyển điều
khiển xuống dòng lệnh kế tiếp
Dạng 2:
If expression then statement 1 Else statement 2;
Trong đó, expression là một biểu thức logic Nếu expression trả về giá trị
đúng thì lệnh statement 1 sẽ được thực hiện, ngược lại, nếu expression trả về giá trị sai thì lệnh statement 2 sẽ được thực hiện