SÁNG KIẾN KINH NGHIỆMSỬ DỤNG KIỂU DỮ LIỆU XÂU ĐỂ XỬ LÍ CÁC BÀI TOÁN CẦN XỬ LÍ DỮ LIỆU SỐ LỚN I.. Hằng năm, trong kỳ thi học sinh giỏi tỉnh vẫn thường gặp những bài toán cần xử lý dữ liệu
Trang 1SÁNG KIẾN KINH NGHIỆM
SỬ DỤNG KIỂU DỮ LIỆU XÂU ĐỂ XỬ LÍ CÁC BÀI TOÁN CẦN XỬ LÍ
DỮ LIỆU SỐ LỚN
I ĐẶT VẤN ĐỀ.
1 Lý do chọn sáng kiến kinh nghiệm: Khi gặp các bài toán có yêu cầu về xử
lý số nguyên có giá trị khá lớn trong Pascal ta thường gặp phải một số lỗi: lỗi tràn
ngăn xếp stack overflow error; lỗi vượt phạm vi khai báo Range check error; lỗi tràn vùng nhớ Heap Heap overflow error và khi đó chương trình không thực
hiện được
Hằng năm, trong kỳ thi học sinh giỏi tỉnh vẫn thường gặp những bài toán cần
xử lý dữ liệu số lớn với số lượng test khá lớn đòi hỏi thí sinh phải tìm cách để xửlí( Nếu không sử dụng Free Pascal)
Vấn đề đặt ra là phải tìm một số phương pháp xử lí các số lớn mà tránh gặpphải những lỗi có thể gặp như trên Trong phạm vi của sáng kiến này, tôi xin giớithiệu một số bài toán phải xử lí số nguyên có giá trị lớn bằng cách lưu trữ (đọc)
chúng dưới dạng kiểu dữ liệu xâu
Ngoài ra, việc sử dụng kiểu dữ liệu xâu còn giúp người lập trình có nhiều thuậntiện hơn trong việc xử lí Điều này cũng được chỉ ra trong từng bài toán cụ thể màsáng kiến kinh nghiệm có đệ cập đến
2 Mục đích nghiên cứu: Sáng kiến kinh nghiệm nghiên cứu nhằm mục đích
phục vụ công tác giảng dạy và bồi dưỡng học sinh giỏi trường, học sinh giỏi tỉnh,học sinh thi tin học trẻ Ngoài ra sáng kiến kinh nghiệm còn được sử dụng làmchuyên đề báo cáo cấp tổ
Sáng kiến kinh nghiệm chỉ ra ưu điểm của kiểu dữ liệu xâu trong việc ứng dụng
nó để giải các bài toán yêu cầu xử lí dữ liệu số lớn Kiểu dữ liệu xâu là kiểu dữ liệuđược đề cập khá đầy đủ, chi tiết, sáng tỏ trong sách giáo khoa tin học 11 và các tàiliệu tham khảo khác
3 Đối tượng nghiên cứu: Kiểu dữ liệu xâu và các thao tác xử lí xâu Cách vận
dụng kiểu dữ liệu xâu để thay thế kiểu dữ liệu số Một số dạng bài tập liên quanđến sáng kiến kinh nghiệm Thuật toán để giải một số bài toán tiêu biểu
4 Đối tượng khảo sát , thực nghiệm: Học sinh khối 11 đã học xong bài 12
kiểu dữ liệu xâu, học sinh giỏi trường, đội tuyển học sinh giỏi tỉnh, đội tuyển thi tinhọc trẻ và các đồng nghiệp
5 Phương pháp nghiên cứu: Đi từ nhu cầu thực tiễn trong công tác giảng dạy,
đặc biệt là công tác bồi dưỡng học sinh giỏi Cụ thể là qua các bài toán xử lí dữ liệu
số lớn, để tìm ra cách xử lí đơn giản, hiệu quả hơn Đó là cách sử dụng kiểu dữ liệuxâu Vận dụng và kiểm tra tính hiệu quả, tính tối ưu của phương pháp Từ đó từngbước xây dựng nên sáng kiến kinh nghiệm
Tóm lại , phương pháp nghiên cứu là đi từ thực tiễn đến lí luận, rồi từ lí luận quay về phục vụ thực tiễn.
6 Phạm vi và kế hoạch nghiên cứu: Sáng kiến kinh nghiệm được thực hiện
theo kế hoạch của ban chuyên môn nhà trường, bắt bầu từ tháng 8 /2016 đến tháng
Trang 25/2017, và là kết quả tích luỹ của nhiều năm dạy học và bồi dưỡng học sinh giỏitỉnh.
II NỘI DUNG.
1 Cơ sở lí luận: Kiểu dữ liệu xâu là dãy hữu hạn các kí tự trong bộ mã ASCII.
Mỗi kí tự là một phần tử của xâu Số lượng các phần tử của xâu được gọi là độ dàicủa xâu Độ dài tối đa của xâu là 255
Một số nguyên lớn cũng được xem như là “một xâu” mà mỗi phần tử của nó là
một kí tự thuộc phạm vi [’0’ ’9’] Ngoài ra, các phép so sánh chữ số còn có thể ápdụng để so sánh các kí tự số Do đó việc ứng dụng kiểu dữ liệu xâu vào xử lí dữliệu số là khá thuận lợi và cho phép xử lí các số lớn có số chữ số lên tới 255 số.Tuy nhiên, các phần tử của xâu là các kí tự nên không áp dụng các phép toán sốhọc lên nó được Việc này được giải quyết đơn giản bằng cách sử dụng các hàm,thủ tục xử lí xâu mà học sinh đã được trang bị sẵn
Việc sử dụng kiểu dữ liệu xâu để giải quyết bài toán có dữ liệu số lớn thườngđược thực hiện qua 3 bước cơ bản như sau:
B1 Đọc số dưới dạng xâu;
B2 Xử lí các phần tử của xâu tuỳ vào yêu cầu cụ thể của từng bài toán;
B3 Đưa ra kết quả của bài toán
2 Thực trạng và giải pháp: Sau đây là những bài toán đã sử dụng kiểu dữ liệu
xâu để giải quyết và có so sánh tính hiệu quả của phương pháp này với một sốphương pháp khác
Bài toán 1 SỐ ĐƠN ĐIỆU_Bài 3 Đề thi hsg tỉnh lớp 11 bảng A năm học
2013-2014
Số a1, a2, …, aN được gọi là số đơn điệu nếu ai < ai+1 > ai+2 hoặc ai > ai+1 < ai+2
(i:=1N-2) Số có một chữ số, số có hai chữ số khác nhau cũng được gọi là số đơnđiệu lần lượt có độ dài bằng 1, 2
Yêu cầu: Viết chương trình xác định số chữ số lớn nhất tạo thành số đơn điệu
trong biểu diễn của một số cho trước
Dữ liệu: Vào từ tệp văn bản bai1.inp là một số nguyên dương N (N có số
chữ số < 256 )
Dữ liệu ra: Ghi ra tệp văn bản bai1.out một số nguyên, là số chữ số lớn nhất
tạo thành đoạn số đơn điệu của số N Nếu không có đoạn số đơn điệu thì ghi số 0
VD:
- Xác định bài toán:
Input: Số nguyên dương N.
Output: Số chữ số lớn nhất tạo thành đoạn số đơn điệu của N.
- Ý tưởng thuật toán:
Đọc N dưới dạng xâu s
Ta xét tất cả các đoạn con trong biểu diễn của N có độ dài giảm dần từ số lượngchữ số của N về 1(Xét các đoạn con của xâu s có độ dài giảm từ độ dài của xâu s
Trang 3về 1) Mỗi đoạn con đã xét nếu thoả mãn điều kiện bài toán thì đưa ra độ dài củađoạn con đó rồi thoát.
Sử dụng hàm để kiểm tra một đoạn con có thoả mãn điều kiện bài toán haykhông
- Chương trình tham khảo
for l:=length(s) downto 3 do
begin for i:=1 to length(s)-l+1 do
if kt(i,l) then begin write(g,l+2);close(g);exit;end;
Trang 4Dữ liệu ra: Ghi ra tệp văn bản Bai2.OUT một số, là số ở vị trí thứ N trong số
Input: Số nguyên dương N
Output: Số ở vị trí thứ N trong số vô hạn
+ Ý tưởng thuật toán:
Sử dụng xâu s để biểu diễn số vô hạn Trong khi độ dài của xâu s còn bé thua N thì
ta lấy các số chính phương lần lượt từ bé đến lớn rồi đổi thành xâu và ghép vào xâus
Đưa ra phần tử thứ N của xâu s(S[N])
+ Chương trình tham khảo.
Trang 5Dữ liệu ra: Ghi lên tệp văn bản Bai3.out chỉ một số, là số chữ số của N
VD:
+/ Xác định bài toán:
- Input: Số nguyên dương N
- Output: Số chữ số có trong biểu diễn của N.
+/ Ý tưởng thuật toán:
Cách 1 Ta biết mỗi lần chia lấy phần nguyên của N cho 10 thì số chữ số mới của Ngiảm đi 1 số, ví dụ 123 div 10=12 Như vậy để đếm số lượng chữ số có trong N tachỉ cần đếm số lần gán N:=N div 10 cho đến khi N=0 Để thực hiện công việc này
ta dùng câu lệnh While_do
Cách 2 Đọc N thành xâu s Length(s) chính là số chữ số có trong biểu diễn của N
+/ Chương trình tham khảo
Trang 6Bài toán 4 Cho số nguyên dương N, đếm xem trong N có bao nhiêu chữ số 0 ?
Dữ liệu vào: Từ tệp văn bản Bai4.inp chỉ một số, là số nguyên N.
Dữ liệu ra: Ghi lên tệp văn bản Bai4.out chỉ một số, là số chữ số 0 của N
VD
- Xác định bài toán:
+ input: Số nguyên dương N;
+ Output: Số chữ số 0 trong biểu diễn của N
- Ý tưởng thuật toán
Cách 1 Ta tách các chữ số của N từ hàng đơn vị trở lên, mỗi lần ta tách 1 số Khitách được một số thì ta kiểm tra xem số đó có phải là số 0? Nếu đúng thì tăng demlên 1 đơn vị Việc tách số được thực hiện bằng 2 phép toán mod và div
Cách 2 Đọc N thành xâu s, sau đó đếm số lần xuất hiện của kí tự ‘0’ có trong xâus
- Chương trình tham khảo:
Trang 7For i:=1 to length(s) do if s[i]=’0’ then inc(dem);
Write(g,‘so chu so 0 co trong bieu dien cua N la:’, dem);
Còn cách 2, ta thấy độ phức tạp và trừu tường thấp vì chỉ cần kiểm tra từngphần tử của xâu xem nó có phải là kí tự ‘0’? Mặt khác sử dụng xâu để thay thế số
có thể xử lí được số rất lớn với số chữ số là 255 số
Trang 8Bài toán 5 Cho số nguyên dương N, hãy cho biết tần số xuất hiện của mỗi chữ
số có trong N
Dữ liệu vào: Từ tệp văn bản Bai5.inp chỉ một số, là số nguyên N.
Dữ liệu ra: Ghi lên tệp văn bản Bai5.out tần số xuất hiện của mỗi chữ số từ 0
đến 9 có trong N Tần số của mỗi số trên một dòng
VD
Tan so cua 1: 6Tan so cua 2: 6Tan so cua 3: 6Tan so cua 4: 1Tan so cua 5: 1Tan so cua 6: 1Tan so cua 7: 1Tan so cua 8: 1Tan so cua 9: 0
+ Xác định bài toán:
- Input: Số nguyên dương N;
- Output: Tần số xuất hiện của mỗi chữ số có trong biểu diễn của N
+ Ý tưởng thuật toán
- Sau khi việc tách số kết thúc thì ta đưa ra các phần tử của mảng dem
Cách 2 Với N lớn
- Sử dụng một mảng một chiều dem:array[‘0’ ’9’] of byte Dem[i] dùng để đếm
số lần xuất hiện của kí tự số ‘i’ có trong trong biểu diễn dạng xâu của N
- Đọc N thành xâu sau đó đếm số lần xuất hiện của mỗi kí tự có trong xâu
- Đưa ra các phần tử của mảng dem
+ Chương trình tham khảo:
Trang 9N:=N div 10;
End;
For i:=0 to 9 do writeln(g,‘tan so cua ‘,i, ‘ : ’,dem[i]);
For i:=’0’ to ’9’ do writeln(g,‘tan so cua ‘,i, ‘ : ’,dem[i]);
Close(g); close(f);
End;
Trang 10- Input: Số nguyên dương N và số nguyên dương k
- Output: Số lớn nhất tìm được sau khi đã xóa đi k chữ số.
+ Ý tưởng thuật toán
- Ta thấy với N là kiểu số thì việc xóa đi k chữ số trong N là khá phức tạp Việcnày sẽ đơn giản hơn nếu N được biểu diễn dưới dạng xâu Từ đó ta đọc N sang xâus
- Để có được số lớn nhất sau khi xóa k kí tự ta có thể thực hiện như sau:
+/ Tìm kí tự lớn nhất trong k+1 kí tự đầu của xâu s Chẳng hạn kí tự s[i]
+/ Ghép kí tự tìm thấy vào xâu kết quả P, p:=p+s[i];
+/ Xóa đi i kí tự đầu của xâu s;
+/ Gán lại giá trị mới cho k, k:=k-i+1;
- Quá trình trên lặp lại cho đến khi k=0, tức là đã xóa hết k kí tự
- Sau đó lấy xâu p ghép với phần còn lại của xâu s, p:=p+s; Đổi P thành số hoặcđưa ra xâu p
+ Chương trình tham khảo
Trang 11If s[i]>s[max] then max:=i;
Bài toán 7 Cho số nguyên dương N, hãy tìm biểu diễn nhị phân của N
Dữ liệu vào: Từ tệp văn bản Bai7.inp chỉ một số, là số nguyên N.
Dữ liệu ra: Ghi lên tệp văn bản Bai7.out biểu diễn nhị phâ của số N.
VD
- Xác định bài toán:
+ Input: Số nguyên dương N
+ Output: Biểu diễn nhị phân của số N
- Ý tưởng thuật toán:
Cách 1: Sử dụng mảng một chiều để lưu số dư của phép chia N cho 2, sau mỗi lầnchia thì N được gán lại với giá trị mới là N chia 2 Quá trình kết thúc khi N=0 Sau
đó đưa ra các phần tử của mảng theo thứ tự từ phần tử có chỉ số lớn đến phần tử cóchỉ số nhỏ nhất Để xác định chỉ số cuối của mảng một chiều ta dùng một biến kiểunguyên d Ban đầu d:=0, sau đó mỗi lần chia lấy dư N cho 2 ta tăng biến d lên mộtđơn vị
Cách 2 Sử dụng biến xâu s để lưu các số dư trong phép chia N cho 2 Ban đầus:=’’, sau đó mỗi lần lấy được một số dư trong phép chia N cho 2, ta chuyển nósang kiểu xâu và ghép vào biến xâu s Dạng nhị phân của N là xâu ngược của s
- Chương trình tham khảo
Theo cách 1
program bai7c1;
const maxd=100;
Trang 13Bài toán 8 Cho số nguyên dương N, hãy sắp xếp các số trong biểu diễn của N
để được số lớn nhất
Dữ liệu vào: Từ tệp văn bản Bai8.inp chỉ một số, là số nguyên N.
Dữ liệu ra: Ghi lên tệp văn bản Bai8.out chỉ một số, là số đã được sắp xếp VD
- Xác định bài toán:
+ Input: Số nguyên dương N
+ Output: Số lớn nhất sắp xếp được trong biểu diễn của số N
- Ý tưởng thuật toán:
Cách 1: Sử dụng mảng một chiều để lưu các số có trong biểu diễn của N(Với Nnhỏ) Để lấy được các số có trong biểu diễn của N, ta dùng phép chia N mod 10,sau mỗi lần chia thì N được gán lại với giá trị mới là N div 10 Quá trình kết thúckhi N=0 Sau đó đưa ra các phần tử của mảng theo thứ tự từ tăng dần Để xác địnhchỉ số cuối của mảng một chiều ta dùng một biến kiểu nguyên d Ban đầu d:=0, sau
đó mỗi lần chia lấy dư N cho 10, ta tăng biến d lên một đơn vị
Cách 2: Sử dụng biến xâu s để lưu N( đọc N dưới dạng xâu) Sau đó ta đưa ra cácphần tử của xâu s theo thứ tự giảm dần
- Chương trình tham khảo
Trang 14for i:= 1 to length(s) do
if s[i]=j then write(s[i]);
Trang 15Bài toán 9 Số đối xứng là số mà ta đọc các chữ số của nó từ trước ra sau hay
theo thứ tự ngược lại thì có kết quả như nhau Cho hai số nguyên dương M, N( M<N<=100000000), đếm xem trong đoạn [M, N] có bao nhiêu số đối xứng?
Dữ liệu vào: Từ tệp văn bản Bai9.inp gồm 2 số, là số nguyên M, N Hai số
+ Input: 2 số nguyên dương M, N (M<N<=10000)
+ Output: Số các số đối xứng có trong đoạn [M, N]
- Ý tưởng thuật toán
Cách 1: Với mỗi số trong đoạn [M, N] ta sẽ chuyển nó thành mảng một chiều rồikiểm tra tính đối xứng của các phần tử mảng một chiều đó Việc kiểm tra đối xứngđược thực hiện bằng một chương trình con
Cách 2: Với mỗi số trong đoạn [M, N] ta sẽ chuyển nó thành xâu rồi kiểm tra tínhđối xứng của xâu đó Việc kiểm tra đối xứng cũng được thực hiện bằng mộtchương trình con
- Chương trình tham khảo
while (i<=t div 2) and (c[i]=c[t-i+1]) do inc(i);
if (t>1) and (i>t div 2 ) then dx:=true else dx:=false;
end;
begin
Trang 16begin i:=1; t:=length(c);
while (i<=t div 2) and (c[i]=c[t-i+1]) do inc(i);
if (t>1) and (i>t div 2 ) then dx:=true else dx:=false;
Trang 17Còn theo cách 2, ta thấy thuật toán rõ ràng và đơn giản hơn, chương trình cũngngắn hơn.
Bài toán 10 Nhập từ bàn phím số M gồm các chữ số 0 và 1 M là biểu diễn nhị
phân của số nguyên dương N Tìm và đưa ra màn hình số nguyên dương N
Dữ liệu vào: Từ tệp văn bản Bai10.inp chỉ số, là số nguyên M Hai số cách
+ Output: Số nguyên dương N mà biểu diễn nhị phân của nó là M
- Ý tưởng thuật toán:
Cách 1 Tách lần lượt các số trong M, mỗi lần tách được một số ta cộng tích của số
đó với lũy thừa của 2 với số mũ tương ứng vào tổng, đồng thời gán lại giá trị mớicho M M=M div 10 Quá trình trên kết thúc khi M=0 Giá trị của tổng chính là N
Để tính lũy thừa cơ số 2, ta sử dụng chương trình con luythua
Cách 2 Dùng biến xâu s, sau đó chuyển M sang xâu s Gán L:=length(s); Choi:=L-1 tới 0, thực hiện tính tổng:=tổng +j*lũy thừa của 2 với số mũ i Với j là kếtquả của thủ tục val(s[i],j,code) Để tính lũy thừa cơ số 2, ta sử dụng chương trìnhcon luythua
- Chương trình tham khảo
Trang 18Nhận xét: Theo cách 1, ta thấy vì chưa xác định được số lượng chữ số có trong
M nên khi lập trình ta phải sử dụng câu lệnh while-do
Trang 19Còn theo cách 2, sau khi đã đổi M sang kiểu dữ liệu xâu thì thông qua hàmlength(s) ta đã xác định được số chữ số trong M nên khi lập trình ta có thể dùngcâu lệnh For-do Tuy nhiên theo cách 2 ta phải dùng thêm thủ tục Val(s[i],j,code).Đây cũng là thủ tục khá hay gặp trong việc giải một số bài toán Với các học sinhtrong đội tuyển HSG thì GV phải trang bị cho HS cả 2 thủ tục này.
Bài toán 11 Cho số nguyên dương N(N<100000000), hãy tìm đoạn đối xứng
liên tiếp dài nhất trong biểu diễn của N
Dữ liệu vào: Từ tệp văn bản Bai11.inp chỉ một số, là số nguyên N.
Dữ liệu ra: Ghi lên tệp văn bản Bai11.out gồm 2 số, là số đối xứng dài nhất
trong biểu diễn của N và độ dài của số đó Hai số cách nhau một dấu cách
- Xác định bài toán:
+ Input: Số nguyên dương N
+ Output: Số đối xứng dài nhất trong biểu diễn của N và độ dài của nó
- Ý tưởng thuật toán:
+ Tạo 1 hàm kiểm tra tính đối xứng của một xâu;
+ Đọc số N dưới dạng xâu s;
+ Duyệt các xâu con của xâu s có độ dài giảm dần từ length(s)1 Nếu xâu conđầu tiên tìm thấy thoả mãn điều kiện thì đưa ra xâu đó và độ dài của nó rồi kết thúcchương trình
- Chương trình tham khảo:
while p<=length(y) div 2 do
if y[p]=y[length(y)-p+1] then inc(p);
if p>=length(y) div 2 then kt:=true else kt:=false;
Trang 20III Kết quả thực hiện: Sáng kiến kinh nghiệm là kết quả đúc kết của nhiều năm
nghiên cứu và giảng dạy, bồi dưỡng học sinh giỏi Trên thực tế đã đạt được một sốkết quả trong bồi dưỡng học sinh giỏi cấp tỉnh như sau:
IV KẾT LUẬN VÀ KIẾN NGHỊ
Ta thấy việc giải các bài toán trên có thể xử lý bằng các phép toán số họcthường gặp như phép toán chia lấy phần dư ( mod ) và phép chia lấy phần nguyên (div ) Tuy nhiên khi xử lý bằng các phép toán này thì độ trừu tượng tương đối cao
và yêu cầu học sinh phải hiểu bản chất nên chỉ phù hợp với một bộ phận nhỏ họcsinh có năng lực khá trở lên Mặt khác, với N khá lớn thì độ phức tạp càng cao hơn.Nhận thấy các bài toán trên có những điểm chung là:
- Phải xử lý đến từng chữ số ( Kí tự số ) trong N, việc này giống như tham chiếuđến từng phần tử của kiểu dữ liệu xâu- một việc làm rất đơn giản và dễ hiểu đốivới nhiều học sinh;
- Phải xác định N là số có bao nhiêu chữ số, việc này giống như xác định sốlượng ký tự của xâu- một việc làm đơn giản trong kiểu dữ liệu xâu mà học sinh
có thể nhận ra, đó là sử dụng hàm length
Từ những nhận xét trên ta thấy nếu chuyển được N sang dạng xâu thì sẽ rấtthuận tiện khi giải các bài toán trên và việc N là khá lớn cũng không thành vấn đề
Thủ tục để chuyển số N thành xâu s là str(N,s) Thủ tục này làm ẩn đi bản chất của
nó nên làm giảm đi độ phức tạp đối với học sinh mà ý nghĩa và cấu trúc của nó lạirất dễ nhớ, dễ áp dụng