Professional Documents
Culture Documents
1
CHƯƠNG 1. MỘT SỐ VẤN ĐỀ CƠ SỞ
Chương 1. Một số vấn đề cơ sở 12/12/2020
• Thuật toán được hiểu là sự đặc tả chính xác một dãy các bước có
thể thực hiện được một cách máy móc để giải quyết một vấn đề.
• Mỗi thuật toán có 1 dữ liệu vào (Input) và 1 dữ liệu ra (Output);
➔ khi thực hiện thuật toán (thực hiện các bước đã mô tả), thuật
toán cần cho ra các dữ liệu ra tương ứng với các dữ liệu vào.
12/12/2020
• Tính đúng đắn(correctness): khi thực hiện nó phải cho ra các dữ liệu mà ta
mong muốn tương ứng với các dữ liệu vào.
• Tính hiệu quả (efficiency): lựa chọn thuật toán để áp dụng dựa vào các tiêu
chí: TT đơn giản, dễ hiểu; TT dễ cài đặt (dễ viết chương trình); TT cần ít bộ
nhớ; TT chạy nhanh.
12/12/2020
• Tính đơn định: mỗi bước của thuật toán, các thao tác phải hết sức rõ ràng khong gây nên sự nhập nhằng, đa
nghĩa. Thực hiện đúng các bước của thuật toán thì với 1 dữ liệu vào chi có duy nhất 1 dữ liệu ra.
• Tính dừng: thuật toán phải dừng và cho kết quả sau 1 số hữu hạn bước, không được phép rơi vào vòng vô
hạn.
• Tính đúng: sau khi thực hiện thuật toán với quá trình đã định, ta phải được kết quả mong muốn với mọi bộ
dữ liệu đầu vào. Kết quả đó được kiểm chứng bằng yêu cầu bài toán.
• Tính phổ dụng: thuật toán phải dễ sửa đổi để thích ứng với bất kỳ bài toán nào trong một lớp các bài toán và
có thể làm trên các dữ liệu khác nhau
• Tính khả thi: kích thước phải đủ nhỏ, hiệu quả của thuật toán sẽ bằng 0 nếu kích thước của nó vượt khả
năng lưu trữ của bộ nhớ; thuật toán phải được kết thúc trong thời gian cho phép; Phải dễ hiểu và dễ cài đặt
12/12/2020
Để đảm bảo tính chính xác, chỉ có thể hiểu một cách duy nhất, thụât toán cần
được mô tả:
• Dưới dạng mã (code): trong một ngôn ngữ lập trình thành một chương trình
(hoặc một hàm, một thủ tục);
• Dưới dạng giả mã (pseudo code): trình bày một thuật toán để cho ngắn gọn
nhưng vẫn đảm bảo đủ chính xác, sử dụng các câu lệnh trong một ngôn ngữ
lập trình (pascal hoặc C++) và cả các ký hiệu toán học, các mệnh đề trong
ngôn ngữ tự nhiên
12/12/2020
Tính hiệu quả của thuật toán: gồm 2 yếu tố: độ phức tạp không gian (dung
lượng bộ nhớ lưu dữ liệu vào, dữ liệu ra, và các kết quả trung gian khi thực hiện
thuật toán) và độ phức tạp thời gian (thời gian thực hiện thuật toán)
12/12/2020
Độ phức tạp tính toán của giải thuật: thời gian thực hiện 1 giải thuật bằng máy tính phụ thuộc
vào nhiều yếu tố:
• Kích thước dữ liệu vào: DL càng lớn thì thời gian xử lý càng chậm. Nếu gọi kích thước
DLvào là n thì thời gian thực hiện giải thuật có thể biểu diễn một cách tương đối như 1 hàm
của n: T(n)
• Phần cứng máy tính, ngôn ngữ viết CT và CTD đều ảnh hưởng đến thời gian thực hiện.
Tuy nhiên các yếu tố này không giống nhau trên các loại máy → không thể xác định T(n) dựa
trên chúng được, nói cách khác T(n) không thể biểu diễn bằng đơn vị đo thời gian được
12/12/2020
Vẫn cần phải có cách để so sánh các giải thuật về mặt tốc độ.
Nếu 2 giải thuật có thời gian tính toán là: T1(n) = n2 và T2(n) = 100n thì với n đủ
lớn, giải thuật T2 có thời gian thực hiện nhanh hơn T1
Nói: thời gian thực hiện thuật toán tỷ lệ với n và n2 cho phép ta đánh giá một
cách tương đối về tốc độ thực hiện của giải thuật đó khi n khá lớn. Cách đánh
giá giải thuật độc lập với máy tính và các yếu tốc liên quan đến máy tính đó gọi
là độ phức tạp tính toán của giải thuật
12/12/2020
Cho f và g là 2 hàm xác định tương đương với mọi n. Hàm f(n) được gọi là
O(g(n)) nếu tồn tại 1 hằng số c>0 và một giá trị n0 sao cho:
f(n) c.g(n) với mọi n ≥ n0
Khi đó, nếu f(n) là thời gian thực hiện 1 giải thuật thì giải thuật đó có cấp g(n),
ký hiệu là O(g(n)) – ký pháp O lớn
12/12/2020
Một số hàm số dùng để ký hiệu độ phức tạp tính toán và bảng giá trị của chúng
Log2n n nlog2n n2 n3 2n
0 1 0 1 1 2
1 2 2 4 8 4
2 4 8 16 64 16
3 8 24 64 512 256
4 16 64 256 4096 65536
5 32 160 1024 32768 2147483648
12/12/2020
• Một chương trình chạy đúng không có nghĩa là việc lập trình đã xong, ta cần sửa đổi
lại một một vài chi tiết để chương trình có thể chạy nhanh hơn, hiệu quả hơn.
• Trước khi kiểm thử, cần đặt mục tiêu viết chương trình đơn giản: chương trình chạy
ra kết quả đúng, sau đó khi tối ưu chương trình, ta xem lại những chỗ nào viết chưa
tốt thì tối ưu lại mã lệnh để chương trình ngắn hơn, chạy nhanh hơn.
• Không nên viết đến đâu tối ưu đến đấy, vì chương trình có mã lệnh tối ưu thường
phức tạp và khó kiểm soát
• Việc tối ưu chương trình dựa trên các tiêu chuẩn sau:
12/12/2020
• Tính tin cậy: Chương trình phảu chạy đúng như dự định, mô tả đúng một giải thuật
đúng. Khi viết chương trình, nên kiểm tra tính đúng đắn của các bước mỗi khi có thể
• Tính uyển chuyển: chương trình phải dễ sửa đổi. Chương trình dễ sửa đổi sẽ làm
giảm bớt công sức của lập trình viên khi phát triển chương trình
• Tính trong sáng: chương trình viết ra phải dễ đọc, dễ hiểu, để sau một thời gian dài,
đọc lại vẫn hiểu được mình làm cái gì. Để nếu có điều kiện, thì có thể sửa sai ( nếu
phát hiện lỗi mới), cải tiến hay biến đổi để chương trình giải quyết bài toán khác.
Tính trong sáng của CT phụ thuộc rất nhiều vào công cụ lập trình và phong cách lập
trình
12/12/2020
• Tính hữu hiệu: chương trình phải chạy nhanh và ít tốn bộ nhớ tức là tiết kiệm được
cả không gian và thời gian. Tuy nhiên tiêu chuẩn này không cần được đề cao quá vì
phần cứng phát triển rất nhanh.
→Việc viết ra một chương trình đòi hỏi rất nhiều công đoạn và tiêu tốn nhiều công
sức, nghĩ ra cách giải quyết vấn đề đã khó, biến ý tưởng đó thành hiện thực cũng
không dễ chút nào.
→Không nên viết chương trình khi chưa suy xét kỹ về giải thuật và các dữ liệu cần
thao tác, vì có thể mắc lỗi sai giải thuật hoặc giải thuật không thể triển khai nổi trên
cấu trúc dữ liệu không phù hợp
12/12/2020
19 12/12/2020
12/12/2020
Viết chương trình tính n! bằng phương pháp lặp và phương pháp đệ
quy.
Tính thời gian thực hiện thuật toán cho cả 2 trường hợp.
Kết luận gì về thời gian thực hiện thuật toán theo 2 phương pháp đó.
12/12/2020
VÍ dụ 2:
( Hàm tính giai thừa của số nguyên dương n).
int Fact(int n)
{
if (n = = 1)
return 1;
else return n * Fact(n-1);
}
Giả sử thời gian chạy của hàm là T(n), với n = 1 ta có T(1) = O(1).
Với n > 1, ta cần kiểm tra điều kiện của lệnh if-else và thực hiện
phép nhân n với kết quả của lời gọi hàm, do đó T(n) = T(n-1) + O(1).
22
b. sum = 0;
for ( int i = 0; i < n; i + +)
for ( int j = 0; j < n*n; j + +)
for ( int k = 0; k < j; k + +)
sum + + ;
23 12/12/2020