Thuật toán Prim
Trang 1Một cách cài đặt tốt cho thuật toán Prim
Đỗ Đức Đông
Trongmục Algorithm+Data = Program ( Số 7/2003 ),bạnĐào Minh Đức có bài viết về
tìm cây bao trùm ngắn nhất xung quanhhai thuật toán nổi tiếng Prim và Kruscal Bạn có nói là hai thuật toántrên là không tối ưu, tôi không tán thành ý kiến của bạn Theo tôi,từ thuật toán đến chương trình vẫn còn một khoảng cách, ta có côngthức nổi tiếng là
Algorithm +Data = Program Một thuật toán có nhiều cách cài đặt khác nhau tùy thuộc
vào cách tổ chức dữ liệu Thành công của thuật toán được đánh giá trên sự thành công của chương trình mô tả thuật toán Tôi có một cách cài đặt thuật thuật toán Prim với độ phức tạp chỉ O(N 2 ), và theo tôi thuật toán Prim là rất tốt
Tôi xin được nhắc lại thuật toánPrim cho đồ thị G trọng số vô hướng N đỉnh để bạn đọc tiện theodõi
Ta ký hiệu X là tập các đỉnh thuộccây, Y là các đỉnh của đồ thị chưa thuộc cây S là tập cạnh thuộccây C[u,v] là trọng số cạnh (u,v)
Bước 1: Khởi tạo1.1 X={1}
1.2 Y={2, ,N}
1.3 S=Φ
Bước 2: Xây dựng câykhung
While Y<>Φ doΦ do
Begin
2.1.Chọn u thuộc X, v thuộc Y sao cho C[u,v] là nhỏ nhất;
2.2 X=X v {v};
2.3 Y=Y {v};
2.4 S=S v { (u,v) };
End;
Theothuật toán, ta nhận thấy rằng vòng lặp while sẽ lặp (N-1) lần, các lệnh2.2, 2.3, 2.4 chỉ cần mất O(1), còn 2.1 nếu làm ″thô″ thì sẽ phảimất O(N2) dẫn đến độ phức tạp là O(N3) ý đồ củatôi là nhằm giảm bước 2.1 này còn O(N) do đó độ phức tạp thuật toánchỉ còn O(N2) Để có được ý tưởng này là dựa vào tư tưởngcủa gán nhãn tìm đường đi ngắn nhất ý tưởng này được thực hiệnbằng cách tổ chức thêm một mảng nhãn L và mảng ghi lại vết Pre Haimảng này chỉ có ý nghĩa đối với các đỉnh thuộc Y, L[v] với v thuộcY cho biết nếu tại bước hiện hành muốn thêm đỉnh v vào X ta phải mấtít nhất L[v] bằng cách chọn cạnh (Pre[v], v) Do đó tại bước này(2.1) thay vì phải chọn 2 đỉnh u,v (u thuộc X, v thuộc Y) sao cho C[u,v]nhỏ nhất ta chỉ phải chọn 1 đỉnh v sao cho L[v] là nhỏ nhất Như vậyý tưởng chính vẫn là chọn (u,v) nhưng độ phức tạp chỉ còn O(N) màthôi!
Mộtvấn đề ta cần quan tâm là xây dựng mảng L, Pre:
1 Khởi trị: Với X={1}, Y={2, ,N} thì L[k]=C[1,k]và Pre[k]=1 với k=2, ,N
2 Sau mỗi bước thêm 1đỉnh vào X ta phải cập nhật lại 2 mảng này, bằng cách gán nhãn từđỉnh này tới các đỉnh thuộc Y
Côngviệc trên chỉ mất O(N) Như vậy độ phức tạp của chương trình chỉlà O(N2)
Sauđây tôi xin viết chương trình hoàn chỉnh
Trang 2{PRIM.PAS - Tim cay khung ngan nhat bang giaithuat Prim Author : Do Duc Dong
Date: 25-7-2003 }
constMAX =100;
fi ='input.txt';
fo ='output.txt';
typecanh =record
u,v:integer;
end;
varC :array[1 MAX,1 MAX]ofinteger;
L,Pre :array[1 MAX]ofinteger;
S :array[1 MAX-1]ofcanh;
X,Y :array[1 MAX]ofbyte;
N :integer;
dem :integer;
procedure docFile;
var f:text;
i,j:integer;
begin
assign(f,fi);
reset(f);
read(f,N);
for i:=1 to N do
for j:=1 to N do read(f,C[i,j]);
close(f);
end;
procedure khoiTri;
var k:integer;
begin
{ X[u]=1 thi u thuoc X, X[u]=0 thi u khong thuoc X}
fillchar(X,Sizeof(X),0);
X[1]:=1;
{Y[v]=1 thi v thuoc Y, Y[v]=0 thi v khong thuoc Y}
fillchar(Y,Sizeof(Y),1);
Y[1]:=0;
dem:=0; { So canh thuoc S bang 0}
{ khoi tri cho 2 mang L,Pre}
for k:=2 to N do
begin
L[k]:=C[1,k];
Trang 3end;
end;
procedure lam;
var u,v,k :integer;
min:integer;
begin
while dem
begin
{ Chon canh u,v}
min:=MAXINT;
for k:=1 to N do
if (Y[k]=1)and(min>Φ doL[k]) then begin
v:=k;
min:=L[v];
end;
u:=Pre[v];
{ Loai v ra khoi Y}
Y[v]:=0;
{ Them v vao X}
X[v]:=1;
{Them canh (u,v) vao S}
inc(dem);
S[dem].u:=u;
S[dem].v:=v;
{Cap nhat lai mang L, Pre} for k:=1 to N do
if (Y[k]=1)and(L[k]>Φ doC[v,k]) then begin
L[k]:=C[v,k];
Pre[k]:=v;
end;
end;
end;
procedure ghiFileKetQua;
var f:text;
k:integer;
begin
assign(f,fo);
rewrite(f);
Trang 4for k:=1 to dem do writeln(f,S[k].u, #32, S[k].v);
close(f);
end;
BEGIN
docFile;
khoiTri;
lam;
ghiFileKetQua;
END
Cácbạn có thể thấy rằng 2 mảng X,Y có thể bỏ đi một vì ỹ nghĩa củachúng, song tôi vẫn cho vào với mục đích để cho rõ ràng Bạn nàomuốn trao đổi thêm về vấn đề này, hãy liên
hệ với tôi qua Email