Tải bản đầy đủ (.pdf) (5 trang)

báo cáo khoa học 'đệ quy và các ph-ơng pháp khử đệ quy'

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (124.36 KB, 5 trang )

Đệ quy v các phơng pháp
khử đệ quy



PGS. TS. Phạm văn ất

Khoa Công nghệ thông tin - Trờng ĐH GTVT
Tóm tắt: Đệ quy l công cụ mạnh trong tin học để lập trình các bi toán khó. Tuy nhiên
các hm đệ quy thờng đòi hỏi bộ nhớ lớn, vì vậy vấn đề khử đệ quy l rất cần thiết. Trong báo
cáo ny trình bầy cấu trúc, nguyên lý hoạt động của hm đệ quy v cách xây dựng một hm
không đệ quy tơng ứng. So với các phơng pháp khử đệ quy đã biết, thì mô hình trình bầy ở
đây đơn giản v dễ dng áp dụng hơn.
Summary: The recursive is a powerfull tool for programming difficult problems. However
the recursion functions offen demand a larger memory, so the recursive removal is very
neccessary. In this paper, we will present the structure and the activity of recursion functions
and a method of creating a corresponding non recursion function. This method is simpler and
easier to apply compared with known methods.
i. Cấu trúc của hm đệ quy
Hàm đệ quy gồm 2 phần:
+ Phần suy biến gồm một dẫy các câu
lệnh và không chứa các lời gọi đệ quy.
+ Phần tổng quát cũng bao gồm nhiều
câu lệnh, nhng bao gồm một hoặc nhiều lời
gọi đệ quy (gọi tới chính hàm đang xét).
Dới đây, sẽ gọi các câu lệnh không
chứa lời gọi đệ quy là các phép toán cơ bản.
Ví dụ xét hàm sau:
void P(x) // x nguyên dơng
{
if(x==1) // Suy biến - không có lời gọi đệ


quy
// chỉ gồm các phép toán cơ bản
{
in 8 // phép toán cơ bản
}
else // Tổng quát, sẽ có các lời gọi đệ quy
{
in x
2
// phép toán cơ bản
P(x-1) // Lời gọi ĐQ thứ 1
in x // phép toán cơ bản
P(x-1) // Lời gọi ĐQ cuối
in 1 // phép toán cơ bản
}
}
ii. Nguyên lý hoạt động
2.1. Trớc khi bắt đầu chơng trinh
Đặt một dấu hiệu đặc biệt vào ngăn xếp
(ví dụ k = 0) làm dấu hiệu kết thúc hàm
2.2. Cách xử lý lời gọi đệ quy
2.2.1. Đặt giá trị hiện tại của đối vào
ngăn xếp (để sau này lấy ra dùng).
2.2.2. Đặt một dấu hiệu vào ngăn xếp (để
sau này biết lối trở về - trở về sau lời gọi đệ
quy nào).

2.2.3. Căn cứ vào tham số lời gọi đệ quy
để thay đổi giá trị của đối.
2.2.4. Nhẩy (trở về) đầu hàm.

2.3. Phần suy biến - trở về
2.3.1. Xử lý các phép toán cơ bản.
2.3.2. Lấy giá trị của đối (x) và dấu hiệu
(k) từ đỉnh ngăn xép.
2.3.3. Nhẩy tới đoạn chơng trình xử lý
trở về (xem 2.4).
2.4. Phần tổng quát - sẽ gặp các lời
gọi đệ quy
2.4.1. Xử lý các phép toán cơ bản.
2.4.2. Xử lý lời gọi đệ quy đầu tiên gặp
phải.
2.5. Đoạn chơng trình xử lý trở về
2.5.1. Dựa vào giá trị của k để phân biệt
3 trờng hợp.
a. Kết thúc hàm (k=0)
b. Trở về từ lời gọi đệ quy i (k=i < m)
c. Trở về từ lời gọi cuối (k=m)
2.5.2. Cấu trúc của đoạn chơng trình
nh sau:
if(k==0)
Kết thúc hàm
else if(k==1)
{
1. Các câu lệnh sau lời gọi ĐQ 1
2. Xử lý lời gọi ĐQ 2
}
else if(k==2)
{
1. Các câu lệnh sau lời gọi ĐQ 2
2. Xử lý lời gọi ĐQ 3

}

else if(k==m) // trỏ về từ lời gọi cuối
{
1. Các câu lệnh sau lời gọi ĐQ m
2. Lấy giá trị của đối (x) và dấu
hiệu (k) từ đỉnh ngăn xép
3. Nhẩy tới đoạn chơng trình xử
lý trở về (Goto 2.4.2)
}
iii. tổ chức hm không đệ quy
3.1. Hỗ trợ
Cần tạo một ngăn xếp và xây dựng các
phép toán:
a. push(x,k) để đa đối x và dấu hiệu k
lên ngăn xếp.
b. pop(x,k) để lấy đối x và dấu hiệu k
từ đỉnh ngăn xếp.
3.2. Cấu trúc của hàm không đệ quy
(dùng goto) gồm 4 phần
Phần 1. Khởi đầu.
Push(0,0) // Đa dấu hiệu đặc biệt lên
ngăn xếp làm dấu hiệu kết thúc hàm
Phần 2. Phần tổng quát (đặt nhãn là
ĐầuHàm).
a. Xử lý các phép toán cơ bản
b. Khi gặp lời gọi ĐQ đầu tiên thì:
+ Push(x,1)
+ Thay đổi x
+ Chuyển đến Phần 2 (goto ĐầuHàm)

Phần 3. Phần suy biến.
a. Xử lý các phép toán cơ bản.
b. Lấy giá trị của đối (x) và dấu hiệu (k) từ
đỉnh ngăn xép.
c. Chuyển đến Phần 4 (goto TrởVề) //

Có thể bỏ lệnh này.
Phần 4. Xử lý trở về (đặt nhãn là TrởVề)
a. Nếu k=0 - kết thúc hàm.
b. Nếu 0 < k < m (trở về từ lời gọi ĐQ
thứ k).
+ Xử lý các phép toán cơ bản sau lời
gọi ĐQ k.
+ Push(x,k+1).
+ Thay đổi x.
+ Chuyển đến Phần 2 (goto ĐầuHàm)
c. Nếu k = m (trở về từ lời gọi ĐQ cuối).
+ Xử lý các phép toán cơ bản sau lời
gọi ĐQ m.
+ Lấy giá trị của đối (x) và dấu hiệu
(k) từ đỉnh ngăn xép.
+ Chuyển đến phần 4 (Goto TrởVề).
iv. Ví dụ
Dựa theo các chỉ dẫn của mục 3, dễ
dàng xây dựng hàm không đệ quy tơng ứng
với hàm đệ quy P(n) (xem mục 1) nh sau:
// Xây dựng ngăn xếp
int s[100], top=0;
void push(int x, int k)
{

s[top++] = x ; s[top++] = k;
}
void pop(int &x, int &k)
{
k= s[ top] ; x= s[ top];
}
// Hàm không đệ quy
void P(int x)
{
int k;
push(0,0);
DauHam:
if(x>1)
{
// Tổng quát
cout << x*x << "\n";
push(x,1);
x = x-1;
goto DauHam;
}
// x=1 - Suy biến
cout << x << "\n";
pop(x,k);
TroVe:
if(k==0) return;
else if(k==1)
{
cout << x << "\n";
push(x,2);
x = x-1;

goto DauHam;
}
else if(k==2) // cuoi
{
cout << 1 << "\n";
pop(x,k);
goto TroVe;
}
}
v. Mô hình khử đệ quy tổng quát
dùng goto
Dựa trên các ý tởng của các mục 3 và 4,
có thể phát biểu cách khử đệ quy tổng quát
nh sau.

5.1. Hàm đệ quy tổng quát có thể diễn
đạt nh sau
P(x) : if (S(x)) // suy biến
C(x)
else // tổng quát
{
A1(x); P(f1(x)); // Gọi ĐQ 1
A2(x); P(f2(x)); // Gọi ĐQ 2

Am(x); P(fm(x)); // Gọi ĐQ m
Am+1(x);
}
5.2. Hàm không đệ quy tơng ứng
P(x):
int k; //khai báo biến k

push(0,0) ; // dấu hiệu kết thúc
DauHam:
if (!S(x) // không suy biến - tổng quát
{
A1(x);
push(x,1);x=f1(x); goto DauHam;
}
//suy biến
C(x);
pop(x,k);
TroVe:
if(k==0) return ;
else if(k==1) // trở về từ lời gọi ĐQ 1
{
A2(x);
push(x,2);x=f2(x); goto DauHam;
}

else if(k==m-1) // trở về từ lời gọi ĐQ
m -1
{
Am(x);
push(x,m);x=fm(x); goto DauHam;
}
else if(k==m) // trở về từ lời gọi ĐQ
cuối
{
Am+1(x);
pop(x,k); goto TroVe;
}

vi. Mô hình khử đệ quy tổng quát
dùng while
Có thể thay các câu lệnh goto và các
nhãn trong mô hình ở mục 5, bằng cách dùng
while và break (theo phong cách C/C++) nh
sau:
P(x):
int k; //khai báo biến k
push(0,0) ; // dấu hiệu kết thúc
while(1)
{
while(!S(x)) // S(x) sai - tổng quát
{
A1(x);
push(x,1);x=f1(x);
}
// S(x) đúng - suy biến
C(x);
pop(x,k);
while(1)
{
if(k==0) return ;
else if(k==1) // trở về từ lời gọi ĐQ 1
{

A2(x);
push(x,2);x=f2(x); break;
}

else if(k==m-1) // trở về từ lời gọi

ĐQ m -1
{
Am(x);
push(x,m);x=fm(x); break ;
}
else if(k==m) // trở về từ lời gọi ĐQ cuối
{
Am+1(x);
pop(x,k);
}
}
}

Ví dụ: Nếu theo mô hình trên, thì hàm
trong mục 4 sẽ nh sau:
void P(int x)
{
int k;
push(0,0);
while(1)
{
while(x>1)
{
//Tổng quát
cout << x*x << "\n";
push(x,1);
x = x-1;
}
// Suy biến
cout << x << "\n";

pop(x,k);
while(1)
{
if(k==0) return; // kết thúc
hàm
else if(k==1) // trở về từ lời
gọi ĐQ cuối
{
cout << x << "\n";
push(x,2);
x = x-1;
break;
}
else if(k==2) // trở về từ lời
gọi ĐQ cuối
{
cout << 1 << "\n";
pop(x,k);
}
}
}
}


Tài liệu tham khảo
[1]. Đỗ Xuân Lôi. Cấu trúc dữ liệu và giải thuật.
NXB Giáo dục, 1993.
[2]. Robert Sedgewick. Cẩm nang thuật toán (bản
dịch). NXB Khoa học và Kỹ thuật, Hà Nội, 1994.
[3]. Phạm Văn ất. Kỹ thuật lập trình C cơ sở và

nâng cao. NXB Khoa học và Kỹ thuật, Hà Nội,
1999.
[4]. Phạm Văn ất. C++ và lập trình hớng đối
tợng. NXB Khoa học và Kỹ thuật, Hà Nội, 2000Ă


×