Để giải quyết vấn đề này Tôi viết sáng kiến kinh nghiệm với đề tài “ Tìm hiểu thời gian thực của thuật toán để lựa chọn thuật toán tối ưu trong các bài toán quen thuộc bằng ngôn ngữ lập
Trang 1Nội dung Trang
2.2 Thực trạng vấn đề trước khi áp dụng sáng kiến 2
2.4 Hiệu quả của sáng kiến kinh nghiệm đối với hoạt động giáo
dục với bản thân, đồng nghiệp và nhà trường
20
5 Danh mục các đề tài SKKN mà tác giả đã được Sở
GD&ĐT xếp loại
22
Trang 21 MỞ ĐẦU
1.1Lý do chọn đề tài
Trong quá trình giảng dạy và đặc biệt trong các cuộc thi học sinh giỏichúng ta thường thấy có các bài yêu cầu các dữ liệu với nhiều bộ test khácnhau Khi gặp các bài toán này học sinh mới bắt đầu thường làm với thuậttoán đơn giản chỉ áp dụng cho dữ liệu nhỏ, vì vậy với những bộ test yêu cầu
số liệu lớn học sinh rất dễ mất điểm (test) Để giải quyết vấn đề này Tôi viết
sáng kiến kinh nghiệm với đề tài “ Tìm hiểu thời gian thực của thuật toán
để lựa chọn thuật toán tối ưu trong các bài toán quen thuộc bằng ngôn ngữ lập trình C++ ” Nội dung của đề tài là kiến thức các thuật toán về số
nguyên tố và việc vận dụng các thuật toán đó trong giải quyết các bài toán
1.2 Mục đích nghiên cứu.
Làm tài liệu cho giáo viên phục vụ giảng dạy, bồi dưỡng học sinh giỏi
1.3 Đối tượng nghiên cứu
Nhóm đội tuyển tin của nhà trường THPT Mai Anh Tuấn
1.4 Phương pháp nghiên cứu
- Phân loại theo thuật toán tương ứng với cái bài toán cụ thể
- Tiến hành thực nghiệm
- Đánh giá và tổng kết kinh nghiệm
1.5 Những điểm mới của sáng kiến
Chia nhóm nghiên cứu thành hai nhóm , kết hợp giũa thuật toán vàthực hành để đánh giá mức độ vận dụng
2 NỘI DUNG SÁNG KIẾN KINH NGHIỆM
2.1 Cơ sở lí luận của sáng kiến kinh nghiệm.
Việc phân loại các dạng bài theo thuật toán sẽ làm cho học sinh dễ dangnhận biết được thuật toán nào áp dụng cho bài toán nào một cách dễ dàng vàlinh hoạt
2.2 Thực trạng vấn đề trước khi áp dụng sáng kiến kinh nghiệm
Trong quá trình dạy đội tuyển những năm trước, học sinh nhiều khi khôngbiết cách sử dụng từng thuật toán cho từng bài bài, đẫn tới việc giải các bàimất rất nhiều thời gian hoặc mất điểm do bị thiếu test do quá thời gian hoặc
bị giới hạn dữ liệu
Trang 32.3 Nội dung chính của sáng kiến
2.3.1 Tìm hiểu thời gian thực hiện thuật toán
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.Cách thứ nhất bằng thực nghiệm, chúng ta viết chương trình và cho chạychương trình với các dữ liệu vào khác nhau trên một máy tính Cách thứ haibằ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 (cỡ của dữ liệu vào là một tham số đặc trưng cho
dữ liệu vào, nó có ảnh hưởng quyết định đến thời gian thực hiện chươngtrình Ví dụ đối với bài toán kiểm tra số nguyên tố thì cỡ của dữ liệu vào là
số cần kiểm tra; hay với bài toán sắp xếp dãy số, cỡ của dữ liệu vào là sốphần tử của dãy) Thông thường cỡ của dữ liệu vào là một số nguyên dương
n , ta sử dụng hàm số T(n) trong đó n là cỡ của dữ liệu vào để biểu diễn thời
gian thực hiện của một thuật toán
Trong tài liệu này, chúng ta hiểu hàm số T(n) là thời gian nhiều nhất cần thiết để thực hiện thuật toán với mọi bộ dữ liệu đầu vào cỡ n
Sử dụng kí hiệu toán học ô lớn để mô tả độ lớn của hàm Giả sử n là một số nguyên dương, T(n) và f(n) là hai hàm thực không âm Ta viết T(n)= O(f(n)) nếu và chỉ nếu tồn tại các hằng số dương c và n0 , sao cho T(n)≤ c x f(n), mọi n ≥ n 0
Nếu một thuật toán có thời gian thực hiện T(n)= O(f(n)) chúng ta nói rằng thuật toán có thời gian thực hiện cấp f(n).
Ví dụ: Giả sử T(n) = n 2 + 2n, ta có n 2 + 2n ≤ 3n 2 với mọi n ≥ 1
Vậy T(n) = O(n 2) trong trường hợp này ta nói thuật toán có thời gian
thực hiện cấp n 2
2.3.2 Một số quy tắc đánh giá thời gian thực hiện thuật toán
Để đánh giá thời gian thực hiện thuật toán được trình bày bằng ngônngữ
C++, ta cần biết cách đánh giá thời gian thực hiện các câu lệnh của C++Trước tiên, chúng ta hãy xem xét các câu lệnh chính trong C++ Cáccâu lệnh trong C++ được định nghĩa như sau:
1 Các phép gán, đọc, viết là các câu lệnh (được gọi là lệnh đơn)
2 Nếu S1, S2, , Sm là câu lệnh thì { S1; S2; …; Sm; } là câu lệnh(được gọi là lệnh hợp thành hay khối lệnh)
3 Nếu S1 và S2 là các câu lệnh và E là biểu thức lôgic thì If (E) S1;else S2; là câu lệnh (được gọi là lệnh rẽ nhánh hay lệnh If)
Trang 44 Nếu S là câu lệnh và E là biểu thức lôgic thì
While (E) S; là câu lệnh (được gọi là lệnh lặp điều kiện trước hay lệnhWhile)
5 Nếu S1, S2,…,Sm là các câu lệnh và E là biểu thức lôgic thì
Do
S1; S2; …; Sm;
While (E);
là câu lệnh (được gọi là lệnh lặp điều kiện sau hay lệnh Do While)
6 Nếu S là lệnh, E1 và E2 là các biểu thức cùng một kiểu thứ tự đếmđược Thì For (i=E1; i<= E2;i++) S; là câu lệnh (được gọi là lệnh lặp với sốlần xác định hay lệnh For)
Để đánh giá, chúng ta phân tích chương trình xuất phát từ các lệnh đơn,rồi đánh giá các lệnh phức tạp hơn, cuối cùng đánh giá được thời gian thựchiện của chương trình, cụ thể:
1 Thời gian thực hiện các lệnh đơn: gán, đọc, viết là O(1)
2 Lệnh hợp thành: giả sử thời gian thực hiện của S1, S2,…,Sm tương
ứng là O((f 1 (n)), O((f 2 (n)), …, O((f m (n)) Khi đó thời gian thực hiện của lệnh hợp thành là O(max(f 1 (n), f 2 (n), …, f m (n)))
3 Lệnh If: giả sử thời gian thực hiện của S1, S2 tương ứng là
Khi đó thời gian thực hiện của lệnh If là: O((f 1 (n)), O((f 2 (n)) Khi đó thời gian thực hiện của lệnh If là O(max(f 1 (n), f 2 (n)))
4 Lệnh lặp While: giả sử thời gian thực hiện lệnh S (thân của lệnh
While) là O(f(n)) và g(n) là số lần lặp tối đa thực hiện lệnh S Khi đó thời gian thực hiện lệnh While là O(f(n)g(n)).
5 Lệnh lặp Repeat: giả sử thời gian thực hiện khối lệnh{ S1; S2;…;Sm; }
là O(f(n)) và g(n) là số lần lặp tối đa Khi đó thời gian thực hiện lệnh Do While là O(f(n)g(n)).
6 Lệnh lặp For: giả sử thời gian thực hiện lệnh S là O(f(n)) và g(n) là
số lần lặp tối đa Khi đó thời gian thực hiện lệnh For là O(f(n)g(n)).
2.3.3 Ước lượng độ phức tạp thuật toán tương ứng với độ lớn dữ liệu
Độ phức tạp thuật toán là một hàm phụ thuộc đầu vào Tuy nhiêntrong những ứng dụng thực tiễn, chúng ta không cần biết chính xác hàm này
mà chỉ cần biết một ước lượng đủ tốt của chúng Việc ước lượng rất quan
Trang 5trọng trong việc giải quyết bài toán lập trình thi đấu, đòi hỏi nhanh nhạy và
có quyết định chính xác ngay từ đầu Độ phức tạp của thuật toán có thể cónhững giá trị là O(1), O(log n), O(n), O(nlogn), O(n2), O(n3)… Và thời gianthực hiện được so sánh như sau:
O(1)< O(log n) < O(n) < O(nlogn) < O(n2) < O(n3)…
Sau đây là bảng ước lượng (*) giới hạn dữ liệu vào tương ứng với độphức tạp của thuật toán đảm bảo thực hiện trong thời gian tối đa 1 giây
Dạng 1 Áp dụng định lí, công thức, kết quả… của toán học có thể cho ta
cách giải rất tối ưu Những cách giải đó có thể đáp ứng với dữ liệu lớn
VD : Tính tổng S(N) = 12 + 22 +…+ N2
Dữ liệu vào: Từ tệp văn bản TONGBP.INP gồm:
Dòng đầu chứa số nguyên dương T T là số lượng test (T ≤ 105)
T dòng tiếp theo, mỗi dòng là một số nguyên dương N
141015
Trang 6Với cách 1, độ phức tạp là O(n2) Đối chiếu theo bảng ước lượng (*) đã
nêu với thời gian thực hiện tối đa 1 giây thì đáp ứng được giới hạn với 1 ≤
N, T ≤ 103 chiếm 80% số test theo yêu cầu của đề.
Với cách 2, độ phức tạp O(n) đáp ứng được thời gian thực hiện tối đa 1
giây cho cả 20% số test còn lại với 10 3 < N, T ≤ 105
Code tham khảo
#include <bits/stdc++.h>
using namespace std;
int main()
{
freopen ("tongbp.inp" , "r" , stdin);
freopen ("tongbp.out" , "w" , stdout);
ios_base::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
long long n,t; cin >> n;
for (long long i = 0 ; i < n ; i++)
Trang 7Cách 1: sử dụng hàm kiểm tra tính nguyên tố thông thường:
bool check(long long x)
{if (x==1) return false;
for(long long i=2;i<=sqrt(x);i++)
if (x%i==0) return false;
- Kiểm tra tính nguyên
tố của ít số Hoặc kiểm tra số lượng phần tử khoảng n≤ 105
- Kiểm tra và liệt kê nhiều số nguyên tố-Kiểm tra số lượng phần
tử khoảng n≤ 107
Bài tập vận dụng:
Xét bài toán Số nguyên tố trong đoạn1
1 Bài N1002B trên http://laptrinhphothong.vn/
Trang 8Yêu cầu: Đếm số lượng số nguyên tố trong đoạn [a; b]
Dữ liệu: Một dòng ghi hai số nguyên a, b với 0 < a, b ≤ 107
Kết quả: Một dòng là số lượng số nguyên tố trong đoạn [a; b].
cin >> a >> b ; int dem = 0;
for (long long i = a; i <=b ; i++)
Trang 9{if (isPrime[i]) dem++;}
cout << dem;
return 0;
}
Dạng 3: Thuật toán sắp xếp
* Thuật toán sắp xếp nổi bọt (Bubble Sort)
Đoạn chương trình thực hiện thuật toán:
- Code đơn giản, dễ hiểu
- Sắp xếp được với phần tử có miền giá trị lớn |ai| ≤ 1018
Nhược điểm:
- Độ phức tạp của thuật toán lớn O(n 2)
- Chạy không đủ nhanh (quá 1 giây) với dữ liệu lớn, chỉ áp dụng với
số phần tử n ≤ 104
* Thuật toán sắp xếp nhanh (Quick Sort)
Trong C++ đây là hàm có sẵn nên khi sử dụng chỉ cần gọi hàm
sort(a,a+n) để sắp xếp dãy từ a0,… ,an là được
Trang 10*Thuật toán sắp xếp bằng phương pháp đếm phân phối
Ý tưởng:
- Khởi tạo giá trị ban đầu cho mảng dem
- Dùng mảng dem để đếm số lần xuất hiện của số a[i] trong dãy
- Duyệt i từ giá trị nhỏ nhất (gtmin) của các a[i] đến giá trị lớn nhất(gtmax) của các a]i] Duyệt j từ 1 đến dem[i] rồi in ra i
So sánh Sắp xếp nổi bọt Sắp xếp nhanh Sắp xếp đếm phân phối
hợp xấu nhất có
thể là O(n 2 )
O(max(n,gtmax))
phụ thuộc miền giá trị
Đặc điểm Code đơn giản,
Code đơn giản
Bài tập áp dụng
Bài 1: Cho một dãy số nguyên a 1 , a 2, , a n Hãy đếm số lượng giá trịkhác nhau trong dãy và đưa ra số lần lặp của giá trị xuất hiện nhiều nhất
Dữ liệu vào: File BAI1.INP gồm 2 dòng:
+ Dòng đầu số nguyên dương N (N <= 10 6 )
+ Dòng thứ hai gồm dãy số nguyên a 1 , a 2, , a n ( |a i | <= 10 9)
Trang 11Dữ liệu ra: File BAI1.OUT gồm 2 dòng:
+ Dòng đầu tiên ghi số lượng giá trị khác nhau trong dãy
+ Dòng thứ 2 ghi số lần lặp của giá trị xuất hiện nhiều nhất
Giới hạn: - 60% số test với 1 ≤ N ≤ 104
- 40% số test với 10 4 < N ≤ 106
B1: Sắp xếp dãy số tăng dần (các số bằng nhau đứng liên tiếp nhau) B2: Khởi tạo d:=1 (số phần tử khác nhau), dd:=1 (số phần tử liên tiếp bằng nhau) và dmax:=1 (số lần lặp của giá trị xuất hiện nhiều nhất) Duyệt i từ phần tử thứ 2 đến n, nếu 2 số đứng cạnh nhau mà bằng nhau thì tăng dd, ngược lại khác nhau thì tăng d, đồng thời tìm dmax.
Cách 1: nếu sử dụng thuật toán sắp xếp nổi bọt thì chương trình lúc đó
sẽ có độ phức tạp là O(max(n 2 ,n)) = O(n 2 )( O(n 2 ) của đoạn chương trình sắp xếp B1, O(n) của đoạn chương trình B2)
Cách 2: Nếu sử dụng thuật toán sắp xếp nhanh thì chương trình lúc đó
sẽ có độ phức tạp là O(max(=nlogn,n)) = O(nlogn) )( O(nlogn) của đoạn chương trình sắp xếp B1, O(n) của đoạn chương trình B2)
Trường hợp nếu dữ liệu vào là |a i | <= 10 6 , N <= 10 7 thì sử dụng thuật
toán đếm phân phối là phù hợp nhất
Code tham khảo:
#include <bits/stdc++.h>
using namespace std;
long long n, d, dmax, dd, i, a[1000001] = {};
main(){
freopen ("bai1.inp" , "r" , stdin);
freopen ("bai1.out" , "w" , stdout);
cin >> n;
for (i = 0; i < n ; i++){
cin >> a[i];
Trang 12Bác Bình có một khu vườn rất nhiều cây, mỗi cây có một chiều cao
khác nhau Bác Bình muốn chọn tất cả các cây đặc biệt trong vườn để trồng
thành một hàng cây mới (cây đặc biệt là cây có chiều cao là một số nguyên
tố) Hơn nữa để cây tiếp xúc tốt với ánh sáng bác có ý tưởng bố trí vị trí các
cây đặc biệt theo quy tắc thực hiện lần lượt như sau:
- B1: Chọn cây cao nhất đặt làm mốc;
- B2: Cây cao tiếp theo đặt ở bên trái của mốc;
- B3: Cây cao tiếp theo nữa ở bên phải của mốc;
Thực hiện lặp đi lặp lại B2, B3 cho đến khi hết số cây đặc biệt
Yêu cầu: Cho trước số lượng và chiều cao của các cây trong vườn, hãy giúp
bác Bình trồng hàng các cây đặc biệt theo đúng ý tưởng của bác
Dữ liệu vào: Từ tệp văn bản TRONGCAY.INP gồm hai dòng:
Dòng thứ nhất chứa số nguyên N (1 ≤ N ≤ 106) là số lượng cây trong
Trang 13 Dòng thứ hai chứa N số nguyên a i (1 ≤ a i ≤ 107, 1 ≤ i ≤ N) là chiều cao của cây thứ i.
Kết quả: Ghi ra tệp văn bản TRONGCAY.OUT gồm các số trên cùng một
dòng là chiều cao của các cây đặc biệt trong hàng cây mới Nếu không cócây đặc biệt nào thì ghi ra tệp giá trị ˗1
Theo bảng ước lượng (*) :
- Cách 1: độ phức tạp O(n 2 ) => chỉ đáp ứng được 75% số test với 1 ≤ N
≤ 10 4
- Cách 2: độ phức tạp O(nlogn) => đáp ứng 100% số test của bài toán với
N ≤ 10 6
Vậy sử dụng thuật toán sắp xếp nhanh để cài đặt chương trình
Code tham khảo
#include <bits/stdc++.h>
#define LL long long
#define TASK "TRONGCAY"
const int maxn=1e6+2;
const int q=1e7+2;
using namespace std;
int c[maxn], n; bool b[q];
/// -void sangnt()
Trang 14memset(b,true,sizeof(b));
b[1]=false;
for(int i=2;i*i<=q;++i) if(b[i])
for(int j=i*i;j<=q;j+=i) b[j]=false;
for(int j=1;j<=i;j+=2) cout<<c[j]<<" ";
for(int j=i;j>1;j-=2) cout<<c[j]<<" ";
}
else
{
for(int j=2;j<=i;j+=2) cout<<c[j]<<" ";
for(int j=i;j>=1;j-=2) cout<<c[j]<<" ";
}
}
Trang 15
Dạng 4: Sử dụng thuật toán tìm kiếm
Ta xét bài toán tìm kiếm dạng đơn giản sau:
Cho dãy A gồm N số nguyên A 1 , A 2 , …A N và số nguyên x Cần biết có hay không chỉ số i (1≤ i ≤ N) mà A i = x Nếu có hãy cho biết chỉ số đó.
Trong giới hạn đề tài, chúng ta sẽ tìm hiểu 2 thuật toán tìm kiếm phổ biến nhất là: tìm kiếm tuần tự và tìm kiếm nhị phân
(giới hạn của mảng)
- Dãy đã sắp xếp
- Nếu dãy chưa sắp xếp thì phụ thuộc thuật toán sắp xếp sử dụng
Bài tập áp dụng
Bài 1: Tìm kiếm nhị phân3
Cho dãy số a(n) nguyên dương Tìm phần tử trong dãy a(n) có giá trị bằng x
Dữ liệu: Dòng đầu ghi số nguyên dương n (n≤105)
Dòng 2 ghi n số nguyên dương ai (ai ≤1018)
Dòng 3 ghi số nguyên dương T (T ≤105)
T dòng kế tiếp, mỗi dòng ghi số nguyên dương x
3 Bài SB02B trên http://laptrinhphothong.vn/
Trang 16Kết quả: ghi ra T dòng, dòng thứ i ghi Y nếu trong dãy a(n) tồn tại
phần tử x, nếu không tồn tại ghi N
Giới hạn: - 60% số test với 1 ≤ N ≤ 10 3
- 40% số test với 10 3 < N ≤ 10 5
Dữ liệu vào gồm dãy số a(n) nguyên dương (n≤105, ai ≤1018) và T test
(T≤105), mỗi test là một số nguyên dương x
Đây là bài toán tìm kiếm đơn giản, đa số học sinh sẽ dùng cách tìm kiếm tuần tự Tuy nhiên vì dữ liệu khá lớn (n≤105 ,T ≤105) nên ta xét các cách giải sau:
Như vậy theo bảng ước lượng (*) :
Nếu sử dụng cách 1 (tìm kiếm tuần tự cho mỗi T) thì chỉ đáp ứng được
60% số test với 1 ≤ N ≤ 10 3 , T≤105
Nếu sử dụng cách 2 (sắp xếp dãy an tăng dần theo sắp xếp nhanh và
dùng tìm kiếm nhị phân cho mỗi T) thì đáp ứng 100% test với N ≤ 10 5 , T≤105
Vậy lựa chọn cách 2 để cài đặt chương trình
Code tham khảo
#include<bits/stdc++.h>
using namespace std;
const long long maxn=1e5+7;
long long n,x,t;
long long a[maxn];
int nhiphan(long long B[], long long m, long long y)
Trang 17Yêu cầu: Đếm số cặp (i, j) thỏa mãn điều kiện: i ≤ j và L ≤ |ai+…+aj| ≤ R
Dữ liệu vào: Từ file văn bản SEQ.INP gồm:
- Dòng đầu tiên chứa 3 số nguyên n, L, R (n ≤ 105 ; 0 ≤ L ≤ R ≤ 109)
- Dòng thứ hai chứa n số nguyên dương a1, a2,…, an (ai ≤ 109)
4 Nguồn Đề thi học sinh giỏi Tỉnh năm học 2016-2017