Lý do chọn đề tài Trong quá trình đào tạo bồi dưỡng học sinh giỏi ở trường phổ thông, chúng tôinhận thấy khả năng số học đối với số nguyên lớn của ngôn ngữ lập trình Free Pascalkhi biểu
Trang 1PHẦN I ĐẶT VẤN ĐỀ
1 Lý do chọn đề tài
Trong quá trình đào tạo bồi dưỡng học sinh giỏi ở trường phổ thông, chúng tôinhận thấy khả năng số học đối với số nguyên lớn của ngôn ngữ lập trình Free Pascalkhi biểu diễn các số nguyên có hàng chục nghìn chữ số đến hàng trăm nghìn chữ sốgây rất nhiều khó khăn cho học sinh Nhằm khắc phục nhược điểm trên và để đáp ứngđược yêu cầu của công tác bồi dưỡng học sinh giỏi của thầy và trò trường trung họcphổ thông Vinh Xuân, chúng tôi mạnh dạn nghiên cứu đề tài “KĨ THUẬT LẬPTRÌNH ĐỐI VỚI SỐ NGUYÊN LỚN TRONG CÔNG TÁC BỒI DƯỠNG HỌCSINH GIỎI TẠI TRƯỜNG TRUNG HỌC PHỔ THÔNG VINH XUÂN” nhằm phục
vụ công tác bồi dưỡng học sinh giỏi có hiệu quả hơn
2 Mục đích nghiên cứu
- Hệ thống hóa cơ sở lý thuyết về lập trình đối với số nguyên lớn
- Xây dựng bộ công cụ lập trình đối với số nguyên lớn nhằm hỗ trợ cho hoạt độngdạy và học của thầy và trò của nhà trường trong quá trình tiếp xúc và làm việc với sốnguyên lớn
- Trên cơ sở nghiên cứu, từ đó đưa ra nhận xét, đánh giá và đề xuất giải pháp gópphần hoàn thiện công tác dạy và học lập trình đối với các bài toán mà dữ liệu vào haykết quả ra có giá trị nguyên dương rất lớn
3 Đối tượng nghiên cứu
Kỹ thuật lập trình đối với số nguyên lớn trong công tác dạy và học bồi dưỡng củathầy và trò tại trường THPT Vinh Xuân
4 Phạm vi nghiên cứu
Tập hợp số nguyên dương lớn và hệ thống bài tập có dữ liệu vào hay kết quả ra làkiểu số nguyên dương có giá trị rất lớn
5 Phương pháp nghiên cứu
- Phương pháp nghiên cứu cơ sở lý luận
- Phương pháp thu thập tài liệu
- Phương pháp xử lý số liệu và lập trình
Trang 2- Phương pháp thực nghiệm
- Phương pháp điều tra phát vấn
6 Kết cấu đề tài
Phần I Đặt vấn đề
Phần II Nội dung và kết quả nghiên cứu
Chương 1 Cơ sở lý luận về lập trình số nguyên lớn
Chương 2 Giải pháp cho một số bài toán số nguyên lớn
Phần III Kết luận
Trang 3PHẦN II NỘI DUNG VÀ KẾT QUẢ NGHIÊN CỨU
Chương 1 CƠ SỞ LÝ LUẬN VỀ LẬP TRÌNH VỀ SỐ NGUYÊN LỚN 1.1 TỔ CHỨC KIỂU DỮ LIỆU MẢNG MỘT CHIỀU BIỂU DIỄN SỐ NGUYÊN LỚN.
Trong thực tế có những bài toán số học có giá trị lớn từ hàng chục nghìn đến hàngtrăm nghìn chữ số, nhưng khả năng biểu diễn và lưu trữ của các kiểu dữ liệu chuẩntrong Free Pascal là hữu hạn nên không thể đáp ứng những bài toán số học có giá trịlớn Từ đó chúng tôi xây dựng mảng một chiều có 1.000.001 phần tử, mỗi phần tử biểudiễn một hay một số chữ số của số nguyên lớn
Trong phần lý thuyết, chúng tôi khai báo mảng một chiều mỗi phần tử biểu diễnmột chữ số của số nguyên lớn, được khai báo như sau:
Với số nguyên lớn A = 876328 được biểu diễn trên các phần tử mảng và quản
lý giá trị A[0] như sau:
0 1 2 3 …… 999.995 999.996 999.997 999.998 999.999 1.000.000
Với A := 0 được viết:
Fillchar(A, Sizeof(A),0); A[0]:= Hang_So;
Với A := 1 được viết:
Fillchar(A,Sizeof(A),0);
A[0]:= Hang_So; A[Hang_so]:=1;
Trên cơ sở biểu diễn số nguyên lớn như vậy, chúng tôi xây dựng các phép toán trên
tập hợp số nguyên dương lớn như phép toán quan hệ, phép toán cộng, phép trừ, phép
Trang 4nhân, phép chia lấy phần nguyên (Div), phép chia lấy phần dư (Mod) và ứng dụng cácphép toán đó để giải quyết một số bài toán trong công tác bồi dưỡng học sinh giỏi.
1.2 PHÉP TOÁN QUAN HỆ
1.2.1 Phép toán so sánh bằng
* Thuật toán: Hai số nguyên dương lớn A B kiểu Big_number được biểu diễn
bằng mảng một chiều, mỗi phần tử mảng biểu diễn một chữ số của số nguyên lớn Tadựa vào các giá trị A0, B0, Ai với A0 ≤ i ≤ Hang_so và Bk với B0 ≤ k ≤ Hang_so để thựchiện so sánh và trả về giá trị True hoặc False
Function Bằng(A,B:Big_number): Boolean
Begin
Nếu A0 <> B0 thì Bằng:=falseNgược lại
{ + i:= A0; Trong khi (i ≤ Hang_so) và (Ai = Bi) làm i:= i+1; + Bằng:= i > Hang_so;}
* Thuật toán: So sánh hai số A, B: Big_Number; nếu A lớn hơn B thì trả về kết
quả True nếu không thì trả về giá trị False
Trang 5If A[0]<B[0] Then Lon_Hon:=True
Else If A[0]>B[0] Then Lon_Hon:=False
Else Begin
I:=A[0]; While (I<=Hang_So) And (A[I]=B[I]) Do I:=I+1;
If I>Hang_So Then Lon_Hon:=False
Else Lon_Hon:=A[I]>B[I];
End;
End;
1.2.3 Phép toán so sánh nhỏ hơn
* Thuật toán: So sánh hai số A, B: Big_Number; nếu A nhỏ hơn B thì trả về kết
quả True nếu không thì trả về giá trị False
Trang 6If A[0]<B[0] Then Nho_Hon:=False
Else If A[0]>B[0] Then Nho_Hon:=True
Else Begin
I:=A[0]; While (I<=Hang_So) And (A[I]=B[I]) Do I:=I+1;
If I>Hang_So Then Nho_Hon:=False
1.3.1.1 Cộng số nguyên lớn với số nguyên nhỏ
* Thuật toán: Thực hiện cộng một số nguyên lớn A kiểu Big_number với một số
nguyên nhỏ k kiểu dữ liệu chuẩn như Integer, Word hay Longint như sau:
Procedure Cong_min(Var C:Big_number; A:Big_number; k:Longint);
Begin
- Fillchar(C,sizeof(C),0);
- Cho i:= Hang_so về A0 làm {k:= k+Ai; Ci:= k Mod 10; k:= k Div 10;}
- Trong khi (k >0) làm {i:= i-1; Ci:= k Mod 10; k:= k Div 10}
- C0:= iEnd;
Trang 7For I:=Hang_So Downto A[0] Do
Begin K:=K+A[I]; C[I]:=K Mod 10; K:= K Div 10; End;
While K>0 Do Begin I:=I-1;C[I]:=K Mod 10;K:=K Div 10; End;
C[0]:=I;
End;
1.3.1.2 Cộng hai số nguyên lớn
* Thuật toán: Phép cộng hai số nguyên dương lớn được thực hiện từ phải sang trái
và phần nhớ được mang sang trái một chữ số
Procedure Cong_Max(Var C:Big_Number;A,B:Big_Number);
Begin
Fillchar(C,sizeof(C),0); Tg:= 0;
Cho i:= Hang_so về Min(A0, B0) làm
{Tg:= Tg + Ai + Bi; Ci:= Tg Mod 10; Tg:= Tg Div 10}
Nếu Tg = 0 thì C0:= i Ngược lại {C0:= i -1; Ci-1:= Tg}
If A[0]>=B[0] Then Min:=B[0] Else Min:=A[0];
For I:=Hang_So Downto Min Do
Begin Tg:=Tg+A[I]+B[I]; C[I]:=Tg Mod 10; Tg:=Tg Div 10; End;
If Tg=0 Then C[0]:= i Else Begin C[0]:= i - 1; C[i -1]:=Tg; End;
End;
Trang 81.3.2 Phép trừ
1.3.2.1 Trừ số nguyên lớn với số nguyên nhỏ
* Thuật toán: Nếu A ≥ k thì C:= A – k nếu không thì C:= – (k – A).
Procedure Tru_Min(Var C:Big_Number; A:Big_Number; K:Longint);
Begin
Trường hợp A≥ k: C:= A – k
Fillchar(c,sizeof(C),0); Tg:= 0Trong khi (k>0) và (i≥ A0) làm
{+ Tg:= k Mod 10; k := k Div 10;
+ Nếu Ai ≥ Tg thì Ci:= Ai – Tg Ngược lại {Ci:= Ai +10 – Tg; k:= k+1;}
+ I:= i + 1}
Trong khi I ≥ A0 làm {Ci:=Ai; i:= i – 1}
i:=A0; Trong khi (i<Hang_So) và (Ci=0) làm i:=i+1;
Procedure Tru_Min(Var C:Big_Number;A:Big_Number;K:Longint);
Var I,M,Tg,Lt: Longint;
If A[I]>=Tg Then C[I]:=A[I]-Tg
Else Begin C[I]:=A[I]+10-Tg; K:=K+1; End;
I:=I-1;
End;
Trang 9If K=0 Then
Begin
While I>=A[0] Do Begin C[I]:=A[I]; I:=I-1; End;
I:=A[0]; While (I<Hang_So)And(C[I]=0)Do I:=I+1;
* Thuật toán: Thực hiện trừ từng chữ số từ trái sang phải và giá trị vay mượn 1 ở
hàng cao hơn được nhớ bằng biến Tg làm cơ sở cho phép trừ kế tiếp ở hàng cao hơn;
xét cả hai trường hợp A ≥ B và A < B:
Nếu A ≥ B thì C:= A – B nếu không thì C:= – (B – A).
Procedure Tru_Min(Var C: Big_Number; A,B: Big_Number);
Trong khi (i< Hang_so) và (Ci = 0) làm i:= i+1;
C0:= i;}
Trang 10Ngược lại
Cho i:= Hang_so về B0 làm
Nếu Bi ≥ Tg + Ai thì {Ci:= Bi – Ai – Tg; Tg:= 0;}Ngược lại {Ci:= Bi + 10 – Ci – Tg; Tg:= 1;}
Trong khi (i< Hang_so) và (Ci = 0) làm i:= i+1;
C0:= i; Ci:= 1 – Ci}
End;
* Chương trình
Procedure Tru_Max(Var C:Big_Number;A,B:Big_Number);
Var I,Tg:Longint; Ok:Boolean;
Begin
Fillchar(C,Sizeof(C),0);
If A[0]<B[0] Then Ok:=True
Else If A[0]>B[0] Then Ok:=False
Else Begin
I:=A[0];While (I<=Hang_So) And (A[I]=B[I]) Do I:=I+1;
If I>Hang_So Then Ok:=False Else Ok:=A[I]>B[I];
Trang 111.3.3.1 Nhân số nguyên lớn với số nguyên nhỏ
* Thuật toán: thực hiện nhân từ phải sang trái từng chữ số Ai với số k sau đó lấy
hàng đơn vị của tích trả về cho Ci còn phần bội số của 10 (sau khi bỏ đi chữ số hàngđơn vị của tích số) được cộng vào tích của phép nhân sau
Procedure Nhan_Min(Var C:Big_Number;A:Big_Number;K:Longint);
Begin
Fillchar(C,Sizeof(C),0); Tg:=0;
Cho i:=Hang_So về A0 làm
{Tg:= Tg+Ai * k; Ci:= Tg Mod 10; Tg:= Tg Div 10;}
Trong khi Tg>0 làm {i:= i – 1; Ci:= Tg Mod 10; Tg:= Tg Div 10;}
For I:=Hang_So Downto A[0] Do
Begin Tg:=Tg+A[I]*K; C[I]:=Tg Mod 10; Tg:=Tg Div 10; End; While Tg>0 Do Begin I:=I-1; C[I]:=Tg Mod 10; Tg:=Tg Div 10; End; C[0]:=I;
End;
Trang 121.3.3.2 Nhân hai số nguyên lớn
* Thuật toán: Thực hiện nhân từ phải sang trái các chữ số Ai với số nguyên lớn B
được tích bằng D; ứng với mỗi giá trị D được cộng dồn vào cho C và kết thúc khi i =A0; suy ra được kết quả bằng số nguyên lớn C
Procedure Nhan_Max(Var C:Big_Number;A,B:Big_Number);
Procedure Nhan_Max(Var C:Big_Number;A,B:Big_Number);
Var I,J,U,Tg: Longint; D: Big_Number;
Trang 131.3.4 Phép chia
1.3.4.1 Chia lấy phần nguyên
1.3.4.1.1 Số nguyên lớn chia số nguyên nhỏ
* Thuật toán: Thực hiện từ trái sang phải, lấy các giá trị Ai chia k lấy phần nguyên
trả về cho Ci và phần dư nhân 10 cộng với Ai+1 để xác định giá trị Ci+1; sau đó xác địnhgiá trị Ci khác không đầu tiên tính từ trái sang và cho C0 bằng i
Procedure Div_Min(Var C:Big_Number;A:Big_Number;K:Longint);
Begin
C:= 0; Tg:= 0;
Cho A0 đến Hang_so làm
{Tg:=10*Tg+Ai; Ci:= Tg Div 10; Tg:= Tg Mod 10}
i:=A0; Trong khi (i<Hang_so) và (Ci=0) làm i:=i+1;
For I:=A[0] To Hang_So Do
Begin Tg:=10*Tg+A[I]; C[I]:=Tg Div K; Tg:=Tg Mod K; End;
I:=A[0]; While (I<Hang_So)And(C[I]=0)Do I:=I+1;
Trang 14Schia:=BB0 BB0+1 … BB0+i lấy tối đa 7 chữ số đầu của số nguyên lớn B
làm số chia thay cho B; với mọi 0 ≤ i ≤ 6 và không nhất thiết
bằng 6 mà phụ thuộc vào số chữ số của B.
U:=Hang_So–BB0 + i {là số chữ số còn lại của B sau khi tách lấy Schia}Nếu U>0 thì Schia:= Schia + 1;
Repeat
Ok:= A bỏ đi U chữ số cuối cùng < Schia;
Nếu Ok = False thì{ D:= A Div Schia;
C:= C + D;
E:= B * D;
A:= A – E; }Until Ok;
Nếu A ≥ B thì C:= C + 1;
End;
* Chương trình
Procedure Div_Max(Var C:Big_Number;A,B:Big_Number);
Var I,J,Min,U,V,Schia,Tg:Longint; Ok:Boolean;
If A[0]<B[0] Then Ok:=False
Else If A[0]>B[0] Then Ok:=True
Trang 15Ok:=(Tg<Schia)
End;
If Ok=False Then
Begin
{D:=A Div Schia} Fillchar(D,Sizeof(D),0);D[0]:=Hang_So; Tg:=0;
For I:=A[0] To Hang_So-U Do
If C[0]<D[0] Then Min:=C[0] Else Min:=D[0];
For I:=Hang_So Downto Min Do
If Tg=0 Then K[0]:=J-V
Else Begin K[0]:=J-V-1; K[K[0]]:=Tg; End;
Trang 16V:=V+1;Tg:=0;
If E[0]<K[0] Then Min:=E[0] Else Min:=K[0];
For J:=Hang_So Downto Min Do
For I:=Hang_So Downto A[0] Do
If A[I]>=E[I]+Tg Then Begin A[I]:=A[I]-E[I]-Tg; Tg:=0; End Else Begin A[I]:=A[I]+10-E[I]-Tg; Tg:=1; End;
While (I<Hang_So)And(A[I]=0) Do I:=I+1; A[0]:=I;
End;
Until Ok;
If A[0]<B[0] Then Ok:=True
Else If A[0]>B[0] Then Ok:=False
Else Begin
I:=A[0]; While (I<=Hang_So) And (A[I]=B[I]) Do I:=I+1;
If I>Hang_So Then Ok:=True Else Ok:=A[I]>=B[I];
End;
If Ok Then
Begin
Tg:=1;
For I:= Hang_So Downto C[0] Do
Begin Tg:=Tg+C[I]; C[I]:=Tg Mod 10; Tg:=Tg Div 10; End;
If Tg >0 Then Begin C[0]:= C[0]-1; C[C[0]]:=Tg; End;
End;
End;
Trang 171.3.4.2 Chia lấy phần dư (Mod)
1.3.4.2.1 Số nguyên lớn chia số nguyên nhỏ
* Thuật toán: Thực hiện trừ trái sang phải, lấy các giá trị Ai chia cho giá trị k phần
dư nhân 10 cộng với Ai+1 rồi chia k cho đến khi kết thúc ta thu được phần dư cần tìm
Procedure Mod_Min(Var C:longint;A:Big_Number;K:Longint);
1.3.4.2.2 Số nguyên lớn chia số nguyên lớn
* Thuật toán: Tương tự phép chia lấy phần nguyên giữa hai số nguyên lớn nhưng
lấy giá trả về là số dư của phép chia A Mod B
* Chương trình
Procedure Mod_Max(Var C:Big_Number;A,B:Big_Number);
Var I,J,Min,U,V,Schia,Tg:Longint; Ok:Boolean;
If A[0]<B[0] Then Ok:=False
Else If A[0]>B[0] Then Ok:=True
Else Begin
I:=A[0];Tg:=A[I];
Trang 18{D:=A Div B} Fillchar(D,Sizeof(D),0);D[0]:=Hang_So; Tg:=0;
For I:=A[0] To Hang_So-U Do
Trang 19If A[0]<B[0] Then Ok:=True
Else If A[0]>B[0] Then Ok:=False
Else Begin
I:=A[0]; While (I<=Hang_So) And (A[I]=B[I]) Do I:=I+1;
If I>Hang_So Then Ok:=True Else Ok:=A[I]>=B[I];
End;
If Ok Then
Begin
Tg:=0;
For I:=Hang_So Downto A[0] Do
If A[I]>=B[I]+Tg Then Begin A[I]:=A[I]-B[I]-Tg; Tg:=0; End Else Begin A[I]:=A[I]+10-B[I]-Tg; Tg:=1; End;
While (I<Hang_So)And(A[I]=0) Do I:=I+1;
A[0]:=I;
End;
For I:=A[0] To Hang_So Do C[I]:=A[I]; C[0]:=A[0];
End;
Lưu ý: Tùy thuộc vào giá trị của dữ liệu vào và kết quả ra của bài toán để chúng
ta khai báo giá trị Hang_so phù hợp sẽ giảm thiểu bộ nhớ lưu trữ cho các biến không cần thiết giúp cho chương trình thực hiện nhanh hơn.
Trang 20Chương II GIẢI PHÁP CHO MỘT SỐ BÀI TOÁN SỐ NGUYÊN
LỚN 2.1 TÍNH GIÁ TRỊ GIAI THỪA
* Kết quả ra: ghi ra file văn bản Ketqua.OUT có nhiều dòng; dòng thứ i lưu các giá
trị giai thừa tương ứng của Ai
Ví dụ:
28102030
24032036288002432902008176640000265252859812191058636308480000000
2 Cấu trúc dữ liệu và giải thuật
* Cấu trúc dữ liêu:
- Xây dựng mảng A: để lưu giá trị số nguyên lớn X!
- Sử dụng thủ tục nhân một số nguyên lớn A với một số nguyên nhỏ k
Nhan_Min(C,A,k) để tính giá trị của X!.
Trang 21Var A: Big_Number; I, M: Longint; F1, F2: Text;
For I:= Hang_So Downto A[0] Do
Begin Tg:= Tg + A[I] * K; C[I]:= Tg Mod 10000; Tg:= Tg Div 10000; End;
While Tg>0 Do Begin I:=I-1; C[I]:= Tg Mod 10000; Tg:= Tg Div 10000; End;
C[0]:= I;
End;
{========================}
Begin
Assign(F1, 'E:\Chuyen De\Tinh Giai Thua\Dulieu.Inp'); Reset(F1);
Assign(F2, 'E:\Chuyen De\Tinh Giai Thua\Ketqua.Out'); Rewrite(F2);
While Not Eof(F1) Do
Begin
Readln(F1,M); A[0]:=1000000; A[1000000]:=1;
For I:=1 To M Do Nhan_Min(A,A,I);
Write(F2, A[A[0]]);
For I:=A[0]+1 To Hang_So Do
If A[I]>=1000 Then Write(F2, A[I])
Else If A[I]>=100 Then Write(F2, '0', A[I])
Else If A[I]>=10 Then Write(F2, '00', A[I])
Else Write(F2, '000', A[I]);
Writeln(F2);
End;
Close(F1); Close(F2);
End
Trang 222.2 BÀI TOÁN BIÊN DỊCH
1 Đề bài: (đề thi học sinh giỏi tỉnh Thừa Thiên Huế 2013)
Một nhà lập trình soạn một trình biên dịch quyết định phát triển một ánh xạ giữamỗi từ có 20 kí tự với một số nguyên duy nhất Việc lập ra bản đồ để biên dịch rất đơngiản và được đánh dấu bởi từ abc… và sau đó theo thứ tự của từ đó Một phần củadanh sách được hiển thị dưới đây:
Yêu cầu: Viết một chương trình có thể dịch (hai chiều) giữa các số, từ duy nhất
dựa theo bản đồ trên
* Dữ liệu vào: cho trong file văn bản với tên là BIENDICH.INP trong đó có nhiều
dòng Đầu vào cho chương trình là các từ và các con số Một số sẽ chỉ có các chữ sốthập phân (0 đến 9, không có dấu chấm ở các số) Một từ sẽ bao gồm một đến haimươi kí tự viết thường (a đến z)
* Dữ liệu ra: file văn bản với tên là BIENDICH.OUT có nhiều dòng.
Đầu ra có một dòng duy nhất cho mỗi từ hoặc số tương ứng với dữ liệu đầu vào.Trên mỗi dòng sẽ có hai cột , cột đầu tiên là các từ, cột thứ 2 là các số giữa hai cộtcách nhau ít nhất một dấu cách Các số có nhiều hơn ba chữ số phải được phân cáchbằng dấu chấm tại hàng nghìn, hàng triệu, …
Ví dụ:
BIENDICH.INP
29697684282993transcendental28011622636823854456520computationally