Lý do chọn đề tài Trong quá trình tham gia giảng dạy và bồi dưỡng học sinh giỏi môn Tin học thì việc tạo test để chấm bài cho học sinh là một công việc hết sức quan trọng.. Khi chấm các
Trang 1MỤC LỤC
I PHẦN MỞ ĐẦU 1
1 Lý do chọn đề tài 1
2 Mục đích của đề tài 1
3 Nhiệm vụ và phương pháp nghiên cứu 1
4 Điểm mới trong kết quả nghiên cứu 1
II NỘI DUNG 2
1 Sinh số nguyên ngẫu nhiên trong đoạn từ [0, 32767] 2
2 Sinh số nguyên ngẫu nhiên trong đoạn từ [0, 106] 2
3 Sinh số nguyên ngẫu nhiên trong đoạn từ [0, 109] 2
4 Sinh số nguyên ngẫu nhiên trong đoạn từ [1, 109] 3
5 Sinh số nguyên ngẫu nhiên trong đoạn từ [X, Y] 3
6 Sinh số nguyên ngẫu nhiên trong đoạn từ [0, 2.109] 3
7 Sinh số nguyên ngẫu nhiên trong đoạn từ [0, 1018] 3
8 Sinh số nguyên ngẫu nhiên có giá trị tuyệt đối ≤ 2.109 4
9 Sinh số nguyên ngẫu nhiên có giá trị tuyệt đối ≤ 1018 4
10 Sinh n số nguyên có giá trị tuyệt đối ≤ 2.109 theo cấu trúc 4
11 Sinh n số tự nhiên tăng dần (tăng không ngặt) và các số nằm trong đoạn từ [0, 2.109] 5
12 Sinh n số tự nhiên tăng dần (tăng ngặt) và các số nằm trong đoạn từ [0, 2.109] 5
13 Sinh n số tự nhiên khác nhau đôi một và các số nằm trong đoạn từ [0, 2.109] 5
14 Tạo xâu kí tự chỉ gồm các chữ cái in thường từ ‘a’ đến ‘z’ 7
15 Tạo xâu kí tự chỉ gồm các chữ cái in thường từ ‘A’ đến ‘Z’ 7
16 Tạo xâu kí tự chỉ gồm các chữ cái in thường và các chữ cái in hoa 7
17 Tạo xâu kí tự chỉ gồm các chữ cái in thường, các chữ cái in hoa và các chữ số 8
18 Tạo xâu kí tự chỉ gồm các chữ cái in thường, các chữ cái in hoa, các chữ số và có dấu cách trống 8
19 Tạo ra n xâu kí tự chỉ gồm các chữ cái in thường, các chữ cái in hoa và các chữ số Mỗi xâu nằm trên 1 hàng và độ dài mỗi xâu không quá x kí tự 10
III PHẦN KẾT LUẬN 12
Tài liệu tham khảo 12
Trang 2I PHẦN MỞ ĐẦU
1 Lý do chọn đề tài
Trong quá trình tham gia giảng dạy và bồi dưỡng học sinh giỏi môn Tin học thì việc tạo test để chấm bài cho học sinh là một công việc hết sức quan trọng Khi chấm các bài toán lập trình thi đấu trong Tin học thì chúng ta dùng test để chấm Để có được test chấm thì chúng ta phải tạo ra các test bằng tay hoặc bằng code tạo ra file input của bài toán, từ file input của bài toán ta chạy trương trình code chuẩn hoặc code trâu để sinh ra file output đúng Một bộ test chuẩn là bộ test phải vét được tất cả các trường hợp của bài toán
Từ các lí do trên, tôi xin trình bày sáng kiến kinh nghiệm “TẠO TEST CHẤM NGẪU NHIÊN BẰNG CÁCH DÙNG HÀM rand() TRONG C++”, để giúp giáo viên tạo ra được các file input đúng theo bài toán yêu cầu.
2 Mục đích của đề tài
Tạo test chấm bài là công việc hết sức quan trong trọng Giúp giáo viên chấm bài một cách nhanh chóng và hiệu quả.
3 Nhiệm vụ và phương pháp nghiên cứu
Viết sáng kiến kinh nghiệm thường xuyên liên tục cũng là nhiệm vụ chính trị của mỗi giáo viên, nhưng cần phải lựa chọn phương pháp nghiên cứu đúng đắn và phù hợp với nhà trường trung học phổ thông Sáng kiến kinh nghiệm đang trình bày của tôi dựa theo các luận cứ khoa học hướng đối tượng, cụ thể: thuyết trình, quan sát, điều tra cơ bản, phân tích kết quả thực nghiệm sư phạm,v.v… phù hợp với bài học và môn học thuộc lĩnh vực Tin học.
4 Điểm mới trong kết quả nghiên cứu
Hiện nay, nhiều giáo viên ở cấp THCS và THPT đang còn chưa biết cách tạo test để chấm bài cho học sinh Sau khi biết tạo file input ngẫu nhiên thì công việc tạo test chấm bài được thực hiện một cách nhanh chóng và hiệu quả hơn.
Trang 3II NỘI DUNG
Trong C++ ta có hàm rand() sẽ cho ra giá trị ngẫu nhiên từ 0 đến 32767 Để sau mỗi lần chạy cần kết quả khác nhau thì ta cho hàm srand(time(0)); viết trước khi sử dụng rand() Trong các file input được trình bày ở dưới, tôi sẽ sinh ra file SONGUYEN.INP hoặc file XAU.INP:
1 Sinh số nguyên ngẫu nhiên trong đoạn từ [0, 32767]
#include <bits/stdc++.h>
using namespace std;
int a;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
a= rand(); //Cho giá trị ngẫu nhiên từ 0 đến 32767
cout << a;
return 0;
}
2 Sinh số nguyên ngẫu nhiên trong đoạn từ [0, 106]
Trong C++ không có hàm sinh ra số đến 106, vì vậy ta dùng mẹo như sau: rand() chỉ sinh ra được số lớn nhất là 32767 Nếu ta lấy 32767 * 32767 > 106 Rồi dùng tính chất % N sẽ cho ra giá trị trong đoạn từ [0, N-1], muốn có giá trị
từ [0, N] thì ta lấy kết quả đó % (N+1) Ta có rand()*rand() có giá trị max < 2.109 nên rand()*rand() ta không cần phải ép sang kiểu long long vì tích của nó không bị tràn số.
#include <bits/stdc++.h>
const int N = 1e6+1;
using namespace std;
int a;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
a= rand()*rand()% (N+1);
cout << a;
return 0;
}
3 Sinh số nguyên ngẫu nhiên trong đoạn từ [0, 109]
#include <bits/stdc++.h>
const int N = 1e9+1;
using namespace std;
int a;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
a= rand()*rand()% (N+1);
Trang 4cout << a;
return 0;
}
4 Sinh số nguyên ngẫu nhiên trong đoạn từ [1, 109]
#include <bits/stdc++.h>
const int N = 1e9;
using namespace std;
int a;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
a= rand()*rand()% N +1;
cout << a;
return 0;
}
5 Sinh số nguyên ngẫu nhiên trong đoạn từ [X, Y]
Trong đó X ≤ Y và X, Y có giá trị trong phạm vi từ 0 đến 109 Trong ví dụ sau, tôi lấy giá trị X = 10 và Y = 20 (còn nếu cần thay đổi giá trị khác thì chúng
ta thay giá trị của X và Y là được)
#include <bits/stdc++.h>
using namespace std;
int a;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
int X = 10;
int Y = 20;
a= X + rand()*rand()%(Y - X + 1);
cout << a;
return 0;
}
6 Sinh số nguyên ngẫu nhiên trong đoạn từ [0, 2.109]
Trong code dưới, ta dùng 1LL (nghĩa là số 1 ở kiểu long long) để nhân với rand() (vì rand() là kiểu int) nên tích đó sẽ ép được về kiểu long long.
#include <bits/stdc++.h>
const int N = 2e9;
using namespace std;
int a;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
a= 1LL*rand()*rand()*rand()%(N+1);
cout << a;
return 0;
}
7 Sinh số nguyên ngẫu nhiên trong đoạn từ [0, 1018]
#include <bits/stdc++.h>
const long long N = 1e18;
using namespace std;
Trang 5long long a;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
a= 1LL*rand()*rand()*rand()*rand()%(N+1);
cout << a;
return 0;
}
8 Sinh số nguyên ngẫu nhiên có giá trị tuyệt đối ≤ 2.109
Ta sẽ sinh một số nguyên ngẫu nhiên từ 0 đến 2.109 trừ đi một số nguyên ngẫu nhiên trong đoạn từ 0 đến 2.109.
#include <bits/stdc++.h>
const int N = 2e9;
using namespace std;
int a;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
a= 1LL*rand()*rand()*rand()%(N+1) - 1LL*rand()*rand()*rand()%(N+1);
cout << a;
return 0;
}
9 Sinh số nguyên ngẫu nhiên có giá trị tuyệt đối ≤ 1018
Ta sẽ sinh một số ngẫu nhiên từ 0 đến 1018 trừ đi một số ngẫu nhiên trong đoạn từ 0 đến 1018.
#include <bits/stdc++.h>
const long long N = 1e18;
using namespace std;
long long a;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
a= 1LL*rand()*rand()*rand()*rand()%(N+1) - 1LL*rand()*rand()*rand()*rand()% (N+1);
cout << a;
return 0;
}
10 Sinh n số nguyên có giá trị tuyệt đối ≤ 2.109 theo cấu trúc
Dòng đầu tiên là số nguyên dương n, dòng thứ hai là n số nguyên thỏa mãn điều kiện trên và các số cách nhau bởi một dấu cách trống.
#include <bits/stdc++.h>
const int N = 2e9;
using namespace std;
int a;
int n;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
Trang 6n = 100;// thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n";
for(int i = 1; i<= n; i++){
a = 1LL*rand()*rand()*rand()%(N+1)-1LL*rand()*rand()*rand()%(N+1); cout << a << " ";
}
return 0;
}
11 Sinh n số tự nhiên tăng dần (tăng không ngặt) và các số nằm trong đoạn
từ [0, 2.109]
#include <bits/stdc++.h>
const int N = 100;
using namespace std;
int a;
int n;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
n = 100;// thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n";
a = 0; // thay đổi giá trị bắt đầu nếu cần
for(int i = 1; i<= n; i++){
cout << a << " ";
a = a + rand()%N; // thay đổi giá trị N nếu cần
}
return 0;
}
12 Sinh n số tự nhiên tăng dần (tăng ngặt) và các số nằm trong đoạn từ [0, 2.109]
#include <bits/stdc++.h>
const int N = 100;
using namespace std;
int a;
int n;
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
n = 100; // thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n";
a = 0; // thay đổi giá trị bắt đầu nếu cần
for(int i = 1; i<= n; i++){
cout << a << " ";
a = a + rand()%N + 1; // thay đổi giá trị N nếu cần
}
return 0;
}
13 Sinh n số tự nhiên khác nhau đôi một và các số nằm trong đoạn từ [0, 2.109]
Để sinh ra số thứ n nó khác với n-1 số trước đó, thì ta sẽ lặp trong khi nó sinh ra được số thứ n mà nó khác thì mới thoát Như vậy n nhỏ thì chạy trong thời gian cho phép Còn nếu n mà lớn thì thời gian chạy rất lâu Vì vậy để sinh
Trang 7ra n số nguyên đôi một khác nhau ta có mẹo như sau: Ta sinh ra một mảng tăng dần, rùi thực hiện nhiều lần phép đổi chỗ 2 vị trí trong mảng đó là được
#include <bits/stdc++.h>
const int N = 100;
const int NN = 1e6;
using namespace std;
int a;
int n;
int b[NN+3];
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
n = 100;// thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n";
// sinh mảng b là mảng các số tăng
a = rand()%N; // thay đổi giá trị bắt đầu nếu cần
b[1] = a;
for(int i = 2; i<= n; i++){
a = a + rand()%N + 1; // thay đổi giá trị N nếu cần
b[i] = a;
}
// đổi chỗ ngẫu nhiên 2 phần tử của mảng b
int t = 1000; // thay đổi giá tri t là số lần đổi chỗ
for(int i = 1; i <= t; i++)
swap(b[rand()*rand()%n+1], b[rand()*rand()%n+1]);
for (int i =1; i <= n; i++)
cout << b[i] << " ";
return 0;
}
Hoặc code tương tự khác:
#include <bits/stdc++.h>
const int N = 100;
const int NN = 1e6;
using namespace std;
int a;
int n;
int b[NN+3];
int main(){
freopen("SONGUYEN.INP","w", stdout);
srand(time(0));
n = 100;// thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n";
// sinh mảng b là mảng các số tăng
a = 2e9 - rand()%N; // thay đổi giá trị bắt đầu nếu cần
b[1] = a;
for(int i = 2; i<= n; i++){
a = a - (rand()%N + 1); // thay đổi giá trị N nếu cần
b[i] = a;
}
// đổi chỗ ngẫu nhiên 2 phần tử của mảng b
int t = 1000; // thay đổi giá tri t là số lần đổi chỗ
for(int i = 1; i <= t; i++)
swap(b[rand()*rand()%n+1], b[rand()*rand()%n+1]);
for (int i =1; i <= n; i++)
cout << b[i] << " ";
return 0;
}
Trang 814 Tạo xâu kí tự chỉ gồm các chữ cái in thường từ ‘a’ đến ‘z’
Ta đã biết ký tự ‘a’ trong bảng mã ASCII là 97, ‘b’ là 98, … ‘z’ là 122 Nên ta sẽ sinh ngẫu nhiên các số trong đoạn [97, 122] rồi ép chúng về kiểu char.
#include <bits/stdc++.h>
using namespace std;
int n;
char ch;
int main(){
freopen("XAU.INP","w", stdout);
srand(time(0));
n = 100;// thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n"; // Nếu đề bài không yêu cầu in n thì ta bỏ dòng này for (int i = 1; i <= n; i ++){
ch = 97+ rand()%26;
cout << ch;
}
return 0;
}
Hoặc:
#include <bits/stdc++.h>
using namespace std;
int n;
int main(){
freopen("XAU.INP","w", stdout);
srand(time(0));
n = 100;// thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n"; // Nếu đề bài không yêu cầu in n thì ta bỏ dòng này for (int i = 1; i <= n; i ++)
cout << char(97+ rand()%26);
return 0;
}
15 Tạo xâu kí tự chỉ gồm các chữ cái in thường từ ‘A’ đến ‘Z’
Ta đã biết ký tự ‘A’ trong bảng mã ASCII là 65, ‘B’ là 66, … ‘Z’ là 90 Nên ta sẽ sinh ngẫu nhiên các số trong đoạn [65, 90] rồi ép chúng về kiểu char.
#include <bits/stdc++.h>
using namespace std;
int n;
int main(){
freopen("XAU.INP","w", stdout);
srand(time(0));
n = 100; // Thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n"; // Nếu đề bài không yêu cầu in n thì ta bỏ dòng này for (int i = 1; i <= n; i ++)
cout << char(65 + rand()%26);
return 0;
}
16 Tạo xâu kí tự chỉ gồm các chữ cái in thường và các chữ cái in hoa
#include <bits/stdc++.h>
using namespace std;
Trang 9int n, nn;
char s[300]; // khai báo thừa
int main(){
freopen("XAU.INP","w", stdout);
srand(time(0));
n = 100;// thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n"; // Nếu đề bài không yêu cầu in n thì ta bỏ dòng này
nn = 0;
for (char ch = 'a'; ch <= 'z'; ch++){
nn++;
s[nn] = ch;
}
for (char ch = 'A'; ch <= 'Z'; ch++){
nn++;
s[nn] = ch;
}
// nn = 52;
for (int i = 1; i <= n; i++ )
cout << s[rand()%nn + 1];
return 0;
}
17 Tạo xâu kí tự chỉ gồm các chữ cái in thường, các chữ cái in hoa và các chữ số
#include <bits/stdc++.h>
using namespace std;
int n, nn;
char s[300]; // khai báo thừa
int main(){
freopen("XAU.INP","w", stdout);
srand(time(0));
n = 100;// thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n"; // Nếu đề bài không yêu cầu in n thì ta bỏ dòng này
nn = 0;
for(char ch = 'a'; ch <= 'z'; ch++){
nn++;
s[nn] = ch;
}
for(char ch = 'A'; ch <= 'Z'; ch++){
nn++;
s[nn] = ch;
}
for(char ch = '0'; ch <= '9'; ch++){
nn++;
s[nn] = ch;
}
// ta tính ra được nn = 62;
for (int i = 1; i <= n; i++ )
cout << s[rand()%nn + 1];
return 0;
}
18 Tạo xâu kí tự chỉ gồm các chữ cái in thường, các chữ cái in hoa, các chữ
số và có dấu cách trống
#include <bits/stdc++.h>
using namespace std;
int n, nn;
char s[300]; // khai báo thừa
int main(){
Trang 10freopen("XAU.INP","w", stdout);
srand(time(0));
n = 100;// thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n"; // Nếu đề bài không yêu cầu in n thì ta bỏ dòng này
nn = 0;
for(char ch = 'a'; ch <= 'z'; ch++){
nn++;
s[nn] = ch;
}
for(char ch = 'A'; ch <= 'Z'; ch++){
nn++;
s[nn] = ch;
}
for(char ch = '0'; ch <= '9'; ch++){
nn++;
s[nn] = ch;
}
// ta tính ra được nn = 62;
nn++;
s[nn]= ' ';
for(int i =1; i<= nn-1; i++)
cout << s[rand()%nn+1];
cout << s[rand()%(nn-1)+1]; // in ký tự cuối không có dấu cách trống return 0;
}
Trong thực tế, nếu cần xâu đó có nhiều dấu cách trống xuất hiện thì ta có thể cho thêm nhiều dấu cách trống vào mảng s, để sinh rand() ngẫu nhiên hoặc trong lúc in ta sẽ in thêm dấu cách trống như code sau:
#include <bits/stdc++.h>
using namespace std;
int n, nn;
char s[300]; // khai báo thừa
int main(){
freopen("XAU.INP","w", stdout);
srand(time(0));
n = 100;// thay đổi giá trị của n để sinh ra các test khác nhau
cout << n << "\n"; // Nếu đề bài không yêu cầu in n thì ta bỏ dòng này
nn = 0;
for(char ch = 'a'; ch <= 'z'; ch++){
nn++;
s[nn] = ch;
}
for(char ch = 'A'; ch <= 'Z'; ch++){
nn++;
s[nn] = ch;
}
for(char ch = '0'; ch <= '9'; ch++){
nn++;
s[nn] = ch;
}
// ta tính ra được nn = 62;
cout << s[rand()%nn+1]; // đảm bảo không có dấu cách ở đầu
for(int i =1; i<= nn - 1; i++){
int t = rand()%5; // % số càng nhỏ thì dấu cách càng xuất hiện nhiều
if (t==0) cout << " ";
else cout << s[rand()%nn+1];