3.1.1.Phát biểu bài toán
Một bệnh viện A chuyên cung cấp các dịch vụ cấp cứu tại nhà. Bệnh viện này có n bác sĩ có trình độ chuyên môn cao, trong đó mỗi bác sĩ có khả năng thực hiện một vài dịch vụ cấp cứu nào đó. Chúng ta biết rằng với mỗi bác sĩ khi thực hiện một dịch vụ cấp cứu thì bác sĩ đó sẽ đƣợc bệnh viện trả thêm một mức thu nhập ngoài lƣơng. Với m danh sách các dịch vụ cấp cứu có sẵn, bệnh viện A cần phải sắp lịch trực cho n bác sĩ để phục vụ cho m dịch vụ cấp cứu sao cho chi phí phải trả là nhỏ nhất.
Bảng 3.1. Danh sách các bác sĩ và các dịch vụ mà bác sĩ đó có thể thực hiện trong trƣờng hợp tổng quát
Dịch vụ Cấp cứu Bác sĩ A Bác sĩ B Bác sĩ C Bác sĩ D Bác sĩ E Bác sĩ F Dịch vụ 1 Dịch vụ 2 Dịch vụ 3 Dịch vụ 4 Dịch vụ 5 Dịch vụ 6
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
Ta có thể biểu diễn bài toán trên thành ma trận liên thuộc m n
A trong đó
aij=1 nếu bác sĩ j có thể thực hiện dịch vụ cấp cứu i và aij=0 trong trƣờng hợp ngƣợc lại. Ta gọi cj với j=1, …, n là mức thu nhập ngoài lƣơng mà bệnh viện A phải trả cho bác sĩ j sau khi hoàn thành dịch vụ cấp cứu.
Ta đặt biến xj=1 nếu bác sĩ j là đang thực hiện dịch vụ cấp cứu và bằng 0 trong trƣờng hợp ngƣợc lại.
Ta cần tìm
1 min n j j
j c x thỏa điều kiện ràng buộc: ij 1 1, 1,..., , {0,1}, 1,..., n j j j a x i m x j n
3.1.2.Cài đặt thuật toán tham lam
Thuật toán tham lam có đƣợc một giải pháp tối ƣu cho một bài toán bằng cách thực hiện một chuỗi các lựa chọn. Đối với mỗi quyết định chỉ ra trong thuật toán, sự lựa chọn này dƣờng nhƣ tốt nhất tại thời điểm đƣợc chọn. Chiến lƣợc phỏng đoán này không luôn tạo ra giải pháp tối ƣu. Tuy nhiên việc tìm ra một giải pháp theo phƣơng pháp tham lam luôn cho kết quả nhanh với độ phức tạp tính toán thấp.
Trong phần này chúng tôi trình bày phƣơng pháp sử dụng thuật toán tham lam để giải quyết một dạng ứng dụng của bài toán phủ tập hợp đó là bài toán phân lịch trực cho bác sĩ trong bệnh viện A đã đƣợc nêu ở phần phát biểu bài toán. Giải thuật tham lam đƣợc phát biểu nhƣ sau:
Gọi Sj,j 1,...,n là tập các dịch vụ mà bác sĩ j có thể thực hiện, biến xj dùng để xác định bác sĩ j có đang thực hiện dịch vụ cấp cứu hay không.
Input: Cho tập m danh sách các dịch vụ cấp cứu U e e1, 2,...,em , Tập danh sách các dịch vụ mà n bác sĩ có thể thực hiện: S S1, 2,...,Snvà danh sách các chi phí phải trả cj, j 1,...,n.
Output: Vectơ x 0,1 k
Thuật toán:
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
2.While C U do
(a) Tìm tập các dịch vụ có chi phí phải trả là nhỏ nhất, ta gọi là S. (b) Đặt xs=1và với mỗi e S C ta gán p ic er e( ) c S( ) / S C
(c) C C S
3.Return x.
Giá trị price tại mỗi phần tử đƣợc phủ bằng với chi phí hiệu quả của tập phủ. Nhƣ vậy, trong thuật toán này mỗi bƣớc chúng ta sẽ chọn tham lam danh sách phục vụ nào mà có chi phí phải trả cho bác sĩ là thấp nhất làm đầu vào cho phƣơng án khả thi của thuật toán. Dựa vào kết quả trả về của thuật toán chúng ta sẽ biết đƣợc danh sách các bác sĩ đƣợc phân công trực và chi phí thấp nhất mà bệnh viện phải trả là bao nhiêu. Trong quá trình lặp chúng ta có thể dùng biến đánh dấu để đƣa ra danh sách những dịch vụ mà mỗi bác sĩ đƣợc chọn phải thực hiện.
3.1.3.Cài đặt thuật toán Nhánh cận
Khi ứng dụng thuật toán tham lam để giải quyết bài toán phân lịch trực bác sĩ chúng ta luôn tìm đƣợc giải pháp nhanh hơn các thuật toán khác nhƣng kết quả thì chƣa hẳn đó là giải pháp tối ƣu nhất. Vì vậy, trong phần này chúng tôi sẽ cài đặt thuật toán nhánh cận để giải quyết bài toán phân lịch trực bác sĩ, từ đó chúng ta sẽ đối sánh hai giải pháp trong những trƣờng hợp đầu vào của bài toán.
Ta gọi tập tất cả các phƣơng án của bài toán (tập S0) sẽ đƣợc chia nhỏ dần thành nhiều tập con rời nhau, mỗi tập con bao gồm những phƣơng án có sử dụng bác sĩ và không sử dụng bác sĩ i cho các dịch vụ cấp cứu hay không sẽ đƣợc ấn định dần trong quá trình giải bài toán. Mỗi tập con này đƣợc gắn với một số nguyên không âm, biểu thị cận dƣới của chi phí đối với mọi phƣơng án thuộc tập này. Tập con Sk
có cận dƣới nhỏ nhất sẽ có nhiều khả năng chứa phƣơng án tối ƣu, vì thế tập Sk sẽ đƣợc chọn để chi nhỏ tiếp (phân nhánh). Khi phân nhánh Sk Sk1 Sk2 sao cho một tập Sk2 bắt buộc có sử dụng bác sĩ xr nào đó, một tập Sk1 không đƣợc sử dụng bác sĩ
xr. Khi một tập con nào đó chỉ gồm một phƣơng án duy nhất thì ta sẽ tính đƣợc chi
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
biết, giá trị hàm mục tiêu của bài toán ứng với phƣơng án tốt nhất hiện biết gọi là
giá trị kỷ lục. Tập con nào có cận dƣới lớn hơn hay bằng giá trị kỷ lục sẽ bị loại
(không cần xem xét tiếp nữa), vì chắc chắn tập này không chứa phƣơng án nào tốt hơn phƣơng án tốt nhất hiện biết. Quá trình giải kết thúc khi không còn tập con nào cần xem xét tiếp. Khi đó, phƣơng án tốt nhất hiện biết sẽ là phƣơng án tối ƣu của bài toán. Tính hữu hạn của thuật toán đƣợc suy ra từ tính hữu hạn của tập S0.
Giả sử ta cần phân nhánh tập Sp S0. Cách hay dùng là phân chia tập này thành hai tập con rời nhau '
p S , '' p S với ' | , 0 p p r S x x S x , '' | , 1 p p r S x x S x
Trong đó, xr là biến chƣa cố định ở giá trị 0 hay 1 trong tập Sp. Giá trị r dùng để phân nhánh đƣợc chọn sao cho tập ''
p
S có nhiều khả năng chứa phƣơng án tối ƣu, còn tập '
p
S thì không. Nói cách khác, r đƣợc chọn sao cho hiệu số các cận dƣới của ''
p
S và '
p
S là lớn nhất có thể đƣợc.
Để giải quyết vấn đề này, ta chỉ cần xét tập các phƣơng án ban đầu S0 đƣợc chia thành hai tập rời nhau S1 và S2 với
1 | 1, r 0
S x x S x ,
2 | 2, r 1
S x x S x
Trong tập S2 cấu trúc bài toán không thay đổi, trừ ra cột r bị loại. Các cột còn
lại chỉ chứa các phần tử không âm, vì thế ứng với S2 một cận dƣới đối với giá trị hàm mục tiêu tăng thêm là (S2) cr.
Trong tập S1 do cố định xr 0 nên từ các điều kiện của bài toán suy ra phải có một xj 1(j r). Vì thế ( )1 min j
j r
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
thêm. Ta sẽ chọn biến xr sao cho hiệu giữa các cận dƣới này là lớn nhất, nghĩa là đạt
1 2
ax ( ) ( )
r
m S S
Nếu cr 0 thì ( )S1 0 (do trên cột r của C đều chứa số 0), còn (S2) cr 0, từ đó ( )S1 (S2) 0. Vì thế để có hiệu giữa các cận dƣới này là lớn nhất ta chỉ cần xét r với cr 0. Trong trƣờng hợp này (S2) 0 và ( )S1 0.
Điều này có nghĩa là ta có thể chọn biến xr để phân nhánh theo qui tắc
0 ( ) ax ( ) min p p c p r m p c
Lập luận trên đây cũng đủng cả khi các tập phƣơng án Si về sau đƣợc chia thành các tập S Sr, r 1 nhƣng thay cho mức tăng của các cận dƣới ( )S1 và (S2) ta xét mức tăng của các cận dƣới (Sr) và (Sr 1) tƣơng ứng.
Nếu tập đƣợc xét không phải là S0 mà là
1 2 0 1 2 | , , ,..., k p i i i k S x x S x x x ,
Thì qui tắc chọn biến để phân nhánh về cơ bản vẫn nhƣ trƣớc, tuy nhiên cần tiến hành một số thay đổi. Trƣớc hết, đó là việc thực hiện các lựa chọn bắt buộc. Chẳng hạn, nếu xj 0, j 1,...,v 1,v 1,...,n thì tất nhiên phải có xv 1. Cũng làm vậy đối với các cột.
Một số loại lựa chọn bắt buộc khác: Khi đã cố định xr 1 thì phải có xr 0
bằng cách đặt cr . Hơn nữa, nếu Sp chứa r gồm ít nhất 2 và nhiều nhất n-2 bác sĩ
1 ... 1 ... 1 1
u u v v
i i r i i i
x x x x x (1 v n 3)
ở mỗi bƣớc lặp, trƣớc khi tính các cận dƣới cho các tập mới, cần thực hiện những lựa chọn bắt buộc nêu trên. Có nhƣ vậy mới thu đƣợc những cận dƣới chính
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
xác và tránh đƣợc những phân nhánh vô ích.
Input: Cho tập m danh sách các dịch vụ cấp cứu U e e1, 2,...,em , Tập danh sách các dịch vụ mà n bác sĩ có thể thực hiện: S S1, 2,...,Snvà danh sách các chi phí phải trả cj,j 1,...,n.
Output: Vectơ x 0,1k
Thuật toán:
Bƣớc 1: Xuất phát từ x1, xây dựng một phƣơng án mẫu f*
Bƣớc i: - Đã xây dựng đƣợc nghiệm thành phần x x1, 2,...,xi 1 - Đánh giá cận: tìm g xác định trên xi: 1 1 1 ( ,..., i i) min{ ( ) : ( ,..., n) g c x c x f a a a a thuộc X, cixi=ai, i=1,…,n}
- Giả sử x* là lời giải tốt nhất tại thời điểm đó, f* là giá trị tốt nhất * *
( )
f f x
o Nếu *
f g có thể bỏ đi không cần phát triển lời giải bộ phận 1, 2,..., i
x x x
o Ngƣợc lại: tiến hành bƣớc i+1 để xác định xi+1
Thuật toán nhánh cận giải quyết bài toán phân lịch bác sĩ sẽ cho kết quả tối ƣu nhất đƣợc xác định bởi mảng x. Trong đó với mỗi bác sĩ đƣợc chọn thì x[i] sẽ nhận giá trị là 1 ngƣợc lại là 0. Trong trƣờng hợp chúng ta có n bác sĩ thì trong trƣờng
hợp xấu nhất độ phức tạp tính toán là 2n. Tuy nhiên trong quá trình tìm kiếm các bƣớc chọn kế tiếp x[i+1], x[i+2],.. chúng ta đã loại bỏ sớm những phƣơng án chắc chắn không phải là tối ƣu.
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
3.2. XÂY DỰNG CHƢƠNG TRÌNH PHÂN LỊCH TRỰC BÁC SĨ 3.2.1. Công cụ lựa chọn 3.2.1. Công cụ lựa chọn
Để hoàn thành ứng dụng này thì chƣơng trình demo đƣợc viết trên ngôn ngữ lập trình C# trong bộ Visual Studio 2010 kết hợp với cơ sở dữ liệu đƣợc lƣu trữ trên hệ quản trị cơ sở dữ liệu SQL Server 2005, chƣơng trình có chạy trên hệ điều hành Windows XP, Vista, Windows 7.
3.2.2. Modul chƣơng trình
3.2.2.1. Modul tìm chi phí tối ưu nhất bằng phương pháp tham lam
Với đầu vào là danh sách chi phí đƣợc cho bởi mảng c, mỗi bƣớc của thủ tục sẽ tìm mức chi phí nhỏ nhất chƣa đƣợc xét giả sử là ci, mức chi phí này sẽ tƣơng ứng với việc chúng ta chọn bác sĩ i là ngƣời tiếp theo đƣợc xem xét để xếp lịch.
public int FindMinimumCost(int[] c, int n) {
int vt = -1;
int min = MAXCOST; for (int i = 0; i < n; i++) { if (consider[i]) continue; if (min > c[i]) { min = c[i]; vt = i; } } return vt; }
3.2.2.2. Modul phân lịch trực bằng phương pháp tham lam
Đầu vào của thủ tục là danh sách các bác sĩ và những dịch vụ mà bác sĩ đó có thể phục vụ đƣợc cho bởi mảng ds, danh sách chi phí C. Mỗi khi tìm đƣợc bác sĩ kế tiếp có chi phí phục vụ nhỏ nhất chúng ta sẽ cập nhật lại mảng price. Kết quả của
thủ tục sẽ trả về mảng kiểu số nguyên x trong đó mỗi phần tử thứ i của mảng x sẽ
nhận giá trị 1 tƣơng ứng với bác sĩ i đƣợc xếp lịch phục vụ và bằng 0 trong trƣờng hợp ngƣợc lại.
private int[] ScheduleDoctor(ArrayList[] ds, int[] C,float [] price) {
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/ int[] x = new int[ds.Count()];
for (int i = 0; i < x.Length; i++) x[i] = 0; ArrayList dsC = new ArrayList();
int m = price.Length, n = ds.Count(); while (dsC.Count < m)
{
int sdoctor = FindMinimumCost(C, n, dsC); x[sdoctor] = 1;
ArrayList temp = SubElement(ds[sdoctor], dsC); float cost = (float)C[sdoctor] / temp.Count; consider[sdoctor] = true;
foreach (int z in temp) { price[z] = cost; dsC.Add(z); } } return x; }
3.2.2.3. Modul tìm chi phí tối ưu nhất bằng phương pháp nhánh cận
Đầu vào của thủ tục bao gồm mảng x trong đó mỗi phần tử thứ i của mảng sẽ nhận giá trị 1 hoặc 0 tƣơng ứng với việc bác sĩ i đƣợc xem xét phục vụ hoặc không. Phần tử đƣợc lựa chọn để xem xét selected trong mảng x, mức chi phí tƣơng ứng
với nhánh hiện tại countselected, mảng chi phí phải trả cho các bác sĩ C, danh sách các dịch vụ U, danh sách các bác sĩ và các dịch vụ mà bác sĩ đó có thể phục vụ listSets. Sau khi thủ tục kết thúc, giải pháp tối ƣu sẽ đƣợc lƣu trữ trong mảng result.
Dựa vào mảng result ta có thể biết đƣợc bác sĩ i có đƣợc sử dụng (result[i]=1) hoặc không đƣợc sử dụng (result[i]=0). Từ đó chúng ta có thể biết đƣợc tổng chi phí mà bệnh viện sẽ chi trả ứng với giải pháp tối ƣu là bao nhiêu.
public static void branchAndBound(ref int[] x, int selected,
int countselected,int[] C, ArrayList U, List<ArrayList> listSets) {
// Nếu bác sĩ đang xét được chọn thì tính lại chi phí if (x[selected] == 1) countselected += C[selected]; ArrayList univers = new ArrayList();
univers.AddRange(U);
ArrayList selectedSet = new ArrayList();
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/ if(x[selected] == 1)
foreach (Object o in selectedSet) univers.Remove(o); if (univers.Count == 0)
{
// Nếu giải pháp tìm được tốt hơn giải pháp hiện thời thì cập nhật lại if (countselected < bestSoFar)
{
bestSoFar = countselected; for (int i = 0; i < x.Length; i++) {
result[i] = x[i]; }
} }
if (selected == x.Length - 1) return; x[selected + 1] = 1;
// Phân nhánh kế tiếp
if (countselected + C[selected+1] < bestSoFar) {
branchAndBound(ref x, selected + 1, countselected, C, univers, listSets); }
x[selected + 1] = 0;
branchAndBound(ref x, selected + 1, countselected, C, univers, listSets); }
3.2.3. Giao diện chƣơng trình
Chƣơng trình mô phỏng có thể chạy tốt với dữ liệu đầu vào có hơn 1000 bác sĩ và thực hiện đƣợc các chức năng sau:
- Tạo danh sách bác sĩ:
o Tạo mảng chứa danh sách bác sĩ
o Mở tệp chứa danh sách thông tin về bác sĩ và chi phí thực hiện của mỗi bác sĩ
o Mở tệp chứa thông tin về những dịch vụ mà bác sĩ có thể thực hiện
o Lƣu thông tin của bác sĩ - Tạo danh sách dịch vụ
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
o Lƣu trữ thông tin dịch vụ
- Phân lịch trực của bác sĩ theo phƣơng pháp tham lam - Phân lịch trực của bác sĩ theo phƣơng pháp nhánh cận Một số hình ảnh giao diện của chƣơng trình:
- Khi bắt đầu chạy cửa sổ của chƣơng trình sẽ hiện ra nhƣ sau:
Hình 3.1. Giao diện chính của chƣơng trình
- Ngƣời dùng thực hiện nạp thông tin của bác sĩ và thông tin của các dịch vụ mà bệnh viện cung cấp từ các tệp nguồn:
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
Hình 3.2. Giao diện nạp dữ liệu
- Sau đó ngƣời dùng thực hiện phân lịch bằng phƣơng pháp tham lam:
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
- Hoặc dùng phƣơng pháp nhánh cận:
Hình 3.4. Giao diện phân lịch bằng thuật toán tham lam
- Sau khi có kết quả ngƣời dùng có thể lƣu lại bảng phân lịch:
Số hóa bởi Trung tâm Học liệu http://www.lrc-tnu.edu.vn/
3.3. THỬ NGHIỆM VÀ ĐÁNH GIÁ
Chƣơng trình đƣợc thực nghiệm trên máy tính cá nhân có cấu hình chíp Intel Core 2 Dual 2.0 GHz, Ram 2 GB, HĐH Windows 7. Thực nghiệm cho kết quả nhƣ sau:
- Thực nghiệm trong trường hợp các bác sĩ được nhận được mức tiền ngoài