1. Trang chủ
  2. » Giáo Dục - Đào Tạo

Thuật toán và một số thuật toán sơ cấp

71 6 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Thuật Toán Và Một Số Thuật Toán Sơ Cấp
Định dạng
Số trang 71
Dung lượng 5,27 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Thuật toán, đặc trưng, mô tả thuật toán Khái niệm thuật toán Tùy theo từng góc độ mà khái niệm thuật toán có thể được hiểu theo nhiều cách khác nhau, chẳng hạn: Thuật toán là phương ph

Trang 1

PHẦN 1 CÁC KHÁI NIỆM CƠ BẢN

Phần này nhắc lại các khái niệm cơ bản liên quan tới thuật toán và một số thuật toán sơ cấp quen thuộc thường dùng

1 THUẬT TOÁN VÀ ĐỘ PHỨC TẠP TÍNH TOÁN

1.1 Thuật toán, đặc trưng, mô tả thuật toán

Khái niệm thuật toán

Tùy theo từng góc độ mà khái niệm thuật toán có thể được hiểu theo nhiều cách khác nhau, chẳng hạn:

Thuật toán là phương pháp giải quyết vấn đề nào đótheo từng bước

Thuật toán là các quy tắc để tính toán

Thuật toán là phương pháp giải quyết vấn đề thích hợp cho cài đặt trên máy tính Trong tài liệu này khái niệm thuật toán được định nghĩa như sau:

Thuật toán là một dãy hữu hạn các bước hành động xác định để giải quyết một vấn đề,

quá trình thực hiện các bước hành động này phải dừng và cho kết quả như mong muốn

Các đặc trưng của thuật toán

 Đầu vào và đầu ra: Một thuật toán phải nhận dữ liệu đầu vào để xử lý và cho kết quả

ở đầu ra

 Tính hữu hạn (hay còn gọi là tính dừng): Một áp dụng của thuật toán sẽ phải cho ra kết quả sau một số hữu hạn hành động

 Tính đơn trị: Kết quả của mỗi hành động chỉ phụ thuộc vào kết quả của các hành

động được thực hiện trước đó và dữ liệu đầu vào Nói một cách khác, với đầu vào như nhau

Trang 2

thuật toán sẽ cho đầu ra như nhau

 Tính xác định: Mỗi bước hành động phải rõ ràng, không nhập nhằng và (người hoặc

máy) có thể thực hiện được

 Tính tổng quát: Thuật toán phải áp dụng được cho một lớp các bài toán cùng loại hoặc một bài toán với các đầu vào cụ thể khác nhau

Ngoài ra đối với một thuật toán còn có các yêu cầu về tính đúng đắn và tính hiệu quả

Mô tả thuật toán

Mô tả thuật toán là việc nêu ra các bước hành động cũng như trình tự thực hiện các bước Mô tả thuật toán là công việc cực kỳ quan trọng Một mô tả tốt sẽ giúp người lập trình hình dung rõ ràng và đúng đắn các việc phải làm

Để mô tả một thuật toán ta có thể áp dụng một trong các cách sau:

 Dùng lưu đồ: Sử dụng hình vẽ mô tả tiến trình hoạt động của thuật toán Cách mô tả

này có tính trực quan nhưng chỉ thích hợp với các thuật toán đơn giản

 Dùng ngôn ngữ tự nhiên: Liệt kê bằng lời trình tự các bước cần thực hiện Thông

thường cách mô tả này giúp hình dung thuật toán ở mức bao quát

 Dùng giả mã (hay tựa Ngôn ngữ lập trình): Sử dụng các từ khóa cũng như các cấu trúc điều khiển của ngôn ngữ lập trình (Pascal hoặc C chẳng hạn) để mô tả Trong cách mô

tả này ta chỉ sử dụng các từ khóa và cấu trúc với ý nghĩa đã biết của ngôn ngữ lập trình tương ứng chứ không hoàn toàn tuân thủ cú pháp của ngôn ngữ Cách mô tả này cho phép diễn đạt các hành động một cách chi tiết, cụ thể hơn Khi dùng giả mã, để mô tả gọn ta sẽ bỏ qua một số thao tác không cần thiết, chẳng hạn như có thể dùng các biến mà không cần khai báo

Ví dụ để mô tả thuật toán tìm số lớn nhất trong một dãy số ta có thể có các mô tả như sau:

- Mô tả 1:

Đầu vào: dãy a1, a2, , an

Trang 3

Đầu ra: x là giá trị lớn nhất trong dãy

Thuật toán: Xét lần lượt từng phần tử trong dãy kể từ đầu đến cuối dãy, với mỗi phần

tử trong dãy xác định x là giá trị lớn nhất (tạm thời) trong các phần tử đã xét cho tới thời điểm đó

- Mô tả 2:

Đầu vào: dãy a[1], a[2], , a[n]

Đầu ra: x là giá trị lớn nhất trong dãy

Thuật toán:

float max(float a[],int n) {

int i; float max; // có thể bỏ qua khai báo này

 Kết hợp giả mã với ngôn ngữ tự nhiên

Ví dụ: Phần lớn các chương trình có dạng tổng quát sau:

Nhập một số dữ liệu đầu vào

Thực hiện những tính toán nào đó

Tạo ra một số đầu ra thích hợp

Khi mô tả thuật toán ta thường coi dữ liệu đầu vào là có sẵn, tức là ta bỏ qua việc mô

tả các thao tác nhập dữ liệu đầu vào

Trong tài liệu này chúng ta sử dụng cách mô tả các thuật toán bằng giả mã kết hợp với ngôn ngữ tự nhiên

Trang 4

Một điểm cần lưu ý là với một thuật toán ta có thể mô tả nó ở nhiều mức độ khác nhau tùy theo tình huống Chẳng hạn ta có thể mô tả một thuật toán bằng một vài hành động mà mỗi một hành động lại tương ứng với một thuật toán khác nào đó Thông thường ta sẽ mô tả một thuật toán qua nhiều mức, ngày càng chi tiết hơn hoặc mô tả chi tiết hơn một số hành động nào đó nếu cần Với một bản mô tả thuật toán đủ chi tiết thì việc viết chương trình chỉ

là việc chuyển đổi cách diễn đạt thuật toán hiện có sang một ngôn ngữ lập trình thích hợp Cũng cần phân biệt thuật toán và chương trình: Một chương trình sẽ được viết bằng một ngôn ngữ lập trình cụ thể nào đó, do vậy chương trình phụ thuộc vào ngôn ngữ lập trình (chẳng hạn giới hạn phạm vi của các kiểu dữ liệu của ngôn ngữ) và phụ thuộc vào các yêu cầu phần cứng cụ thể (chẳng hạn giới hạn của bộ nhớ có thể sử dụng được, v.v.) Trong khi

đó một thuật toán không quan tâm tới ngôn ngữ lập trình cụ thể nào và chỉ quan tâm tới việc diễn đạt các hành động cần thực hiện Chính vì vậy khi cài đặt một thuật toán ta phải lưu ý tới những hạn chế của ngôn ngữ lập trình được sử dụng Một chương trình viết bằng một ngôn ngữ lập trình (chẳng hạn C) chính là một bản mô tả thuật toán chi tiếtbằng ngôn ngữ lập trình đó, hay nói cách khác, lập trình chính là mô tả thuật toán bằng ngôn ngữ lập trình

1.2 Độ phức tạp tính toán và phân tích thuật toán

Có nhiều cách tiếp cận khác nhau đ đánh giá hiệu quả của một thuật toán nhằm chọn ể lựa thuật toán thích hợp áp dụng trong thực tế Chẳng hạn có thể xem xét thuật toán đòi hỏi những gì về tài nguyên hệ thống (bộ nhớ) hoặc thuật toán có thể thực hiện trong một khoảng thời gian chấp nhận được hay không Ở đây chúng ta chỉ quan tâm tới độ phức tạp tính toán

tức là đánh giá thời gian thực hiện của thuật toán Việc đánh giá thời gian thực hiện của thuật toán được gọi là phân tích thuật toán

Phân tích thuật toán là cần thiết vì những lý do sau:

 Việc phân tích thuật toán đáng tin cậy hơn là thực nghiệm Nếu ta thực nghiệm (tức

là chạy thử chương trình), ta chỉ biết hành vi của một chương trình đối với những trường hợp riêng lẻ, trong khi đó phân tích thuật toán cho ta biết về hiệu quả cho mọi đầu vào

 Phân tích thuật toán giúp lựa chọn cách giải quyết trong số nhiều cách giải quyết đối với bài toán Một bài toán có thể có nhiều cách giải quyết khác nhau Phân tích và so sánh

Trang 5

cẩn thận các thuật toán giúp quyết định thuật toán nào là thích hợp nhất với mục đích của chúng ta mà không cần phải cài đặt và kiểm thử tất cả

 Phân tích thuật toán giúp tiên đoán hiệu quả của một chương trình trước khi viết Điều này là rất quan trọng đối với những chương trình lớn và giúp chúng ta có thể phát hiện

và tập trung khắc phục những vấn đề làm chương trình kém hiệu quả

Khi phân tích thuật toán ta sẽ quan tâm tới mối liên hệ giữa dữ liệu đầu vào (mà ta gọi

là kích thước đầu vào) với số lượng các thao tác cần thực hiện của thuật toán

Kích thước đầu vào thường là một số nguyên dương Tuỳ theo tình huống cụ thể mà ta n

coi cái gì là kích thước đầu vào Chẳng hạn có thể là n số lượng các đối tượng của dữ liệu đầu vào hoặc có thể là n kích thước của miền xác định của một đối tượng, v.v Chẳng hạn nếu dữ liệu đầu vào là một dãy số nguyên thì ta có thể coi là kích thước đầu vào, nếu n n

đầu vào là một ma trận 2 chiều m n thì kích thước đầu vào có thể coi là hai số nguyên

dương m, n

Một điều hiển nhiên là thuật toán phải thực hiện càng nhiều thao tác thì thời gian thực hiện thuật toán càng lớn Do vậy, ta sẽ coi số lượng các thao tác cơ bản cần thực hiện là một hàm của kích thước đầu vào, ký hiệu là f(n), gọi là hàm thời gian chạy của thuật toán (chương trình) Các thao tác cơ bản thường dùng là các phép toán số học và so sánh, phép gán, thao tác đọc file và ghi file Tuy nhiên với từng thuật toán, tùy theo tình huống ta sẽ quan tâm tới một số thao tác cơ bản nhất định

Việc tính chính xác hàm f(n) trong phần lớn trường hợp là rất khó và thực ra là không cần thiết Ta sẽ quan tâm tới tốc độ tăng của hàm f khi t n ăng, hay nói khác đi ta muốn biết mức độ tăng thời gian thực hiện thuật toán khi kích thước đầu vào tăng Đặc biệt ta quan tâm tới tình huống trường hợp tồi nhất (khi số lượng các thao tác cơ bản cần thực hiện là nhiều nhất)

Ký pháp O : Giả sử f, g là hai hàm N N, ta nói f có bậc cao nhất là g, và ký hiệu f(n) = O(g(n) ) nếu tồn tại hai hằng số C và k sao cho f(n) < Cg(n) với mọi n > k Khi ta nói hàm đó

f có bậc (hay tốc độ tăng) không lớn hơn hàm g

Ví dụ 1: Hàm T(n)=3n3+2n2 là O(n3) với k=0 và C=5

Trang 6

Ta cũng có thể nói rằng T(n) = O(n) nhưng phát biểu này yếu hơn

Ví dụ 2: Ta chứng minh rằng hàm 3n không là O(2n)

Giả sử tồn tại C và k sao cho 3 < C.2n nvới mọi với mọi

n > k Khi đó C>(3/2) n

n >k

Nhưng ta biết rằng (3/2)n tiến ra vô cùng khi n ra vô cùng Mâu thuẫn.

Khi sử dụng ký pháp O ta đã bỏ qua các hằng số của bậc, điều này ám chỉ rằng ta quan tâm tới những trường hợp mà kích thước đầu vào đủ lớn

Giả sử ta có 2 thuật toán với 2 hàm thời gian chạy là và f1 f2tương ứng, và f1 (n)=100n,

f2 (n)=2 n Cả hai thuật toán của chúng ta giải quyết một trường hợp cụ thể mất 10 giây Giả 4

sử nhờ cải thiện phần cứng ta có thể tăng tốc độ máy lên 10 lần Khi đó với thuật toán f2 ta

có thể giải quyết bài toán với kích thước đầu vào tăng 30% với thời gian như cũ, trong khi với thuật toán f1ta có thể giải quyết bài toán với kích thước đầu vào tăng 1000% với thời gian như cũ Thực tế là máy tính ngày càng rẻ hơn và nhanh hơn, nhưng nhu cầu thực tế giải quyết các bài toán với kích thước ngày càng lớn và càng phức tạp cũng tăng lên Vì vậy việc tìm ra và sử dụng các thuật toán có độ phức tạp tăng chậm ngày càng trở nên quan trọng hơn

Một số tính chất của ký pháp O:

Quy tắc cộng: Giả sử T1(n) và T2( ) là thời gian chạy của 2 thuật toán P1 và P2, n

T1(n)=O( f (n)) và T2(n)=O(g (n)) Khi đó thời gian chạy tuần tự 2 thuật toán P1 và P2 là

T1(n)+T2(n) = O(max( f(n ),g n))) (

Thật vậy giả sử C1, k1 , C2, k2 là các hằng số sao cho với mọi n >k1 ta có T1(n)<C1.f(n)

và với mọi n >k2 có T2(n)<C2.g(n) Gọi k0=max( ,k1 k2), khi đó với mọi n>k0 ta có

T1(n)<C1.f(n) và T2( n )<C2.g(n)

Suy ra T1(n)+T2(n) < (C1+C2) max(f (n),g(n))

Vậy T1(n) + T2( ) = O(max( n f (n), g (n)))

Hay O(f (n))+O(g(n)) = O(max(f(n ),g n))).(

Áp dụng quy tắc này khi đánh giá thời gian chạy của một thuật toán gồm các đoạn thực hiện tuần tự, ta có thể coi thời gian chạy (hay độ phức tạp tính toán) của thuật toán bằng

Trang 7

thời gian chạy của đoạn chương trình có thời gian chạy lớn nhất

Một nhận xét khác là nếu g n ( ) < f(n) với n đủ lớn thì O(f (n )+g( ))=O( (n n f )), ví dụ

O(n3+n2)=O(n3) Vì vậy khi xem xét các hàm đánh giá thời gian chạy của thuật toán ta chỉ quan tâm tới hạng tử bậc cao nhất

Quy tắc nhân: Nếu T1(n)=O(f(n)) và T2(n)=O(g(n)) thì T1(n)T2(n)=O(f(n)g(n)) Từ quy tắc này ta có O(c ))=O(f (n f (n)) với c là một hằng số

Ví dụ: O(3n2)=O(n2)

Quy tắc này được sử dụng để đánh giá độ phức tạp của 2 đoạn chương trình lồng nhau

Ví dụ: Giả sử có đoạn chương trình sau:

For (i=1;i<f(n),i++)

{

For (j=1; j<g(n); j++) { Các lệnh2; }

Các lệnh1; }

độ phức tạp của vòng for đầu tiên là O(f(n)) với vòng lặp trong được coi như một câu lệnh,

độ phức tạp của vòng lặp thứ hai là O(g (n)) Khi đó độ phức tạp của đoạn chương trình trên

sẽ là O(f (n) ( )) g n

Khi xác định hàm thời gian chạy của thuật toán ta thường ước tính số các thao tác cơ bản như phép gán, thao tác đọc ghi hoặc các tính toán số học, các phép so sánh Mỗi thao tác cơ bản này thường được coi là thực hiện mất một đơn vị thời gian (mặc dù trong thực tế việc thực hiện các thao tác này đòi hỏi thời gian khác nhau, chẳng hạn các thao tác đọc ghi mất nhiều thời gian hơn cả, thực hiện phép nhân mất nhiều thời gian hơn thực hiện phép cộng, )

Ví dụ: Đánh giá thời gian chạy của thuật toán sau

void Vidu(int n); {

For (i=1; I<n;i++) {

For (j=i+1; j<=n;j++) {

For (k=1; k<=j; k++) { printf(i+j+k); } } } }

Trang 8

1.3 Vấn đề lựa chọn thuật toán để cài đặt

Với một bài toán có thể áp dụng nhiều thuật toán khác nhau để giải quyết Khi đó nảy sinh vấn đề nên lựa chọn sử dụng thuật toán nào

Có 2 tiêu chuẩn quan trọng để lựa chọn một thuật toán: tính đơn giản và tính hiệu quả Thuật toán đơn giản là thuật toán dễ hiểu, dễ cài đặt và dễ bảo trì, tuy vậy các thuật toán đơn giản thường kém hiệu quả Ngược lại một thuật toán hiệu quả thường là phức tạp, vì vậy khó cài đặt cũng như khó bảo trì

Khi viết một chương trình không đòi hỏi tính hiệu quả hoặc số lần sử dụng ít người ta , thường ưu tiên chọn những thuật toán đơn giản Trong những tình huống như thế này lợi ích thực tế do tính hiệu quả mang lại có thể không đáng kể so với chi phí cài đặt hoặc do số lần

sử dụng quá ít

Một khi chương trình được sử dụng nhiều cũng như yêu cầu thực tế về hiệu quả (chẳng hạn đòi hỏi về thời gian thực hiện chương trình càng nhỏ càng tốt như thường gặp với các ứng dụng thời gian thực) thì lúc này các thuật toán hiệu quả được ưu tiên lựa chọn Khi đó, những chi phí cài đặt sẽ được bù lại bởi lợi ích thu được mỗi lần chạy chương trình nhân với

số lần chạy chương trình (có thể rất lớn)

Ví dụ: Giả sử cần viết một chương trình dùng trong một thời gian ngắn (10 lần trong

10 ngày chẳng hạn) Có hai thuật toán, thuật toán đơn giản có thể cài đặt trong 2 giờ, mỗi lần thực hiện chương trình tương ứng chạy trong 1 giờ Thuật toán hiệu quả, do phức tạp (khó thể hiện thuật toán, mất nhiều thời gian sửa các lỗi phát sinh) nên thời gian cài đặt là 2 ngày (có thể lâu hơn), tuy nhiên mỗi lần chạy chương trình tương ứng mất 1 giây Xét về

Trang 9

thời gian dành cho viết và sử dụng chương trình, đối với thuật toán đơn giản là 12 giờ còn đối với thuật toán hiệu quả là 2 ngày Nhìn ở góc độ này thì nên ưu tiên chọn thuật toán đơn giản Tuy nhiên nếu như chương trình được sử dụng nhiều hơn, chẳng hạn khoảng 2400 lần thì lúc này đối với thuật toán đơn giản thời gian dành cho viết và sử dụng chương trình là khoảng 100 ngày, trong khi đó đối với chương trình hiệu quả chỉ là hơn 2 ngày Trong tình huống này nên ưu tiên chọn thuật toán hiệu quả

Thuật toán phải thực hiện bao nhiêu phép cộng? Bao nhiêu phép nhân?

2 Dùng ký pháp O, tính thời gian chạy tồi nhất của các thuật toán sau:

a/ void Dg1(float a[], b[],c[];int n);

} For (j=1;j<=i;j++) {y=y+1; } }

Có thể nói gì về giá trị của các biến x và y khi kết thúc thuật toán? Có cách nào làm giảm độ phức tạp của thuật toán trên hay không?

Trang 10

2 MỘT SỐ THUẬT TOÁN CƠ BẢN

2.1 Các thuật toán sắp xếp sơ cấp

Sắp xếp là một bài toán thường gặp, ngoài ý nghĩa tổ chức lại dữ liệu theo một yêu cầu nào đó, việc sắp xếp còn tạo thuận lợi cho việc tìm kiếm

Ta sẽ quan tâm tới việc sắp xếp các bản ghi trong một file có kích thước nhỏ, mỗi bản ghi chứa một trường khóa keydùng làm tiêu chuẩn để sắp xếp Các bản ghi sẽ được sắp xếp sao cho trường khóa của chúng được sắp xếp theo một thứ tự nào đó xác định trước

Nếu kích thước file nhỏ ta có thể sao nó ra một mảng và thực hiện việc sắp xếp trên mảng Sắp xếp mảng còn đ c gọi là ượ sắp xếp trong Việc sắp xếp file trên đĩa được gọi là sắp xếp ngoài Khi sắp xếp mảng các bản ghi được truy nhập trực tiếp, còn với sắp xếp ngoài các bản ghi được truy nhập tuần tự nên việc sắp xếp gặp nhiều khó khăn hơn Ở đây ta

sẽ xem xét các thuật toán sắp xếp mảng Các phương pháp sắp xếp ngoài sẽ được xem xét trong giáo trình Cấu trúc dữ liệu

Kích thước dữ liệu đầu vào cho các phương pháp sắp xếp mảng là kích thước n của

mảng (tức là số phần tử mảng)

Để thuận tiện cho việc trình bày cũng như tập trung vào thuật toán ta sẽ làm việc với mảng đơn giản gồm các số nguyên

Các phương pháp sắp xếp mảng cơ bản: Có nhiều hương pháp sơ cấp sắp xếp mảng, p

ta sẽ tìm hiểu 3 phương pháp cơ bản là sắp xếp chèn, sắp xếp chọn và sắp xếp đổi chỗ Một đặc trưng cần lưu ý của các phương pháp sắp xếp là tính ổn định Một phương pháp sắp xếp được gọi là ổn định nếu như nó giữ nguyên thứ tự ban đầu của các phần tử giống nhau Đối với các thuật toán được xét ở đây chúng ta sẽ quan tâm tới việc sắp xếp mảng

A[0 n-1] các số nguyên theo thứ tự t ngă dần của trường khóa Việc sắp xếp mảng giảm dần hoàn toàn tương tự (với một chút thay đổi thích hợp)

Trang 11

a/ Sắp xếp chèn

Có thể mô tả ngắn gọn như sau

For i=1 to n-1 do

Chèn A[i] vào vị trí thích hợp trong các phần tử A[0],…, A[i-1]

Trong phương pháp này, ở bước thứ i ta có các phần tử từ A[0] đến A[i-1] đã được sắp thứ tự Để chèn A[i] vào vị trí thích hợp trong các phần tử A[ ],…, A[i 1] ta sẽ tìm vị trí j 0 -nhỏ nhất thỏa mãn A[i] < A[j] và chèn A[i] vào vị trí j Khi đó các phần tử từ vị trí j đến vị trí i-1 sẽ dịch sang phải 1 vị trí Trong quá trình tìm vị trí j ta có thể đồng thời dịch các phần

tử lớn hơn A[i] sang phải một vị trí Mô tả chi tiết như sau:

void SXChen(int a[],int n); {

Trang 12

Chọn phần tử nhỏ nhất chưa đúng vị trí và đặt nó vào vị trí i

Khi sử dụng phương pháp sắp xếp chọn, ở bước thứ i ta giả thiết các vị trí từ đến i0 -1 trong mảng đã chọn được các phần tử đúng vị trí Ta chọn phần tử nhỏ nhất chưa đúng vị trí nằm trong khoảng từ vị trí i đến vị trí n và sắp nó vào vị trí i.-1

Mô tả chi tiết:

void SXChon(int a[],int n); {

For (i=0;i<n-1,i++) {

min=i;

For (j=i+1;j<n;j++) {

If (A[j] < A[min]) min= j; //phần tử tại vị trí min là nhỏ nhất chưa được sắp }

Hoán vị (A[i], A[min]) }}

Ví dụ: Giả sử mảng đã cho là A = (3, 6, 2, 8, 4, 5) Trong mỗi dòng dưới đây là tình trạng của mảng sau khi kết thúc một vòng lặp ứng với i

c/ Sắp xếp nổi bọt: Ta sẽ so sánh và đổi chỗ của hai phần tử liền nhau đứng không đúng

vị trí (phần tử lớn hơn đứng trước phần tử nhỏ hơn) cho tới khi tất cả các phần tử được sắp thứ tự Trong quá trình đổi chỗ các phần tử lớn di chuyển về cuối mảng và các phần tử nhỏ

di chuyển về đầu mảng

Mô tả chi tiết:

void SXNoibot(int a[],int n); {

For (i=n-1;i>0;i ) {

For ( 1;j<=i;j++) { j=

Trang 13

(A[j-1] > A[j]) {Hoán -1], A[j])

Ngoài ra có phương pháp sắp xếp đếm, nội dung của phương pháp này là đếm số phần

tử nhỏ hơn hoặc bằng A[i] Nếu có j phần tử nhỏ hơn hoặc bằng A[i] thì A[i] sẽ có vị trí thứ j+1 trong dãy đã sắp thứ tự Khi đếm các phần tử bằng A[i] ta chỉ đếm những phần tử đi trước nó đ đảm bảo các số đếm là khác nhau.ể

Mô tả:

void SXDem(int a[],int n); {

For ( 0;i<n;i++) {Count[i]=0;} i=

Thủ tục này trả lại s là mảng các phần tử của A đã sắp thứ tự

Ví dụ: Giả sử mảng đã cho là A = (2, 6, 4, 8, 3, 5) Trong mỗi dòng dưới đây là tình

Trang 16

2.3 Các thuật toán tìm kiếm sơ cấp trên mảng

Tìm kiếm là một thao tác đóng vai trò quan trọng trong nhiều tính toán và được áp dụng nhiều trong thực tế Có nhiều yêu cầu tìm kiếm khác nhau cũng như có nhiều thuật toán tìm kiếm khác nhau Ta sẽ quan tâm tới một số thuật toán tìm kiếm sơ cấp trên mảng Bài toán đặt ra là tìm một phần tử trong mảng A[0 n-1] cho trước có giá trị bằng T cho trước Có thể , xét bài toán tổng quát hơn là tìm một phần tử trong mảng A[0 n-1] cho trước có tính chất P cho trước

a Tìm tuần tự: Để tìm kiếm tuần tự một phần tử x cho trước trong mảng A ta sử dụng

một vòng lặp, tại mỗi bước của vòng lặp ta thao tác với một phần tử xác định của mảng, ở đây cụ thể là so sánh lần lượt mỗi phần tử của mảng với (hay kiểm tra xem phần tử tương x

ứng có tính chất P hay không) Thao tác này được gọi là duyệt mảng Việc duyệt mảng được dừng lại khi bắt gặp một phần tử bằng hoặc đã so sánh hết các phần tử của mảng Khi gặp x

một phần tử bằng (hay có tính chất P) ta sẽ ghi nhận sự kiện này.x

Mô tả: Duyệt (so sánh mỗi phần tử mảng với ) toàn bộ mảng cho tới khi gặp một phần x

Trang 17

b Tìm nhị phân: Trong tình huống A là mảng sắp thứ tự ta có thuật toán tìm kiếm tốt hơn tìm tuần tự, đó là tìm kiếm nhị phân Ý tưởng của thuật toán tìm nhị phân là ta so sánh x

với phần tử ở giữa mảng A, dựa vào kết quả so sánh mà kết thúc hoặc tiếp tục tìm kiếm bằng cách thu hẹp phạm vi tìm kiếm còn bằng một nửa phạm vi trước đó Quá trình này được thực hiện lặp đi lặp lại cho tới khi gặp một phần tử bằng hoặc phạm vi tìm kiếm là x

rỗng

Mô tả thuật toán: Giả sử A[0 n-1] là mảng sắp thứ tự tăng

int timnhiphan(int a[], int int n, x)

Return vt;

}

Nếu x < A[g] thì với mọi chỉ số k > g ta có < A[g] < A[k] (do mảng sắp thứ tự tăx ng),

do đó ta chỉ cần tìm trong mảng ứng với các chỉ số k < g Lúc này ta thu hẹp phạm vi tìm x kiếm bằng cách xác định lại vị trí cuối của phạm vi là g-1

Nếu x > A[g] thì ta thực hiện một hành động tương tự là tìm trong mảng ứng với các x chỉ số k > g Lúc này ta thu hẹp phạm vi tìm kiếm bằng cách xác định lại vị trí đầu của phạm vi là g+1

Phạm vi là rỗng khi d > c

Ví dụ: Giả sử A = (1, 6, 14, 17, 22, 25, 25, 30,45)

- Với = 14 ta có các bước tìm kiếm được thể hiện như saux

Trước khi vào vòng lặp: d=0, c=8, g=-1, vt=-1

Trang 18

Kết luận giá trị x có trong mảng tại vị trí thứ 2

- Với = 50 ta có các bước tìm kiếm được thể hiện như saux

Trước khi vào vòng lặp: d=0, c=8, g=-1, vt=-1

Trang 19

2.4 BÀI TẬP

1 Đánh giá độ phức tạp của các thuật toán tìm kiếm sơ cấp

2 Mô tả thuật toán Tìm phần tử lớn thứ nhì trong một dãy tùy ý có n phần tử và

đánh giá độ phức tạp của thuật toán (Lưu ý: phần tử lớn thứ nhì có thể không tồn tại)

3 Chạy từng bước thuật toán tìm nhị phân trên một mảng cụ thể có 16 phần tử, xét hai trường hợp: phần tử cần tìm có trong mảng và không có trong mảng

4 Mô tả một thuật toán Tìm phần tử xuất hiện nhiều lần nhất trong một dãy tùy ý có

nphần tử và đánh giá độ phức tạp của thuật toán đã mô tả

Trang 20

2.5 Đệ quy

2.5.1 Thuật toán đệ quy

Các thuật toán đệ quy đóng vai trò quan trọng trong việc giải quyết nhiều bài toán Một thuật toán đệ quy là thuật toán có yêu cầu thực hiện lại chính thuật toán đó với mức độ dữ liệu thấp hơn Các thuật toán đệ quy có liên quan chặt chẽ với các định nghĩa đệ quy hoặc các quan hệ truy hồi

Một thuật toán đệ quy gồm 2 phần:

Phần cơ sở: là các trường hợp không cần thực hiện lại thuật toán

Phần đệ quy: là các trường hợp yêu cầu thực hiện lại thuật toán

2.5.2 Một số bài toán

Bài toán 1: Tìm ước số chung lớn nhất (ƯCLN của hai số tự nhiên a và b cho trước.)

Có nhiều thuật toán khác nhau để giải bài toán này, ở đây ta sử dụng một tính chất của ƯCLN là: nếu a > b thì ƯCLN(a,b)=ƯCLN(a b,b) Thuật toán được mô tả như sau:-

int Ucln(int int b); { a, // gỉả thiết b<>0

If (a==b||b==1) { return b;} // phần cơ sở

Bài toán 2 : Định nghĩa các số Fibbonacci như sau: f 0 =1, f 1 =1, f n =f n-1 +f n-2với n>1 Tính

f n với n cho trước

Thuật toán đệ quy sử dụng ngay định nghĩa đệ quy của các số Fibbonacci, vì thế thuật toán chỉ là diễn đạt lại định nghĩa

int Fib(int n); {

If (n==0 || n=1) return 1;} {

return Fib(n-1)+Fib(n-2); }

Trang 21

Bài toán 3 : Tháp Hà Nội Có 3 cột 1, 2, 3 Trên cột 1 có n đĩa bán kính khác nhau được

xếp theo thứ tự đĩa lớn nằm dưới, đĩa nhỏ nằm trên Các đĩa có thể được chuyển từ cột này sang cột khác Quy tắc chuyển như sau:

Mỗi lần chỉ được chuyển 1 đĩa Không được đ đĩa lớn nằm trên đĩa nhỏ ể

Viết thuật toán chuyển n đĩa từ cột 1 sang cột 3 dùng cột 2 làm trung gian

Để giải quyết bài toán ta chỉ cần giả thiết đã chuyển được n-1 đĩa nhỏ nhất từ cột 1 tới cột 2 Khi đó ta chỉ cần chuyển đĩa lớn nhất từ cột 1 tới cột 3 sau đó chuyển n-1 đĩa từ cột 2 tới cột 3

Như vậy ta cần một thủ tục có thể chuyển đĩa nhỏ nhất từ cột i tới cột j Thủ tục này sẽ m

làm việc như sau: đầu tiên chuyển m-1 đĩa từ cột i tới cột 6-i-j, sau đó chuyển 1 đĩa từ cột i tới cột j, cuối cùng chuyển m-1 đĩa từ cột 6 j tới cột j -i-

Mô tả thủ tục như sau

void HanoiTower(int n,int i, int j)

Mỗi ngôn ngữ lập trình cho phép sử dụng đệ quy đều dùng một ngăn xếp các bản ghi

kích hoạt để ghi lại các giá trị của các biến thuộc về một thủ tục đang kích hoạt (tức là thủ tục đang được yêu cầu thực hiện) Khi một thủ tục P được gọi, một bản ghi kích hoạt mới cho P được đặt vào ngăn xếp mà không cần biết đã có bản ghi kích hoạt nào khác của P trong ngăn xếp hay chưa Khi P kết thúc, bản ghi kích hoạt của nó phải ở trên đỉnh của ngăn xếp vì P không thể kết thúc chừng nào tất cả các thủ tục mà nó đã gọi chưa kết thúc Vì vậy

Trang 22

ta có thể lấy bản ghi kích hoạt cho lời gọi P để kiểm soát điểm mà P đ c gọi (điểm này còn ượđược gọi là địa chỉ trả lại, lưu trong bản ghi kích hoạt của P khi gọi P) Như vậy có một cơ chế có thể sử dụng để loại bỏ đ quy ệ bằng cách tạo ra một ngăn xếp thích hợp

Việc sử dụng ngăn xếp để loại bỏ đệ quy sẽ được xem xét trong giáo trình Cấu trúc dữ liệu Ở đây ta chỉ xem xét một số tình huống loại bỏ đệ quy khi lời gọi đệ quy nằm ở cuối

thủ tục (còn gọi là đệ quy đuôi)

Nếu thủ tục P(x) có lời gọi đệ quy P(y) ở cuối thủ tục thì ta có thể thay thể lời gọi này bằng phép gán x=y sau đó nhảy tới đầu thủ tục P Ở đây y có thể là một biểu thức, nhưng x phải là tham số truyền theo giá trị, sao cho giá trị của nó được lưu ở vị trí cục bộ riêng biệt với lời gọi P

Điều kiện dừng vòng lặp sẽ tương ứng với phần cơ sở trong thủ tục đệ quy

Ví dụ 1: Khử đệ quy của thuật toán tìm ƯCLN của hai số tự nhiên a và b

Thuật toán được mô tả như sau:

int Ucln2(int int b); { a,

Ví dụ 2: Khử đệ quy của thuật toán tìm các số Fibbonacci trong bài toán 2

Thuật toán tìm các số Fibbonacci trong bài toán 2 có dạng đệ quy đuôi, vì vậy ta có thể khử đệ quy như sau:

Trang 23

Các thuật toán đệ quy có ưu điểm là mô tả ngắn gọn, đặc biệt khi giải quyết các bài toán có đặc trưng đệ quy Tuy nhiên do bản chất, việc cài đặt các thuật toán đệ quy thường đòi hỏi nhiều không gian, vì vậy dễ xảy ra tình trạng thiếu bộ nhớ nếu như số lần gọi đệ quy quá nhiều Ngoài ra việc gọi đệ quy trong nhiều trường hợp dẫn tới việc tính lại nhiều lần cùng một giá trị, gây lãng phí về mặt thời gian

Trang 24

2.6 BÀI TẬP

1 Sử dụng tính chất sau của ƯCLN đ đưa ra một thuật toán để ệ quy tìm ƯCLN(a,b): Nếu a>b thì ƯCLN(a,b)=ƯCLN(a mod b,b) và ƯCLN(a,0)=a với a>0 Khử đệ quy của thuật toán này

2 Mô tả thuật toán Sắp xếp hèn dạng đC ệ quy

3 Mô tả thuật toán Tìm Tuần tự dạng đệ quy

4 Viết 2 thuật toán đệ quy tính tổng các số hạng của một dãy số

5 Một robot có thể đi các bước 1m hoặc 2m

a/ Tính số cách đi để robot đ được n méti

b/ Liệt kê tất cả các cách đi đó

Mô tả thuật toán tương ứng với các yêu cầu trên

6 Một robot có thể đi các bước 1m hoặc 2m hoặc 3m

a/ Tính số cách đi để robot đ được n méti

b*/ Liệt kê tất cả các cách đi đó

Mô tả thuật toán tương ứng với các yêu cầu trên

7 Đổi cơ số Để chuyển biểu diễn của một số từ cơ số b sang cơ số 10 ta có thể làm

như sau: Giả sử ta đã biết cách chuyển một số nguyên với chữ số trong cơ số và ta muốn n b

chuyển một số nguyên có +1 chữ số Viết 2 thuật toán đn ệ quy theo cách tiếp cận này (một cách gọi ệ đ quy theo n chữ số đầu, cách kia gọi đệ quy theo n chữ số cuối)

8 Viết thuật toán đệ quy in ra chuỗi đảo ngược của một chuỗi cho trước

9 Cho dãy m số nguyên tuỳ ý Viết thuật toán đệ quy tìm và gán cho B giá trị lớn

nhất trong dãy, đồng thời tìm và gán cho N giá trị lớn thứ nhì trong dãy

10 Hàm f(n) xác định trên tập các số nguyên không âm như sau:

f(0)=0, f(1)=1, f(2n)=f(n), f(2n+1)= f(n)+f(n+1) với n=1, 2,

Trang 25

Mô tả hai thuật toán đệ quy và không đệ quy tính f(N) với N cho trước

11 *Bài toán Josephus: Có một nhóm N người Để chọn một người đi làm một

nhiệm vụ đặc biệt người ta cho N người này đứng thành vòng tròn Bắt đầu đếm từng người

từ một vị trí nào đó theo chiều kim đồng hồ Mỗi khi gặp người thứ M thì loại người đó ra khỏi vòng tròn Tiếp tục quá trình đếm cho tới khi chỉ còn lại một người Đó chính là người được chọn Ví dụ với N=9, M=5 và đếm từ 1 Khi đó thứ tự người bị loại ra lần lượt là: 5, 1,

7, 4, 3, 6, 9, 2 Người còn lại là người thứ 8

Mô tả một thuật toán đệ quy cho biết người được chọn

Có thể tìm được một thuật toán không đệ quy giải bài toán này không?

Trang 26

PHẦN 2 MỘT SỐ KỸ THUẬT THIẾT KẾ THUẬT TOÁN

Phần này giới thiệu một số kỹ thuật thiết kế thuật toán như Chia để trị, Quy hoạch động,

Tham lam, v.v đồng thời giới thiệu các bài toán điển hình có áp dụng các kỹ thuật này

1 CHIA ĐỂ TRỊ

1.1 Mở đầu

Chia để trị là một kỹ thuật thiết kế thuật toán bằng cách chia bài toán đã cho thành một

số bài toán con hoàn toàn tương tự với bài toán đã cho nhưng với kích thước đầu vào nhỏ hơn, giải quyết lần lượt và độc lập các bài toán con này (có thể áp dụng kỹ thuật này đối với các bài toán con), sau đó kết hợp các lời giải của các bài toán con để nhận được lời giải cho bài toán ban đầu

Có thể mô tả một cách tổng quát kỹ thuật này như sau:

void DQ(x); // trả lại một lời giải với đầu vào x

{

If x đủ nhỏ hoặc đơn giản then return ADHOC(x);

Else

Tách x thành các đầu vào nhỏ hơn x1, …, xk;

For i=1 to k do yi=DQ(xi);

Trang 27

đầu vào thành những đầu vào nhỏ hơn và có khả năng kết hợp các lời giải con lại một cách hiệu quả Phải quyết định khi nào thì sử dụng thuật toán cơ sở

Các thuật toán chia để trị, do cách tiếp cận, thường có mô tả tự nhiên dạng đệ quy

1.2 Tìm nhị phân

Bài toán: Giả sử T[n] là mảng sắp thứ tự tăng và x là phần tử nào đó Tìm vị trí để chèn

x vào T sao cho T vẫn là mảng sắp thứ tự

Cụ thể là ta muốn tìm chỉ số i sao cho 0  i <= n và T[i-1] <= x < T[i] với quy ước logic

là T[-1]=- và T[n]=+ Khi   đó vị trí để chèn x vào T là i

Theo cách tiếp cận chia để trị ta sẽ chia phạm vi tìm kiếm thành 2 phần và xác định sẽ

tiếp tục tìm kiếm ở phần nào Do tính chất sắp thứ tự tăng của mảng nên ta thấy:

- Nếu với chỉ số i nào đó mà T[i] < =x thì với mọi chỉ số j < i ta cũng có T[j] <= x Khi

Nếu như x > T[g] thì phạm vi tìm kiếm tiếp tục sẽ là [g= +1,c]

Ngược lại, nếu x T[g] thì phạm vi tìm kiếm tiếp tục sẽ là [< d,g]

Thuật toán có thể mô tả như sau:

int TimNP2(int T[n],int int int x); { i, j,

If (i==j&&T[i]< x) { return i+1; }

Trang 28

ban đầu vì vậy ta không cần kết hợp các lời giải của các bài toán con Nếu như phần tử x có trong mảng thì vị trí chèn x vào mảng tìm được theo thuật toán này sẽ là vị trí xuất hiện cuối cùng của x (nếu có nhiều giá trị bằng x) trong mảng

1.3 Sắp xếp trộn

Giả sử T[n] là mảng, ta đã biết một số thuật toán sơ cấp sắp xếp mảng với độ phức tạp

là O(n2) Theo cách tiếp cận chia để trị ta có một số thuật toán sắp xếp mảng hiệu quả hơn

Để sắp xếp trộn, ta sẽ chia T thành hai phần có kích thước gần bằng nhau và sắp xếp các phần này bằng lời gọi đệ quy, sau đó trộn các kết quả, lưu ý bảo toàn thứ tự Công việc chủ yếu chỉ là trộn 2 mảng sắp thứ tự thành 1 mảng sắp thứ tự Mô tả thuật toán như sau: void SXTron(T,i,j);

If j-i đủ nhỏ then Insert(T,i,j);

Trang 29

pháp chọn phần tử đ đặt mốc Ở y ta sẽ chọn phần tử đầu tiên của mảng làm mốc.ể đâ

Mô tả thuật toán:

Trong thuật toán này ta đã chọn thuật toán sắp xếp chèn làm thuật toán sắp xếp cơ sở.Thủ tục Datmoc có thể mô tả như sau:

Đánh giá độ phức tạp của thuật toán QuyckSort: Giả thiết mảng đã cho có kích thước

n = 2k và giả sử ở mỗi lần đặt mốc mảng được chia làm hai phần có kích thước gần bằng nhau Dễ thấy độ phức tạp của thủ tục Datmoc là n Gọi thời gian chạy của thuật toán

QuyckSort với mảng kích thước n là h( n)

Khi đó theo mô tả của thuật toán ta có h(n) = 2h(n /2)+n

Trang 30

Ta có h(n) = 2(2h( n /4)+n/2)+n = 4h( /4) + 2 = = n n n h(1)+kn =kn

Do đó ta có h( ) = O(n n logn).

1.5 Tính lũy thừa

Nhập môn về Mật mã: vì một lý do nào đó An và Bình muốn thiết lập một bí mật chung

Họ chỉ có thể giao tiếp với nhau qua điện thoại Tuy nhiên họ biết chắc rằng mọi cuộc điện

thoại của họ đều bị C nghe lén (ta nói An và Bình giao tiếp qua một kênh không an toàn)

Vấn đề đặt ra là làm thế nào An và Bình có thể thiết lập được một bí mật chung mà C không thể biết được bí mật này mặc dù C có thể biết hết nội dung mọi cuộc trao đổi giữa An

và Bình? Nói khác đi là có hay không một nghi thức để An và Bình có thể công khaitạo ra một khóa bí mật an toàn Một khóa bí mật được gọi là an toàn nếu thời gian để tìm ra khóa

là không chấp nhận được

Trong Mật mã học có một yêu cầu khác liên quan tới việc tạo ra khóa bí mật là việc tạo

ra khóa phải tương đối đơn giản và không mất nhiều thời gian

Vấn đề này lần đầu tiên được Diffie và Hellman giải quyết vào năm 1976 Cách làm như sau: Đầu tiên An và Bình thỏa thuận chọn một số nguyên p nào đó có hàng trăm chữ số và một số nguyên g , 2<g<p-1 Tất nhiên là C biết rõ hai số này Tiếp theo An và Bình, độc lập với nhau, mỗi người chọn ngẫu nhiên một số là A và B tương ứng

Sau đó An tính số a=gA mod p và gửi số a cho Bình, tương tự Bình tính và gửi cho An

số b g= B mod Cuối cùng An tính x= b p A mod và Bình tính p y =aB mod p

Dễ thấy x=yvà không ai có thể kiểm soát trước được giá trị này Đây có thể coi là bí mật chung của An và Bình mà họ hy vọng là C sẽ không bi được ết

Dễ dàng chứng minh được rằng nếu A’ khác với A mà gA mod p = gA’ mod p thì bA’

Trang 31

giây và thậm chí nếu chỉ có khoảng 30 chữ số thì thời gian thực hiện /2 lần lặp cũng mất p p

khoảng 4 tỉ năm Cho tới nay vẫn chưa có thuật toán nào có thể tính x từ p, , và mà g a b

không phải tính logarithm rời rạc

Vì vậy hiện nay C vẫn không thể biết được bí mật của An và Bình mặc dù người ta vẫn chưa thể chứng minh được điều này Nói cách khác độ an toàn của mật mã khóa công khai vẫn còn được đảm bảo chừng nào người ta còn chưa tìm ra được thuật toán hiệu quả hơn để

tính x từ p, , và mà g a b không phải tính logarithm rời rạc

Nếu C sử dụng thuật toán nói trên để tìm A thì An và Bình cũng không thể sử dụng thuật toán tương tự như vậy để tính a, , và b x y Chẳng hạn như thuật toán sau

Trang 32

leûAneáu 0Aneáu

)1(1

0)

(

div A h A h A h

Gọi s là độ dài của dãy nhị phân biểu diễn số A với A>1, khi đó dễ dàng chứng minh được s  h(A)  2s Như vậy với , A, B có 200 chữ số An chỉ phải thực hiện không quá p

3000 phép nhân các số có 200 chữ số và không quá 3000 phép nhân các số có 400 chữ số Điều này là hoàn toàn có thể chấp nhận được

Ta minh họa qua một ví dụ: tính g35

Trang 33

thuật chia để trị tính số Fibbonacci thứ n

Gợi ý: áp dụng kỹ thuật chia để trị ể tính đ lũy thừa

5 Giả sử T[n] là mảng phần tử Dễ dàng tìm phần tử lớn nhất của T bằng cách n

thực hiện 1 phép so sánh như saun

-max=T[0]; index=0;

For ( 1;i<n;i++) { If (max < T[i]) { max=T[i]; index=i;} }i=

Ở đây ta chỉ quan tâm tới số phép so sánh giữa các phần tử Ta có thể tiếp tục tìm phần

tử nhỏ nhất của T bằng cách thực hiện 2 phép so sánh nữa như saun

-T[index]=T[0]; min=T[1 ];

For ( 2;i<n;i++) { If (min > T[i]) { min=T[i]; } }i=

Hãy đưa ra một thuật toán có thể tìm cả phần tử lớn nhất và phần tử nhỏ nhất của mảng gồm phần tử bằng cách thực hiện ít hơn 2 3 phép so sánh Có thể giả thiết n n- n là lũy thừa của 2

Giải quyết trường hợp n không là lũy thừa của 2

6 Giả sử T[n] là mảng sắp thứ tự tăng các số nguyên đôi một khác nhau, một vài số trong mảng có thể là số âm

Trang 34

8 Lịch thi đấu Cần tổ chức cuộc thi đấu có người tham gia Mỗi người tham gia n

thi đấu một lần với mỗi đối thủ Hơn nữa mỗi đấu thủ phải thi đấu ng một trận trong một đúngày ngoại trừ là có thể nghỉ thi đấu một ngày

Nếu n là lũy thừa của 2, hãy đưa ra một thuật toán xây dựng một lịch thi đấu cho phép cuộc thi kết thúc sau n-1 ngày

9 *Với mỗi số nguyên n >1 hãy đưa ra thuật toán xây dựng một lịch thi đấu cho phép cuộc thi kết thúc sau 1 ngày nếu chẵn hoặc sau ngày nếu lẻ Chẳng hạn với n- n n n

10 Phần tử đa số Giả sử T[n] là mảng phần tử Một phần tử được gọi là phần tử n x

đa số trong T nếu tập hợp {i | T[i]= } có nhiều hơn /2 phần tử Hãy đưa ra một thuật toán x n

chia để trị xác định xem mảng T có phần tử đa số hay không và tìm phần tử đó nếu nó tồn tại

Trang 35

2 QUY HOẠCH ĐỘNG

2.1 Mở đầu

Phương pháp quy hoạch động thường được áp dụng để giải quyết các bài toán tối ưu, vì vậy đây là một phương pháp có ý nghĩa trong việc giải quyết các bài toán thực tế Phương pháp này dựa trên nguyên lý tối ưu của Bellman: Nếu một dãy các lựa chọn là tối ưu thì mọi dãy con của nó cũng là tối ưu Ngoài ra ý tưởng của phương pháp quy hoạch động cũng rất

đơn giản: để tránh tính lại cùng một đối tượng hơn một lần, ta lưu lại các kết quả đã tính

được trong một bảng và chỉ việc sử dụng lại khi cần

Khác với Chia để trị là kỹ thuật tiếp cận từ trên xuống, Quy hoạch động là kỹ thuật tiếp cận từ dưới lên Theo kỹ thuật Chia để trị ta xuất phát từ bài toán đã cho, chia nhỏ dần bài

toán cho đến khi gặp trường hợp đơn giản thì giải quyết trực tiếp rồi kết hợp các lời giải Còn trong kỹ thuật Quy hoạch động ta thường bắt đầu từ những trường hợp đơn giản nhất Bằng cách kết hợp các kết quả đã có ta thu được lời giải cho những trường hợp có kích thước lớn hơn, cho tới khi nhận được lời giải của bài toán ban đầu

Ví dụ: Tính hệ số nhị thức C(n,k)

Nếu ta sử dụng công thức truy hồi C(n,k)=C(n-1,k)+C(n-1,k-1) để tính nhờ một thủ tục

đệ quy như sau

đó mỗi ô (i,j) chính là giá trị của C(i,j)

Thuật toán tương ứng có thể mô tả như sau:

int C2(n,k);

Ngày đăng: 24/09/2022, 20:35

TRÍCH ĐOẠN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w