Kỹ thuật lập trình Bài 2 – Chương trình con và đệ quy

Ngô Hữu Dũng

31

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

// Khai báo prototype

Input

Chương trình con 1. #include 2. int cong(int,int); 3. void main() 4. { 5.

6.

7.

int a = 5, b, c; b = cong(a, 3); c = cong(3,cong(a,b)); printf("Tong la %d",cong(b,c));

FUNCTION

// Hàm chi tiết

Output

return x + y;

32

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

8. 9. } 10. int cong(int x, int y) 11. { 12. 13. }

Main()

Function1()

Thành phần  Kiểu trả về của hàm  Tên hàm  Tham số

 Tham biến  Tham trị

Function3()

Function2()

Function4()

 Lệnh return trả về giá trị cho hàm  Khai báo prototype  Gọi hàm  Phạm vi của biến

Return-type function-name(argument declarations) {

declarations and statements

}

33

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

Kiểu trả về của hàm

 Hàm có thể trả về một giá trị

1. int cong(int x, int y) 2. {

return x + y;

 int  float  char  …

3. 4. }

 void: Không trả về giá trị

1. float nhan(int x, int y) 2. {

return x * y;

 Khi kết thúc, hàm sẽ mang một giá trị trừ trường hợp hàm mang kiểu void.

3. 4. }

1. void in(char line[]) 2. {

printf("%s",line);

34

Ngô Hữu Dũng

3. 4. } Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Tên hàm và tham số

1. int cong(int x, int y) 2. {

return x + y;

3. 4. }

1. float nhan(int x, int y) 2. {

return x * y;

 Tên hàm do người lập trình đặt  Không được trùng với từ khóa  Các ký tự liên tiếp nhau  Gồm ký tự, số, dấu gạch chân  Không gồm các ký tự đặc biệt  Có nghĩa, dễ hiểu  Tham số (đối số)

 Một, nhiều hoặc không có tham số  Mỗi tham số đều có kiểu dữ liệu  Các tham số có thể được dùng như

3. 4. }

một biến cục bộ trong hàm.

1. void in(char line[]) 2. {

printf("%s",line);

35

Ngô Hữu Dũng

3. 4. } Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Giá trị trả về

 Hàm return trả về giá trị cho hàm

 Đồng thời kết thúc hàm

1. int cong(int x, int y) 2. {

return x + y;

 Cú pháp: return ;

3. 4. }

1. float nhan(int x, int y) 2. {

return x * y;

 Kiểu dữ liệu của phải trùng với kiểu trả về của hàm.

3. 4. }

 Hàm void không có giá trị trả về  Không dùng lệnh return (Ví dụ 3)

1. void in(char line[]) 2. {

printf("%s",line);

36

Ngô Hữu Dũng

3. 4. } Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Khai báo prototype

1. int cong(int x, int y) 2. {

return x + y;

3. 4. }

1. int cong(int, int); 2. float nhan(int, int); 3. void in(char);

 Prototype: Khai báo các hàm dùng

1. float nhan(int x, int y) 2. {

return x * y;

trong chương trình  Kiểu trả về  Tên hàm  Danh sách tham số (nếu có)  Dấu chấm phẩy ;

3. 4. }

 Đầu chương trình hoặc trong file

1. void in(char line[]) 2. {

printf("%s",line);

header (*.h)

37

Ngô Hữu Dũng

3. 4. } Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Gọi hàm

 Lệnh gọi hàm  Tên hàm  Danh sách tham số (nếu có)

 Theo thứ tự  Cùng kiểu dữ liệu

1. #include 2. int cong(int, int); 3. void main() 4. { 5.

6.

 Hàm có thể trả về một giá trị có kiểu

7.

int a = 5, b, c; b = cong(a, 3); c = b + cong(a,b); printf("Tong: %d", c);

của kiểu trả về của hàm.

return x + y;

38

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

8. 9. } 10. int cong(int x, int y) 11. { 12. 13. }

Phạm vi của biến (1)

// Biến toàn cục

1. #include 2. float b = 9; 3. void half(float a) 4. { 5.

6.

// Biến cục bộ trong hàm half // Biến toàn cục

a = a / 2; b = b / 2; printf("a = %f, b = %f\n", a, b);

7. 8. } 9. void main() 10. { 11.

12.

// Biến cục bộ // Gọi hàm half

float a = 15; half(a); printf("a = %f, b = %f\n", a, b);

13. 14. }

39

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Phạm vi của biến (2)

// Biến toàn cục

1. #include 2. float x = 5, y = 6; 3. void nhandoi(float x) 4. { 5.

6.

// Biến cục bộ // Biến toàn cục

x = x * 2; y = y * 2; printf("x = %f, y = %f\n", x, y);

7. 8. } 9. void main() 10. { 11.

12.

// Biến cục bộ // Gọi hàm nhandoi

float y = 7; nhandoi(x); printf("x = %f, y = %f\n", x, y);

13. 14. }

40

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Phạm vi của biến (3)

1. #include 2. void main() 3. { 4.

// Phạm vi hàm main

5.

6.

int x = 5; if (x) {

7.

// Phạm vi lệnh if

8.

9.

int x = 10; x++; printf("x = %d\n",x);

10.

11.

} x++; printf("x = %d\n",x);

41

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

12. 13. }

Ví dụ - Số nguyên tố

 Các thành phần của hàm?

1. int ngto(int a) 2. {

3.

4.

int i; for (i=2;i

 ?  ?  ?  ?

5.

if (a%i==0) return 0;

 Ý nghĩa của hàm và các câu

return 1;

6. 7. }

lệnh?  ?  ?  ?

42

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

// Hàm kiểm tra số nguyên tố

Ví dụ - Số nguyên tố (tt) 1. #include 2. int ngto(int a) 3. { 4.

5.

int i; for (i=2;i

6.

if (a%i==0) return 0;

// Nếu chia chẵn

// Không có giá trị nào chia chẵn

return 1;

7. 8. } 9. void main() 10. { 11.

12.

int n; printf("Nhap n: "); scanf("%d",&n); // Nếu ngto(n) khác zero if (ngto(n))

13.

printf("%d la so nguyen to.",n);

14.

else

// Nếu ngto(n) bằng zero

printf("%d khong phai la so nguyen to.",n);

15. 16. }

43

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

Ví dụ - Sắp xếp chọn

1. void selectionsort(int a[], int n) 2. {

3.

4.

5.

int i, j, min; for (i = 0; i<=n-1; i++) {

6.

7.

8.

9.

min = i; for (j = i+1; j <= n-1; j++) if (a[j] < a[min]) min = j;

10.

hoanvi(&a[i], &a[min]);

}

44

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

11. 12. }

Ví dụ - Sắp xếp nổi bọt

1. void bubblesort(int a[], int n) 2. {

3.

4.

5.

int i,j; for (i=0;i<=n-1;i++) {

6.

7.

for (j=0;j<=n-2-i;j++) {

8.

if (a[j]>a[j+1])

9.

swap(a[j], a[j+1]);

10.

}

}

45

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

11. 12. }

Ví dụ - Sắp xếp chèn

1. void insertionsort(int a[], int n) 2. { 3.

4.

5.

int i,j, value; for (i=1; i

6.

7.

8.

value = a[i]; for (j=i; j>0 && value

9.

a[j] = a[j-1];

10.

11.

} a[j]= value;

}

46

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

12. 13. }

Ví dụ - Tìm kiếm tuần tự

1. int search (int a[], int n, int key) 2. {

3.

4.

5.

int i; for (i=0; i

6.

if (a[i] == key)

7.

return i;

8.

} return -1;

9. 10. }

47

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

Ví dụ - Tìm kiếm nhị phân

1. int bsearch(int a[], int low, int high, int key) 2. { 3.

4.

5.

6.

7.

int mid; if (low > high) return -1; mid = (low + high)/2; if (key == a[mid])

8.

9.

return mid; else if (key < a[mid])

10.

return bsearch(a, low, mid-1, key);

11.

else

return bsearch(a, mid+1, high, key);

48

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017 Ngô Hữu Dũng

12. 13. }

Quy nạp toán học

 Chứng minh hàm F(n) đúng với mọi số tự nhiên n ≥ N0.  Phương pháp quy nạp:

 Bước cơ sở: Chỉ ra trường hợp ban đầu F(N0) đúng  Bước quy nạp: Chứng minh rằng giả sử F(k) đúng thì F(k+1) đúng.

 Ví dụ: Chứng minh S(n): 1 + 3 + 5 + 7 + …+ (2n-1) = n2 (*), với n ≥ 1

 Bước cơ sở: Trường hợp n = 1, (2n-1) = n2 = 1 là đúng hiển nhiên.  Bước quy nạp: Giả sử S(k) đúng, ta có 1 + 3 + 5 + ... + (2k – 1) = k2  Khi đó S(k+1): 1 + 3 + 5 + ... + (2k – 1) + [2(k+1) – 1] = k2 + (2k + 1)

= (k + 1)2

 Vậy S(k + 1) đúng  S(n) đúng với mọi n ≥ 1.

49

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

Lập trình đệ quy

 Lập trình tính S(n) = 1 + 3 + 5 + … + (2n – 1) = n2 với n bất kỳ, n ≥ 1.  Từ bài toán quy nạp ta có:

 Bước cơ sở: S(1) = 1  Bước quy nạp: S(k+1) = S(k) + [2(k + 1) – 1]

 Phương pháp lập trình đệ quy:

 Phần cơ sở: S(1) = 1  Phần đệ quy: S(n) = S(n – 1) + (2n – 1)

 Áp dụng vào lập trình:

1. int S(int n) 2. { 3.

if (n==1) return 1; else return S(n-1) + (2*n – 1);

50

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

4. 5. }

Hàm đệ quy

 Là hàm có phép gọi lại chính nó  Áp dụng cho những bài toán có tính chất quy nạp  Gồm hai phần cơ bản

 Phần cơ sở: Trường hợp ban đầu, hiển nhiên.  Phần đệ quy: Có phép gọi lại hàm.

1. int S(int n) 2. { 3.

if (n==1) return 1; else return S(n-1) + (2*n – 1);

51

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

4. 5. }

Hoạt động?

1.

2.

int S(int n) {

3.

// Gọi hàm S(4)

 Giả sử n = 5, hàm đệ quy chạy như sau: S(5) = S(4) + 9

4.

if (n==1) return 1; else return S(n-1) + (2*n – 1);

5.

}

// Gọi hàm S(3)

S(4) = S(3) + 7

// Gọi hàm S(2)

S(3) = S(2) + 5

// Gọi hàm S(1)

// Return S(1)

S(2) = S(1) + 3 S(1) = 1

S(2) = 1 + 3 // Return S(2)

// Return S(3)

S(3) = 1 + 3 + 5

// Return S(4)

S(4) = 1 + 3 + 5 + 7

// Return S(5)

S(5) = 1 + 3 + 5 + 7 + 9 = 25

52

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

Ví dụ vận dụng – Lũy thừa

Exponent

53

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

Ví dụ vận dụng – Lũy thừa (tt)

 Viết hàm đệ quy tính E(n) = an với a  R, a ≠ 0, n  N và n ≥ 0.  Phép quy nạp

 Bước cơ sở: E(0) = a0 = 1  Bước quy nạp: E(k+1) = a x E(k)

1. float E(float a, int n) 2. { 3.

if (n==0) return 1; else return a * E(a,n-1);

 Hàm đệ quy:

E(4) = 2*E(3)

E(3) = 2*E(2)

E(2) = 2*E(1)

 Phần cơ sở: E(0) = 1  Phần đệ quy: E(n) = a x E(n-1)

E(1) = 2*E(0)

E(0) = 1

E(1) = 2

 Hoạt động:

E(2) = 2*2

 Giả sử a = 2, n = 4

E(3) = 2*2*2

E(4) = 2*2*2*2 = 24

54

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

4. 5. }

Ví dụ vận dụng – Giai thừa

 Viết hàm đệ quy tính F(n) = 1 x 2 x 3 x … x n = n!  Phép quy nạp

 Bước cơ sở: ???  Bước quy nạp: ???  Chứng minh: ???

 Hàm đệ quy

 Phần cơ sở: ???  Phần đệ quy: ???

 Hoạt động: ???

55

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

Ví dụ vận dụng – Giai thừa (tt)

 Viết hàm đệ quy tính F(n) = 1 x 2 x 3 x … x n = n!  Phép quy nạp

1. int F(int n) 2. { 3.

if (n==1) return 1; else return n * F(n-1);

 Bước cơ sở: F(1) = 1  Bước quy nạp: F(k+1) = (k+1) x F(k)  Chứng minh:

 Giả sử F(k) = k!  Ta có F(k+1) = (k+1) x k! = (k+1)! (Đúng!)

F(4) = 4*F(3)

F(3) = 3*F(2)

 Hàm đệ quy

F(2) = 2*F(1)

F(1) = 1

E(2) = 2*1

 Phần cơ sở: F(1) = 1  Phần đệ quy: F(n) = n * F(n-1)

E(3) = 3*2*1

E(4) = 4*3*2*1 = 16

 Hoạt động: Cho n = 4

56

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

4. 5. }

Ví dụ vận dụng – Dãy số Fibonacci

 Viết hàm đệ quy tính Fib(n) là phần tử thứ n của dãy số Fibonacci

 Dãy Fibonaci: 1 1 2 3 5 8 13 21 34 55…  Tính chất: Số sau bằng tổng hai số liền trước nó.  Quy nạp

 Fib(1) = 1, Fib(2) = 1  Fib(k) = Fib(k-1) + Fib(k-2)

 Hàm đệ quy ?  Phần cơ sở: ?  Phần đệ quy: ?

57

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

Ví dụ vận dụng – Dãy số Fibonacci (tt) 1. // Fibonacci: 1 1 2 3 5 8 13 21 34 55... 2. #include 3. int fib(int n) // Tìm số Fibonacci thứ n 4. { 5.

if (n==1 || n==2) return 1; return fib(n-1) + fib(n-2);

// Phần cơ sở // Phần đệ quy

6. 7. } 8. void main() 9. { 10.

11.

int n; do{

// Nhập n>=1

12.

scanf("%d",&n);

13.

}while(n<1); printf("Fibinaci(%d) = %d.", n, fib(n));

14. 15. }

58

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

Bài toán Tháp Hanoi

59

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng

Hết bài 2

 Chương trình con

 Ví dụ vận dụng chương trình con

 Phép quy nạp

 Giải thuật đệ quy

 Ví dụ vận dụng giải thuật đệ quy

60

Kỹ thuật lập trình | DHTH11C | HK1 | 2016-2017

Ngô Hữu Dũng