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

Đồ án giải thuật và lập trình

20 824 2

Đ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 20
Dung lượng 914,88 KB

Nội dung

Làm thế nào để chọn được thuật toán tốt nhất, thông thường căn cứ theo các tiêu chuẩn sau: 1. Giải thuật đúng đắn 2. Giải thuật đơn giản 3. Giải thuật thực hiện nhanh Áp dụng những kiến thức về vòng lặp, mảng ... chúng em đã xây dựng được thuật toán giải quyết bài toán Biểu diễn số nguyên lớn. Bài toán được thực hiện bằng ngôn ngữ C sharp để thiết lập thêm giao diện nhằm thuận lợi cho việc sử dụng.

Trang 1

LỜI NÓI ĐẦU

Phân tích và thiết kế giải thuật là một môn học cơ sở ngành rất quan trọng đối với sinh viên công nghệ thông tin Có thể xem đây là nền tảng của lập trình Đối với một bài toán nào đó, chúng

ta có thể có nhiều giải thuật khác nhau Vấn đề đặt ra là làm thế nào để chọn được thuật toán tốt nhất, thông thường căn cứ theo các tiêu chuẩn sau:

1 Giải thuật đúng đắn

2 Giải thuật đơn giản

3 Giải thuật thực hiện nhanh

Làm thế nào để xây dựng một thuật toán tối ưu nhất, cách tổ chức cơ sở dữ liệu hợp lí và cung cấp kiến thức cho sinh viên về tác động của giải thuật với dữ liệu Đó chính là mục đích của môn học này

Với đồ án môn học này, mục đích của chúng em là củng cố thêm kiến thức của mình về việc thiết kế thuật toán sao cho hợp lí, đánh giá độ phức tạp của thuật toán một cách chính xác và hơn hết là có thể xây dựng một chương trình theo yêu cầu sao cho tối ưu nhất

Áp dụng những kiến thức về vòng lặp, mảng chúng em đã xây dựng được thuật toán giải quyết bài toán Biểu diễn số nguyên lớn Bài toán được thực hiện bằng ngôn ngữ C sharp để thiết lập thêm giao diện nhằm thuận lợi cho việc sử dụng

Dưới sự hướng dẫn nhiệt tình của thầy Lê Quý Lộc, chúng em đã hoàn thành tốt nhất đồ án môn học Phân tích và thiết kế giải thuật Chúng em mong sẽ nhận được sự quan tâm và góp ý từ phía thầy để bài làm của chúng em được hoàn thiện

Trang 2

ĐỀ BÀI

Số nguyên lớn là một mảng các chữ số Yêu cầu:

 Xây dựng các hàm tiện ích để tính toán trên số lớn: tăng 1 số 1 đơn vị, tổng hai số, tích hai số

Viết chương trình minh họa việc sử dụng các hàm này Các số có không quá n chữ

số Đánh giá độ phức tạp của các hàm theo n

Viết chương trình nhập n và in ra số Fibonacci thứ n (n >1000)

Trang 3

MỤC LỤC

LỜI NÓI ĐẦU 1

ĐỀ BÀI 2

MỤC LỤC 3

I GIỚI THIỆU 4

II BIỂU DIỄN DỮ LIỆU VÀO RA 4

III THUẬT TOÁN CỘNG 5

1 Thuật toán cộng 2 số 5

2 Thuật toán cộng 1 số thêm 1 đơn vị 6

3 Cài đặt 6

4 Đánh giá 7

IV THUẬT TOÁN TRỪ 7

1 Thuật toán trừ 2 số 7

2 Thuật toán trừ 1 số đi 1 đơn vị 8

3 Cài đặt 8

4 Đánh giá 9

V THUẬT TOÁN NHÂN 10

1 Thuật toán nhân 10

2 Cài đặt 10

3 Đánh giá 11

VI THUẬT TOÁN CHIA 12

1 Thuật toán chia 12

2 Cài đặt 12

3 Đánh giá 15

VII THUẬT TOÁN TÍNH SỐ FIBONACCI THỨ N 16

1 Thuật toán 16

2 Cài đặt 17

3 Đánh giá 17

KẾT LUẬN 19

CÁC NGUỒN THAM KHẢO 20

Trang 4

I GIỚI THIỆU

Trong quá trình lập trình chắc có lẽ chúng ta cũng đã gặp một số trường hợp số xử lí quá lớn

mà các kiểu double hay long double cũng không lưu trữ được

Đa phần các phương pháp đều tập trung vào chia nhỏ số lớn ra để xử lí, sau đó nối các phần lại với nhau theo thứ tự để hoàn thiện Tuy nhiên, có một cách đơn giản hơn và khá hiệu quả, đó

là xử lí số lớn bằng mảng Coi số lớn như một mảng các chữ số, việc tính toán trên số lớn chính

là tính toán trên các phần tử của mảng Sau đó thực hiện cộng các phần tử của mảng tương ứng

để được mảng mới hiện thị ra màn hình

Chẳng hạn, muốn biểu diễn số 1 tỷ thay vì viết 1.000.000.000 thì ta thể viết thành 109 Việc làm như vậy giúp chúng ta dễ đọc hiểu hơn, tránh được tình trạng thiếu sót, thừa chữ số

Ứng dụng trong thiên văn, mã hóa, máy tính (số bits trên đĩa cứng) …

II BIỂU DIỄN DỮ LIỆU VÀO RA

Dữ liệu được nhập vào từ bàn phím thông qua 1 inputbox, chương trình có giao diện như

dưới đây:

Hình 1: nhập dữ liệu vào chương trình

Dữ liệu ra được xuất trên màn hình thông qua 1 textbox bên phải cửa sổ như sau:

Trang 5

Hình 2: dữ liệu được hiển thị thông qua textbox

III THUẬT TOÁN CỘNG

1 Thuật toán cộng 2 số

Đầu vào: bi1 và bi2 là 2 số cần tính tổng

Đầu ra: result là kết quả của phép cộng bi1 và bi2

Xử lý:

 Bước 1:

o So sánh độ dài của bi1 và bi2 Giả sử bi1 có độ dài lớn hơn bi2

o Khởi tạo result chứa kết quả với độ dài của result bằng độ dài bi1 + 1

 Bước 2:

o Khởi tạo biến carry = 0

o Cộng lần lượt các chữ số của bi1, bi2 và carry từ hàng đơn vị lên For i = 0 to result.length - 1 do

result.data[i] = bi1.data[i] + bi2.data[i] + carry

if result.data[i] > 9 then

carry = 1 result.data[i] -= 10

Trang 6

else

carry = 0 endif

endfor

 Bước 3: Tính toán lại độ dài của result cho phù hợp và kết thúc thuật toán

Ví dụ: 1234567891011121314151617181920 + 98765432123456789 = 1234567891011220079583740638709

2 Thuật toán cộng 1 số thêm 1 đơn vị

Đầu vào: bi là số cần tăng lên 1 đơn vị

Đầu ra: bi là số đã được tăng lên 1 đơn vị

Xử lý:

 Bước 1: khởi tạo carry = 1 và tăng chiều dài bi lên 1

 Bước 2: cộng carry vào bi

for i = 0 to bi.length – 1 do

bi.data[i] += bi.data[i] + carry

if bi.data[i] > 9 then

carry = 1 bi.data[i] -= 10 else

break endif

endfor

 Bước 3: tính toán lại độ dài của bi cho phù hợp và kết thúc thuật toán

Ví dụ: 1234567891011121314151617181920++ = 1234567891011121314151617181921

3 Cài đặt

Thuật toán cộng 2 số:

BigInt result = new BigInt();

BigInt own = bi1 length > bi2 length ? bi1 : bi2 ;

result length = own length ;

if ( result length < result Capacity ) result length ++;

byte carry = 0;

for ( int = 0; i < result length ; i ++)

{

result data [ ] = ( byte )( bi1 data [ ] + bi2 data [ ] + carry );

if ( result data [ ] > 9)

{

carry = 1;

result data [ ] -= 10;

}

else

{

carry = 0;

}

}

if ( carry > 0) throw new OverflowException();

result AdjustLength ();

Trang 7

Thuật toán tăng 1 số lên 1 đơn vị:

if ( bi length < bi Capacity ) bi length ++;

byte carry = 1;

for ( int = 0; i < bi length ; i ++)

{

bi data [ ] = ( byte )( bi data [ ] + carry );

if ( bi data [ ] > 9)

{

carry = 1;

bi data [ ] -= 10;

}

else

{

carry = 0;

break ;

}

}

if ( carry > 0) throw new OverflowException();

bi AdjustLength ();

4 Đánh giá

Thuật toán cộng ở trên là thuật toán tối ưu Độ phức tạp của nó là hàm bậc nhất theo n và

phụ thuộc vào chiều dài(số lượng chữ số) lớn nhất của trong 2 số tham gia vào phép cộng

Trong mọi trường hợp số lần lặp luôn là n + 1 với n là chiều dài lớn nhất của 2 số tham gia vào phép cộng vì thế nên độ phức tạp luôn là n

Thuật toán tăng 1 số lên 1 đơn vị cũng tương tự thuật toán cộng với độ phức tạp là hàm

bậc nhất theo n với n là chiều dài của số cần tăng lên 1 đơn vị

Ưu điểm:

 Dễ cài đặt

 Thực hiện nhanh

 Đơn giản

Nhược điểm: vì xử lý trên mảng tĩnh nên bị giới hạn kích thước bộ nhớ, nghĩa là với đầu vào quá lớn và cài đặt tổng kích thước bộ nhớ cho phép của mảng không đáp ứng được thì sẽ

dễ bị tràn dữ liệu

IV THUẬT TOÁN TRỪ

1 Thuật toán trừ 2 số

Đầu vào: bi1 và bi2 lần lược là số bị trừ và số trừ

Đầu ra: result chứa kết quả của phép trừ bi1 và bi2

Xử lý:

 Bước 1: cài đặt biến carry = 0, đặt chiều dài của result bằng chiều dài lớn nhất của

1 trong 2 số bi1 hoặc bi2

Trang 8

 Bước 2: trừ lần lược các chữ số của bi1, bi2 và carry từ hàng đơn vị lên

for i = 0 to result.length - 1 do

result.data[i] = bi1.data[i] – bi2.data[i] – carry

if result.data[i] < 0 then

carry = 1 result.data[i] += 10 else

carry = 0 endif

endfor

 Bước 3: tính toán lại độ dài của result cho phù hợp và kết thúc thuật toán

Ví dụ: 1234567891011121314151617181920 - 98765432123456789 = 1234567891011022548719493725131

2 Thuật toán trừ 1 số đi 1 đơn vị

Đầu vào: số bi là số cần giảm đi 1 đơn vị

Đầu ra: số bi là số đã giảm đi 1 đơn vị

Xử lý:

 Bước 1: cài đặt biến carry = 1

 Bước 2: trừ số bi cho biến carry

for i = 0 to bi.length - 1 do

bi.data[i] -= carry

if bi.data[i] < 0 then

carry = 1 bi.data[i] -= 10;

else

break endif

endfor

 Bước 3: tính toán lại độ dài của số bi cho phù hợp và kết thúc thuật toán

Ví dụ: 1234567891011121314151617181920 = 1234567891011121314151617181919

3 Cài đặt

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

BigInt result = new BigInt();

byte [] temp1 , temp2 ;

int comp = bi1 CompareTo ( bi2 , true );

if ( comp == 0)

{

result data [0] = 0;

result length = 1;

return result ;

}

else if ( comp == 1)

{

temp1 = bi1 data ;

Trang 9

temp2 = bi2 data ;

result length = bi1 length ;

}

else

{

temp1 = bi2 data ;

temp2 = bi1 data ;

result length = bi2 length ;

}

sbyte temp , carry = 0;

for ( int = 0; i < result length ; i ++)

{

temp = ( sbyte )( temp1 [ ] - temp2 [ ] - carry );

if ( temp < 0)

{

carry = 1;

result data [ ] = ( byte )( temp + 10);

}

else

{

carry = 0;

result data [ ] = ( byte ) temp ;

}

}

result AdjustLength ();

Thuật toán trừ 1 số đi 1 đơn vị:

byte carry = 1;

sbyte temp ;

for ( int = 0; i < bi length ; i ++)

{

temp = ( sbyte )( bi data [ ] - carry );

if ( temp < 0)

{

carry = 1;

bi data [ ] = ( byte )( temp + 10);

}

else

{

bi data [ ] = ( byte ) temp ;

break ;

}

}

bi AdjustLength ();

4 Đánh giá

Thuật toán trừ ở trên là thuật toán tối ưu Độ phức tạp của nó là hàm bậc nhất theo n và

phụ thuộc vào chiều dài (số lượng chữ số) lớn nhất của trong 2 số tham gia vào phép trừ Trong

mọi trường hợp số lần lặp luôn là n với n là chiều dài lớn nhất của 2 số tham gia vào phép trừ

vì thế nên độ phức tạp luôn là n

Trang 10

Thuật toán giảm 1 số đi 1 đơn vị cũng tương tự thuật toán trừ với độ phức tạp là hàm bậc

nhất theo n với n là chiều dài của số cần giảm đi 1 đơn vị

Ưu điểm:

 Dễ cài đặt

 Thực hiện nhanh

 Đơn giản

V THUẬT TOÁN NHÂN

1 Thuật toán nhân

Đầu vào: số bi1 và số bi2

Đầu ra: result là kết quả phép nhân giữa bi1 và bi2

Xử lý:

 Bước 1: kiểm tra xem nếu 1 trong 2 số bằng 0 thì gán result bằng 0 và kết thúc thuật toán

 Bước 2: khởi tạo 2 biến temp1 và temp2, với temp1 tham chiếu đến số có độ dài lớn hơn, temp2 tham chiếu đến số có độ dài nhỏ hơn trong 2 số bi1 và bi2 Khởi tạo result để lưu kết quả phép nhân Khởi tạo biến k = 0

 Bước 3: thực hiện nhân lần lượt từng chữ số của temp2 cho temp1

for i = 0 to temp2.length – 1 do

if temp2.data[i] = 0 then

continue endif

carry = 0

k = i for j = 0 to temp1.length – 1 do

temp = temp1.data[j] * temp2.data[i] + result.data[k] + carry carry = temp / 10

result.data[k] = temp % 10

k += 1 endfor

if carry > 0 then

result.data[i + temp1.length] = carry endif

endfor

 Bước 4: tính toán lại độ dài của result cho phù hợp và kết thúc thuật toán

Ví dụ: 1234567891011121314151617181920 * 98765432123456789

= 121932631241458101023327185205570305877072054880

2 Cài đặt

Thuật toán nhân 2 số:

Trang 11

BigInt result = new BigInt();

if ( bi1 length == 1

&& bi1 data [0] == 0 || bi2 length == 1 && bi2 data [0] == 0) {

result length = 1;

result data [0] = 0;

return result ;

}

BigInt temp1 , temp2;

byte temp , carry ;

if ( bi1 length > bi2 length )

{

temp1 = bi1 ;

temp2 = bi2 ;

}

else

{

temp1 = bi2 ;

temp2 = bi1 ;

}

for ( int = 0; i < temp2 length ; i ++)

{

if ( temp2 data [ ] == 0) continue ;

carry = 0;

for ( int = 0, k = i ; j < temp1 length ; j ++, k ++)

{

temp = ( byte )( temp1 data [ ] * temp2 data [ ] + result data [ ] + carry ); carry = ( byte )( temp / 10);

result data [ ] = ( byte )( temp % 10);

}

if ( carry > 0)

{

if ( i + temp1 length >= result Capacity ) throw new OverflowException(); result data [ + temp1 length ] = carry ;

}

}

result length = bi1 length + bi2 length ;

result AdjustLength ();

3 Đánh giá

Thuật toán nhân trên sẽ tiến hành nhân lần lượt từng chữ số của số có ít chữ số cho số có nhiều chữ số do đó sẽ cần đến 2 vòng lặp lồng nhau để thực hiện Trong trường hợp xấu nhất, tất cả các số của số ít chữ số hơn đều khác 0 thì độ phức tạp của thuật toán là hàm bậc 2 theo

độ dài của số nhân Trong trường hợp tốt nhất, một trong 2 số bằng 0 thì độ phức tạp chỉ bằng

1

Ưu điểm:

 Đơn giản để cài đặt

 Hiệu quả và thực hiện nhanh

Trang 12

Nhược điểm: giống như phép cộng, do kích thước mảng bị cố định nên với những dữ liệu quá lớn và vượt quá khả năng lưu trữ của mảng sẽ gây tràn dữ liệu

VI THUẬT TOÁN CHIA

1 Thuật toán chia

Đầu vào: 2 số bi1 và bi2 lần lược là số bị chia và số chia

Đầu ra: result là kết quả phép chia bi1 cho bi2

Xử lý:

 Thuật toán 1: thuật toán chia đơn giản, ý tưởng của thuật toán này là lấy số bị chia trừ dần cho số chia và sau mỗi lần trừ ta tăng biến đếm lên 1 cho đến khi số bị chia nhỏ hơn số chia thì dừng lại Kết quả của biến đếm sẽ là thương số cần tìm

 Bước 1: kiếm tra các trường hợp đặt biệt và các trường hợp lỗi

o Nếu bi2 = 0 thì thông báo lỗi chia cho 0

o Nếu bi1 = 0 thì gán result bằng 0 và kết thúc thuật toán

o Nếu bi1 = bi2 thì gán result bằng 1 và kết thúc thuật toán

o Nếu bi2 = 1 thì gán result bằng bi1 và kết thúc thuật toán

 Bước 2: khởi tạo biến temp và sao chép dữ liệu của bi1 vào temp

 Bước 3: temp = temp – bi2

 Bước 4: nếu temp còn lớn hơn hoặc bằng bi2 thì tăng biến đếm lên 1 và quay lại bước 3

 Bước 5: gán biến đếm cho result và kết thúc thuật toán

 Thuật toán 2: thuật toán chia phân đoạn, ý tưởng của thuật toán này là ta sẽ chia

số bị chia thành các phần (part) có độ dài bằng số chia và tiến hành chia nó cho số

chia bằng thuật toán 1

 Bước 1: kiếm tra các trường hợp đặc biệt và các trường hợp lỗi

o Nếu bi2 = 0 thì thông báo lỗi chia cho 0

o Nếu bi1 = 0 thì gán result bằng 0 và kết thúc thuật toán

o Nếu bi1 = bi2 thì gán result bằng 1 và kết thúc thuật toán

o Nếu bi2 = 1 thì gán result bằng bi1 và kết thúc thuật toán

 Bước 2: lấy 1 phần của số bị chia (part) có độ dài bằng độ dài số chia và

lấy từ bên trái cùng sang Thực hiện phép chia part cho bi2 bằng thuật

toán 1 Phần dư sẽ được lưu trở lại part, kết quả phép chia sẽ có giá trị từ 0

đến 9 và được lưu vào result

 Bước 3: kiếm tra nếu part không phải là bên phải cùng thì dịch part sang phải 1 chữ số và quay lại bước 2

 Bước 4: tính toán lại độ dài của result và kết thúc thuật toán

Ví dụ: 1234567891011121314151617181920 / 98765432123456789 = 12499999893362

2 Cài đặt

Các hàm hỗ trợ cho 2 thuật toán:

private static BigInt check_div (BigInt bi1 , BigInt bi2 )

Ngày đăng: 20/07/2015, 23:40

TỪ KHÓA LIÊN QUAN

w