Tải bản đầy đủ (.doc) (32 trang)

sang kien kinh nghiem độ phức tạp của thuật toán

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (197.9 KB, 32 trang )

TRƯỜNG THPT CẨM PHẢ
-----------------------

TÊN SÁNG KIẾN: ®é phøc t¹p cña
thuËt
to¸n

TÁC GIẢ: Vò

ThÞ HiÒn

CHỨC VỤ: Giáo viên
ĐƠN VỊ CÔNG TÁC: Trường THPT Cẩm Phả

Quảng Ninh, tháng 12 năm 2017

1


2


MỤC LỤC
MỤC LỤC.................................................................................................................1
PHẦN I. MỞ ĐẦU....................................................................................................2
1.1. Lý do viết sáng kiến kinh nghiệm.................................................................................................................... 2
1.2. Mục tiêu của sáng kiến................................................................................................................................... 2
1.2.1. Mục tiêu chung........................................................................................................................................ 3
1.2.2. Mục tiêu cụ thể...................................................................................................................................... 3
1.3. Giới hạn của sáng kiến.................................................................................................................................... 3
1.3.1. Đối tượng nghiên cứu:............................................................................................................................. 3


1.3.2. Về không gian:......................................................................................................................................... 3
1.3.3. Về thời gian:............................................................................................................................................ 4
1.4. Phương pháp nghiên cứu............................................................................................................................... 4

PHẦN II. NỘI DUNG...............................................................................................5
CHƯƠNG 1: TỔNG QUAN.....................................................................................5
1. Cơ sở lý luận:..................................................................................................................................................... 5
2. Cơ sở thực tiễn:................................................................................................................................................. 5

CHƯƠNG 2: NỘI DUNG NGHIÊN CỨU...............................................................7
1. Lý thuyết........................................................................................................................................................... 7
1.1. Cách thứ nhất: Bằng thực nghiệm.............................................................................................................. 7
1.2. Cách thứ hai: Bằng phương pháp lý thuyết................................................................................................. 8
1.3. Các quy tắc xác định độ phức tạp của thuật toán........................................................................................ 8
1.4. Phân loại câu lệnh trong ngôn ngữ lập trình bậc cao................................................................................... 8
2. Ví dụ.................................................................................................................................................................. 9
3. Bài tập............................................................................................................................................................ 12

CHƯƠNG 3: KẾT QUẢ ĐẠT ĐƯỢC...................................................................23
1. Kết quả sau khi áp dụng sáng kiến kinh nghiệm............................................................................................... 23
2. Điều kiện để áp dụng sáng kiến....................................................................................................................... 24

PHẦN III: KẾT LUẬN, KIẾN NGHỊ.....................................................................25
1. Kết luận:.......................................................................................................................................................... 25
2. Kiến nghị:........................................................................................................................................................ 25

TÀI LIỆU THAM KHẢO.......................................................................................26

1



PHẦN I. MỞ ĐẦU
1.1. Lý do viết sáng kiến kinh nghiệm
Như chúng ta đã biết, có thể có nhiều thuật toán để giải một bài toán. Một
điều đặt ra là khi giải một bài toán, ta cần lựa chọn thuật toán như thế nào mà được
cho là “tốt” hơn cả, và dựa trên cơ sở nào để đánh giá thuật toán này “tốt” hơn
thuật toán kia? Chính vì thế tôi đã đi tìm và nghiên cứu về “Độ phức tạp của thuật
toán”.
Và một lý do quan trọng nữa là, đối với các em học sinh học chương trình
Tin học phổ thông, việc cài đặt thuật toán xong mà chạy không có lỗi cú pháp đã là
một thành công và thành công hơn nữa khi chạy chương trình cho kết quả đúng thì
đó là một niềm vui rất lớn đối với các em. Nhưng, đối với những bài toán khó
trong các kì thi chọn học sinh giỏi môn Tin học hay kỳ thi Tin học trẻ không
chuyên, có rất nhiều em cài đặt được thuật toán, chạy được các test đemo nhưng
các em lại không được điểm tối đa khi các giám khảo chấm và chạy các bộ test với
dữ liệu lớn, đó là vì các em chưa quan tâm đến thời gian thực hiện thuật toán, đến
dung lượng không gian nhớ cần thiết để lưu các dữ liệu vào, ra và các kết quả
trung gian trong quá trình tính toán. Đấy là một vấn đề mà giáo viên ôn đội tuyển
Tin học rất cần quan tâm và hướng dẫn các em.
Tôi lấy một ví dụ trong kì thi chọn học sinh giỏi cấp tỉnh năm học 2017
vừa qua, với câu 1 được đánh giá là dễ hơn cả so với 2 câu còn lại, xin trích một
phần đề bài như sau: “Cho một sâu S chỉ gồm các chữ cái tiếng Anh (a..z, A..Z).
Bạn hãy thống kê các chữ cái xuất hiện trong xâu S theo thứ tự từ điển và số lần
xuất hiện của nó. Việc thống kê các chữ cái không phân biệt chữ hoa, chữ thường.
Ví dụ xâu S=’afbAAffbbDda’ có kết quả thống kê như sau:
- A xuất hiện 4 lần,
- B xuất hiện 3 lần,
- D xuất hiện 2 lần,
- F xuất hiện 4 lần …”
Với đề bài trên, thì có em lựa chọn thuật toán là:

+ B1: Sắp xếp các chữ cái trong xâu;
+ B2: Đếm số lần xuất hiện của các chữ cái trong xâu.
Nhưng có những em thì lại lựa chọn thuật toán:
+ B1: Đếm số lần xuất hiện của các chữ cái trong xâu;
+ B2: Sắp xếp các chữ cái trong xâu.
Với hai cách lựa chọn trên thì cách nào là lựa chọn tốt hơn? Đó cũng là một
lý do càng thôi thúc tôi muốn nghiên cứu đề tài này.
1.2. Mục tiêu của sáng kiến

2


1.2.1. Mục tiêu chung
Mục tiêu của đề tài “Độ phức tạp của thuật toán” không chỉ là để phân
tích, để so sánh thuật toán, để đánh giá xem thuật toán nào tốt hơn thuật toán nào
mà còn dựa vào kết quả phân tích đánh giá đó để hiệu chỉnh, cải tiến thuật toán đã
có được tốt hơn.
1.2.2. Mục tiêu cụ thể
Để đánh giá độ phức tạp của thuật toán, thông thường ta dựa vào các tiêu chí
sau:
- Tiêu chí 1: Chi phí cài đặt thuật toán: thuật toán dễ hiểu, dễ cài đặt, công
sức cài đặt của người lập trình.
- Tiêu chí 2: Tính hiệu quả của thuật toán: thời gian thực hiện thuật toán,
dung lượng không gian nhớ cần thiết để lưu các dữ liệu vào, ra và các kết quả
trung gian trong quá trình tính toán.
Nếu chỉ là các chương trình đơn giản như những bài tập ví dụ trên lớp, hay
những bài tập về nhà đơn giản ở sách giáo khoa chương trình phổ thông, chỉ thực
hiện một số ít lần trên dữ liệu vào và dữ liệu không quá lớn thì tôi hướng học sinh
của tôi thực hiện theo tiêu chí thứ nhất l, có thể hướng dẫn học sinh dùng một thuật
toán có sẵn, ít hiệu quả hơn cũng được miễn sao có thể dễ dàng lập trình.

Nhưng nếu các em tham gia các kì thi học sinh giỏi thì các em sẽ vấp phải
những bộ test với dữ liệu lớn, khi đó tính hiệu quả của thuật toán là yêu cầu quan
trọng và tôi hướng các em đặt tiêu chí thứ 2 là quan trọng hơn. Trong trường hợp
này, cần chọn một thuật toán chạy nhanh và sử dụng ít các tài nguyên cho dù thuật
toán có thể phải cài đặt phức tạp để nhận được chương trình chạy nhanh hơn, hiệu
quả hơn.
Quay trờ lại ví dụ bài 1 của bài thi chọn học sinh giỏi Tỉnh vừa qua, nếu bộ
Input là xâu dài với vài nghìn kí tự thì việc sắp xếp trước có tối ưu hơn hay không
hay là sau khi ta tính được có bao nhiêu loại kí tự (tối đa là 24 chữ cái) và chỉ phải
sắp xếp 24 chữ cái thay vì hàng nghìn chữ cái?
Như vậy, việc lựa chọn thuật toán tốt hơn còn phụ thuộc vào rất nhiều yếu tố
như bài toán ấy sẽ phải viết chương trình dùng nhiều hay ít lần? Các yêu cầu về hệ
thống máy tính dùng để thực hiện chương trình là như thế nào?... Điều mấu chốt
là các em phải dựa vào các đặc tính cụ thể của từng bài toán để các em xác định
tiêu chí nào hiệu quả cho sự lựa chọn của thuật toán.
1.3. Giới hạn của sáng kiến
1.3.1. Đối tượng nghiên cứu:
Đề tài “Độ phức tạp của thuật toán” được thực nghiệm trong các giờ dạy
môn Tin học trên lớp và các buổi ôn học sinh giỏi Tin học.
1.3.2. Về không gian:
Trường Trung học phổ thông Cẩm Phả - Thành phố Cẩm Phả - Tỉnh Quảng

3


Ninh
1.3.3. Về thời gian:
Đề tài được thực hiện trong năm học 2015 - 2016 và tiếp tục nghiên cứu đến
nay, có sự đúc kết kinh nghiệm từ những năm học trước năm 2015
1.4. Phương pháp nghiên cứu

- Dựa trên cơ sở lý thuyết của các môn Khoa học tự nhiên, nhất là môn Toán
môn học cơ sở cho sự phát triển tư duy lập trình trong Tin học.
- Dựa trên cơ sở lý thuyết của Ngôn ngữ lập trình Pascal, sự hoạt động tuần tự
từng bước của máy tính khi thực hiện chương trình.
- Thu thập dữ liệu thông qua việc hỏi học sinh về mức độ biết, hiểu và vận
dụng ngôn ngữ lập trình Pascal vào giải các bài tập Toán.
- Phân tích đánh giá mức độ học sinh hiểu vận dụng, từ đó xây dựng, giới thiệu
các bài toán phù hợp với từng đối tượng học sinh.
- Tổng kết rút kinh nghiệm sau mỗi giờ dạy, mỗi học kỳ đã qua để tìm ra
phương pháp cũng như nội dung phù hợp nhất đối với từng đối tượng học sinh.

4


PHẦN II. NỘI DUNG
CHƯƠNG 1: TỔNG QUAN
1. Cơ sở lý luận:
Thực hiện Nghị quyết trung ương số 29-NQ/TW “về đổi mới căn bản, toàn
diện giáo dục và đào tạo, đáp ứng công nghiệp hóa, hiện đại hóa trong điều kiện
kinh tế thị trường định hướng xã hội chủ nghĩa và hội nhập quốc tế ” đã được hội
nghị Trung ương 8 (khóa XI) thông qua. Trong đó có mục tiêu: “Đối với giáo dục
phổ thông, tập trung phát triển trí tuệ, thể chất, hình thành phẩm chất, năng lực
công dân, phát hiện và bồi dưỡng năng khiếu, định hướng nghề nghiệp cho học
sinh. Nâng cao chất lượng giáo dục toàn diện, chú trọng giáo dục lý tưởng, truyền
thống, đạo đức, lối sống, ngoại ngữ, tin học, năng lực và kĩ năng thực hành, vận
dụng kiến thức vào thực tiễn…”
Thực hiện Chỉ thị số 05 - CT/TW của Bộ chính trị về đẩy mạnh học tập và
làm theo tư tưởng, đạo đức, phong cách Hồ Chí Minh. Đó là: phong cách tư duy
độc lập, tự chủ, sáng tạo, luôn gắn chặt lý luận với thực tiễn, phong cách làm việc
dân chủ, khoa học, kỹ lưỡng, cụ thể, tới nơi, tới chốn; phong cách ứng xử văn hóa,

tinh tế, đầy tính nhân văn, viết và nói ngắn gọn, dễ hiểu, dễ nhớ, dễ làm,…
2. Cơ sở thực tiễn:
Môn Tin học đến nay không còn là môn học mới mẻ đối với học sinh phổ
thông, bởi học sinh đã được làm quen nó ngay ở các cấp học dưới. Đây là một
thuận lợi cho học sinh, học sinh không phải học từ đầu để làm quen với môn học.
Sự liên quan của môn Tin học với các môn học khác là nhiều, vì vậy học sinh sẽ
phải vất vả để xem lại, tìm kiếm lại tri thức ở các môn học khác. Đặc biệt nội dung
lập trình trong môn học Tin học lại có liên quan rất nhiều đến kiến thức các môn
khoa học tự nhiên như Toán, Lí, liên quan nhiều đến tư duy Toán học. Nếu học
sinh yếu tư duy về Toán học thì sẽ rất là khó khăn khi lập trình. Muốn giải quyết
được việc này thì giáo viên cần phải dẫn dắt học sinh tiếp cận với môn học một
cách tự nhiên, hào hứng thông qua những kiến thức sẵn có của các em ở các môn
học mà các em yêu thích.
Nhiều giáo viên còn hạn chế về trình độ, khả năng cập nhật thông tin. Không
chỉ vậy, một số giáo viên tư duy về thuật toán còn chậm, hay nói cách khác là chưa
hiểu rõ thuật toán để diễn đạt trong việc dạy lập trình. Chính điều này đã làm cho
giáo viên có những hạn chế khi dạy cho các em các thuật toán tối ưu.
Khi bước vào học phổ thông thì học sinh đã bắt đầu định hình học theo khối
để thi đại học. Thời gian học chủ yếu dành cho các môn học chính như Toán, Lý,
Hóa, Văn, Anh. Tin học là một môn phụ nên thời gian để học chỉ là những tiết học
ở trên lớp. Đối với Tin học 10, 12 thì tính ứng dụng của môn học trong thực tế các
em dễ dàng nhìn thấy và thực hiện được luôn. Còn với Tin học 11 thuộc về lĩnh
vực lập trình, khó có sản phẩm để các em nhìn thấy. Hơn thế việc tư duy thuật toán
cũng là một nội dung khó đối với các em. Điều này dẫn đến rất nhiều học sinh
không thích và không đầu tư cho đội tuyển.

5


Ngoài ra, chúng ta cần giải quyết các bài toán không chỉ đúng mà còn xử lý

phải nhanh với những bộ test dữ liệu lớn.
Từ thực tế trên tôi muốn nghiên cứu “Độ phức tạp thuật toán” để có thể hiểu
rõ hơn, có cái nhìn sâu, rộng hơn để có thể giúp các em lựa chọn và tư duy thuật
toán tốt hơn.

6


CHƯƠNG 2: NỘI DUNG NGHIÊN CỨU
1. Lý thuyết
Việc giải một bài toán trên máy tính thường được tiến hành qua các bước
sau:
Bước 1: Xác định bài toán
Bước 2: Thiết kế hoặc lựa chọn thiết kế thuật toán
Bước 3: Viết chương trình
Bước 4: Kiểm thử và hiệu chỉnh chương trình
Bước 5: Viết tài liệu
Mỗi bài toán được đặc tả bởi hai thành phần: Input và Output. Việc xác định
bài toán chính xác là xác định rõ hai thành phần này và mối quan hệ giữa chúng.
Các thông tin đó cần được nghiên cứu cẩn thận để có thể lựa chọn thuật toán, cách
thể hiện các đại lượng đã cho, các đại lượng phát sinh trong quá trình giải bài toán
và ngôn ngữ lập trình thích hợp.
Ví dụ, trong một bài toán tin học khi đề cập đến một số nguyên dương N, là
tuổi của một người, có thể chỉ rõ phạm vi giá trị của N là từ 0 đến 150, để lựa chọn
cách thể hiện N bằng kiểu dữ liệu thích hợp.
Để giải một bài toán bước 1 là bước đơn giản nhất nhưng cũng khá quan
trọng. Nhưng quan nhất trong 5 bước trên thì là bước 2: Bước lựa chọn hoặc thiết
kế thuật toán.
Mỗi thuật toán chỉ giải một bài toán nào đó, nhưng có thể có nhiều thuật
toán khác nhau cùng giải một bài toán. Vì thế, cần phải thiết kế hoặc lựa chọn một

thuật toán phù hợp đã có để giải bài toán cho trước.
Có hai cách tiếp cận để đánh giá thời gian thực hiện của một thuật toán:
1.1. Cách thứ nhất: Bằng thực nghiệm
Chúng ta viết chương trình và cho chạy chương trình với các dữ liệu vào
khác nhau trên máy tính.
Ví dụ với yêu cầu như bài 1 của kỳ thi chọn học sinh giỏi Tỉnh năm 2017
của tỉnh Quảng Ninh vừa qua, nếu giải theo cách thứ nhất là:
+ Bước 1: Đếm số lần xuất hiện của các chữ cái trong xâu;
+ Bước 2: Sắp xếp các chữ cái trong xâu.
Với cách này việc đếm số lần xuất hiện của các kí tự trong xâu là rất đơn
giản, kể cả dãy kí tự có hàng ngàn kí tự, vì đó chỉ là phép tính cộng đơn giản; sau
đó là sắp xếp, nếu có nhiều tối đa cũng chỉ là 24 kí tự phải sắp xếp. Khi đó chương
trình sẽ chạy nhanh hơn rất nhiều so với cách thứ hai như sau:
+ Bước 1: Sắp xếp các chữ cái trong xâu;
+ Bước 2: Đếm số lần xuất hiện của các chữ cái trong xâu.

7


Với cách giải như này nếu ta có xâu đưa vào là hàng nghìn kí tự thì việc
máy tính phải sắp xếp hàng nghìn kí tự như vậy sẽ mất rất nhiều thời gian, có thể
dẫn đến treo máy.
1.2. Cách thứ hai: Bằng phương pháp lý thuyết
Chúng ta coi thời gian thực hiện thuật toán như hàm số của cỡ dữ liệu vào
(dữ liệu phần input). Ta sử dụng hàm số T(n) để biểu diễn thời gian thực hiện của
một thuật toán (trong đó n là cỡ của dữ liệu vào).
Chúng ta xem xét việc xác định độ phức tạp của thuật toán thông qua kí
pháp hàm O lớn.
1.3. Các quy tắc xác định độ phức tạp của thuật toán
- Quy tắc hằng số:

Nếu một thuật toán T có thời gian thực hiện T(n)=O(c1f(n)) với c1 là một
hằng số dương thì có thể coi thuật toán T có độ phức tạp tính toán là O(f(n)).
- Quy tắc cộng:
Giả thiết, thuật toán gồm hai phần liên tiếp T1 và T2. Khi đó, nếu phần T1 của
thuật toán có thời gian thực hiện là T1(n) = O(f(n)) và phần T2 có thời gian thực
hiện là T2(n)=O(g(n)), khi đó thời gian thực hiện thuật toán sẽ là:
T1(n) + T2(n) = O(f(n) + g(n))
- Quy tắc lấy max:
Nếu thuật toán T có thời gian thực hiện T(n) = O(f(n) + g(n)) thì có thể coi
thuật toán T có độ phức tạp tính toán là O(max(f(n),g(n)))
Chú ý: Với quy tắc này, nếu T(n) là một đa thức thì các toán hạng bậc thấp
có thể bỏ qua khi đánh giá độ phức tạp của thuật toán.
- Quy tắc nhân:
Nếu thuật toán T có thời gian thực hiện T(n) = O(f(n)). Khi đó nếu thực hiện
k(n) lần đoạn thuật toán T với k(n)=O(g(n)) thì độ phức tạp tính toán sẽ là:
O(f(n).g(n))
1.4. Phân loại câu lệnh trong ngôn ngữ lập trình bậc cao
a. Câu lệnh đơn thực hiện một thao tác, ví dụ câu lệnh gán đơn giản (không
chứa lời gọi hàm trong biểu thức), đọc/ ghi đơn giản, câu lệnh chuyển điều khiển
đơn giản (break, goto, return …)
b. Nếu S1, S2, Sm là các câu lệnh thì
Begin S1; S2,…; Sm; End;
Là các câu lệnh hợp hay khối lệnh
c. Nếu S1, S2 là các câu lệnh và E là biểu thức logic thì

8


If E then S1 else S2;
Là câu lệnh rẽ nhánh hay câu lệnh If

d. Nếu S là câu lệnh và E là biểu thức logic thì
While E do S;
Là câu lệnh lặp điều kiện trước
e. Nếu S1, S2,…,Sm là các câu lệnh và E là biểu thức logic thì
Repeat
S1;S2;…Sm;
Until E;
Là câu lệnh lặp điều kiện sau hay Repeat
f. Nếu S là lệnh, E1, E2 là các biểu thức cùng một kiểu thứ tự đếm được thì
For i:= e1 to e2 do S;
Là câu lệnh lặp với số lần xác định
Để đánh giá thời gian thực hiện chương trình, cần thiết phải biết đánh giá
thời gian thực hiện các câu lệnh. Để áp dụng điều đó ta có thể áp dụng các quy tắc
tính độ phức tạp của thuật toán, cụ thể:
- Thời gian thực hiện một lệnh đơn không phụ thuộc vào kích thước dữ liệu
là: O(1).
- Thời gian thực hiện một câu lệnh hợp thành sẽ được tính theo qui tắc cộng
và quy tắc max: O(max(f1(n),f2(n)…,fm(n)).
- Thời gian thực hiện câu lệnh điều kiện IF: Giả sử thười gian thực hiện hai
câu lệnh thành phần dạng đủ là f(n) và g(n) thì thời gian thực hiện của câu lệnh IF
sẽ được tính theo qui tắc max: O(max(f(n),g(n)).
- Thời gian thực hiện câu lệnh lặp sẽ áp dụng theo qui tắc nhân, nghĩa là
O(k(n)f(n)), trong đó k(n) là số lần lặp và f(n) là thời gian thực hiện câu lệnh bên
trong vòng lặp.
Chú ý: Trong một thuật toán, ta chú ý đặc biệt đến một phép toán gọi là
phép toán tích cực. Đó là phép toán mà số lần thực hiện không ít hơn các phép
toán khác
2. Ví dụ
Ví dụ 1:
S:=0;

For i:=0 to n do
Begin

9


P:=1;
For j:=1 to i do
P:=p*x/j;
S:=s+p;
End;
Phép toán tích cực là p = p * x / j
Số lần thực hiện phép toán này là
0+1+2+…+n = n(n-1)/2 = n2/2 – n/2
=> Vậy độ phức tạp là O(n2/2 – n/2)
= O(n2/2)
sử dụng quy tắc lấy max
2
= O(n )
sử dụng quy tắc bỏ hằng số
Ví dụ 2:
S:=1; p:=1;
For i:=1 to n do
Begin
p:=p*x/i;
s:=s+p;
End;
Phép toán tích cực là p = p * x / i
Số lần thực hiện phép toán là n
=> Vậy độ phức tạp của thuật toán là O(n)

Ví dụ 3:
S: =n*(n-1) /2;
=> Độ phức tạp của thuật toán là O(1), nghĩa là thời gian tính toán không phụ
thuộc vào n
Ví dụ 4: Thuật toán tính tổng các số từ 1 đến n
S:=0;
For i:=1 to n do s:=s+i;
Vòng lặp lặp n lần phép gán s = s+i, nên thời gian tính toán tỉ lệ thuận với n, tức độ
phức tạp là O(n).
=> độ phức tạp là O(n)
Ví dụ 5:
for i:=1 to n do
for j:=1 to n do

10


{lệnh;}
=> Dùng quy tắc nhân ta có O(n^2)
tương tự như vậy ta sẽ có O(n^3), O(n^4).
Ví dụ 6:
For i:=1 to n do
For j:=1 to m do
{lệnh;}
=> Dùng quy tắc nhân ta có O(n*m)
Ví dụ 7:
for i:=1 to n do
{lệnh 1;}
for j:=1 to m do
{lệnh 2;}

=> Dùng quy tắc cộng và quy tắc lấy max, sẽ có
O(max (n,m))
Ví dụ 8:
For i:= 1 to n do
begin
For u:=1 to m do
For v:=1 to n do
{lệnh;}
for j:=1 to x do
for k:=1 to z do
{lệnh ;}
End;
=> O(n*max (n*m,x*z))
Ví dụ 9:
for i:=1 to n do
for j:=1 to m do
begin
for k:=1 to x do
{lệnh ;}
for h:=1 to y do
{lệnh ;}

11


end;
=> O(n*m* max (x,y))
Ví dụ 10:
for i:= 1 to n do
for u:= 1 to m do

for v:= 1 to n do
{lệnh;}
for j:= 1 to x do
for k:= 1 to z do
{lệnh;}
=> O(max (m*n^2,x*z))
Ví dụ 11: Đoạn chương trình tính tổng 2 đa thức
P(x) = xmxm+am-1xm-1+ …+a1x+a0
Q(x) = bnxn+bn-1xn-1+…+b1x+b0
if (mfor i:=0 to p do
c[i]=a[i] + b[i];
if (pfor i:=p+1 to m do c[i] := a[i]
else
for i:=p+1 to n do c[i]: = b[i];
while ((p>0) and (c[p] = 0)) do p: = p-1;
=> Độ phức tạp: O(max(m,n))
Ví dụ 12: Đoạn chương trình tính tích hai đa thức
P(x) = xmxm+am-1xm-1+ …+a1x+a0
Q(x) = bnxn+bn-1xn-1+…+b1x+b0
P: = m+n;
for i=0 to p do c[i] = 0;
for i=0 to m do
for j=0 to n do
c[i+j]: = c[i+j] + a[i] + b[j];
=> Độ phức tạp là O(m.n)
3. Bài tập

12



Bài 1. Phân tích thời gian thực hiện của chương trình sau:
Var I, j, n: longint;
S1, s2: longint;
Begin
{1} readln(n);
{2} s1:= 0;
{3} for i:= 1 to n do
{4}

s1:=s1+i;

{5} s2:=0;
{6} for j: = 1 to n do
{7}

s2:=s2+ j*j;

{8} writeln(‘1+2+3+…+’,n,’=’,s1);
{9} writeln(‘1^2+2^2+…+’,n,’^2=’,s2);
End.
Phân tích:
Thời gian thực hiện chương trình phụ thuộc vào n. Các lệnh {1},{2}, {4},
{5}, {7}, {8}, {9} có thời gian thực hiện là O(1);
Lệnh lặp for {3} có số lần lặp là n, nên thời gian thực hiện là O(n);
Tương tự, lệnh lặp for {6} có thời gian thực hiện là O(n).
Vậy thời gian thực hiện của chương trình là: max(O(1), O(1), O(n), O(1),
O(1), O(n), O(1), O(1)) = O(n).
Bài 2: Phân tích thời gian thực hiện của đoạn chương trình sau:

{1} c:=0;
{2} for i :=1 to 2*n do
{3}

c :=c+1;

{4} for i :=1 to n do
{5}

for j :=1 to n do

{6}

c:=c+1;

Phân tích:
Thời gian thực hiện chương trình phụ thuộc vào n.
Các lệnh {1}, {3}, {6} có thời gian thực hiện là O(1).
Lệnh lặp for {2} có số lần lặp là 2n, như vậy lệnh {2} có thời gian thực hiện

13


là O(n).
Lệnh lặp for {5} có số lần lặp là n, như vậy lệnh {5} có thời gian thực hiện
là O(n). Lệnh lặp for {4} có số lần lặp là n, như vậy lệnh {4} có thời gian thực
hiện là O(n2).
Vậy thời gian thực hiện đoạn chương trình trên là:
Max(O(1), O(n), O(n2)) = O(n2).
Bài 3. Phân tích thời gian thực hiện của đoạn chương trình sau:

{1} for i :=1 to n do
{2}

for j :=1 to i do

{3}

c:=c+1;

Phân tích:
Thời gian thực hiện chương trình phụ thuộc vào n.
Các lệnh {3} có thời gian thực hiện là O(1)
Khi i = 1, j chạy từ 1 đến 1  lệnh lặp For {2} lặp 1 lần
Khi i = 2, j chạy từ 1 đến 2  lệnh lặp For {2} lặp 2 lần

Khi i = n, j chạy từ 1 đến n  lệnh lặp For {2} lặp n lần
Như vậy lệnh {3} được lặp 1+ 2 + 3 +..+ n =

n(n + 1)
lần, do đó lệnh {1} có thời
2

gian thực hiện là O(n2).
Vậy thời gian thực hiện chương trình là: O(n2).
Bài 4. Phân tích đoạn chương trình sau:
function is_prime(n):boolean;
begin
for k:=2 to n-1 do
if (n mod k=0) then exit(false);
exit(true);

end;
end;
Phân tích:
Ta thấy rằng nếu n là một số nguyên tố ta cần phải mất n-2 phép toán mod.
Giả sử một siêu máy tính có thể tính được trăm nghìn tỉ (10 14) phép mod trong một
giây, như vậy để kiểm tra một số khoảng 25 chữ số ta cần 10 25/1014*60*60*24*365

14


tức khoảng 3170 năm. Trong khi đó, nếu ta sử dụng thuật toán
function is_prime(n):boolean;
begin
for k:=2 to trunc(sqrt(n)) do
if (n mod k=0) then exit(false);
exit(true);
end;
end;
Như vậy để kiểm tra một số khoảng 25 chữ số mất khoảng

10 25
tương
1014

đương khoảng 0.03 giây.
Bài 5. Phân tích độ phức tạp của thuật toán sắp xếp bằng tráo đổi (Exchange Sort)
- Xác định bài toán:
+ Input: Dãy A gồm N số nguyên a1, a2,…, aN
+ Output: Dãy A được sắp xếp lại thành dãy không giảm
- Ý tưởng: Với mỗi cặp số hạng đứng liền kề trong dãy, nếu số trước lớn hơn

số sau ta đổi chỗ chúng cho nhau. Việc đó được lặp lại, cho đến khi không có sự
đổi chỗ nào nữa.
{1} for j:=n downto 2 do
{2} for i:=1 to j-1 do
{3} if a[i]> a[i+1] then
{4}

begin

{5}

tg:=a[i];

{6}

a[i]:=a[i+1];

{7}

a[i+1]:=tg;

{8}

end;

Phân tích:
Câu lệnh {5}, {6}, {7} là các lệnh đơn nên có thời gian thực hiện là O(1);
Câu lệnh {4} là câu lệnh hợp thành nên có thời gian thực hiện là
O(max(O(1), O(1), O(1))) = O(1);


15


Câu lệnh {3} là câu lệnh IF nên có thời gian thực hiện là O(max(O(1))) =
O(1);
Câu lệnh {2} có số lần lặp là n-1 lần nên có thời gian thực hiện là O(n-1).
Tương tự câu lệnh {1} cũng có số lần lặp là n-1 nên có thời gian thực hiện là O(
( n − 1) 2);
Vậy thời gian thực hiện thuật toán là O(max(O(1), O(n-1), O( ( n − 1) 2))) = O(
( n − 1) 2);
Bài 6. Phân tích độ phức tạp của thuật toán tìm kiếm tuần tự (Sequential Search)
- Xác định bài toán:
+ Input: Dãy A gồm N số nguyên khác nhau a1, a2,…, aN và số
nguyên k;
+ Output: Chỉ số i mà ai = k hoặc thông báo không có số hạng nào của
dãy A có giá trị bằng k.
- Ý tưởng: Tìm kiếm tuần tự được thực hiện một cách tự nhiên. Lần lượt từ
số hạng thứ nhất, ta so sánh giá trị số hạng dang xét với khóa cho đến khi hoặc gặp
một số hạng bằng khóa hoặc dãy đã được xét hết và không có giá trị nào bằng
khóa. Trong trường hợp thứ hai dãy A không có số hạng nào bằng khóa.
{1} vt:=0;
{2} for i:=1 to n do
{3} if a[i]=k then
{4}

vt:=i;

Phân tích:
Câu lệnh {1}, {4} là các lệnh đơn nên có thời gian thực hiện là O(1).
Câu lệnh {3} là câu lệnh IF nên có thời gian thực hiện là O(max(O(1))) =

O(1).
Câu lệnh {2} có số lần lặp là n lần nên có thời gian thực hiện là O(n).
Vậy thời gian thực hiện thuật toán là O(max(O(1), O(1), O(n))) = O(n);
Bài 7. Phân tích độ phức tạp của thuật toán tìm kiếm nhị phân (Binary Search)
- Xác đinh bài toán:
+ Input: Dãy A là dãy tang gồm N số nguyên khác nhau a1, a2,…, aN
và một số nguyên k;
+ Output: Chỉ số i mà ai = k hoặc thông báo không có số hạng nào của
dãy A có giá trị bằng k.
- Ý tưởng:

16


Sử dụng tính chất dãy A là dãy tang, tìm cách thu hẹp nhanh phạm vi tìm
kiếm sau mỗi lần so sánh khóa với số hạng được chọn. Để làm điều đó, ta chọn số
 N + 1

hạng agiữa ở giữa dãy để so sánh với k, trong đó: giữa = 
.
 2 
Khi đó chỉ xảy ra một trong ba trường hợp sau:
- Nếu agiữa = k thì giữa là chỉ số cần tìm. Việc tìm kiếm kết thúc.
- Nếu agiữa > k thì phạm vi tìm kiếm từ a1 đến agiữa - 1
- Nếu agiữa < k thì phạm vi tìm kiếm từ agiữa +1 tới aN
{1} i:=1;
{2} j:=n;
{3} while (i<>j) do
{4}


begin

{5}

m:=(i+j) div 2;

{6}

if a[m]>k then j:=m;

{7}

if a[m]
{8}

end;

{9} if a[i]=k then vt:=i else vt:=0;
Phân tích:
Câu lệnh {1}, {2}, {5}, {6}, {7} là các lệnh đơn nên có thời gian thực hiện
là O(1).
Câu lệnh {4} là câu lệnh hợp thành nên có thời gian thực hiện là
O(max(O(1), O(1), O(1))) = O(1);
Câu lệnh {3} là câu lệnh while nên có thời gian thực hiện là O(n).
Vậy thời gian thực hiện thuật toán là O(max(O(1), O(1), O(n))) = O(n);
Bài 8. Tìm ước chung lớn nhất (ƯCLN) của hai số nguyên dương M và N.
- Xác định bài toán:
+ Input: Cho M, N;
+ Output: ƯCLN(M,N).

Cách 1: Dưới đây là thuật toán tìm UCLN bằng cách trừ đi nhau:
- Nếu M = N thì giá trị chung là ƯCLN của M và N;

17


- Nếu M < N thì ƯCLN(M, N) = ƯCLN(M, N - M);
- Nếu M > N thì ƯCLN(M, N) = ƯCLN(M - N,N);
{1} var m, n, UCLN, BCNN:integer;
{2} begin
{3} readln(m,n);
{4} BCNN:=m*n;
{5} While m<>n do If m>n then m:=m-n else n:=n-m;
{6} UCLN:=m;
{7} BCNN:=BCNN div UCLN;
{8} write(UCLN,' ',BCNN);
{9} end.
Phân tích:
Câu lệnh {1}, {3}, {4}, {6}, {7}, {8} là các lệnh đơn nên có thời gian thực
hiện là O(1).
Câu lệnh {5} là câu lệnh while nên có thời gian thực hiện là O(max(m,n));
Vậy thời gian thực hiện thuật toán là O(max(O(1), O(1), O(max(m,n)) )) =
O(max(m,n));
Cách 2: Thuật toán Euclide: Ngoài cách tìm UCLN như trên. Các bạn có thể sử
dụng cách chia lấy dư (mod), chương trình sẽ tối ưu do phải thực hiện ít phép tính
hơn.
Ý tưởng: UCLN của 2 số x, y cũng là UCLN của 2 số n và m mod n, vậy ta sẽ đổi
m là n, n là m mod n cho đến khi n bằng 0. Khi đó UCLN là m.
var x,y,UCLN,BCNN, t :integer;
{1} begin

{2} readln(m,n);
{3} BCNN:=m*n;
{4} t:= n mod m;
{5} While t <> 0 do
{6} Begin
{7}

t:= m MOD n;

{8}

m:= n;

18


{9}

n:= t;

{10} End;
{11} ucln:=m;
{12} BCNN:=BCNN div UCLN;
{13} write(UCLN,' ',BCNN);
{14} end.
Phân tích:
Câu lệnh {2}, {3}, {4}, {7}, {8}, {9}, {11}, {12}, {13} là các lệnh đơn nên
có thời gian thực hiện là O(1).
Câu lệnh {5} là câu lệnh while nên có thời gian thực hiện là O(max(m,n));
Vậy thời gian thực hiện thuật toán là O(max(O(1), O(1), O(max(m,n)) )) =

O(max(m,n));
Cách 3: Tìm UCLN bằng cách dùng đệ quy: Đệ quy được hiểu đơn giản là sự gọi
nhiều lần chương trình con trong chương trình. Thực sự, đối với bài toán đơn giản,
không ai sử dụng đệ quy vì sẽ làm phức tạp vấn đề và làm chương trình trở nên rắc
rối, phải thực hiện nhiều phép tính hơn. Tuy nhiên, nếu bắt buộc phải dùng đệ quy,
các bạn có thể tham khảo cách làm dưới đây:
{1} function ucln(x,y:integer):integer;
{2} begin
{3} if x = y then ucln:=x
{4} else if x > y then ucln:=ucln(x mod y,y)
{5} else ucln:=ucln(x, y mod x);
{6} end;
{7} var m, n:integer;
{8} begin
{9} readln(m,n);
{10} write('Ước chung lớn nhất là: ', UCLN(m,n), ' Bội
chung nhỏ nhất là: ', (m*n) div UCLN(m,n));
{11} end.
Phân tích:
Câu lệnh {1}, {7}, {8}, {9}, {10} là các lệnh đơn nên có độ phức tạp là

19


O(1);
Câu lệnh {2} đến {6} là có độ phức tạp là O(log(max(x,y)));
Vậy thời độ phức tạp của thuật toán là O(log(max(x,y))).
Bài 9. Cho một xâu S chỉ gồm các chữ cái tiếng Anh (a..z, A..Z). Bạn hãy thống kê
các chữ cái xuất hiện trong xâu S theo thứ tự từ điển và số lần xuất hiện của nó.
Việc thống kê các chữ cái không phân biệt chữ hoa, chữ thường.

Ví dụ xâu S=’afbAAffbbDda’ có kết quả thống kê như sau:
- A xuất hiện 4 lần,
- B xuất hiện 3 lần,
- D xuất hiện 2 lần,
- F xuất hiện 4 lần …”
Dữ liệu: Từ tệp VANBAN.INP có một dòng ghi trong xâu S.
Kết quả: Ghi vào tệp VANBAN.OUT. Dòng đầu tiên ghi số lượng chữ cái
khác nhau có trong xâu S (không phân biệt chữ hoa, chữ thường). Mỗi dòng tiếp
theo ghi một chữ cái in hoa, tiếp theo là một dẫu cách và số lần xuất hiện của nó
trong xâu S.
Cách 1
{1} assign(f1,’vanban.inp’);
{2} reset(f1);
{3} assign(f2,’vanban.out’);
{4} rewrite(f2);
{5} m:=0;
{6} while not eof(f1) do
{7} begin
{8} read(f1,c);
{9} t:=t+1;
{10} a[t]:=upcacse[c];
{11}end;
{12} for i:=1 to t do
{13} begin
{14} p:=false;
{15} for j:=1 to i-1 do

20



{16} if a[j]=a[i] then p:=true;
{17} if not(p) then m:=m+1;
{18} end;
{19} writeln(f2,m)
{20} for c:=’A’ to ‘Z’ do
{21} begin
{22} d:=0;
{23} for i:=1 to t do
{24} if a[i]=c then d:=d+1;
{25} if d>=1 then writeln(f2,c,’ ‘,d);
{26}end;
{27} close(f1);
{28} close(f2);
Phân tích:
Câu lệnh {1}, {2}, {3}, {4}, {5} có độ phức tạp là O(1);
Câu lệnh {8}, {9}, {10} có độ phức tạp là O(1), và 3 lệnh này nằm trong
câu lệnh {7}, {11} nên khối lệnh này có độ phức tạp là O(1);
Câu lệnh {6} có độ phức tạp là O(n), n=t là số kí tự;
Tương tự như các bài trên, đoạn lệnh từ {12} đến {18} có độ phức tạp là
O(n2);
Câu lệnh từ {20} đến {26} có độ phức tạp là O(m.n) với m là số kí tự từ A
đến Z ≈ O(n2);
Vậy độ phức tạp của cả bài toán là O(n2).
Cách 2:
{1} assign(f1,'vanban.txt');
{2} assign(f2,'vanban2.txt');
{3} reset(f1);
{4} rewrite(f2);
{5} while not eof(f1) do read(f1,s);
{6} for i:=1 to length(s)-1 do

{7}
for j:=i+1 to length(s) do
{8}
if s[i]>s[j] then
{9}
begin

21


{10}
{11}
{12}
{13}
{14}
{15}
{16}
{17}
{18}
{19}
{20}
{21}
{22}
{23}
{24}
{25}
{26}
{27}
{28}
{29}

{30}
{31}
{32}

tg:=s[i];
s[i]:=s[j];
s[j]:=tg;
end;
for i:=1 to length(s) do
begin
p:=false;
for j:=1 to i-1 do
if s[i]=s[j] then p:= true;
if not(p) then m:=m+1;
end;
writeln(f2,m);
for i:=1 to length(s) do s[i]:=upcase(s[i]);
for c:='A' to 'Z' do
begin
d:=0;
for i:=1 to length(s) do
if s[i]=c then d:=d+1;
if d>=1 then writeln(f2,c,' ',d);
end;
close(f1);
close(f2);

Phân tích:
Cách 1 và cách 2 cơ bản giống nhau về tính toán, nhưng khác nhau ở chỗ
cách 2 phải thêm thuật toán sắp xếp có độ phức tạp là O(n2) và thuật toán của cả

cách 2 là O(n2).

22


CHƯƠNG 3: KẾT QUẢ ĐẠT ĐƯỢC
1. Kết quả sau khi áp dụng sáng kiến kinh nghiệm
- Hiệu quả đầu tiên mà dễ nhận thấy nhất của sáng kiến kinh nghiệm là việc
giáo viên Tin học tiếp cận được với nội dung đổi mới giáo dục hiện nay đó là tự
học, tự nghiên cứu.
- Học sinh rất có hứng thú với môn học vì nó nâng cao khả năng tư duy và
phân tích thuật toán.
- Học sinh có ý thức hơn trong việc lập trình.
Tôi đã áp dụng sáng kiến kinh nghiệm đối với học sinh trường THPT Cẩm Phả
từ năm học 2015 - 2016 đến nay. Kết quả tôi thu được như sau:
Số lượng học sinh trong từng năm là 200 học sinh tương đương với 5 lớp 11
mà tôi phụ trách.
- Năm học 2014 - 2015 tôi chưa áp dụng sáng kiến kinh nghiệm thì học lực
môn Tin của học sinh có kết quả như sau:
Học lực

Số học sinh

Tỉ lệ

Giỏi

10

5%


Khá

80

40%

Trung bình

90

45%

Yếu

15

7,5%

Kém

5

2,5%

- Năm học 2015 - 2016 tôi bắt đầu áp dụng sáng kiến kinh nghiệm thì học lực
môn Tin của học sinh có kết quả như sau:
Học lực

Số học sinh


Tỉ lệ

Giỏi

20

10%

Khá

100

50%

Trung bình

70

35%

Yếu

10

5%

Kém

0


0%

- Năm học 2015-2016 tôi áp dụng sáng kiến kinh nghiệm đến thời điểm kết
thúc học kì 1 thì học lực môn Tin của học sinh có kết quả như sau:

23


×