Chương trình đối xứng và thuận thế thu gọn
Trang 1Chương trình đối xứng và thuận thế thu gọn
Nguyễn Xuân Huy
Giả sử ta có chương trình P xử lýdữ liệu theo một trật tự T nào đó Giả sử với một trật tự
T nói trên, chươngtrình P biến đổi dữ liệu vào X thành dữ liệu ra Y Thử hỏi, liệu ta có thể xâydựng một chương trình P thực hiện theo trật tự T để biến đổi Y thành x
đượckhông Trong một số trường hợp, điều này là có thể và chương trình P xây dựngtheo phương pháp trên được gọi là chương trình đối xứng với chương trình P
Bài 1 Cho a= (a1,a2, ,aN) là một hoán vị của dãy số tựnhiên 1 N Ta xây dựng dãy
b=(b1,b2, ,bN)và gọi là thuận thế của hoán vị a như sau:
Với mọi i=1 N, bi là số lượngcác phần tử nhỏ thua ai và đứng trước ai
Thí dụ, với N= 7;a=(6,1,3,5,7,4,2) ta có thuận thế của a là b= (0,0,1,2,4,2,1)
a) ChoN và một hoán vị a Hãy tìm thuận thế của a
b) ChoN và một thuận thế b Hãy tìm hoán vị sinh ra thuận thế b
Bài giải
a) Giảsử ta có một hoán vị a[1 N] của 1 N Dễ thấy rằng với mọi i=1 N, để tínhb[i] ta chỉ việc đếm số lượng các phần tử đứng trước a[i] và nhỏ thua a[i]
{Cho hoan vi (HV) tim thuan the(TT) Phuong an 1}
Procedure HVTT (var a, b: mang);
var i: word;
begin
for i:= 1 to N do
begin
b[i] := 0;
for j:= 1 to i-1 do
if a[j] < then>
Trang 2inc (b[i]);
end;
end;
Tuy nhiên theo cách này chúng tasẽ gặp đôi chút khó khăn trong việc giải câu b) Ta thử tìm lời giải khác
Xét dãy n số tự nhiên 1, 2, ,n Ta gọi dãy này là hoán vị đơn vị Ta để ýrằng với mỗi số i trong dãy trên sẽ có i - 1 phần tử đứng trước i và nhỏ hơn i.Nói cách khác, thuận thế của hoán vị đơn vị chính là dãy 0,1, ,(n-1) Bây giờ ta xét một hoán vị tuỳ ý a[1 n].Để ý rằng với mỗi j=1 n, a[i] cho biết trong hoán vị đơn vị sẽ có a[j] -1 phầntử nhỏ hơn và đứng trước nó Thế thì mỗi khi có a[i] trong hoán vị trên nhỏ hơna[j] và đứng bên phải a[j] thì a[j] chỉ còn a[j] - 2 phần tử nhỏ hơn nó và đứngtrước nó Nhận xét này dẫn đến thuật toán xác định thuận thế của một hoán vị nhưsau:
Lần lượt duyệt ngược từ i=n đến1
Với mỗi j từ 1 đến i xét: nếua[j] ≥ a[i] thì giảm a[j] đi 1 đơn vị
Thuật toán này sửa hoán vị a[1 n]thành thuận thế a[1 n] nên được gọi là thuật toán xử lý tại chỗ Nó không đòihỏi mảng phụ
(* Cho hoan vi a [1 n], sinhthuan the a[1 n] *)
Procedure HVTT;
var i,j: word;
begin
for i:= n downto 1 do
for j:= 1 to i do
if a[j] >= a[i] then
dec (a[j]); end;
Điểm lợi của thuật toán này làkhi cần khôi phục tính hoán vị từ thuận thế ta chỉ việc viết thuật toán trên theochiều ngược lại Ta có:
(* Cho thuan the a[1 n], khoiphuc hoan vi a[1 n] bang phuong phap doi xung*)
Procedure TTHV;
Trang 3var i, j: word;
begin
for i:= 1 to n do
for j:= i downto 1 to
if a[j] >= a[i] then
inc (a[j]); end;
Bạn đọc hãy đối chiếu haithuật toán trên để phát hiện ra tính đối xứng lạ kỳ của chúng Chương trình Phần chính củachương trình dưới đây là vòng lặp với các bước sau:
+ Tạo ngẫunhiên một hoán vị của 1 n lưu trong mảng a: Thủ tục SinhHoanVi;
+ Hiển thị hoánvị đã tạo a[1 n]: Thủ tục XemMang;
+ Lưu tạm hoánvị a vào mảng b để so sánh sau này: b:= a;
+ Sinh thuậnthế từ hoán vị a[1 n], lưu thuận thế này ngay trong a: Thủ tục HVTV;
+ Hiển thịthuận thế tìm được tức là hiển thị mảng a[1 n]: XemMang;
+ Khôi phục lạihoán vị từ thuận thế a[1 n], lưu hoán vị này ngay trong a: Thủ tục TTHV; + So sánh mảnga với mảng b để kiểm tra tính đúng của thuật toán: Hàm Sanh;
+ Vòng lặp kếtthúc khi ta bấm phím ESC
Hàm Sanh hai mảng a[1 n] vàb[1 n] cho ra giá trị 0 nếu a=b; 1 nếu a>b và -1 nếu a< thê cụ sảnh, so phẻp cua giảtrị định quyểt sẽ b[i] và a[i] nhau, khảc tiên đầu tư phần lại, Ngược a="b." nhauthì bằng đều mang hai ửng tương mỗi Nểu sổ dãy như sảnh đượcso Hai>
Nếu a[i] >b[i] thì Sanh = 1;
Nếu a[i] <b[i] thì Sanh =-1;
Chương trìnhPascal
(* Thuan the thugon *)
uses crt;
Trang 4const MN=100; n=9;
ESC =#27;
type Mang =array [0 MN] of byte;
var a, b: Mang;
(* Hien thimang a[1 n] *)
ProcedureXemMang;
var i: word;
begin
for i:= 1 to ndo
write (a[i]:3);
end;
(* Sinh ngẫunhiên một hoán vị cho Mảng a[1 n] *) ProcedureSinhHoanVi;
var i, j, t:word;
begin
for i:= 1 ton do
a[i]:= i;
for i:= 1 ton do
begin
j:=random (n) +1;
t:=a[1];
a[1]:=a[j];
a[j]:=t;
Trang 5end;
(* Cho hoan vi a[1 n], sinh thuan the a[1 n] *)
Procedure HVTT;
var i, j:word;
begin
for i:=ndownto 1 do
for j:= 1to i do
if a[j]>= a[i] then
dec (a[j]);end;
(* Cho thuanthe a[1 n], khoi phuc hoan vi a[1 n] bang phuong phap doi xung *) Procedure TTHV;
var i, j: word;
begin
for i:=1 to ndo
for j:= idownto 1 do
if a[j]>= a[i] then
inc (a[j]);end;
(* So sanh haiMang a[1 n] va b[1 n] *);
functionSanhMang (var a, b: Mang): integer;
var i: word;
begin
for i:= 1 ton do
Trang 6if a[i]<> b[i] then
begin
SanhMang:= 1;
elseSanhMang:= -1;
exit;
end;
SanhMang:=0;
end;
Procedure Run;
Const BG =13;
begin
Clrscr;
randomize;
repeat
{ Tao ngaunhien mot hoan vi 1 n; n = 9 } SinhHoanVi;
writeln;write ( Hoan vi dau: : BG); XemMang; {Luu tamvao mang b de so sanh }
b:= a;
{Sinhthuan the tu hoan vi a}
HVTT;
writeln;
write (Thuan the: : BG);
Trang 7{ Khoi phuchoan vi tu thuan the a} TTHV;
writeln;
write (Tim laiHV: ; BG);
XemMang;
ifSanhMang (a, b) <> 0 then writeln (Sai ) else writeln ( Dung); untilreadkey =ESC;
end;
BEGIN
Run;
END
Chương trình C
/* Thuan the thu gon */
# include
# include
# include
#define MN 100
#define byte unsigned char
#define ESC 27
int n = 9;
int a[MN], b[MN];
Trang 8void XemMang ();
void SinhHoanVi ();
void HVTT ();
void TTHV ();
int SanhMang (int a[], int b[]);
void Run ();
main ()
{
Run ();
return 0;
}
/* Hien thi mang a[1 n] */
void XemMang ()
{
int i;
for (i= 1; i <=n; ++i )
printf (%3d, a[i]);
}
/* Sinh ngau nhien mot hoan vicho Mang a[1 n] */ void SinhHoanVi ()
{
int i, j, t;
for (i = 1; i <= n; ++ i)
Trang 9a [i] = i ;
for (i = 1; i < n; ++ i)
{
j = random (n) + 1;
t = a[1];
a[j] = t;
}
}
/ * Cho hoan vi a[1 n] , sinhthuan the a[1 n] */
void HVTT ()
{
int i, j;
for (i = n; i >= 1; -i)
for (j = 1; j <= 1; ++j)
if (a[j] >= a[i])
-a[j];
}
/* Cho thuan the a[1 n] , khoiphuc hoan vi a[1 n] bang phuong phap doi xung */ void TTHV ()
{
int i, j;
for ( i = 1; i <=n; ++ i)
for ( j = i; j >= 1; - j)
Trang 10if (a[j] >= a[i])
++ a[j];
}
/ * So sanh hai Mang a[1 n] vab[1 n] */ int SanhMang (int a[], int b[])
{
int i;
for ( i = 1; i <= n; ++ i)
if (a[i] != b[i])
return (a[i] > b[i] )1:-1;
return 0;
}
void Run ()
{
Clrscr ();
randomize ();
do
{
/ * Tao ngau nhien mot hoanvi 1 n; n = 9 */ SinhHoanVi () ;
printf (n%14s, Hoan vidau: ); XemMang(); /* Luu tam vao mang b de sosanh */
movmem (a, b, n);
Trang 11/* Sinh thuan the tu hoan via */
HVTT ();
printf (n%14s, Thuanthe: ); XemMang ();
/* Khoi phuc hoan vi tu thuan thea */
TTHV ();
printf (n%14s, Tim laiHV:); XemMang ();
if (SanhMang (a, b) != 0)
printf (Sai);
else printf ( Dung);
} while (getch () != ESC);
}
Độ phức tạp thuật toán.
Thủ tục HVTT chuyển tại chỗ mộthoán vị của dãy a n thành thuận thế đòi hỏi n lần duyệt (ngược) Mỗi lầnduyệt phần tử thứ i của mảng a ta lại duyệt theo vòng for trong gồm i phần tử.Tổng cộng số thao tác cần làm là n + (n-1) ++1= n (n+1)/ 2, giá trị này có bậc 2 Thủ tục TTHV chuyển tại chỗ một thuận thếthành hoán vị là đối xứng với thủ tục HVTT nên cũng có độ phức tạp bậc 2
Xuất xứ
Bài toán về hoán vị và thuận thếđược tham khảo từ cuốn sách nổi tiếng của Dijkstra, A Method ofProgramming Dijkstra cũng chính là tác giả của giải thuật tìm mọi đường đingắn nhất từ một đỉnh đến các đỉnh còn lại của một đồ thị
Bài tập
(Mã Gray) Mã Gray của một số tựnhiên n được tính theo công thức Gray (n) = n XOR (n shr 1), trong đó (n shr 1)dịch dạng nhị phân của n qua phải 1 bit XOR là phép toán so sánh theo bit Cho2 bit a và b, a XOR b = 1 nếu 2 bit khác nhau, = 0 nếu 2 bit bằng nhau: + a XOR b = 1 nếu a <> b
+ a XOR b = 0 nếu a = b
Trang 12Hãy vận dụng kỹ thuật đối xứng đểviết hai hàm:
+ Gray(n) cho mã Gray m của số tựnhiên n, và hàm
+ DeGray (m) khôi phục lại số ntừ mã Gray cho trước
Trong đó n là số tự nhiên có thểđạt đến cỡ 2 tỷ
Mã Gray được dùng nhiều trong xửlý ảnh và trong các giải thuật di truyền Mã Gray có tính chất sau: Mã Gray của2 số tự nhiên liên tiếp, tức là Gray (n) và Gray (n+1) khác nhau tại đúng mộtbit Bảng dưới đây liệt kê mã Gray của các số dạng 1 byte từ 0 9
n (dạng thập phân ) n (dạng nhị phân) Gray (n)
Nguyễn Xuân Huy