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

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

Đ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

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.

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. ĐỀ 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). 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 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 10 9 . 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: 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 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 i = 0; i < result.length; i++) { result.data[i] = (byte)(bi1.data[i] + bi2.data[i] + carry); if (result.data[i] > 9) { carry = 1; result.data[i] -= 10; } else { carry = 0; } } if (carry > 0) throw new OverflowException(); result.AdjustLength(); 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 i = 0; i < bi.length; i++) { bi.data[i] = (byte)(bi.data[i] + carry); if (bi.data[i] > 9) { carry = 1; bi.data[i] -= 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.  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; temp2 = bi2.data; result.length = bi1.length; } else { temp1 = bi2.data; temp2 = bi1.data; result.length = bi2.length; } sbyte temp, carry = 0; for (int i = 0; i < result.length; i++) { temp = (sbyte)(temp1[i] - temp2[i] - carry); if (temp < 0) { carry = 1; result.data[i] = (byte)(temp + 10); } else { carry = 0; result.data[i] = (byte)temp; } } result.AdjustLength(); Thuật toán trừ 1 số đi 1 đơn vị: byte carry = 1; sbyte temp; for (int i = 0; i < bi.length; i++) { temp = (sbyte)(bi.data[i] - carry); if (temp < 0) { carry = 1; bi.data[i] = (byte)(temp + 10); } else { bi.data[i] = (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. 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ố: [...]... chia là 1000000000 SO SÁNH THUẬT TOÁN 1 VÀ THUẬT TOÁN 2 100000000 90000000 88888897 80000000 SỐ LẦN LẶP 70000000 60000000 50000000 Thuật toán 1 40000000 Thuật toán 2 30000000 20000000 10000000 0 8888896 11 8893 1000000 888895 11 10000 11 1000 11 100 SỐ CHIA Hình 3: đồ thị so sánh số lần lặp giữa thuật toán 1 và thuật toán 2 VII THUẬT TOÁN TÍNH SỐ FIBONACCI THỨ N 1 Thuật toán Đầu vào: số n Đầu ra: số... 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ị... 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... chia Ưu điểm: với thuật toán 1 thì ưu điểm lớn nhất của nó là đơn giản và dễ cài đặt, còn đối với thuật toán 2 thì ưu điểm của nó lại là tốc độ thực thi Nhược điểm: thuật toán 1 có nhược điểm là tốc độ thực thi chậm, với thuật toán 2 thì lại khó khăn trong cài đặt Dưới đây là đồ thị so sánh số lần lặp để hoàn tất 1 phép chia với các đầu vào khác nhau giữa 2 thuật toán chia ở trên, trong đồ thị bên dưới... Hiệu quả và thực hiện nhanh 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à... của các thuật toán trên đều phụ thuộc vào độ dài của số nhập vào hoặc có thể phụ thuộc vào giá trị của nó Trong thực tế, các thuật toán cộng, trừ và nhân có số lần lặp khá ổn định với những dữ liệu đầu vào khác nhau, tuy vậy thuật toán chia lại có số lần lặp khá biến thiên với cùng 1 với cùng 1 bộ dữ liệu đầu vào nhưng tổng thể thì số lần lặp vẫn tăng theo chiều dài chuỗi số đầu vào Bên dưới là đồ thị... quotient.AdjustLength(); 3 Đánh giá Thuật toán 1 là thuật toán khá đơn giản và dễ hiểu nhưng cần số lần lặp rất lớn nếu số lượng chữ số của số bị chia lớn hơn nhiều so với số lượng chữ số của số chia Trong trường hợp xấu nhất độ phức tạp của thuật toán 1 là hàm bậc nhất theo giá trị của số bị chia Ngược lại, thuật toán 2 tuy khá dễ hiểu nhưng lại phức tạp trong cài đặt nhưng tốc độ thực hiện của thuật toán này lại... 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... độ 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... so với thuật toán 1 Thuật toán 2 dựa trên việc chia nhỏ số bị chia thành các phần và thực hiện chia các phần đó cho số chia Trong trường hợp xấu nhất ta cũng chỉ cần chia số bị chia làm n phần với n là chiều dài của số bị chia, đối với việc chia các phần của số bị chia cho số chia trong trường hợp xấu nhất cũng chỉ cần thực hiện 9*n’ với n’ là chiều dài của số bị chia Độ phức tạp của thuật toán này . khác nhau giữa 2 thuật toán chia ở trên, trong đồ thị bên dưới thì số bị chia là 1000000000. Hình 3: đồ thị so sánh số lần lặp giữa thuật toán 1 và thuật toán 2 VII. THUẬT TOÁN TÍNH SỐ FIBONACCI. LẦN LẶP SỐ CHIA SO SÁNH THUẬT TOÁN 1 VÀ THUẬT TOÁN 2 Thuật toán 1 Thuật toán 2 for i = 2 to n – 1 do f = t1 + t2 t1 = t2 t2 = f endfor  Bước 3: kết thúc thuật toán. Ví dụ: số fibonacci. 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ố

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

Tài liệu cùng người dùng

Tài liệu liên quan