Tại sao cần biết thời gian chạy của thuật toán • Trong thiết kế thuật toán • Định hướng thiết kế: từ input size + time limited → định hướng cần phải thiết kế thuật toán có phức tạp bao
Trang 1ƯỚC LƯỢNG
CỦA THUẬT TOÁN
Khoa Công nghệ thông tin Trường Đại học Ngoại ngữ - Tin học TP.HCM (HUFLIT)
Trang 2• Quy trình giải bài toán
• Đọc bài toán vài lần, gạch dưới những từ quan trọng
• Giải bài toán trên giấy, giải các ví dụ
• Tinh chỉnh lời giải
• Viết mã giả, dự kiến các hàm, các lớp
• Cài đặt chương trình: cài đặt từng bước của mã giả
• Kiểm tra/kiểm thử với những dữ liệu khác nhau, chạy từng bước để phát
Trang 3Nội dung
• Thời gian chạy của thuật toán
• Khái niệm Big O
• Quy tắc tính Big O
• Một số Big O thông dụng
Trang 4THỜI GIAN CHẠY
CỦA THUẬT TOÁN
Trang 5Tại sao cần biết thời gian chạy của thuật toán
• Trong thiết kế thuật toán
• Định hướng thiết kế: từ input size + time limited → định hướng cần phải
thiết kế thuật toán có phức tạp bao nhiêu → dùng phương pháp gì
• Đánh giá thuật toán có thể chạy trong thời gian cho phép không (trước
khi tiến hành cài đặt)
• Xác định những điểm yếu trong thuật toán để cải tiến
• Trong sử dụng thuật toán
• Trước khi sử dụng một thuật toán trong thư viện, cần biết thuật toán có
thời gian chạy bao nhiêu
• Trong so sánh các thuật toán
• Là thước đo các thuật toán cùng giải quyết một bài toán
Trang 6Thời gian chạy của thuật toán
• Phép toán cơ bản
• Ví dụ: phép gán, cộng, trừ, nhân, chia, so sánh, … (=
, +, −,×,/, >, <, …) trên các kiểu dữ liệu cơ bản
Phép toán cơ bản (primitive operators) là phép
thuộc vào kích thước dữ liệu
Trang 7Thời gian chạy của thuật toán
• Thời gian chạy của thuật toán
thuật toán thực hiện khi giải quyết bài toán
Trang 8Thời gian chạy của thuật toán
• Ví dụ: tính thời gian chạy của thuật toán sau
② for (int i=0; i<n; i++)
𝑇𝑇 𝑛𝑛 = 1 + 2𝑛𝑛 + 2 + 2𝑛𝑛 = 4𝑛𝑛 + 3
Trang 9Thời gian chạy của thuật toán
• Bài 1 tính thời giai chạy của thuật toán sau
• Bài 2 tính thời giai chạy của thuật toán sau
② for (int i=0; i<n; i++)
③ for (int j=0; j<i; j++)
Trang 10Thời gian chạy của thuật toán
• Bài 3 tính thời gian chạy của thuật toán sau
Trang 11Phân loại thời gian chạy của thuật toán
• Thời gian chạy của thuật toán có thể phân làm 3 loại
• Thời gian chạy trong trường hợp xấu nhất (worst case)
• Thời gian chạy trong trường hợp tốt nhất (best case)
• Thời gian chạy trong trường hợp trung bình (average case)
Trang 12Vấn đề với running time
• Việc đếm chính xác số lượng phép toán nhiều lúc rất
tốn công sức
• Cách tính running time vẫn còn phụ thuộc
• Ngôn ngữ lập trình: C, C#, Rust, Python, …
• Kỹ thuật cài đặt
Trang 13Vấn đề với running time
• Ví dụ: tính thời gian chạy của thuật toán sau
Algorithm: Sum of Array
② for ( int i=0; i<n; i++ )
③ sum = sum + a[i];
① sum=0;
② for ( int i=0; i<n; i=i+1 )
③ sum = sum + a[i];
Trang 14Vấn đề với running time
• Một công cụ đo lường hay ước lượng (estimate) running
time đơn giản hơn
• Vẫn có chức năng như running time
• Không phụ thuộc ngôn ngữ lập trình, kỹ thuật cài đặt
Trang 15KHÁI NIỆM BIG O
Trang 17Khái niệm Big O
• Ý nghĩa của Big (O)
• Nếu thuật toán có thời gian là 𝑂𝑂(𝑓𝑓(𝑛𝑛)) nghĩa là thuật toán có số
phép toán nhỏ hơn 𝑐𝑐 𝑓𝑓(𝑛𝑛) với c là một hằng số
• Ví dụ
• 𝑂𝑂(𝑛𝑛2) thuật toán có thời gian chạy tối đa 𝑐𝑐 𝑛𝑛2
• 𝑂𝑂(𝑛𝑛3) thuật toán có thời gian chạy tối đa 𝑐𝑐 𝑛𝑛3
• So sánh: thuật toán 𝑂𝑂(𝑛𝑛 2 ) có thời gian chạy nhanh hơn thuật toán
𝑂𝑂(𝑛𝑛3), …
• 𝑂𝑂( ) chỉ là ước lượng (estimate) thời gian chạy của chương trình
Trang 18QUY TẮC TÍNH BIG O
Trang 21BÀI TẬP VẬN DỤNG
Trang 22Vận dụng
• Bước 1. Tìm phép toán chạy nhiều nhất (dominant)
• Bước 2. Ước tính số lần thực hiện 𝑇𝑇(𝑛𝑛) của phép toán dominant
• Bước 3. Tính 𝐵𝐵𝑖𝑖𝐵𝐵 𝑂𝑂 của 𝑇𝑇(𝑛𝑛)
• Bỏ qua các hằng số
• Bỏ qua các biến có số mũ có bậc thấp hơn
Trang 23Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
{
return n;
}
Trang 24Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
int Sum(int[] a){
Trang 25Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
Trang 26Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
bool Sum(int[] a, int[] b){
Trang 27Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
bool Sum(int[,] a, int x)
{
sum += a[i,j];
return sum;
}
Trang 28Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
bool Sum(int[,] a, int x)
{
for (int j=0; j<i; j++)sum += a[i,j];
return sum;
}
Trang 29Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)sum[i] += entry[i][j][0];
for (int i=0; i<n; i++)
for (int k=0; k<n; k++)sum[i] += entry[i][0][k];
Trang 30Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
int Sum(int n)
{
while (n>0) {res += n;
n = n / 2;
}
return sum;
}
Trang 31Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
int Sum(int n)
{
int res = 0;
for (int i=1; i<=n; i++) {
int m=i, sum=0;
while (m>0) { sum += m;
m = m / 2;
} res += sum }
return res;
}
Trang 32Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
int Sum(int[,,] a)
return sum;
}
Trang 33Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
int Sum(int[,,] a)
{
for (int i=0; i<n; i++)
for (int j=0; j<i; j++)
for (int k=0; k<n; k++)sum += a[i,j,k];
return sum;
}
Trang 34Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
int Sum(int[,,] a)
{
for (int i=0; i<n; i++)
for (int j=0; j<i; j++)
for (int k=0; k<j; k++)sum += a[i,j,k];
return sum;
}
Trang 35Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
{
return true;
return false;}
Trang 36Vận dụng
• Tìm độ phức tạp thời gian của chương trình sau
sum += (i+j);
Trang 38MỘT SỐ BIG O RUN TIMES THÔNG DỤNG
Trang 39Một số độ phức tạp thời gian
Độ phức tạp thời gian Tên
𝑂𝑂(1) Constant time (Độ phức tạp hằng số) 𝑂𝑂(log log 𝑛𝑛) Double Logarithic time
𝑂𝑂(log 𝑛𝑛) Logarithic time
𝑂𝑂 log 𝑛𝑛 𝑐𝑐 với 𝑐𝑐 > 1 Polylogarithmic time 𝑂𝑂(𝑛𝑛𝑐𝑐) với 0 < 𝑐𝑐 < 1 Fractional power time
𝑂𝑂(𝑛𝑛) Linear time 𝑂𝑂(𝑛𝑛 log 𝑛𝑛) Loglinear hay linearithmic time 𝑂𝑂(𝑛𝑛2) Quadratic time
𝑂𝑂(𝑛𝑛𝑐𝑐) với 𝑐𝑐 > 2 Polynomial or algebraic time 𝑂𝑂(𝑐𝑐𝑛𝑛) với 𝑐𝑐 > 1 Exponential time
𝑂𝑂(𝑛𝑛!) Factorial time
Trang 40ƯỚC TÍNH BIG O
TỪ INPUT SIZE
Trang 41Định hướng lời giải từ input size
• Giả định
• Thời gian chương trình cần chạy trong 1-4 giây
• Máy thực hiện 10 8 phép toán trên 1 giây
n O(…) Số phép toán Định hướng Thuật toán
1.000.000.000 𝑙𝑙𝑙𝑙𝐵𝐵2(𝑛𝑛) 30 Sử dụng công thức hay tìm nhị
phân
1.000.000.000 𝑛𝑛 31.623
can = sqrt(n) for (i=0; i<can; i++) {…}
100.000.000 n 100.000.000
Một vòng lặp có dạng for (i=0; i<n; i++)
{…}
1.000.000 𝑛𝑛 𝑙𝑙𝑙𝑙𝐵𝐵2(𝑛𝑛) 19.931.569
Một vòng lặp và một tìm kiếm nhị phân
for (i=0; i<n; i++) { Tìm nhị phân/thuật toán chia đôi }
Trang 42Định hướng lời giải từ input size
n O(…) Số phép toán Định hướng Thuật toán
100,000 𝑛𝑛 (𝑙𝑙𝑙𝑙𝐵𝐵2(𝑛𝑛))2 27,588,016
for (i=0; i<n; i++) { Tìm nhị phân/thuật toán chia đổi {
Tìm nhị phân/thuật toán chia đổi }