BÀI TẬP NHÓMCHỦ ĐỀ: CÀI ĐẶT THƯ VIỆN SỐ LỚN Tìm hiểu các thuật toán thực hiện trên số lớn như phép toán cộng, trừ, nhân, chia 2 số nguyên lớn.. LỜI MỞ ĐẦUTuy rằng trong cuộc sống và côn
Trang 1BÀI TẬP NHÓM
CHỦ ĐỀ: CÀI ĐẶT THƯ VIỆN SỐ LỚN
( Tìm hiểu các thuật toán thực hiện trên số lớn như phép toán
cộng, trừ, nhân, chia 2 số nguyên lớn).
Giáo viên: HÀ THỊ THANH
THÀNH VIÊN NHÓM
1 Vũ Văn Hậu
2 Lê Mạnh Cường,
3 Hoàng Trọng Quỳnh
4 Lê Văn Trường
5 Nguyễn Hoàng Chung
MỤC LỤC
Trang 2LỜI MỞ ĐẦU 3
Phần 1: 4
THAO TÁC CỘNG 4
1.1 THUẬT TOÁN 4
PHẦN 2 7
PHÉP TRỪ SỐ NGUYÊN LỚN 7
2.1 Giải thuật trừ: 7
2.2 Về chương trình 7
Phần 3 9
PHÉP NHÂN 2 SỐ LỚN 9
3.1 Giải thuật nhân: 9
3.2 Tương tự như phép cộng ta có đoạn chương trình sau: 9
Phần 4 11
PHÉP CHIA 2 SỐ LỚN 11
4.1 Giải thuật chia: 11
4.2 Về chương trình ta có như sau: 11
PHẦN 5 14
MỘT SỐ HÀM BỔ TRỢ 14
5.1 Hàm bỏ tất cả các số 0 ở đầu 1 chuỗi số nguyên 14
5.2 Hàm lấy về chuỗi số lơn hơn trong 2 chuỗi 14
5.3 Hàm chuyển số thập phân thành số nguyên 15
PHẦN 6 17
GIỚI THIỆU VỀ CHƯƠNG TRÌNH 17
1 Giao diện chính của trương trình 17
2 Cách sử dụng: 17
LỜI KẾT 18
Trang 3LỜI MỞ ĐẦU
Tuy rằng trong cuộc sống và công việc bình thường chúng ta chỉ có nhu cầu tính toán với các số đến hàng tỷ (9 chữ số) hay đến tỷ tỷ (18 chữ số) với phần thập phân cũng chỉ cần chính xác đến 4,5 số là đã nhiều, nhưng trong một số nghành khoa học đòi hỏi sự chính xác cao như khoa học nguyên tử, vũ trụ, thì nhu cầu tính toán với những con số lớn hơn nữa với phần thập phân có khi cần chính xác đến vài chục chữ số sau dấu phẩy là hoàn toàn cần thiết Hoặc đơn giản hơn nó chỉ
là một đề bài tập hay bài kiểm tra trong lớp học công nghệ thông tin của chúng ta.
Tư tưởng của thuật toán này là không sử dụng những kiểu dữ liệu số sẵn có của C#, vì tất cả các kiểu dữ liệu này đều bị giới hạn Thay vào đó, ta sử dụng kiểu
dữ liệu string, sử dụng các hàm xử lý chuỗi để nó có thể tính toán theo đúng những nguyên tắc cộng trừ nhân chia số học.
Nhóm chúng em sau một thời gian tìm hiểu và đã xây dựng một chương trình tính toán đơn giản các số nguyên lớn trên (C#) với nhiều chữ số Kết quả đã được test trên nhiều thiết bị máy tính khác, như máy tính của windows đều cho kết quả rất chính xác.
Trang 4Phần 1:
THAO TÁC CỘNG
1.1 THUẬT TOÁN
Thông thường, khi cộng hai số có nhiều chữ số, chúng ta thực hiện từ phải qua trái Với mỗi lần cộng, kết quả của nó còn được cộng với giá trị nhớ của lần trước đó và đồng thời cũng xác định giá trị nhớ mới Ở đây, vì các số được lưu theo chiều ngược lại nên chúng ta thực hiện từ trái qua phải, tức là từ đầu đến cuối xâu.
Giả sử chúng ta có một số nguyên (lớn) a=32145; sẽ có rất nhiều cách lưu trữ số nguyên này (do ta quy định), ở đây ta sẽ đưa ra cách lưu số này qua mảng 1 chiều ngược, cụ thể như sau: a[0]=5, a[1]=4, a[2]=1, a[3]=2 và a[4]=3; nhưng khi xuất thì lại xuất ngược lại so với cách lưu trữ!, số nguyên này có 5 chữ số, khi nhập (input) số này vào chương trình thì chúng ta sẽ dùng dấu khoảng trắng (space) để ngăn cách các chữ số và nhập bình thường theo thứ tự thông thường, ví dụ
a=32145 sẽ được nhập vào như sau: 3 2 1 4 5.
Vậy nếu cần thực hiện phép toán Cộng hai số a và b sau đó lưu vào mảng c (kết quả) thì ta đề xuất các bước (c=a+b)
1.2 CÁC BƯỚC THỰC HIỆN
- Bước 1: Loại bỏ các chữ số 0 vô nghĩa ở 2 mảng a và b.
- Bước 2: Thêm chữ số 0 ở đầu mảng có độ dài ngắn hơn để 2 mảng có cùng độ dài (tức là nếu a=1234, b=21 thì chúng ta thấy: mảng b có 2 phần tử; mảng a có 4 phần tử; vậy để hai mảng này có số phần tử bằng nhau thì chúng ta thêm 2 chữ số 0 nữa vào mảng b; lúc này b=0021).
- Dùng một biến nhớ, để lưu trữ số nhớ sau mỗi bước tính Ta lưu giá trị vào một mảng INT đã được khởi tạo có giá trị là 0
Bước 3: Đảo ngược chuỗi và tính toán
Bước 4: Xuất kết quả ta đảo ngược chuỗi kết quả.
Trang 5THEO CÁC BƯỚC Ở TRÊN TRƯƠNG TRÌNH THỰC HIỆN CỘNG SẼ
CÓ CODE NHƯ SAU:
//hàm cộng 2 số nguyên cực lớn
#region addInt()
public string addInt(string strNum1, string strNum2)
{
//B1:tìm ra chuối ngắn hơn và thêm số 0 vào chuối cho đu"
if(strNum1.Length > strNum2.Length)
strNum2 = strNum2.PadLeft(strNum1.Length, '0');
else if(strNum2.Length > strNum1.Length)
strNum1 = strNum1.PadLeft(strNum2.Length, '0');
//Bước 2: đưa vào ma"ng int int[], thực hiên chuyê"n đố"i các số vê' thành dạng int32 đê" tiện cho việc tính toán nêu số quá lớn
int[] arrNum1 = new int[strNum1.Length];
int[] arrNum2 = new int[strNum2.Length];
for(int i=0; i<strNum1.Length; i++)
{
arrNum1[i] = Convert.ToInt32(strNum1.Substring(i,1));
}
for(int i=0; i<strNum2.Length; i++)
{
arrNum2[i] = Convert.ToInt32(strNum2.Substring(i,1));
}
//Bước 3: Đa"o ngược chuối và tính toán
Array.Reverse(arrNum1);
Array.Reverse(arrNum2);
// Tính toán
int[] arrResult = new int[strNum1.Length+1];
int phanbu = 0;
for(int i=0; i<strNum1.Length; i++)
{
arrResult[i] = arrNum1[i] + arrNum2[i] + phanbu;
if(arrResult[i] >= 10)
{
arrResult[i] -= 10;
phanbu = 1;
}
else
phanbu = 0;
}
if(phanbu==1)
arrResult[arrResult.Length-1] = 1;
//Đa"o ngược chuối kêt qua" và hiê"n thị kêt qua" trên màn hình
Array.Reverse(arrResult);
Trang 6StringBuilder strbd = new StringBuilder(arrResult.Length);
foreach(int i in arrResult)
strbd.Append(i);
string strResult = "";
if(phanbu==1)
strResult = strbd.ToString();
else if(phanbu==0)
strResult = strbd.ToString().Substring(1);
return strResult;
}
#endregion
Trang 7PHẦN 2 PHÉP TRỪ SỐ NGUYÊN LỚN 2.1 Giải thuật trừ:
Giống như cộng, chúng ta cũng thực hiện từ phải qua trái Với mỗi lần trừ,
đề phòng chữ số của số bị trừ nhỏ hơn nên ta cứ vay tạm một chục Nếu lần trước
đã vay rồi thì lần này phải trừ đi Nhưng chỉ đáng vay nếu kết quả sau khi trừ là bé hơn 10, còn lớn hơn 10 là không phải vay Dưới đây ta xem rằng a >= b.
2.2 Về chương trình.
Vì là phép trừ nên khi tính toán ta cần phải nhận biết số trừ và số bị trừ, sẽ có một hàm so sánh như sau:
//1 Tim so lon hon
for(int i=0; i<strNum1.Length; i++)
{
if(arrNum1[i] > arrNum2[i])
{
strNumBig = strNum1;
break;
}
else if(arrNum1[i] < arrNum2[i])
{
strNumBig = strNum2;
break;
}
}
if(strNumBig == "")
{
txtResult.Text = "0"
else //neu 2 so ko bang nhau
{
if(strNumBig == strNum1)
strNumSmall = strNum2;
else
strNumSmall = strNum1;
Trang 8Sau đó đưa phần các phần tử vào mảng INT[]
//Dua vao mang int[]
int[] arrNumBig = new int[strNumBig.Length];
int[] arrNumSmall = new int[strNumSmall.Length];
for(int i=0; i<strNumBig.Length; i++)
{
arrNumBig[i] = Convert.ToInt32(strNumBig.Substring(i,1)); }
for(int i=0; i<strNumSmall.Length; i++)
{
arrNumSmall[i] = Convert.ToInt32(strNumSmall.Substring(i,1)); }
Cuối cùng là đảo ngược chuỗi , tính toán và cho kết quả ra màn hình:
//Dao nguoc chuoi
Array.Reverse(arrNumBig);
Array.Reverse(arrNumSmall);
//so mu~ cua 10 chinh la chi so phan tu trong mang int[]
int[] arrResult = new int[strNumBig.Length];
int phanbu = 0;
for(int i=0; i<strNumBig.Length; i++)
{
arrNumSmall[i] = arrNumSmall[i] + phanbu;
if(arrNumBig[i] >= arrNumSmall[i])
{
arrResult[i] = arrNumBig[i] - arrNumSmall[i];
phanbu = 0;
}
else
{
arrResult[i] = arrNumBig[i]+10 - arrNumSmall[i];
phanbu = 1;
}
}
//Dao nguoc chuoi Result
Array.Reverse(arrResult);
StringBuilder strbd = new StringBuilder(arrResult.Length);
foreach(int i in arrResult)
strbd.Append(i);
Trang 9Phần 3 PHÉP NHÂN 2 SỐ LỚN 3.1 Giải thuật nhân:
Thông thường, khi nhân a với b, chúng ta thực hiện từ phải qua trái Mỗi lần
sẽ nhân một chữ số của b với số a và đặt kết quả dịch sang trái 1 chữ số Nhưng trong mỗi lần đó chúng ta lại lần lượt nhân từng chữ số của a với chữ số nói trên của b Cũng như phép cộng, kết quả cũng phụ thuộc việc nhớ của lần nhân trước và xác định giá trị nhớ mới Việc nhận này được thực hiện từ trái qua phải đó.
3.2 Tương tự như phép cộng ta có đoạn chương trình sau:
// nhập các số đê" tiên hành nhân
#region Phep nhan
private void btnMul_Click(object sender, System.EventArgs e)
{
if(!checkInput())
{
MessageBox.Show("Input Number is not valid");
txtNumber1.Focus();
}
else
{
//Phep nhan 2 so nguyen
string strNum1 = strTempN1[0];
string strNum2 = strTempN2[0];
//Dua vao mang int[]
int[] arrNum2 = new int[strNum2.Length];
for(int i=0; i<strNum2.Length; i++)
{
arrNum2[i] = Convert.ToInt32(strNum2.Substring(i,1));
}
//mang string luu tru cac so hang
string[] arrstr = new string[strNum2.Length];
int x = 0;
for(int i=strNum2.Length-1; i>=0; i )
{
arrstr[x] = Mul(strNum1, arrNum2[i]);
//them so 0 o cuoi chuoi
arrstr[x] = arrstr[x].PadRight(arrstr[x].Length + x, '0');
Trang 10}
//cong cac so hang nguyen bang ham addInt() voi nhau
string strResult = "";
for(int i=0; i<arrstr.Length; i++)
{
strResult = addInt(strResult, arrstr[i]);
}
}
#endregion
Trang 11Phần 4 PHÉP CHIA 2 SỐ LỚN 4.1 Giải thuật chia:
Thông thường, khi chia a với b, chúng ta thực hiện từ trái qua phải Lần đầu tiên lấy nhóm có số chữ số bằng số chữ số của b, các lần sau đó chỉ lần lượt hạ một
số xuống phần dư Thay vì nhẩm xem được mấy lần, ở đây chỉ việc thử lần lượt từ
0 đến có thể Vì chúng ta lưu ngược nên ta thực hiện từ phải qua trái Hàm sau thực hiện chia a cho b, đặt kết quả vào c và phần dư.
4.2 Về chương trình ta có như sau:
//Phep chia - btnDiv_Click
#region Phep chia
private void btnDiv_Click(object sender, System.EventArgs e)
{
if(!checkInput())
{
MessageBox.Show("Input Number is not valid");
txtNumber1.Focus();
}
else
{
string strNum1 = txtNumber1.Text;
string strNum2 = txtNumber2.Text;
// Thuc hien phep chia nhu chia 2 so nguyen
-if(strNum1 == strNum2)
{
txtResult.Text = "1";
}
else //khi 2 so ko bang nhau
{
string strResult = "";
//neu so bi chia (strNum1) lon hon
if(NumBigger(strNum1, strNum2) == strNum1)
{
string strtemp = strNum1.Substring(0, strNum2.Length);
Trang 12string strBookmark = strtemp;
string phandu="";
bool flag = true;
while(phandu != "0" || strtemp != "00")
{
//ket qua co toi da 80 chu so (tinh ca dau )
//result: max 80 char
if(strResult.Length == 80)
break;
string[] arrKetqua = DivInt(strtemp, strNum2).Split(
new char[]{'~'});
strResult += arrKetqua[0];
phandu = arrKetqua[1];
// MessageBox.Show("~"+arrKetqua[0]+"~"+arrKetqua[1]+"~");
if(strBookmark.Length < strNum1.Length) //van con so de ha
{
strBookmark = strNum1.Substring(0, strBookmark.Length+1);
//ha so xuong de tiep tuc chia
strtemp = phandu + strNum1.Substring(strBookmark.Length-1,1);
if(strtemp.IndexOf('0')==0) //Ex: 0.34/0.03
strtemp = strtemp.Substring(1);
}
else if(strBookmark.Length == strNum1.Length) //het so de ha
{
if(flag == true)
{
strResult += ".";
flag = false;
}
//them 0 vao sau phan du de tiep tuc chia
strtemp = phandu + "0";
//neu da them 0 ma strtemp van nho hon so bi chia
if(NumBigger(strtemp, strNum2) == strNum2)
{
// strResult += "0";
strtemp = phandu + "0";
}
//neu so chia lon hon
else if(NumBigger(strNum1, strNum2) == strNum2)
{
strResult = "0.0";
while(NumBigger(strNum1, strNum2) == strNum2)
{
strNum1 += "0";
strResult += "0";
}
bool flag = true;
if(strNum1.Length > strNum2.Length)
Trang 13flag = false;
}
strResult = strResult.Substring(0, strResult.Length-2);
//Thuc hien tuong tu phan tren ===============
string strtemp = strNum1.Substring(0, strNum2.Length);
string strBookmark = strtemp;
string phandu="";
while(phandu != "0")
{
//ket qua co toi da 40 chu so (tinh ca dau )
if(strResult.Length == 40)
break;
string[] arrKetqua = DivInt(strtemp, strNum2).Split(
new char[]{'~'});
strResult += arrKetqua[0];
phandu = arrKetqua[1];
if(strBookmark.Length < strNum1.Length) //van con so de ha
{
strBookmark = strNum1.Substring(0, strBookmark.Length+1);
//ha so xuong de tiep tuc chia
strtemp = phandu + strNum1.Substring(strBookmark.Length-1,1); }
else if(strBookmark.Length == strNum1.Length) //het so de ha
{
//them 0 vao sau phan du de tiep tuc chia
strtemp = phandu + "0";
//neu da them 0 ma strtemp van nho hon so bi chia
if(NumBigger(strtemp, strNum2) == strNum2)
{
// strResult += "0";
strtemp = phandu + "0";
}
}
if(flag == false)
{
strResult = "0." + strResult.Substring(3);
}
}
//neu co dau o cuoi thi bo di
if(strResult.IndexOf('.') == strResult.Length-1)
strResult = strResult.Substring(0, strResult.Length-1);
txtResult.Text = strResult;
// txtResult.Text = "~"+strNum1 +"~"+ strNum2 +"~" + strResult;
}
#endregion
Trang 15PHẦN 5 MỘT SỐ HÀM BỔ TRỢ
Trong quá trình tính toán các số lớn khá phức tạp, vậy để đơn giản hơn trong quá trình tính toán, ta kết hợp một số hàm bổ trợ để trong những chương trình con chúng ta có thể sử dụng chúng để tính toán.
5.1 Hàm bỏ tất cả các số 0 ở đầu 1 chuỗi số nguyên.
//Ham bo tat ca cac so 0 o dau 1 chuoi so nguyen
#region MyRemove()
public string MyRemove(string strnum)
{
char[] ch = strnum.ToCharArray();
int n=0;
for(int i=0;i<ch.Length; i++)
{
if(ch[i] == '0')
n++;
else
break;
}
return strnum.Substring(n);
}
#endregion
5.2 Hàm lấy về chuỗi số lớn hơn trong 2 chuỗi
//Ham lay ve chuoi so lon hon trong 2 chuoi, = nhau thi tra ve ""
#region NumBigger() - so nguyen
private string NumBigger(string strNum1, string strNum2)
{
string strNumBig = "";
if(strNum1.Length > strNum2.Length)
{
strNumBig = strNum1;
strNum2 = strNum2.PadLeft(strNum1.Length, '0');
}
else if(strNum2.Length > strNum1.Length)
{
strNumBig = strNum2;
strNum1 = strNum1.PadLeft(strNum2.Length, '0');
Trang 16else if(strNum1.Length == strNum2.Length)
{
int[] arrNum1 = new int[strNum1.Length];
int[] arrNum2 = new int[strNum2.Length];
for(int i=0; i<strNum1.Length; i++)
{
arrNum1[i] = Convert.ToInt32(strNum1.Substring(i,1));
}
for(int i=0; i<strNum2.Length; i++)
{
arrNum2[i] = Convert.ToInt32(strNum2.Substring(i,1));
}
for(int i=0; i<strNum1.Length; i++)
{
if(arrNum1[i] > arrNum2[i])
{
strNumBig = strNum1;
break;
}
else if(arrNum1[i] < arrNum2[i])
{
strNumBig = strNum2;
break;
}
}
}
return strNumBig;
}
#endregion
5.3 Hàm chuyển số thập phân thành số nguyên
//ham chuyen so thap phan thanh so nguyen
//va lay ve vi tri dau tu ben phai sang
#region myConvert()
public string myConvert(string strnum)
{
string numpos = ""; //return dang 645437~4 hoac 849~0 (so~vitridau.)
if(strnum.IndexOf('.') < 0)
numpos = strnum + "~0";
else if(strnum.IndexOf('.') > 0)
{
string temp1 = strnum.Substring(0, strnum.IndexOf('.'));
string temp2 = strnum.Substring(strnum.IndexOf('.')+1);
numpos = temp1 + temp2 + "~" + temp2.Length.ToString();
}
Trang 17return numpos;
}
#endregion
5.4 Hàm kiểm tra đầu vào.
#region kiem_tra_dau_vao()
public bool kiemtradauvao()
{
bool flag = true ;
string strNum1 = txtNumber1.Text;
string strNum2 = txtNumber2.Text;
Regex regexDou1 = new Regex ( @"^[1-9]+[0-9]*\.{0,1}[0-9]*[1-9]+$" );
Regex regexDou2 = new Regex ( @"^0{1}\.[0-9]*[1-9]+$" );
Regex regexInt = new Regex ( @"^[1-9]{1}[0-9]*$" );
Match m1 = regexInt.Match(strNum1);
Match m2 = regexInt.Match(strNum2);
Match m3 = regexDou1.Match(strNum1);
Match m4 = regexDou1.Match(strNum2);
Match m5 = regexDou2.Match(strNum1);
Match m6 = regexDou2.Match(strNum2);
if ((!m1.Success && !m3.Success && !m5.Success) ||
(!m2.Success && !m4.Success && !m6.Success))
flag = false ;
return flag;
}
#endregion