Thuật toán học trong tin học
Trang 1ứng dụng lý thuyết Toán để giải các bài Tin
Lê Nguyễn Tuấn Thành
(Tiếp theo số trước)
Bài 8
Câu 1 Cho một số thập phân vô hạn tuần hoàn dạng A,B(C) với A là phần trước dấu
phẩy, B là phần sau dấu phẩy không tuần hoàn, C là phần thập phân tuần hoàn (A,B,C là các số nguyên dương, C<>9) Hãy viết phân số tối giản biểu diễn số thập phân đó
File vào: Thapphan.inp
Gồm: 3 số A,B,C ghi trên 3 dòng khác nhau Nếu B= -1 thì sau dấu phẩy của số đóự chỉ
có phần vô hạn tuần hoàn
File ra : Thapphan.out
Gồm : Tử và mẫu của phân số tìm được, ghi trên hai dòng
Câu 2: Cho phân số có tử và mẫu lần lượt là P,Q (P,Q là các số nguyên dương) Hãy viết
dạng biểu diễn thập phân của phân số đó
File vào : Fraction.inp
Gồm: P,Q ghi trên cùng một dòng cách nhau bởi dấu cách
File ra: Fraction.out
Gồm: Nếu biểu diễn thập phân là vô hạn tuần hoàn thì ghi ra dạng A B C với ý nghĩa như
câu 1
Nếu biểu diễn thập phân là hữu hạn thì ghi ra
dạng A B
Giải
Để giải quyết bài này trước hết tôi nhắc lại 2 định lí quan trọng về phân số được đề cập đến trong SGK lớp 7:
Định lí 1: Nếu một phân số tối giản mà mẫu lớn hơn 0 và mẫu không có ước nguyên tố
nào khác 2 và 5 thì phân số đó được viết dưới dạng số thập phân hữu hạn
Định lí 2: Nếu một phân số tối giản mà mẫu lớn hơn 0 và mẫu có ước nguyên tố khác 2
và 5 thì phân số đó được viết dưới dạng số thập phân vô hạn tuần hoàn Bây giờ ta sẽ đi tìm phân số biểu diễn của một số thập phân vô hạn tuần hoàn dạng 0,(c) Gọi k là số chữ
số của c
Trang 2Nếu bạn nào tinh ý sẽ để ý thấy rằng số thập phân 0,(9) sẽ không có phân số nào biểu diễn, bởi vì theo trên
Điều này không đúng Trong chương trình tôi viết cho Câu 1 nếu nhập vào 0 −1 9 thì kết
quả nhận được là
Đây có lẽ là sự thú vị nhất về số thập phân vô hạn tuần hoàn
Định lí 1 và 2 khẳng định mọi phân số đều có thể biểu diễn thành dạng thập phân hữu hạn
hoặc vô hạn tuần hoàn, đến đây ta có thể thấy điều ngược lại không đúng
Từ công thức tìm được ở trên, ta có thể dễ dàng giải quyết được Câu 1
Gọi t là số chữ số của B
Trang 3Như vậy là ta đã tìm được phân số biểu diễn số thập phân vô hạn tuần hoàn A,B(C)
Để giải quyết bài toán một cách trọn vẹn thì các bạn phải giải 2 trường hợp B = -1 và B
≠-1 Công thức ở trên mới chỉ giải trường hợp B≠-1 Đối với trường hợp B = -1, bằng cách làm tương tự như trên, ta có được công thức sau:
Còn một vấn đề cần giải quyết trước khi viết chương trình đó là: có trường hợp trong dạng biểu diễn của B,C có các chữ số 0 đứng đầu Như vậy nếu ta đọc B,C từ file với dạng số thì sẽ làm mất đi các chữ số 0 đứng đầu, vì thế sẽ làm sai lệch số chữ số của B,C
và khi thay vào công thức tính sẽ cho kết quả sai
Tôi đã giải quyết trường hợp này bằng cách đọc giá trị của B,C vào các xâu, sau đó chuyển xâu sang dạng số để giải Bằng cách này ta sẽ lưu được số chữ số thực của B,C Chương trình tôi viết cho hai câu trong bài này chỉ xử lí được các số trong phạm vi
longint Bạn nào muốn xử lí các số lớn hơn phải cài đặt các thuật toán xử lí số lớn Các
thuật toán này đã được giới thiệu trên các số báo trước Các bạn có thể tham khảo lại để cài đặt
Sau đây là chương trình cho Câu 1:
uses crt;
const fi='ThapPhan.inp';
fo='ThapPhan.out';
var a,b,c,nb,nc,p,q:longint;
xaub:string;
xauc:string;
{************************************************}
procedure nhap;
var f:text;
z:integer;
begin
assign(f,fi);
reset(f);
readln(f,a);
readln(f,xaub);
readln(f,xauc);
Trang 4close(f);
val(xaub,b,z);
val(xauc,c,z);
end;
{************************************************} {Tìm ước chung lớn nhất của hai số nguyên dương a,b} function ucln(a,b:longint):longint;
var du:longint;
begin
ucln:=1;
if (a=1) or (b=1) then exit;
if a mod b=0 then begin ucln:=b; exit; end;
if b mod a=0 then begin ucln:=a; exit; end;
while b>0 do
begin
du:=a mod b;
a:=b;
b:=du;
end;
ucln:=a;
end;
{************************************************} {Tính 10a}
function mu10(a:longint):longint;
var i,tich:longint;
begin
tich:=1;
for i:=1 to a do
tich:=tich*10;
mu10:=tich;
end;
{************************************************} { Cho trường hợp B<>-1}
procedure xuli1;
begin
nb:=length(xaub);
nc:=length(xauc);
p:=a*mu10(nb)*mu10(nc)-a*mu10(nb)+b*mu10(nc)+c-b; q:=mu10(nb)*(mu10(nc)-1);
end;
{************************************************} { Cho trường hợp B=-1}
procedure xuli2;
begin
nc:=length(xauc);
p:=a*mu10(nc)+c-a;
Trang 5q:=mu10(nc)-1;
end;
{************************************************}
procedure main;
var g:text;
d:longint;
begin
nhap;
if b=-1 then xuli2
else xuli1;
d:=ucln(p,q);
p:=p div d;
q:=q div d;
assign(g,fo);
rewrite(g);
writeln(g,p);
writeln(g,q);
close(g);
end;
{***********************************************}
BEGIN
clrscr;
main;
END
Bây giờ chúng ta sẽ giải quyết Câu 2
Trước hết ta thấy rằng theo định lí 1 và 2 dạng biểu diễn của phân số chỉ có thể là hữu
hạn hoặc vô hạn tuần hoàn Là hữu hạn hay vô hạn tuần hoàn phụ thuộc vào Q
Để giải quyết bài toán với các số nhỏ hơn ta đưa phân số về dạng tối giản
Ta có thể dễ dàng tìm được giá trị của A : A=P div Q
Lúc đó ta thay P=P mod Q Từ đây trở đi ta chỉ xét các phân số có tử nhỏ hơn mẫu Vì
vậy chỉ cần đi tìm B và C
Ta sẽ phân tích Q thành dạng: 2t*3k*5n*U (Với t,k,n,U là các số nguyên dương)
+ Trường hợp 1 Trong phân tích ra thừa số nguyên tố của Q chỉ chứa 2 và 5 mà không chứa bất kì số nguyên tố nào khác, tức là k=0 và U=1 (Q = 2t*5n)
Lúc này dạng biểu diễn của sẽ là hữu hạn hay có dạng 0,B
Để tìm được B ta làm như sau:
- Nếu t ≤ n ta đưa về dạng
Như vậy B=P*2n-t Gọi nb là số chữ số của B
Trang 6Như vậy trong biễu diễn dạng số thập phân thì số chữ số 0 đứng liền sau dấu phẩy sẽ
bằng: n-nb
- Nếu t >n ta đưa về dạng
Như vậy B=P*5t-n Gọi nb là số chữ số của B
ị Trong biễu diễn dạng số thập phân thì số chữ số 0 đứng liền sau dấu phẩy sẽ bằng: t-nb.
+Trường hợp 2: Trong phân tích ra thừa số nguyên tố của Q có chứa các số nguyên tố
khác 2 và 5 Q có dạng 2t*3k*5n*U
Như vậy dạng biểu diễn của sẽ là vô hạn tuần hoàn hay có dạng 0,B(C)
Theo chứng minh ở trên ta phải đưa về dạng
(X là một số nguyên dương và có j chữ số 1
dưới mẫu)
Ta sẽ dùng một biến thương để lưu lượng phải nhân thêm vào P và Q để đưa được về
dạng , một biến mu10 để lưu số mũ của 10 trong biểu diễn của Q ở trên (cụ thể mu10 =i), một biến sl1 để lưu số chữ số 1 trong biểu diễn của Q (cụ thể sl1
=j)
Để đơn giản, ta sẽ loại bỏ khỏi Q phần chứa 2i*5i*9 sau khi nhân thêm với thương Khi
đó Q mới sẽ là ước của 111 11 (j số 1)
Cách làm như sau:
Khởi tạo thương=1 ; Q:=Q div 2 t *3 k *5 n
Lúc đầu thương, mu10 được tính như sau:
- Nếu t≤ n thì thương:= thương*2 n-t , mu10:=n
Ngược lại nếu t > n thì thương :=<i>thương*5t-n, mu10:=t
- Nếu k >2 thì Q:=Q*3 k-2
Vấn đề còn lại là đi tìm sl1 để 111 11 (có sl1 chữ số1, ta thay j ở trên bằng biến sl1) chia hết cho Q Theo định lí 2 thì sẽ luôn tìm được sl1
Ta sẽ dùng cách làm tương tự như cách đã làm ở bài 6, nhưng phải chú ý cập nhật
thương Tôi dùng cách làm này để các bạn có thể dễ dàng áp dụng khi cài đặt các thuật
toán xử lí số lớn
Thêm hai biến phu và thương10 có ý nghĩa sau:
phu để lưu thương của 111 11 khi chia cho Q
sẽ được cập nhật liên tục khi sl1 tăng lên
Thủ tục tìm sl1 sẽ như sau:
Chú ý: k trong thủ tục thay cho Q
Procedure so_luong_so_1(k:longint);
var du,du10,thuong10,phu:longint;
begin
sl1:=1;
Trang 7if k=1 then exit;
phu:=0;
du:=1; du10:=1;
thuong10:=0;
repeat
inc(sl1);
thuong10:=thuong10*10+du10*10 div k;
du10:=du10*10 mod k;
phu:=phưthuong10+(dưdu10) div k;
du:= (dưdu10) mod k;
until du=0;
thuong:=thuong*phu;
end;
Biến du sẽ lưu số dư của 111 11 khi chia cho Q
Biến thương sẽ được tính lại là: thuong:=thuong*phu
Chương trình bị hạn chế bởi thương, thương10, phu là dạng longint nên chỉ chạy được với Q khá nhỏ Các bạn có thể bỏ các dòng lệnh chứa thương,thương10, phu để có thể tìm sl1 với Q lớn hơn
Còn một lưu ý nữa là: khi mu10 = 0 thì B = -1, do đó phải xử lý thêm trường hợp này Sau khi tìm được thương thì P:=P*thương
Để viết được dạng biểu diễn thập phân, chúng ta phải tìm được B và C Ta thấy rằng nếu
không tính các chữ số 0 đứng đầu thì số chữ số của B Ê mu10, số chữ số của C ≤ sl1
Theo trên P sẽ có dạng: B*(10sl1-1)+C
Từ đó ta sẽ duyệt toàn bộ các giá trị có thể có của B và C (với chú ý điều kiện về số chữ
số của B,C)
Sau khi tìm được B,C cách tính số chữ số 0 đứng đầu trong B,C các bạn có thể làm theo
cách đã giới thiệu ở trong trường hợp 1
Sau đây là chương trình cho câu 2:
uses crt;
const fi='Fraction.inp';
fo='Fraction.out';
var p,q,a,b,c,thuong:longint;
mu2,mu3,mu5,mu10,sl1:word;
f:text;
{************************************************}
procedure nhap;
begin
assign(f,fi);
reset(f);
readln(f,p,q);
close(f);
assign(f,fo);
rewrite(f);
end;
Trang 8{************************************************}
{Tìm số mũ của số nguyên tố a trong dạng phân tích ra thừa số nguyên tố của So} function mu(a:word;var so:longint):word;
var k:word;
begin
k:=0;
while so mod a=0 do
begin
inc(k);
so:=so div a;
end;
mu:=k;
end;
{************************************************}
{Tìm ước chung lớn nhất của hai số nguyên dương a,b}
function ucln(a,b:longint):longint;
var du:longint;
begin
ucln:=1;
if (a=1) or (b=1) then exit;
if a mod b=0 then begin ucln:=b; exit; end;
if b mod a=0 then begin ucln:=a; exit; end;
while b>0 do
begin
du:=a mod b;
a:=b;
b:=du;
end;
ucln:=a;
end;
{************************************************}
{Tinh as }
function mu_as(a,s:word):longint;
var i,tich:longint;
begin
tich:=1;
for i:=1 to s do
tich:=tich*a;
mu_as:=tich;
end;
{************************************************}
{Tìm số chữ số cảu một số nguyên dương a}
function scs(a:longint):longint;
var k:longint;
begin
if a=0 then begin scs:=1; exit; end;
Trang 9k:=0;
while a>0 do
begin
a:=a div 10;
inc(k);
end;
scs:=k;
end;
{************************************************} procedure huu_han;
var so,i:word;
begin
p:=p*thuong;
so:=scs(p);
if mu2>=mu5 then
begin
for i:=1 to mu2-so do write(f,0);
write(f,p);
end
else
begin
for i:=1 to mu5-so do write(f,0);
write(f,p);
end;
close(f);
halt;
end;
{***********************************************} procedure so_luong_so_1(k:longint);
var du,du10,thuong10,phu:longint;
begin
sl1:=1;
if k=1 then exit;
phu:=0;
du:=1; du10:=1;
thuong10:=0;
repeat
inc(sl1);
thuong10:=thuong10*10+du10*10 div k;
du10:=du10*10 mod k;
phu:=phưthuong10+(dưdu10) div k;
du:=(dưdu10) mod k;
until du=0;
thuong:=thuong*phu;
end;
{***********************************************}
Trang 10procedure vo_han_tuan_hoan;
var phu,lim:longint;
begin
so_luong_so_1(q);
p:=p*thuong;
if mu10=0 then
begin
write(f,-1,' ');
for phu:=1 to sl1-scs(p) do write(f,0);
write(f,p);
close(f);
halt;
end;
phu:=mu_as(10,sl1)-1;
lim:=p div phu;
for b:=0 to lim do
if scs(b)<=mu10 then
begin
c:=p-b*phu;
if scs(c)<=sl1 then break;
end;
for phu:=1 to mu10-scs(b) do write(f,0);
write(f,b,' ');
for phu:=1 to sl1-scs(c) do write(f,0);
write(f,c);
close(f);
end;
{************************************************}
procedure xuli;
var d:longint;
begin
d:=ucln(p,q);
p:=p div d;
q:=q div d;
a:=p div q;
p:=p mod q;
write(f,a,' ');
mu2:= mu(2,q);
mu3:= mu(3,q);
mu5:= mu(5,q);
thuong:=1;
if mu2>=mu5 then begin thuong:=thuong*mu_as(5,mu2-mu5); mu10:=mu2; end else begin thuong:=thuong*mu_as(2,mu5-mu2); mu10:=mu5; end;
if (mu3=0) and (q=1) then huu_han;
if mu3<2 then thuong:=thuong*mu_as(3,2-mu3)
Trang 11else if mu3>2 then q:=q*mu_as(3,mu3-2);
vo_han_tuan_hoan;
end;
{************************************************}
procedure main;
begin
nhap;
xuli;
end;
{************************************************}
BEGIN
clrscr;
main;
END
Trước khi kết thúc bài viết này, tôi muốn đưa ra cho các bạn một tính chất thú vị của số 7
về các số thập phân vô hạn tuần hoàn đó là:
Liệu còn số nào có tính chất như vậy không? Các bạn thử tìm xem
Nếu bạn nào cần chương trình nguồn của tất cả các bài đã nêu trên xin hãy liên hệ với tôi