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

Kỹ thuật lập trình xử lý số nguyên lớn trong công tác bồi dưỡng học sinh giỏi tại trường THPT vinh xuâ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 (249.88 KB, 45 trang )

PHẦ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ôi
nhậ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 Pascal
khi 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ọc
phổ thông Vinh Xuân, chúng tôi mạnh dạn nghiên cứu đề tài “KĨ THUẬT LẬP
TRÌNH ĐỐI VỚI SỐ NGUYÊN LỚN TRONG CÔNG TÁC BỒI DƯỠNG HỌC
SINH 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 động
dạ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óp
phầ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 hay
kế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ủa
thầ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
1


- 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

2


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 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àng
tră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ẩn
trong 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ểu diễ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ễn
một chữ số của số nguyên lớn, được khai báo như sau:
Const Hang_so = 1000000;

Type Big_Number = Array[0..Hang_so] of Longint;
Var

A: Big_Number;

trong đó:
+ Chữ số hàng đơn vị của số nguyên lớn được biểu diễn bởi phần tử
A[1.000.000]
+ Chữ số hàng chục của số nguyên lớn được biểu diễn bởi phần tử A[999.999]
+ ………..
+ Chữ số hàng cao nhất của số nguyên lớn được biểu diễn bởi phần tử A[i] và
giá trị i được quản lý bởi giá trị của phần tử A[0].
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:

A

999.995

0

0

0

0

8

7


6

3

2

8

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;

3


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
nhâ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ác
phé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. Ta
dự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ực
hiệ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:=false
Ngược lại
{ + i:= A0; Trong khi (i ≤ Hang_so) và (Ai = Bi) làm i:= i+1;
+ Bằng:= i > Hang_so;}
End;
* Chương trình
Function Bang(A,B:Big_Number):Boolean;

Var

I:Longint;

Begin
If A[0]<>B[0] Then Bang:=False
Else Begin
I:=A[0]; While (I<=Hang_So) And (A[I]=B[I]) Do I:=I+1;
Bang:=I>Hang_So;
End;
End;
1.2.2. Phép toán so sánh lớn hơn
* 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.

4


Function Lớn_hơn(A,B:Big_number):Boolean
Begin
Nếu A0 < B0 thì Lớn_hơn:= true
Ngược lại
Nếu A0 > B0 thì Lớn_hơn:= false
Ngược lại
{ + i:= A0;
+ Trong khi (i<=Hang_so) và (Ai = Bi) làm i:= i+1;
+ Nếu i > Hang_so thì Lớn_hơn:= False
ngược lại i:= Ai > Bi}
End;
* Chương trình

Function Lon_Hon(A,B:Big_Number):Boolean;
Var

I:Longint;

Begin
If A[0]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.
Function Nhỏ_hơn(A,B:Big_number):Boolean
Begin
* Nếu A0 < B0 thì Lớn_hơn:= False
Ngược lại
- Nếu A0 > B0 thì Lớn_hơn:= True
Ngược lại
5


{ + i:= A0;
+ Trong khi (i<=Hang_so) và (Ai = Bi) làm i:= i+1;
+ Nếu i > Hang_so thì Lớn_hơn:= False
ngược lại i:= Ai

End;
* Chương trình
Function Nho_Hon(A,B:Big_Number):Boolean;
Var

I: Longint;

Begin
If A[0]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
Else Nho_Hon:=A[I]End;
End;

1.3. PHÉP TOÁN SỐ HỌC
1.3.1. Phép cộng
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:= i
End;


6


* Chương trình
Procedure Cong_Min(Var C:Big_Number; A:Big_Number; k:Longint);
Var

I: Longint;

Begin
Fillchar(C,Sizeof(C),0);
For 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}
End;
* Chương trình
Procedure Cong_Max(Var C:Big_Number;A,B:Big_Number);
Var


I,Min,Tg:Longint;

Begin
Fillchar(C,Sizeof(C),0); Tg:=0;
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;

7


1.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:= 0
Trong 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 (iC0:=i;
Trường hợp A < k: C:= -(k – A)
k:= k – Ai * 10Hang_so - i với A0 ≤ i ≤ Hang_so

Biểu diễn giá trị k vào mảng C kiểu Big_number;
C[c0]:= - C[c0]
End;
* Chương trình
Procedure Tru_Min(Var C:Big_Number;A:Big_Number;K:Longint);
Var

I,M,Tg,Lt: Longint;

Begin
Fillchar(C,Sizeof(C),0); C[0]:=Hang_So; Tg:=0; I:=Hang_So; M:=K;
While (K>0) And (I>=A[0]) Do
Begin
Tg:=K Mod 10; K:=K Div 10;
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;
8


If K=0 Then
Begin
While I>=A[0] Do Begin C[I]:=A[I]; I:=I-1; End;
I:=A[0]; While (IC[0]:=I;
End
Else
Begin
I:=Hang_So; Lt:= 1;

While I>=A[0] Do
Begin M:=M-A[I]*Lt; Lt:=Lt*10; I:=I-1; End;
Fillchar(C,Sizeof(C),0); I:=Hang_So;
While M>0 Do
Begin C[I]:=M Mod 10; M:=M Div 10; I:=I-1; End;
C[0]:=I+1; C[C[0]]:=-C[C[0]];
End;
End;
1.3.2.2. Trừ hai số nguyên lớn
* 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);
Begin
Fillchar(C,sizeof(C),0); Ok:= A ≥ B;
Nếu Ok thì
{

Tg:= 0;
Cho i:= Hang_so về A0 làm
Nếu Ai ≥ Tg + Bi thì {Ci:= Ai – Bi – Tg; Tg:= 0;}
Ngược lại {Ci:= Ai + 10 – Bi – Tg; Tg:= 1;}
Trong khi (i< Hang_so) và (Ci = 0) làm i:= i+1;
C0:= i;}
9


Ngược lại
{


Tg:= 0;
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]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];
End;
If Ok Then
Begin
Tg:=0;
For I:=Hang_So Downto A[0] Do
If A[I]>=B[I]+Tg Then
Begin C[I]:=A[I]-B[I]-Tg; Tg:=0 End
Else Begin C[I]:=A[I]+10 - B[I] - Tg; Tg:=1; End;

While (IC[0]:=I;
End
Else Begin
Tg:=0;
For I:=Hang_So Downto B[0] Do
10


If B[I]>=A[I]+Tg Then
Begin C[I]:=B[I]-A[I]-Tg; Tg:=0; End
Else Begin C[I]:=B[I]+10-A[I]-Tg; Tg:=1 End;
While (IC[I]:=-C[I];C[0]:=I;
End;
End;
1.3.3. Phép nhân
1.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ố A i 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;}
C0:=i;
End;
* Chương trình

Procedure Nhan_Min(Var C:Big_Number;A:Big_Number;K:Longint);
Var

I, Tg,Schia,Min:Longint;

Begin
Fillchar(C,Sizeof(C),0);Tg:=0;
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;

11


1.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ố A i 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);
Begin
C:=0;
Cho i:= Hang_so về A0 làm {D:= Ai * B* 10Hang_so – i; C:= C + D;}
End;
* Chương trình
Procedure Nhan_Max(Var C:Big_Number;A,B:Big_Number);
Var

I,J,U,Tg: Longint; D: Big_Number;


Begin
Fillchar(C,Sizeof(C),0);C[0]:=Hang_So;U:=0;
For I:=Hang_So Downto A[0] Do
Begin
Fillchar(D,Sizeof(D),0); Tg:=0;
For J:=Hang_So Downto B[0] Do
Begin Tg:=Tg+A[I]*B[J];
D[J-U]:=Tg Mod 10; Tg:=Tg Div 10;
End;
If Tg=0 Then D[0]:=J-U
Else Begin D[J-U-1]:=Tg; D[0]:=J-U-1; End;
U:=U+1; Tg:=0;
For J:=Hang_So Downto D[0] Do
Begin Tg:=Tg+C[J]+D[J];
C[J]:=Tg Mod 10; Tg:=Tg Div 10;
End;
If Tg=0 Then C[0]:= D[0]
Else Begin C[J-1]:=Tg; C[0]:=J-1; End;
End;
End;
12


1.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ị A i 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 định
giá 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 (iC0:=i;
End;
* Chương trình
Procedure Div_Min(Var C:Big_Number;A:Big_Number;K:Longint);
Var

I,Tg:Longint;

Begin
Fillchar(C,Sizeof(C),0);C[0]:=Hang_So;Tg:=0;
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 (IC[0]:=I;
End;
1.3.4.1.2. Số nguyên lớn chia số nguyên lớn
* Thuật toán:
Procedure Div_Max(Var C:Big_Number;A,B:Big_Number);
Begin
C:=0;

13



Schia:=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;

Begin
Fillchar(C,Sizeof(C),0);C[0]:=Hang_So; I:=B[0]; Schia:=B[I];
While (IBegin I:=I+1; Schia:=Schia*10+B[I]; End;
U:=Hang_So-I;
If U>0 Then Schia:=Schia+1;

Repeat
Tg:=0;
If A[0]Else If A[0]>B[0] Then Ok:=True
Else Begin
I:=A[0];Tg:=A[I];
While IBegin I:=I+1; Tg:=Tg*10+A[I];End;
14


Ok:=(TgEnd;
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
Begin
Tg:=10*Tg+A[I];
D[I+U]:=Tg Div Schia; Tg:=Tg Mod Schia;
End;
I:=A[0]+U; While (ID[0]:=I;
{C:=C+D} Tg:=0;
If C[0]For I:=Hang_So Downto Min Do
Begin
Tg:=Tg+C[I]+D[I];
C[I]:=Tg Mod 10; Tg:=Tg Div 10;
End;

If Tg>0 Then C[Min-1]:=Tg;
I:=Min-1; While (I{E:=B*D} Fillchar(E,Sizeof(E),0);E[0]:=Hang_So;V:=0;
For I:=Hang_So Downto D[0] Do
Begin
Fillchar(K,Sizeof(K),0); Tg:=0;
For J:=Hang_So Downto B[0] Do
Begin
Tg:=Tg+D[I]*B[J];
K[J-V]:=Tg Mod 10; Tg:=Tg Div 10;
End;
If Tg=0 Then K[0]:=J-V
Else Begin K[0]:=J-V-1; K[K[0]]:=Tg; End;
15


V:=V+1;Tg:=0;
If E[0]For J:=Hang_So Downto Min Do
Begin Tg:=Tg+K[J]+E[J];
E[J]:=Tg Mod 10; Tg:=Tg Div 10;
End;
If Tg=0 Then E[0]:=Min
Else Begin E[Min-1]:=Tg; E[0]:=Min-1; End;
End;
{A:=A-E} Tg:=0;
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

End;
Until Ok;
If A[0]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;

16


1.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ị A i 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);
Begin
C:=0; Cho i:= A0 đến Hang_so làm {C:=(10*C+Ai) Mod k;
End;
* Chương trình
Procedure Mod_Min(Var C:longint;A:Big_Number;K:Longint);

Var

I, Tg: Longint;

Begin
C:=0; For I:=A[0] To Hang_So Do C:= (10*C + Ai) Mod k
End;
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;

Begin
Fillchar(C,Sizeof(C),0);C[0]:=Hang_So; I:=B[0];Schia:=B[I];
While (IBegin I :=I+1; Schia:=Schia*10+B[I]; End;
U:=Hang_So-I;
If U>0 Then Schia:=Schia+1;
Repeat
Tg:=0;
If A[0]Else If A[0]>B[0] Then Ok:=True
Else Begin
I:=A[0];Tg:=A[I];
17



While IBegin I:=I+1;Tg:=Tg*10+A[I]; End;
Ok:=(TgEnd;
If Ok=False Then
Begin
{D:=A Div B} Fillchar(D,Sizeof(D),0);D[0]:=Hang_So; Tg:=0;
For I:=A[0] To Hang_So-U Do
Begin
Tg:=10*Tg+A[I]; D[I+U]:=Tg Div Schia;
Tg:=Tg Mod Schia;
End;
I:=A[0]+U;
While (ID[0]:=I;
{E:=B*D} Fillchar(E,Sizeof(E),0);E[0]:=Hang_So;V:=0;
For I:=Hang_So Downto D[0] Do
Begin
Fillchar(K,Sizeof(K),0); Tg:=0;
For J:=Hang_So Downto B[0] Do
Begin
Tg:=Tg+D[I]*B[J];
K[J-V]:=Tg Mod 10; Tg:=Tg Div 10;
End;
If Tg=0 Then K[0]:=J-V
Else Begin K[0]:=J-V-1; K[K[0]]:=Tg; End;
V:=V+1;Tg:=0;
If E[0]For J:=Hang_So Downto Min Do

Begin Tg:=Tg+K[J]+E[J];
E[J]:=Tg Mod 10; Tg:=Tg Div 10;
End;
18


If Tg=0 Then E[0]:=Min
Else Begin E[Min-1]:=Tg; E[0]:=Min-1; End;
End;
{A:=A-E} Tg:=0;
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 (IA[0]:=I;
End;
Until Ok;
If A[0]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 (IA[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.
19


Chươ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
1. Đề bài:
Cho dãy số A1, A2, ……, AN là các số nguyên dương và Ai ≤ 20.000. Hãy tính các
giá trị Ai! với mọi 1 ≤ i ≤ N (biểu diễn chính xác đến hàng đơn vị).
* Dữ liệu vào: cho trong file văn bản Dulieu.INP có nhiều dòng mỗi dòng lưu một
số nguyên dương (0 < Ai ≤ 20.000).
* 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ụ:
Dulieu.IN

Ketqua.OUT

P
2

2


8

40320

10

3628800

20

2432902008176640000

30
265252859812191058636308480000000
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!.
* Giải thuật
Trong khi Not Eof(f1) làm:
+Readln(f1, m);
+ A:=1;
+ Cho i:= 1 đến M làm Nhan_Min(A,A,m);
+ Write(f2, A);
3. Chương trình:

được lưu theo đường dẫn E:\Chuyen De\Tinh Giai Thua


Program GIAI_THUA;
Const

Hang_So = 1000000;
20


Type

Big_Number = Array[0..Hang_So] Of Longint;

Var

A: Big_Number; I, M: Longint; F1, F2: Text;

{========================}
Procedure Nhan_Min(Var C: Big_Number; A: Big_Number; K: Longint);
Var

I, Tg: Longint;

Begin
Tg:= 0;
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.
21


2.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ữa
mỗ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 đơn
giản và được đánh dấu bởi từ abc… và sau đó theo thứ tự của từ đó. Một phần của
danh sách được hiển thị dưới đây:
a

1


b

2





z

26

aa

27

ab

28





snowfall

157.118.051.752






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 hai
mươ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ột
cá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ách
bằng dấu chấm tại hàng nghìn, hàng triệu, ….
Ví dụ:
BIENDICH.INP
29697684282993
transcendental
28011622636823854456520
computationally
22


zzzzzzzzzzzzzzzzzzzz
BIENDICH.OUT
elementary

29.697.684.282.993

transcendental


51.346.529.199.396.181.750

prestidigitation

28.011.622.636.823.854.456.520

computationally

232.049.592.627.851.629.097

zzzzzzzzzzzzzzzzzzzz
20.725.274.851.017.785.518.433.805.270
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 một chiều kiểu Big_Number để tổ chức lưu trữ số nguyên lớn
của bài toán và được khai báo như ở phần lý thuyết.
- Xây dựng thủ tục Tru_1(Var A: Big_Number): A:= A - 1 được thực hiện khi A
chia hết cho 26.
- Sử dụng các thủ tục sau:
+ Cong_Max(A,B,C): A:= B + C với A, B, C kiểu Big_Number.
+ Nhan_Min(A,B,k): A:= B * k với A, B kiểu Big_Number và k kiểu Longint.
+ Mod_Min(c,A,k): C:= A Mod k với A kiểu Big_Number và c, k kiểu Longint.
+ Div_Min(A,B,k): A:= B Div k với A, B kiểu Big_Number và k kiểu Longint.
* Giải thuật:
- Xây dựng thủ tục mã hóa để mã hóa một từ thành một số:
Procedure Ma_Hoa(St: một từ để biểu diễn thành số);
Begin
A0:= Hang_So - Length(St) + 1
AHang_So + i - Length(St) := Ord(Sti) - 96; với mọi i =1 .. Length(St)

B:=AHang_So; C:=1;
For I:= Hang_So - 1 Downto A0 Do
Begin
Nhan_Min(C, C, 26);
Nhan_Min(D, C, Ai);
Cong_Max(B, B, D);
End;
Writeln(F2, St, B);
23


End;
- Xây dựng thủ tục giải mã để phân tích một số thành một từ:
Procedure Giai_Ma(St: Số để biểu diễn thành một từ);
Begin
A0:= Hang_So - Length(St) + 1;
AHang_So + i - Length(St) := Ord(Sti) - 48; với mọi i =1 .. Length(St)
B[0]:= Hang_So + 1;
Trong khi (A0<Hang_So) Or (AHang_So>0) làm:
+ B0:= B0 - 1;
+ Mod_Min(BB[0], A, 26);
+ Div_Min(A, A, 26);
+ If BB[0]=0 Then Tru_1(A);
S:= '';
Cho i:= B0 đến Hang_So làm
Nếu Bi=0 Thì S:= S + 'Z' ngược lại S:= S + Chr(B[i]+96);
i:= Length(St) - 2;
Trong khi i >1 Làm
+ Insert('.',St,i);
+ i:= i – 3;

Ghi file(F2, S:20, St:45);
End;
- Chương trình chính
BEGIN
Trong khi Not Eof(F1) làm:
+ Readln(F1, St);
+ Nếu St1<='9' thì Giai_Ma(St) ngược lại Ma_Hoa(St);
END.
3. Chương trình

được lưu trong đường dẫn E:\Chuyen De\Bien Dich

Const Hang_So = 10000;
Type

Big_Number= Array[0..Hang_So] Of Longint;

Var St: String; F1, F2: Text;
{========== A:= B + C ==========}
24


Procedure Cong_Max(Var A: Big_Number; B, C: Big_Number);
Var

I, Min, Tg: Longint;

Begin
Fillchar(A,Sizeof(A),0);
For I:=1 To B[0]-1 Do B[I]:= 0;

For I:=1 To C[0]-1 Do C[I]:= 0;
If C[0]>=B[0] Then Min:=B[0] Else Min:=C[0];
Tg:=0;
For I:=Hang_So Downto Min Do
Begin Tg:= Tg + C[I] + B[I]; A[I]:= Tg Mod 10; Tg:= Tg Div 10; End;
If Tg = 0 Then A[0]:= Min
Else Begin A[0]:= Min-1; A[Min-1]:= Tg; End;
End;
{========== A:= B * k ===========}
Procedure Nhan_Min(Var A: Big_Number; B: Big_Number; k: Longint);
Var

I, Tg: Longint;

Begin
Fillchar(A,Sizeof(A),0); Tg:=0;
For I:=Hang_So Downto B[0] Do
Begin Tg:= Tg + B[I] * K; A[I]:= Tg Mod 10; Tg:= Tg Div 10; End;
While Tg>0 Do Begin I:= I-1; A[I]:=Tg Mod 10; Tg:= Tg Div 10; End;
A[0]:= I;
End;
{==========C:= A Div K ===========}
Procedure Div_Min(Var C: Big_Number; A:Big_Number; K: Longint);
Var

I,Tg: Longint;

Begin
Fillchar(C,Sizeof(C),0); C[0]:= Hang_So; Tg:= 0;
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 (IC[0]:= I;
25


×