1. Trang chủ
  2. » Công Nghệ Thông Tin

Báo cáo môn học về Lập trình tính toán số nguyên lớn bằng java

18 1,9K 1

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 18
Dung lượng 76 KB

Nội dung

Trong nhiều ngành khoa học kỹ thuật khác, chúng ta còn phải sử dụng tới các con số lớn hơn thế rất nhiều. Trong bài báo này, chúng tôi muốn trình bày với đọc giả đôi nét xung quanh vấn đề tính toán với số lớn qua các phép toán phổ biến như: cộng, trừ, nhân, chia, khai căn, luỹ thừa và kiểm tra tính nguyên tố. Trước hết chúng ta cùng bàn về một số môi trường lập trình để thể hiện chương trình tính toán với số lớn: Với ngôn ngữ lập trình Pascal: Nếu sử dụng cấu trúc dữ liệu (CTDL) kiểu chuỗi thì chỉ có thể thao tác với các số trong phạm vi 255 chữ số; nếu sử dụng CTDL kiểu mảng thì có thể thao tác với các số lớn vượt xa giới hạn 255 chữ số gấp nhiều lần, có thể đạt tới hàng ngàn chữ số (lưu ý khi làm việc với CTDL mảng là kích thước của mảng cho phép khai báo một số rất lớn nhưng vẫn bị hạn chế vì bộ nhớ dành cho mảng là 64K); cách làm khác là sử dụng con trỏ để khai thác vùng nhớ Heap của máy tính, bằng cách sử dụng CTDL kiểu mảng động để thao tác với các số lớn tới hàng tỉ con số (vì kích thước mảng động phụ thuộc vào dung lượng Ram thực tế của máy tính). Với ngôn ngữ lập trình Delphi hay Java ta có thể thao tác với các số lớn tới hàng tỉ con số thông qua CTDL kiểu chuỗi hay CTDL kiểu mảng.

Trang 1

VẤN ĐỀ TÍNH TOÁN VỚI CÁC SỐ LỚN

Vấn đề tính toán với các số lớn có ý nghĩa rất lớn trong thực tế Chẳng

hạn, thuật toán mã hoá công khai RSA (do Rivers, Shamir và Adleman viết

ra vào năm 1978) sử dụng tới 512 số khoá (thuật toán này có liên quan đến việc phân tích các số nguyên tố) Trong nhiều ngành khoa học kỹ thuật khác, chúng ta còn phải sử dụng tới các con số lớn hơn thế rất nhiều Trong bài báo này, chúng tôi muốn trình bày với đọc giả đôi nét xung quanh vấn đề tính toán với số lớn qua các phép toán phổ biến như: cộng, trừ, nhân, chia, khai căn, luỹ thừa và kiểm tra tính nguyên tố

Trước hết chúng ta cùng bàn về một số môi trường lập trình để thể hiện chương trình tính toán với số lớn:

- Với ngôn ngữ lập trình Pascal: Nếu sử dụng cấu trúc dữ liệu (CTDL) kiểu chuỗi thì chỉ có thể thao tác với các số trong phạm vi 255 chữ số; nếu sử dụng CTDL kiểu mảng thì có thể thao tác với các số lớn vượt xa giới hạn 255 chữ số gấp nhiều lần, có thể đạt tới hàng ngàn chữ số (lưu ý khi làm việc với CTDL mảng là kích thước của mảng cho phép khai báo một số rất lớn nhưng vẫn bị hạn chế vì bộ nhớ dành cho mảng là 64K); cách làm khác

là sử dụng con trỏ để khai thác vùng nhớ Heap của máy tính, bằng cách sử dụng CTDL kiểu mảng động để thao tác với các số lớn tới hàng tỉ con số (vì kích thước mảng động phụ thuộc vào dung lượng Ram thực tế của máy tính).

- Với ngôn ngữ lập trình Delphi hay Java ta có thể thao tác với các số lớn tới hàng tỉ con số thông qua CTDL kiểu chuỗi hay CTDL kiểu mảng Ngoài ra, cũng có thể sử dụng các ngôn ngữ thông dụng khác như C/C+ + để viết chương trình Chúng ta tạm thời thoả thuận với nhau rằng: tuỳ thuộc yêu cầu mà lựa chọn môi trường lập trình cũng như CTDL phù hợp Vấn đề quan trọng là thuật toán thể hiện, phần tiếp theo sẽ trình bày các thuật toán có liên quan.

1.Thuật toán cộng hai số:

Để cộng hai số nguyên a và b ta thực hiện như phép cộng bằng tay hai

số thông thường Tức là thực hiện lần lượt phép tính từ bên trái, sử dụng một biến trung gian để lưu phần dư tại mỗi bước.

Trang 2

2.Thuật toán trừ hai số:

Giả sử cần tìm c = a - b ( a=a1a2 an; b=b1b2 bm; n m), thuật toán được

mô tả qua đoạn chương trình sau:

du:=0;

for i:= n downto n-m +1 do

begin

tg:=ai -bi - du;

if (tg < 0) then begin

du:=1;tg:=tg+10;

end;

else du:=0;

ci:=tg;

end;

for i:= n-m to 1 do{chuyển đoạn đầu còn lại (nếu có) của a vào c}

begin

ci:= (ai+du ) mod 10;

du:=(a+du) div 10;

end;

if (du>0) then c0:= du;

3.Thuật toán nhân hai số:

Thuật toán này có thể dễ dàng suy ra từ thuật toán cộng hai số, xin dành cho bạn đọc tự tìm hiểu.

4.Thuật toán chia hai số:

- Cắt lần lượt từng “đoạn“ của số bị chia tính từ bên trái (có cộng thêm phần dư của các bước trung gian).

- Đem chia các đoạn đó cho số chia bằng phép toán trừ.

- Thương tìm được chính là dãy các số là kết quả của phép chia các

“đoạn” cho số chia (được phát triển dần về phía bên phải).

- Phần dư của phép chia chính là “đoạn” còn lại không thể chia được nữa.

5.Thuật toán khai căn bậc hai của một số nguyên X (kết quả là một số

nguyên):

b:=1;

l:=<số chữ số của X>;

Trang 3

{chia số X thành tập các số ai có hai chữ số tính từ trái, riêng số a1 có thể có một hoặc hai chữ số tuỳ theo l chẵn hay lẻ }

If ( l div 2 =1) then

Begin j:=1;

Y[j]:=X[1]; i:=2;

End Else Begin i:=1;j:=0; end;

While (i<l) do

Begin

j:=j+1;

Y[j]:=X[i]*10+X[i+1];

i:=i+2;

end;

{ khởi tạo giá trị ban đầu}

a:=round(sqrt(Y[1]));s1:=Y[1]-a;i:=2;

Repeat

s2:=s1*10+Y[i] div 10;

b:=s2 div (2*a);

s2:=s2*10+Y[i] mod 10; i:=i+2;

s1:=20*a*b+b*b;

a:=a*10+b; s1:=s2-s1;

Until (i>=l);

Kết quả cần tìm chính là a.

Ví dụ: cần tính a=

{khởi tạo}

Y[1]=1;Y[2]=30;

a=1;s1=0;

{đoạn thủ tục lặp}

s2=3;

b=3 div(2*a)=1;i=3;

s2=30; s1=21;

a=a*10+b=11;

s1=9;

Ta được kết quả a=11.

130

Trang 4

Để chương trình thực hiện hiệu quả hơn, ta có thể cải thiện thuật toán đi đôi chút (chi tiết về phần cài đặt bạn đọc có thể tham khảo trong chương trình Nếu muốn tìm hiểu về chương trình chi tiết, bạn có thể liên hệ với toà soạn)

6.Thuật toán nâng lên luỹ thừa:

Giả sử cần tính b = ak Thông thường ta sử dụng thuật toán sau:

b:= 1;

for i:=1 to k do b:= b*a;

Như vậy cần phải lặp k bước để tìm kết quả, đây là chi phí quá lớn (đặc biệt khi k lớn tới con số hàng ngàn đơn vị) Ta cải tiến với ý tưởng sau:

b:=1;

while (k > 0) do

if (k mod 2 = 1) then

begin

b:= b*a;

k:=k- 1;

end

else

begin

a:=a*a;

k:=k div 2;

end;

Kết thúc vòng lặp ta thu được biến b chứa kết quả cần tìm Tính đúng đắn của thuật toán xin dành cho bạn đọc tự chứng minh

7.Thuật toán kiểm tra tính nguyên tố của một số A:

Có nhiều thuật toán để giải quyết vấn đề này, nhưng cần phải đưa ra được một thuật toán hiệu quả cho bài toán số lớn ở đây Cơ sở của thuật toán

có thể dựa vào định lý số học sau:

"A là số nguyên tố khi A không chia hết cho bất kỳ một số ngyên tố nào trong dãy số: d0=2, d1=3, , dk Định lý vẫn đúng khi trong dãy có chứa một vài hợp số".

Vì vậy, ta chỉ cần kiểm tra tính không chia hết của A cho các số di

được xây dựng như sau:

d0=2, d1=3, d2=5 và dk (k > 2)được tính dựa vào dk-1:

+Nếu k lẻ: dk =dk-1 +2

+Nếu k chẵn: dk =dk-1 +4

(Ví dụ:d0=2, d1=3, d2=5, d3=7, d4=11, d5=13, d6=19… ).

Trang 5

Nếu xây dựng đầy đủ các phép toán này, ta sẽ có một công cụ cá nhân khá hoàn chỉnh cho phép chúng ta thao tác với số lớn.

Sau đây là minh hoạ cho thuật toán chia hai số nguyên trên ngôn ngữ lập trình Pascal Dữ liệu vào được cho trong tệp CHIA.INP gồm hai dãy số theo thứ tự là số bị chia và số chia Mỗi dãy gồm nhiều dòng, mỗi dòng có độ

dài không lớn hơn 80 kí tự số Hai dãy cách nhau một dòng trống Dữ liệu ra

được ghi vào tệp CHIA.OUT gồm hai dãy số theo thứ tự là thương số và phần

dư của phép chia Quy tắc ghi hai dãy số này cũng tương tự như trên.

Ví dụ: CHIA.INP CHIA.OUT

Toàn bộ chương trình như sau:

Program Phep_Chia_Hai_So_Lon;

var a,b,c:array[0 25000] of shortint;

l,la,lb,lc:integer;

Function ss(d,k:integer):boolean;

{so sanh hai day so: a[d] a[k]va b[1] b[lb]}

var ok,kt:boolean;

i,j:integer;

begin

If k-d+1>lb then ok:=true

else if (k-d+1<lb )or (k>la) then ok:=false

else

begin

ok:=true;kt:=true;i:=d;j:=1;

while kt and(i<=k) do

if a[i] >b[j] then kt:=false

else

if a[i] <b[j] then

Begin kt:=false;

ok:=false

End

else

Begin i:=i+1;j:=j+1;end;

end;

ss:=ok

end;

Procedure tru(d,k:integer);

Trang 6

var tg,phu:byte;j,i:integer;

begin

phu:=0;j:=lb;

for i:=k downto d do

begin

if a[i]-b[j]- phu<0 then

begin

a[i]:=a[i]+10-b[j]-phu;

phu:=1;

end

else

begin

a[i]:=a[i]-b[j]-phu;

phu:=0;

end;

j:=j-1;

end;

end;

Procedure xu_ly;

var t:byte;k,i:integer;

begin

lc:=0;l:=1;k:=lb;

if not ss(l,k)and(k>=la) then exit;

if not ss(l,k) then k:=k+1;

k:=k-1;

repeat

k:=k+1;

while (a[l]=0) and(l<k) do l:=l+1; t:=0;

while ss(l,k) do

begin

tru(l,k);t:=t+1;

if (a[l]=0) and(l<=la) then l:=l+1; end;

lc:=lc+1;c[lc]:=t;

until not ss(l,la)or(k>la);

while (k<la) do

begin

lc:=lc+1;c[lc]:=0;k:=k+1;

end;

Trang 7

while (l<la)and(a[l]=0) do l:=l+1; end;

Procedure init;

var f:text;s:string;x,i,code:integer; Begin

assign(f,'chia.inp');

reset(f);la:=0;lb:=0;

repeat

readln(f,s);

for i:=1 to length(s) do

begin

val(s[i],x,code);

la:=la+1;a[la]:=x;

end;

until s='';

repeat

readln(f,s);

for i:=1 to length(s) do

begin

val(s[i],x,code);

lb:=lb+1;b[lb]:=x;

end;

until s='';

close(f);b[0]:=0;

end;

Procedure test;

var f:text;i:integer;

begin

init; xu_ly;

assign(f,'chia.out');

rewrite(f);

if lc=0 then writeln(f,0)

else

begin

for i:=1 to lc do

begin

write(f,c[i]);

if i mod 60=0 then writeln(f); end;

if i mod 60<>0 then writeln(f);

Trang 8

end;

writeln(f);

if l>la then writeln(f,0)

else

for i:=l to la do

begin

write(f,A[i]);

if i mod 60=0 then writeln(f);

end;

close(f);

end;

Begin

Test;

End.

Có thể sửa đổi chương trình một chút để thực hiện phép chia các số nguyên và có kiểm tra dữ liệu vào Dưới đây là một test khác để kiểm tra cùng kết quả thu được:

CHIA.INP44326437247564346384623786426346238946891688461664264 6814684688644326437247564346384623786426346238946891688461664 2646814684688644326437247564346384623786426346238946891688461 66426468146846886

4432643724756434638462378642634623894689168846166426468146846 886

CHIA.OUT

100000000000000000000000000000000000000000000000000000000000 000010000000000000000000000000000000000000000000000000000000 000000001

0

Dưới đây là chương trình đầy đủ minh hoạ tất cả các thuật toán ở trên

có thể liên hệ trực tiếp với toà soạn, chương trình được viết bằng ngôn ngữ

Java theo cách tiếp cận hướng đối tượng có thể thao tác với các số nguyên

và số thực.

import java.io.*;

//lop so thuc lon voi cac phep toan

//thong thuong:cong , tru, nhan, chia, khai can va kiem

//tra tinh nguyen to

class BigNum {

private int dau;//duong la 0,am la 1

Trang 9

private String val;

private int num;//so chu so sa dau phay

//con structors

public BigNum( BigNum b){ this(b.dau,b.val,b.num);}

public BigNum() { val=new String("0");dau=1;num=-1;}

public BigNum(int d,String s,int p) {dau=d;val=s;num=p;}

public BigNum(String s) {

char c=s.charAt(0);

if (c=='+'){s=s.substring(1);dau=1;}

else if(c=='-'){ s=s.substring(1);dau=0;}

else dau=1;

int i=s.indexOf('.');

if(i==-1)i=0;

else i=s.length()-i-1;

if(i!=0) s=s.substring(0,s.length()-i-1)+s.substring(s.length()-i); val=s;num=i;

} //phep toan cong hai so lon

public BigNum add(BigNum x,BigNum y){

String xx=new String(x.val);

String yy=new String(y.val);

int n=xx.length(),px=x.num;

int m=yy.length(),py=y.num;

int dx=n-px,dy=m-py;

if (dx<dy)for (int i=0;i<dy-dx;i++)xx=0+xx;

else for (int i=0;i<dx-dy;i++)yy=0+yy;

if (py>px)

for (int i=0;i<py-px;i++)xx=xx+0;

else

for (int i=0;i<px-py;i++)yy=yy+0;

if (x.dau != y.dau)//hai so trai dau

if (isLess(xx,yy) ) { return new BigNum( y.dau,sub1(yy,xx),(px<py)? py:px );}

else { return new BigNum( x.dau,sub1(xx,yy),(px<py)?

py:px ) ;}

else //hai so cung dau { return new BigNum( y.dau,add1(yy,xx),(px<py)? py:px );} }

//kiem tra x<y ?

private boolean isLess(String x,String y){

boolean ok=true;

boolean kt=true;

int i=0;

if(x.length()>y.length())ok=false;

else if(x.length()<y.length())ok=true;

else//do dai bang nhau while( (i<x.length() ) & ok & kt )

if (x.charAt(i)> y.charAt(i) ) ok= false;

Trang 10

else if (x.charAt(i)< y.charAt(i) ) kt=false;

else i++;

if(i==x.length())ok=false;

return ok;

}

//thuc hien phep tru hai SO NGUYEN x,y

private String sub1(String x,String y){

int i,odd=0,temp;

int lx=x.length();

int ly=y.length();

String s=new String();

if(lx>ly) for (i=0;i<lx-ly;i++)y=0+y;

else for (i=0;i<ly-lx;i++)x=0+x;

for (i=x.length()-1;i>=0;i ){

temp= x.charAt(i)- y.charAt(i)-odd ;

if (temp<0){odd=1; s=(temp+10) +s;}

else { odd=0;s=temp+s;}

} // cat bo phan bang 0 o dau i=0;

while ( (i<s.length()-1) && (s.charAt(i)=='0') ) i++;

return s.substring(i);

}

//thuc hien phep tru hai SO LON x,y

public BigNum sub(BigNum x,BigNum y){

//x-y=x+(-y)

return new BigNum( add(x,new BigNum(1-y.dau,y.val,y.num) ) ); }

//thuc hien phep cong hai SO NGUYEN x,y

private String add1(String x,String y){

int n=x.length();

int m=y.length();

int i;

if (n<m)for (i=0;i<m-n;i++) x=0+x;

else for ( i=0;i<n-m;i++) y=0+y;

int c=0;

String s=new String();

if (n<m) n=m; else m=n;

for ( i=n-1;i>=0;i ){

s=(x.charAt(i)+ y.charAt(i)+c-96)%10 +s;

c=(x.charAt(i)+ y.charAt(i)+c-96)/10;

}

if (c>0) s=c+s;

return s;

}

//thuc hien phep nhan mot SO NGUYEN x voi so nguyen c(c<9)

private String mul_2(char c,String x){

String s= new String();

int k,i,j=0;

for(i=x.length()-1;i>=0;i ){

k=(c-48)*(x.charAt(i)-48)+j;

Trang 11

j=k/10;

s=k%10+s;

}

if (j>0) s=j+s;

return s;

}

//thuc hien phep nhan hai SO NGUYEN x,y

private String mul_1(String x,String y){

String temp,z=new String();

int i,k=0;

//cat bo phan chu so bang 0 o cuoi i=x.length()-1;

while ( (i>=0) && (x.charAt(i)=='0') ) i ;

k=x.length()-i-1;

x=x.substring(0,i+1);

i=y.length()-1;

while ( (i>=0) && (y.charAt(i)=='0') ) i ;

k=k+y.length()-i-1;

y=y.substring(0,i+1);

for ( i=0;i<x.length();i++){

temp=mul_2(x.charAt(i),y);

z=z+0;

z= add1(z,temp);

} for (i=0 ;i<k;i++) z=z+0;

if(z.length()==0)return "0";

if(z.charAt(0)=='0')z=z.substring(1);

return z;

}

//thuc hien phep nhan hai SO LON x,y

public BigNum mul(BigNum x,BigNum y){

return new

BigNum( (x.dau==y.dau)? 1:0 ,mul_1(x.val,y.val),(x.num+y.num) ); }

//kiem tra so tao boi day so x[d] x[k] co lon hon so y khong private boolean isGreat( int x[],int d,int k,int y[]){

boolean ok,kt;

int i,j,l=y.length-1;

if(k-d+1>l)ok=true;

else if((k-d+1<l)||(k>x.length) )ok=false;

else {ok=true;kt=true;i=d;j=1;

while(kt &&(i<=k) )

if(x[i]>y[j])kt=false;

else if(x[i]<y[j]) {kt=false;ok=false;}

else {i++;j++;}

} return ok;

}

//thuc hien phep chia hai SO NGUYEN xx,yy voi so

//chu so thap phan la n

public String divInt(String xx,String yy,int n){

Trang 12

int x[]=new int[xx.length()+1];

int y[]=new int[yy.length()+1];

String z=new String();

int lx=x.length;

int ly=y.length;

int i,j,d,k,t,p,po;

x[0]=0;

for (i=0;i<lx-1;i++) x[i+1]=xx.charAt(i)-48;

y[0]=0;

for (i=0;i<ly-1;i++) y[i+1]=yy.charAt(i)-48;

d=1;k=ly-1;

if (isGreat(x,d,k,y)==false)

if (k>lx-2 ){z=0+z;}

else k++;

k ;

do{ k++;t=0;

while( (x[d]==0)&&(d<k ) ) d++;

while(isGreat(x,d,k,y)){

p=0;j=ly-1;

for(i=k;i>=d;i ){

if(x[i]-y[j]-p<0) {x[i]=x[i]-y[j]-p+10;p=1;}

else {x[i]=x[i]-y[j]-p;p=0;}

j ;

}

if((x[d]==0)&&(d<lx) ) d++;

t++;//System.out.println("dk:"+d+k);

}//of while z=z+t;//System.out.println("t:"+t);

}//of do

while(isGreat(x,d,lx-1,y)&&(k<lx) ) ; while(k<lx-1){z=z+0;k++;}

while( (d<lx)&&(x[d]==0) ) d++;

//chia tiep phan du con lai

String st=new String();

for (;d<lx;d++){st=st+x[d];}

for (i=0;i<=n+ly;i++)st=st+0;

if(n!=0)

if (st.charAt(0)=='0'){ for (i=0;i<n;i++)z=z+0;}

else{

po=z.length();

lx=st.length()+1;

x=new int[lx];

x[0]=0;

for (i=0;i<lx-1;i++) x[i+1]=st.charAt(i)-48;

d=1;k=ly-1;

if ((isGreat(x,d,k,y)==false)&& (k<=lx-2 )) k++; k ;

do{ k++;t=0;

while( isGreat(x,d,k,y)&&(x[d]!=0) ){

p=0;j=ly-1;

for(i=k;i>=d;i ){

Trang 13

if(x[i]-y[j]-p<0) {x[i]=x[i]-y[j]-p+10;p=1;}

else {x[i]=x[i]-y[j]-p;p=0;}

j ;

}

if((x[d]==0)&&(d<lx) ) d++;

t++;

}//of while z=z+t;

if((x[d]==0)&&(d<lx) ) d++;

}//of do

while(isGreat(x,d,lx-1,y)&&(k<lx) ) ;

z=z.substring(0,po+n);

if (z.charAt(0)=='0') z=z.substring(1);

}//of else return z;

}

//thuc hien phep chia hai SO LON (x:y) voi so

//chu so thap phan la n

public BigNum divReal(BigNum x,BigNum y,int n){

String xx=new String(x.val);

String yy=new String(y.val);

if (x.num>y.num)

for (int i=0;i<x.num-y.num;i++)xx=xx+0;

else for (int i=0;i<y.num-x.num;i++)yy=yy+0;

return new BigNum( (x.dau==y.dau)? 1:0 ,divInt(xx,yy,n),n); }

//tinh luy thua a co so k

public BigNum exp(BigNum a,int k){

String x=new String("1");

String y=new String(a.val);

int i=k,j=k;

while (k>0){

// k chan if(k%2==1){

x=mul_1(x,y);

k ;}

else {

//k le y=mul_1(y,y);

k=k/2;}

} if((i%2==1)&&(a.dau==0)) i=0;

else i=1;

return new BigNum(i,x,a.num*j);

}

//kiem tra xx co chia het cho yy hay khong

public boolean chiaHet(String xx,String yy){

int x[]=new int[xx.length()+1];

int y[]=new int[yy.length()+1];

Ngày đăng: 20/06/2014, 10:52

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w