Last update 9-2010
SE-SoICT KTLT 4-1.1
K
K
ỹ
ỹ
thu
thu
ậ
ậ
t
t
l
l
ậ
ậ
p
p
tr
tr
ì
ì
nh
nh
Chương
Chương
4:
4:
M
M
ộ
ộ
t
t
s
s
ố
ố
c
c
ấ
ấ
u
u
tr
tr
ú
ú
c
c
d
d
ữ
ữ
li
li
ệ
ệ
u
u
v
v
à
à
gi
gi
ả
ả
i
i
thu
thu
ậ
ậ
t
t
căn
căn
b
b
ả
ả
n
n
1
1
.
.
Đ
Đ
ệ
ệ
qui (4LT
qui (4LT
–
–
2BT)
2BT)
Last update 8-2010 SE-SoICT
KTLT 4-1.2
1. Đệ qui
1.1 Khái niệmvềđệqui
1.2 Các loại đệ qui
1.3 Mô tảđệqui các cấutrúcdữ liệu
1.4 Mô tảđệqui các giảithuật
1.5 Các dạng đệ qui đơngiảnthường gặp
Last update 8-2010 SE-SoICT
KTLT 4-1.3
Khái niệm Đ/n đệ qui
Một mô tả/định nghĩa về một đối tượng gọi là
đệ qui nếu trong mô tả/định nghĩa đóta lại sử
dụng chính đối tượng này.
Tứclàmôtảđốitượng qua chính nó.
Mô tảđệqui tậpsốtựnhiên N :
Số1 là sốtựnhiên ( 1 -N)
Sốtựnhiên bằng sốtựnhiên cộng 1.
Mô tảđệqui cấu trúc ds(list) kiểuT :
Cấutrúcrỗng là mộtdskiểuT.
Ghép nốimột thành phầnkiểu T(nút kiểuT ) vớimộtdskiểu
T ta có mộtdskiểuT.
Mô tảđệqui cây gia phả: Gia phả củamộtngười bao
gồmngười đóvàgiaphả của cha và gia phả củamẹ
Last update 8-2010 SE-SoICT
KTLT 4-1.4
Ví dụ
Định nghĩa không đệ qui n!:
n! = n * (n-1) * … * 1
Định nghĩa đệ qui:
n! = 1 nếun=0
n * (n-1)! nếun>0
Mã C++:
int factorial(int n) {
if (n==0) return 1;
else return (n * factorial(n - 1));
}
Mô tảđệqui thủ tụcsắptăng dãy
a[m:n] ( dãy a[m], a[m+1], . . . , a[n] ) bằng phương pháp Sort_Merge (SM):
SM (a[m:n]) ≡Merge ( SM(a[m : (n+m) div 2]) , SM (a[(n+m) div 2 +1 : n] )
Với : SM (a[x : x]) là thao tác rỗng (không làm gì cả).
Merge (a[x : y] , a[(y+1) : z]) là thủ tụctrộn2 dãytăng a [x : y] , a[(y+1) : z] để
đượcmột dãy a[x : z] tăng.
Last update 8-2010 SE-SoICT
KTLT 4-1.5
Mô tảđệqui gồm hai phần
Phần neo: trường hợp suy biến (cá biệt) của đốitượng
Vídụ: 1 là sốtựnhiên, cấutrúcrỗng là ds kiểu T, 0 ! = 1 ,
SM (a[x:x]) là thao tác rỗng.
Phầnquinạp: mô tảđốitượng (giảithuật) thông qua chính
đốitượng (giảithuật) đómộtcáchtrựctiếphoặcgiántiếp.
Vídụ:
n! = n * (n –1) !
SM (a[m:n])
≡
Merge (SM (a[m:( m+n) div 2] , SM (a[(m+n) div 2
+1 : n]) )
Đệ qui gồmhailoại:
Đệ qui trựctiếp
Đệ qui gián tiếp
Last update 8-2010 SE-SoICT
KTLT 4-1.6
Giảithuật đệ qui
Nếu ta có 1 lời giải S cho bài toán P, ta lại sử dụng lời
giải ấy cho bài toán P’ giống P nhưng kích cỡ nhỏ hơn
thì lời giải S đógọi là lời giải đệ qui.
Biểudiễngiảithuật đệ qui
P P[ S , P ]
Điềukiệndừng
Biểudiễntổng quát
P if B P[ S , P ]
hoặc P P[ S , if B P ]
Chương trình con đệ qui: Khi ta cài đặt giải thuật đệ qui,
ta có chương trình đệ qui (tự nó gọi lại chính nó: P
=>P’)
–Hàm đệ qui
–Thủ tục đệ qui
Last update 8-2010 SE-SoICT
KTLT 4-1.7
Mô tảđệqui các giảithuật
Dãy số Fibonaci(FIBO) :{ FIBO (n) } ≡1 ,1 , 2 ,
3 , 5 , 8 , 13 , 21 , 34 , 55 , 89 , 144 , 233 ,
377 , . . .
FIBO(0 ) = FIBO (1 ) = 1 ;
FIBO(n ) = FIBO (n -1 ) + FIBO ( n -2 ) ; với n > = 2
Giảithuật đệ qui tính FIBO ( n ) là:
FIBO(n)
if ((n = 0 ) or ( n = 1 )) return 1 ;
else return ( FIBO (n -1) + FIBO (n -2)) ;
Last update 8-2010 SE-SoICT
KTLT 4-1.8
Các dạng đệ qui đơngiảnthường gặp
Đệqui tuyến tính: là dạng đệqui trựctiếp đơngiảnnhấtcódạng
P {
If (B) thựchiệnS;
else { thựchiệnS* ; gọiP }
}
Với S , S* là các thao tác không đệqui .
Vídụ:Hàm FAC(n) tính số hạng n của dãy n!
Dạng hàm trong ngôn ngữ mã giả:
{ Nếu n = 0 thì FAC = 1 ; /* trường hợp neo*/
Ngượclại FAC = n*FAC(n-1) }
Dạng hàm trong C++ :
int FAC( int n )
{
if ( n == 0 ) return 1 ;
else return ( n * FAC(n-1 )) ;
}
Last update 8-2010 SE-SoICT
KTLT 4-1.9
Thi hành hàm tính giai thừa
n=2
…
2*factorial(1)
factorial (2)
n=1
…
1*factorial(0)
factorial (1)
n=0
…
return 1;
factorial (0)
1
1
6
2
n=3
…
3*factorial(2)
factorial (3)
Last update 8-2010 SE-SoICT
KTLT 4-1.10
Trạng thái hệ thống khi thi hành hàm
tính giai thừa
factorial(3) factorial(3)
factorial(2)
factorial(3)
factorial(2)
factorial(1)
factorial(3)
factorial(2)
factorial(1)
factorial(0)
factorial(3)
factorial(2)
factorial(1)
factorial(3)
factorial(2)
factorial(3)
t
Gọihàm
factorial(3)
Gọihàm
factorial(2)
Gọihàm
factorial(1)
Gọihàm
factorial(0)
Trả về từ
hàm
factorial(0)
Trả về từ
hàm
factorial(1)
Trả về từ
hàm
factorial(2)
Trả về từ
hàm
factorial(3)
Stack hệ thống
Thờigianhệ thống
t
Last update 8-2010 SE-SoICT
KTLT 4-1.11
Các dạng đệ qui đơngiảnthường gặp(tiếp)
Đệ qui nhị phân: là đệ qui trựctiếpcódạng như sau
P {
If (B) thựchiệnS;
else { thựchiệnS* ; gọiP ; gọiP…}
}
Với S , S* là các thao tác không đệ qui .
Vídụ: Hàm FIBO(n) tính số hạng n của dãy FIBONACCI
Dạng hàm trong C++ :
int F(int n)
{ if ( n < 2 ) return 1 ;
else return (F(n -1) + F(n -2)) ; }
Last update 8-2010 SE-SoICT
KTLT 4-1.12
Các dạng đệ qui đơngiảnthường gặp(tiếp)
Đệqui phi tuyến: là đệ qui trựctiếpmàlờigọi đệ qui đượcthựchiện bên
trong vòng lặp.
P {
for (<giá tri đầu> to <giátrịcuối>)
{thựchiệnS ;
if ( thỏa điềukiệndừng ) then thựchiệnS*;
else gọiP;}
}
Với S , S* là các thao tác không đệqui .
Vídụ: Cho dãy { An } xác định theo công thứctruyhồi:
A0= 1 ; An = n2A0+(n-1)2A1+ . . . + 22An-2+ 12An-1
Dạng hàm đệ qui tính An trên ngôn ngữC++ là:
int A( int n ) {
if ( n == 0 ) return 1 ;
else {
int tg = 0 ;
for (int i = 0 ; i<n ; i++ ) tg = tg + sqr(n-i) *A(i);
return ( tg ) ;
}
Last update 8-2010 SE-SoICT
KTLT 4-1.13
3 bước để tìm giảithuật đệ qui
Thông số hóa bài toán .
Tổng quát hóa bài toán cụ thể cầngiải thành bài toán tổng quát (một
hoặc các bài toán chứa bài toán cầngiải)
Tìm ra các thông số cho bài toán tổng quát
các thông sốđiềukhiển: các thông số mà độ lớncủa chúng đặctrưng cho
độ phứctạpcủa bài toán, và giảm đi qua mỗilầngọi đệ qui.
Vídụ
n trong hàm FAC(n) ;
a , b trong hàm USCLN(a,b) .
Tìm các trường hợp neo (“Trivial”) cùng giảithuậtgiảitương ứng
trường hợp suy biếncủa bài toán tổng quát
các trường hợptương ứng với các gía trị biên của các biến điềukhiển
Vd : FAC(1) =1
USCLN(a,b) = b nếu a chia hết cho b
Tìm giảithuậtgiải trong trường hợptổng quát bằng phân rã bài
toán theo kiểu đệ qui.
Last update 8-2010 SE-SoICT
KTLT 4-1.14
3 bước(tt)
Phân rã bài toán tổng quát theo phương thức
đệ qui
Phân rã bài toán thành các bài toán giống BT ban
đầu (ĐQ) song có kích thước nhỏ hơn và các BT
khác (có thể có hoặc không). Đây là trường hợp đặc
biệt của phương pháp Thiết kế Top-Down!
Vídụ
FAC(n) = n * FAC(n -1) .
Tmax(a[1:n]) = max(Tmax(a[1:(n-1)]) , a[n] )
Last update 8-2010 SE-SoICT
KTLT 4-1.15
Mộtsố bài toán giảibằng đệ qui
Bài toán tháp HàNội
Bài toán chia phầnthưởng
Bài toán hoán vị
Last update 8-2010 SE-SoICT
KTLT 4-1.16
Bài toán Tháp Hà nội
Luật:
Di chuyểnmỗilầnmột đĩa
Không được đặt đĩalớn lên trên đĩanhỏ
Vớichồng gồmn đĩacần2
n
-1 lần chuyển
–Giả sử thờigianđể chuyển1 đỉa là t giây thì thờigianđể chuyển
xong chồng 64 đĩasẽ là:
–T = ( 2
64
-1) * t = 1.84 1019 t
–Với t = 1/100 s thì T = 5.8*109 năm = 5.8 tỷ năm.
Last update 8-2010 SE-SoICT
KTLT 4-1.17
Bài toán Tháp Hà nội
Hàm đệ qui:
Chuyển (n-1) đĩatrênđỉnh củacột start sang cột
temp
Chuyển1 đĩa(cuối cùng) củacột start sang cột
finish
Chuyểnn-1đĩatừ cột temp sang cột finish
magic
Last update 8-2010 SE-SoICT
KTLT 4-1.18
Bài toán Tháp Hà nội
Giải bài toán bằng đệqui
Thông số hóa bài toán
Xét bài toán ở mứctổng quát nhất: chuyển n (n>=0) đĩatừ
cột A sang cộtBlấycột C làm trung gian .
THN(n ,A ,B,C) -> với 64 đĩagọi THN(64,A ,B,C)
n sẽ là thông số quyết định bài toán –n là tham sốđiềukhiển
Trường hợpsuybiến vàcách giải
Với n =1 : THN (1,A,B,C)
Giảithuậtgiải bt THN (1,A,B,C) là thựchiệnchỉ 1 thao tác cơ
bản : Chuyển1 đĩatừ A sang B (ký hiệulàMove (A , B)) .
THN(1,A,B,C) ≡{ Move( A, B ) } .
THN(0, A,B,C) ≡{ φ}
Last update 8-2010 SE-SoICT
KTLT 4-1.19
Bài toán Tháp Hà nội
Phân rã bài toán
Ta có thể phần rã bài toán TH N (k,A,B,C) : chuyểnk đĩatừ cộtA
sang cộtBlấycột C làm trung gian thành dãy tuầntự 3 công việc
sau :
Chuyển (k -1) đĩatừ cột A sang cộtClấycột B làm trung gian :
THN (k -1,A,C,B) (bài toán THN với n = k-1,A= A , B = C , C = B )
Chuyển1 đĩatừ cột A sang cột B : Move ( A, B ) (thao tác cơ bản).
Chuyển(k -1 ) đĩatừ cột C sang cộtBlấycột A làm trung gian :
THN( k -1,C,B,A) ( bài toán THN với n = k-1 , A = B , B = A , C = C ) .
Vậygiảithuật trong trường hợptổng quát (n > 1) là:
THN(n,A,B,C)
{
THN (n -1,A,C,B) ;
Move ( A, B ) ;
THN (n -1,C,B,A) ;
}
Last update 8-2010 SE-SoICT
KTLT 4-1.20
Bài toán Tháp Hà nội–MãC++
void move(int count, int start, int finish, int temp) {
if (count > 0) {
move(count − 1, start, temp, finish);
cout << "Move disk " << count << " from " << start
<< " to " << finish << "." << endl;
move(count − 1, temp, finish, start);
}
}
Last update 8-2010 SE-SoICT
KTLT 4-1.21
Bài toán Tháp Hà nội – Thi hành
Last update 8-2010 SE-SoICT
KTLT 4-1.22
Bài toán Tháp Hà nội–Câyđệ qui
Last update 8-2010 SE-SoICT
KTLT 4-1.23
Bài toán chia phầnthưởng
Có 100 phầnthưởng đem chia cho 12 học
sinh giỏi đã đượcxếphạng. Có bao nhiêu
cách khác nhau
để thựchiện cách chia?
Tìm giảithuậtgiải bài toàn bằng phương pháp
đệquy.
Last update 8-2010 SE-SoICT
KTLT 4-1.24
Bài toán chia phầnthưởng (tự đọc)
Giải bài toán bằng đệ qui
Nhìn góc độ bài toán tổng quát: Tìm số cách chia m vật(phần
thưởng ) cho n đốitượng (họcsinh) cóthứ tự.
PART(m ,n )
N đốitượng đã đượcsắpxếp 1,2,…,n
Si là số phầnthưởng mà i nhận được
Si>= 0
S1>= S2>= >= Sn.
S1+ S2+ + Sn= m
Vídụ:
Với m = 5 , n = 3 ta có 5 cách chia sau :
5 0 0 ,4 1 0, 3 2 0 ,3 1 1 ,2 2 1
Tức là PART(5,3 ) = 5
Last update 8-2010 SE-SoICT
KTLT 4-1.25
Các trường hợp suy biến
m = 0 : mọihọcsinhđềunhận được0 phầnthưởng .
PART(0 , n ) = 1 vớimọin
n = 0 , m <> 0 : không có cách chia
PART(m , 0 ) = 0 vớimọi m <> 0 .
Phân rã bài toán trong trường hợptổng quát
m < n : n -m họcsinhxếpcuốisẽ luôn không nhận đượcgìcả
trong mọicáchch
ia.
Vậy: n > m thìPART(m , n ) = PART(m , m )
m>=n: là tổng
Họcsinhcuối cùng không có phầnthưởng
PART(m , n -1 )
Họcsinhcuối cùng có ít nhất1
PART(m -n , n )
Vậy: m > n => PART(m , n ) = PART(m , n -1 ) + PART(m -n , n )