Você está na página 1de 33

Đánh giá hiệu quả thuật toán

Bùi Việt Dũng

8th June 2022

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Độ phức tạp thời gian của thuật toán

Tại sao không viết hẳn chương trình ra để đánh giá hiệu quả
thuật toán:
Việc cài đặt chương trình rất tốn thời gian.
Tốc độ chạy chương trình trên các máy khác nhau là khác
nhau
Máy của thí sinh vs. Máy của giám khảo
Không chương trình nào chạy hai lần trên cùng một máy.
Độ phức tạp tính toán là một cách biểu diễn số phép tính
thuật toán cần thực hiện so với kích thước đầu vào.

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Thế nào là phép tính?
Phép tính là một hay nhiều câu lệnh có thời gian chạy không phụ
thuộc vào độ lớn của dữ liệu vào.
Ví dụ (ở các trường hợp thông thường):
Lệnh đọc một kí tự.
Lệnh gán.
Lệnh cộng, trừ, nhân, chia, so sánh hai số.
Lệnh dịch bit.
Lệnh khởi tạo một mảng biết số phần tử nhưng không đặt
trước các giá trị trong mảng.
Một số hàm Toán học đơn giản như
sin, cos, tan, cot, log, sqrt
Ta có thể ghép một cụm các câu lệnh trên nếu thời gian chạy
vẫn không phụ thuộc vào độ lớn của dữ liệu vào.
Không phải ví dụ:
Hàm sắp xếp (sort)
Hàm lũy thừa (pow)
Bùi Việt Dũng Đánh giá hiệu quả thuật toán
Ví dụ 1

Tính số phép tính của thuật toán sau theo n: Một đáp án có thể:
f (n) = n3 + 12 n(n + 1)

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Kí pháp chữ O lớn

Gọi f (n) là số phép tính thuật toán cần thực hiện nếu độ lớn của
dữ liệu vào là n (n có thể là độ dài mảng, độ lớn của số nguyên
được cho, ...)

f (n) ∈ O(g (n)) ⇔ ∃n0 , c > 0 : ∀n ≥ n0 : f (n) ≤ c · g (n)

Nói cách khác

O(g (n)) = {f (n)|∃n0 , c > 0 : ∀n ≥ n0 : f (n) ≤ c · g (n)}

(Để đơn giản, từ giờ cho đến hết bài trình bày này, chúng ta sẽ
quy ước hàm f có tập xác định là tập số nguyên dương và tập giá
trị là tập số thực dương)
Một số tập O hay gặp: O(1), O(log n), O(n2 ), O(n3 ), O(2n ), O(n!)

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Một số tính chất của O

Tính chất 1
f (n) ∈ O(f (n))

Chọn hàm f (n) bất kì


Để chứng minh f (n) ∈ O(f (n)), ta cần chứng minh
∃n0 , c > 0 : ∀n ≥ n0 : f (n) ≤ c · f (n)
Chọn n0 = c = 1 > 0
Chọn n ≥ n0 bất kì.
Ta có:
f (n) = f (n) ⇒ f (n) ≤ f (n) ⇒ f (n) ≤ 1 · f (n) ⇒ f (n) ≤ c · f (n)
Do cách ta chọn f (n), n0 , c, n, từ đây ta có thể kết luận
∀f (n) : ∃n0 , c > 0 : ∀n ≥ n0 : f (n) ≤ c · f (n)
⇒ ∀f (n) : f (n) ∈ O(f (n))

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Một số tính chất của O

Tính chất 2
Cho hàm f (n) ∈ O(g (n)) và c > 0. Khi đó cf (n) ∈ O(g (n))

Lấy hàm f (n), g (n) và số c bất kì sao cho f (n) ∈ O(g (n)) và
c>0
Do f (n) ∈ O(g (n)) nên ∃n0 , c > 0 : ∀n ≥ n0 : f (n) ≤ c · g (n)
và vì thế ta có thể chọn số n1 , c1 > 0 sao cho
∀n ≥ n1 : f (n) ≤ c1 · g (n)
Để chứng minh cf (n) ∈ O(g (n)), ta cần chứng minh
∃n2 , c2 > 0 : ∀n ≥ n2 : cf (n) ≤ c2 · g (n)
Chọn n2 = ... > 0, c2 = ... > 0
Chọn số n ≥ n2 bất kì
Do ∀n ≥ n1 : f (n) ≤ c1 · g (n) và n ≥ n2 ≥ n1 , f (n) ≤ c1 · g (n)
⇒ cf (n) ≤ cc1 · g (n) ⇒ cf (n) ≤ c2 · g (n)

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Một số tính chất của O
Tính chất 2
Cho hàm f (n) ∈ O(g (n)) và c > 0. Khi đó cf (n) ∈ O(g (n))

Lấy hàm f (n), g (n) và số c bất kì sao cho f (n) ∈ O(g (n)) và
c>0
Do f (n) ∈ O(g (n)) nên ∃n0 , c > 0 : ∀n ≥ n0 : f (n) ≤ c · g (n)
và vì thế ta có thể chọn số n1 , c1 > 0 sao cho
∀n ≥ n1 : f (n) ≤ c1 · g (n)
Để chứng minh cf (n) ∈ O(g (n)), ta cần chứng minh
∃n2 , c2 > 0 : ∀n ≥ n2 : cf (n) ≤ c2 · g (n)
Chọn n2 = n1 > 0, c2 = cc1 > 0
Chọn số n ≥ n2 bất kì
Do ∀n ≥ n1 : f (n) ≤ c1 · g (n) và n ≥ n2 = n1 , f (n) ≤ c1 · g (n)
⇒ cf (n) ≤ cc1 · g (n)
⇒ cf (n) ≤ c2 · g (n)
Do cách ta chọn n2 , c2 , n, ta có thể kết luận:
∃n2 , c2 > 0 : ∀n ≥ n2 : cf (n) ≤ c2 · g (n)
Bùi Việt Dũng Đánh giá hiệu quả thuật toán
Ứng dụng tính chất 2

Tính chất 2
Cho hàm f (n) ∈ O(g (n)) và c > 0. Khi đó cf (n) ∈ O(g (n))

Xét thuật
P toán
Psau:
f (n) = ni=1 nj=1 8 = 8n2 ∈ O(n2 )
g (n) = ni=1 nj=1 1 = n2 ∈ O(n2 )
P P

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Một số tính chất của O

Tính chất 3
Nếu f (n) ≤ g (n) thì O(f (n)) ⊂ O(g (n))

Giả sử ta có hai hàm f (n), g (n) sao cho f (n) < g (n)
Để chứng minh O(f (n)) ⊂ O(g (n)), ta cần chứng minh:
∀h(n) ∈ O(f (n)) : h(n) ∈ O(g (n))
Chọn hàm h(n) ∈ O(f (n)) bất kì
Khi đó ∃n0 , c > 0 : ∀n ≥ n0 : h(n) ≤ cf (n)
Do f (n) ≤ g (n) nên điều này nghĩa là
∃n0 , c > 0 : ∀n ≥ n0 : h(n) ≤ cg (n)
⇒ h(n) ∈ O(g (n))
Do ta chọn h(n) ∈ O(f (n)) bất kì, ta có thể kết luận
O(f (n)) ⊂ O(g (n)) và đây là điều phải chứng minh.

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Ứng dụng tính chất 3

Xét thuật toán sau:


Gọi độ phức tạp của thuật toán là f (n)
Ta có f (n) ≤ g (n) = n2
Do f (n) ≤ g (n) ⇒ O(f (n)) ⊂ O(g (n)) = O(n2 ), và
f (n) ∈ O(f (n)) nên f (n) ∈ O(n2 )

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Một số tính chất của O

Tính chất 4
Nếu f1 (n) ∈ O(g1 (n)) và f2 (n) ∈ O(g2 (n)) thì
f1 (n)f2 (n) ∈ O(g1 (n)g2 (n))

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Một số tính chất của O

Tính chất 5
Nếu f1 (n) ∈ O(g1 (n)) và f2 (n) ∈ O(g2 (n)) thì
f1 (n) + f2 (n) ∈ O(max{g1 (n), g2 (n)})

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Một số tính chất của O

Tính chất 6
Nếu f (n) ∈ O(g (n)) và g (n) ∈ O(h(n)) thì f (n) ∈ O(h(n))

Ứng dụng
Chứng minh rằng O(8n2 ) = O(n2 )
Chọn f (n) ∈ O(8n2 ) bất kì. Ta có 8n2 ∈ O(n2 ) (dùng tính chất 2,
chọn c = 8 > 0) nên theo tính chất 6,
f (n) ∈ O(n2 ) ⇒ O(8n2 ) ⊂ O(n2 )
Chọn f (n) ∈ O(n2 ) bất kì. Ta có n2 ∈ O(8n2 ) (dùng tính chất 2,
chọn c = 18 > 0) nên theo tính chất 6,
f((n) ∈ O(8n2 ) ⇒ O(n2 ) ⊂ O(8n2 )
O(8n2 ) ⊂ O(n2 )
⇒ O(n2 ) = O(8n2 )
O(n2 ) ⊂ O(8n2 )

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Mối quan hệ giữa O và giới hạn dãy số

Cho dãy số thực (un )


lim un = c ∈ R ⇔ ∀ϵ > 0 : ∃n0 > 0 : ∀n ≥ n0 : |un − c| < ϵ

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Mối quan hệ giữa O và giới hạn dãy số

Tính chất 7
Nếu lim gf (n)
(n) = c với c là một số thực không âm thì
f (n) ∈ O(g (n))

Giả sử lim gf (n)


(n) = c với c là một số thực không âm nào đó.
Khi đó ∀ϵ > 0 : ∃n0 ∈ N : ∀n ≥ n0 : | gf (n)
(n) − c| < ϵ
⇒ ∀ϵ > 0 : ∃n0 ∈ N : ∀n ≥ n0 : c − ϵ < gf (n)
(n) < c + ϵ
⇒ ∀ϵ > 0 : ∃n0 ∈ N : ∀n ≥ n0 : f (n) < (c + ϵ)g (n)
⇒ ∀ϵ > 0 : ∃n0 ∈ N : ∀n ≥ n0 : f (n) ≤ (c + ϵ)g (n)
Ta cần chứng minh f (n) ∈ O(g (n)), tức
∃n0 , k > 0 : ∀n ≥ n0 : f (n) ≤ k · g (n)
Lấy k = ... > 0
Lấy ϵ = ... > 0, ta sẽ chọn được n0 > 0 sao cho
∀n ≥ n0 : f (n) ≤ (c + ϵ)g (n) ⇒ ∀n ≥ n0 : f (n) ≤ k · g (n)

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Mối quan hệ giữa O và giới hạn dãy số
Tính chất 7
Nếu lim gf (n)
(n) = c với c là một số thực không âm thì
f (n) ∈ O(g (n))

Giả sử lim gf (n)


(n) = c với c là một số thực không âm nào đó.
Khi đó ∀ϵ > 0 : ∃n0 ∈ N : ∀n ≥ n0 : | gf (n)
(n) − c| < ϵ
⇒ ∀ϵ > 0 : ∃n0 ∈ N : ∀n ≥ n0 : c − ϵ < gf (n) (n) < c + ϵ
⇒ ∀ϵ > 0 : ∃n0 ∈ N : ∀n ≥ n0 : f (n) < (c + ϵ)g (n)
⇒ ∀ϵ > 0 : ∃n0 ∈ N : ∀n ≥ n0 : f (n) ≤ (c + ϵ)g (n)
Ta cần chứng minh f (n) ∈ O(g (n)), tức
∃n0 , k > 0 : ∀n ≥ n0 : f (n) ≤ k · g (n)
Lấy k = c + 1 > 0
Lấy ϵ = 1 > 0, ta sẽ chọn được n0 > 0 sao cho
∀n ≥ n0 : f (n) ≤ (c + 1)g (n) ⇒ ∀n ≥ n0 : f (n) ≤ k · g (n)
Do cách ta chọn k và n0 , ta có thể kết luận
∃k, n0 > 0 : ∀n ≥ n0 : f (n) ≤ k · g (n)
⇒ f (n) ∈ O(g (n)) Bùi Việt Dũng Đánh giá hiệu quả thuật toán
Độ phức tạp của hàm đa thức

Tính chất 8
Nếu f (n) = a1 nb1 + a2 nb2 + ... + ad nbd với
a1 , a2 , ..., ad , b1 , b2 , ..., bd > 0, b1 > b2 > ... > bd thì
f (n) ∈ O(nb1 )

Ta có
b1 b2 bd b1 b2
lim a1 n +a2 nnb1+...+ad n = lim an1 nb1 + lim an2 nb1 + ... =
a1 + 0 + 0 + ... = a1
nên theo tính chất 7, f (n) ∈ O(nb1 )
Thuật toán có độ phức tạp O(nd ) với một số thực d > 0 được gọi
là thuật toán có độ phức tạp đa thức.

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Bài tập

Các hàm nào sau đây thuộc tập O(n2 )?


f1 (n) = 5
f2 (n) = 100n2 + 78n + 4
f3 (n) = 21n + 34

f4 (n) = 5n n + 24
Lưu ý: Để đạt được lợi ích tốt nhất khi tính độ phức tạp tính toán
thì ta chọn tập O nhỏ nhất.
f1 (n) ∈ O(1) ⊂ O(n2 )
f2 (n) ∈ O(n2 )
f3 (n) ∈ O(n) ⊂ O(n2 )
3
f4 (n) ∈ O(n 2 ) ⊂ O(n2 )

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Mở rộng cho trường hợp nhiều biến

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Một số độ phức tạp tính toán thường gặp
O(1)
Không phụ thuộc vào độ lớn của dữ liệu vào.

O(log n)
Độ phức tạp của thuật toán tìm kiếm nhị phân

O( n)
Độ phức tạp của thuật toán kiểm tra số nguyên tố.

O(n)
Độ phức tạp của thuật toán tìm kiếm tuần tự.

O(n log n)
Độ phức tạp của hàm sort trong C++
Bùi Việt Dũng Đánh giá hiệu quả thuật toán
Một số độ phức tạp tính toán thường gặp

O(n2 )
Độ phức tạp của thuật toán Quicksort và hàm sort trong Java

O(2n )
Duyệt hết các tập con của một tập có n phần tử.

O(n!)
Duyệt hết các hoán vị của một tập có n phần tử.

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Ứng dụng trong lập trình thi đấu

Giả sử ta nhìn được độ phức tạp của chương trình là O(f (n)), ta
có thể ước lượng độ phức tạp của thuật toán như sau:
Lấy n lớn nhất có thể có trong dữ liệu vào (được cho trong đề
bài), tính f (n)
Tính f10
(n) 8
8 = t (do máy tính thông thường tính được 10 phép
tính một giây)
Chương trình sẽ chạy trong thời gian ct với c là một hằng số
nào đó.
Nếu t ≤ 0.1 thì thường chương trình sẽ chạy dưới 1 giây. Nếu
t > 0.1 thì ta cố gắng tính tiếp hằng số c. Nếu ct ≤ 1 thì chương
trình nhiều khả năng sẽ chạy dưới 1 giây.

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Mẹo từ Competitive Programming 3

Đối với các kì thi dành cho học sinh cấp 3 thì việc thừa log n trong
độ phức tạp sẽ không gây hậu quả gì lớn.

Nếu có hai cách cài đặt, một cách O(n log n) nhưng dài và
cách còn lại có độ phức tạp O(n log2 n) nhưng ngắn hơn thì
ta chọn cách thứ hai.
Nếu chỉ nghĩ ra cách O(n log2 n) mà không ra cách tối ưu
xuống còn O(n log n) thì không nên bỏ tiếp thời gian để nghĩ
mà cài thuật O(n log n)
Phản ví dụ: Con đường Tùng Trúc (VOI 2014), Street Lamps
(APIO 2019)

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Một số kĩ thuật tối ưu

Lợi dụng hằng số nhỏ


Sử dụng bitset
Chia căn
Hai con trỏ

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Kĩ thuật 1: Lợi dụng hằng số nhỏ

Nhận thấy:
Pn
i=1 1 = n
Pn Pi Pn 1
i=1 j=1 1 = i=1 i = 2 n(n + 1)
Pn Pi Pj Pn 1 1
i=1 j=1 z=1 1 = i=1 2 i(i + 1) = 6 n(n + 1)(n + 2)
Pn Pi Pj Pz Pn 1
i=1 j=1 z=1 k=1 1 = i=1 6 n(n + 1)(n + 2) =
1
24 n(n + 1)(n + 2)(n + 3)
Nếu ta có k vòng lặp lồng nhau, biến lặp sau chỉ chạy đến biến lặp
trước thì tổng số phép tính cần thực hiện là
1
k! n(n + 1)(n + 2)...(n + k)
(Chứng minh bằng quy nạp hoặc sử dụng kiến thức tổ hợp)
Ứng dụng: Bài VTRI - VNOI Marathon 2008

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Bài toán

Cho n (n ≤ 105 ) bóng đèn được đánh số từ 1 đến n. Ban đầu, các
bóng đèn đều tắt. Thực hiện q truy vấn (q ≤ 105 ) sau:
1 Cho hai số nguyên l, r (1 ≤ l ≤ r ≤ n). Bật các bóng đèn
l, l + 1, l + 2, ..., r
2 Cho hai số nguyên l, r (1 ≤ l ≤ r ≤ n). Tắt các bóng đèn
l, l + 1, l + 2, ..., r
3 Tìm bóng đèn được bật có chỉ số nhỏ nhất.

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Kĩ thuật 2: Sử dụng bitset
Ta có thể biểu diễn 64 bóng đèn bằng một số nguyên không
âm 64-bit (kiểu unsigned long long trong C++)
n
⇒ Ta có thể biểu diễn n bóng đèn bằng một dãy 64 ≤ 1600
số nguyên 64-bit.
Khi thực hiện một truy vấn bật / tắt, ta sẽ chỉ cần chuyển
n
nhiều nhất 64 số nguyên thành 264 − 1 hoặc 0, và thay đổi giá
trị của nhiều nhất 128 bit ở "phần đầu" và "phần cuối" của
n
truy vấn. Tổng cộng ta cần nhiều nhất 64 + 128 phép tính.
n
Để tìm bóng đèn bật có chỉ số thấp nhất, ta duyệt mảng 64
số nguyên. Số nguyên đầu tiên khác 0 sẽ chứa bóng đèn bật.
Ta duyệt 64 bit của số nguyên đầu tiên đó bằng nhiều nhất 64
phép tính để có bóng đèn cần tìm.
n
Vậy, ta cần nhiều nhất ≈ q( 64 + 128) ≈ qn
64 phép tính để giải bài
5 qn 8
toán. Với q = n = 10 , 64 ≈ 1.6 × 10 , có khả năng chạy đủ
nhanh trong 1 giây.
Độ phức tạp của thuật toán là O(qn)
Bùi Việt Dũng Đánh giá hiệu quả thuật toán
Kĩ thuật 3: Chia căn
Ta có thể biểu diễn b bóng đèn bằng một mảng boolean
gồm b phần tử. ⇒ Ta cần bn mảng như vậy.
Với mỗi một mảng, ta tạo thêm một biến cho mảng đó là số
lượng bóng đèn bật trong mảng. Nếu số lượng bóng đèn là 0
thì ta coi toàn bộ bóng đèn biểu diễn bởi mảng đó tắt và số
lượng bóng đèn là bn thì ta coi toàn bộ bóng đèn biểu diễn bởi
mảng đó bật.
Khi thực hiện một truy vấn bật / tắt, ta cần thay đổi giá trị
của biến số lượng bóng đèn của nhiều nhất bn mảng, và thay
đổi giá trị của nhiều nhất 2b biến boolean của hai mảng đầu
và cuối. Tổng cộng ta cần nhiều nhất ∼ bn + 2b phép tính.
Để tìm bóng đèn bật có chỉ số thấp nhất, ta duyệt mảng bn số
nguyên để tìm mảng đầu tiên có bóng đèn bật. Sau khi tìm
được mảng, ta duyệt tiếp b biến boolean để xác định bóng
đèn cần tìm. Tổng cộng ta cần ∼ bn + b phép tính.
Vậy ta cần nhiều nhất q( bn + 2b) phép tính.
Bùi Việt Dũng Đánh giá hiệu quả thuật toán
Chọn b thế nào?

n
Do b> 0 và 2b > 0, theo bất đẳng thức AM - GM, ta có
n
pn √ √
+ 2b ≥ 2 · 2b = 2 2 n
b b √ √
⇒ q( bn + 2b) ≥ 2 2q n

Đẳng thức xảy ra khi bn = 2b ⇒ 2b 2 = n ⇒ b = n2


p

(Khi n = 105 , ta p
chọn b = 224) √ √ √
Khi ta chọn b = n2 thì ta sẽ cần 2 2q n ∈ O(q n) phép tính.

Như vậy là ta đã giảm được độ phức tạp từ O(qn) xuống O(q n)

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Kĩ thuật 4: Hai con trỏ

Bài toán
Cho một mảng a gồm n số nguyên dương và một số nguyên dương
S. Tìm một dãy con liên tiếp của mảng a sao cho tổng của dãy
con bằng S

Thuật O(n3 )?
Thuật O(n2 )?
Thuật O(n)?

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Kĩ thuật 4: Hai con trỏ

Nhận xét
Nếu ri=l ai < S thì ∀l + 1 ≤ j ≤ r : ji=l+1 ai ̸= S
P P

P −1
Giả sử ri=l ai < S. Ta cần chứng minh
∀l + 1 ≤ j ≤ r : ji=l+1 ai < S
P

Chọn jP P1 ≤ j ≤ r
bất kì sao cho l +
Ta có ji=l+1 ai < al + ri=l+1 ai (do al > 0)
⇒ ji=l+1 ai < ri=l ai < S ⇒ ji=l+1 ̸= S
P P P

và ta có điều phải chứng minh.

Bùi Việt Dũng Đánh giá hiệu quả thuật toán


Kĩ thuật 4: Hai con trỏ

Ta thấy j sẽ dừng ở các điểm j1 , j2 , ..., jn sao cho


1 ≤ j1 ≤ j2 ... ≤ jn = n
Đặt j0 = 1.
Số
PnphépPjtính cần thực
Pn hiện là:
i
1 = i=1 (ji − ji−1 + 1) =
Pni=1 j=ji−1 Pn
(j
i=1 i − j i−1 ) + i=1 1 = n + n = 2n ∈ O(n)
Vậy thuật toán sử dụng kĩ thuật hai con trỏ có độ phức tạp O(n)

Bùi Việt Dũng Đánh giá hiệu quả thuật toán

Você também pode gostar