Một số thuật toán khác

Một phần của tài liệu Các thuật toán cơ bản trong lý thuyết số (Trang 33 - 89)

1.8.2 Thuật toán nhân nhanh hai số

Để nhân hai số nguyên, ta sử dụng tính chất đơn giản của phép nhân. Nếu a = a1 + a2, b = b1 + b2 thì ab = a1b1 + a2b2 + a1b2 + a2b1. Như vậy, thay cho việc nhân hai số nguyên n bít (trong hệ nhị phân, hoặc số chữ số trong hệ thập phân) thì thuật toán được học từ thời phổ thông này cần sử dụng một số phép tính cộng và nhân bằng bảng cửu chương để tính ra kết quả cuối cùng (phép nhân a với b đã là phép cộng b

lần số a).

Ví dụ 1.18 Tính tích 123.345

123.345 = 123.(300 + 40 + 5) = 123.300 + 123.40 + 123.5 = 36900 + 4920 + 615 = 42435.

Tuy nhiên để có một thuật toán nhân nhanh, ta không thể cộng b lần số a

mà phải tìm được một cách tối ưu để tách b và a thành những phần nhỏ hơn. Thuật toán trình bày dưới đây cho chúng ta một số cách để phân chia như vậy.

1.8.2 Thuật toán Karatsuba - Ofman

Xét bài toán nhân hai số nguyên n chữ số X và Y. Theo giải thuật nhân hai số thông thường thì cần n2 phép nhân và n phép cộng nên tốn O(n2) thời gian. Karatsuba - Ofman đã áp dụng kỹ thuật chia để trị để tăng tốc thuật toán nhân hai số nguyên này.

Giả sử muốn nhân hai số nguyên 2n bit,

a = (a2n−1a2n−2...a1a0)2, b = (b2n−1b2n−2...b1b0)2. Ta viết a = 2nA1 +A0, b = 2nB1 +B0, trong đó A1 = (a2n−1a2n−2...an)2, A0 = (an−1an−2...a1a0)2, B1 = (b2n−1b2n−2...bn)2, B0 = (bn−1bn−2...b1b0)2. Khi đó ta có ab = (22n+ 2n)A1B1 + 2n(A1 −A0)(B1 −B0) + (2n+ 1)A0B0.

Như vậy việc nhân hai số a, b 2n bit được đưa về việc nhân các số n bit, cùng các phép cộng, trừ và dịch chuyển (nhân một số với một lũy thừa bậc

Chương 2

Lập trình và thực thi trên máy tính một số thuật toán số học

Chương này trình bày các chương trình có sẵn hoặc do tác giả luận văn tự lập trình cho một số thuật toán đã nêu trong chương 1 trên máy tính điện tử khoa học (Vinacal 570 ES Plus II), chương trình Pascal và chương trình Maple. Các vấn đề trình bày trong chương này được tham khảo và trích dẫn từ tài liệu [5], [1].

2.1 Tìm thương và số dư

2.1.1 Tìm thương và số dư khi chia một số tự nhiên a cho một số tự nhiên b trên Vinacal 570ES Plus II

Do đã được lập trình sẵn, Vinacal 570ES Plus II có thể giải bài toán tìm thương và số dư khi chia một số tự nhiên a cho một số tự nhiên b chỉ bằng một lệnh (một thao tác) như sau.

Trước tiên bấm phím ON để mở máy.

Vào các tính năng vượt trội của Vinacal 570ES Plus II bằng cách bấm phím: SHIFT 6 (VINACAL)

1: Q...r (tìm thương và số dư).

2: LCM (tìm bội số chung nhỏ nhất). 3: GCD (tìm ước số chung lớn nhất).

4: FACT (phân tích một số ra thừa số nguyên tố). 5: lim (tính giới hạn).

6: MinMax (tìm giá trị lớn nhất và nhỏ nhất của hàm bậc hai). Bấm tiếp phím 1 . Trên màn hình hiện: Q...r( .

Khai báo số bị chia trước, đánh dấu phẩy bằng cách bấm phím SHIFT , , khai báo tiếp số chia, bấm phím = để được kết quả trên màn hình: Q= (Số thương Q=); r= (Số dư r=).

Ví dụ 2.1 (Đề thi học sinh giỏi Giải toán trên máy tính cấp khu vực, Bộ Giáo dục và Đào tạo, lớp 6, 7, 2001)

a) Tìm thương và số số dư khi chia 18901969 cho 2382001; b) Tìm thương và số dư khi chia 3523127 cho 2047.

Cách giải 1 (trên Vinacal 570 ES Plus II) a) Bấm phím

ON SHIFT 6 1 18901969 SHIFT , 2382001 =

(Q=7, R=2227962)

Chú ý Từ nay về sau ta qui ước: Để tránh cồng kềnh, các số được viết trực tiếp mà không viết trong các ô thể hiện phím bấm. Ví dụ, 2382001 không viết 2 3 8 2 0 0 1 . Đáp số là biểu thức được viết trong ngoặc sau phím = . Ví dụ: (Q=7, R=2227962) nghĩa là thương bằng 7 và số dư bằng 2227962. (adsbygoogle = window.adsbygoogle || []).push({});

b) Bấm phím

SHIFT 6 1 3523127 SHIFT , ,2047 = (Q=1721, R=240) Vậy 3523127 chia cho 2047 được thương là 1721 và số dư là 240.

Nhận xét 1 Sau khi bấm phím SHIFT 6 1 , chỉ bằng một thao tác

khai báo các số bị chia và số chia, ta có ngay kết quả. Với các máy tính khác hoặc với chính Vinacal 570 ES Plus II (mà không dùng SHIFT

6 1 ), ta phải sáng tạo vòng vèo (thủ công, không cần thiết) như sau. Cách giải 2 Lần lượt trừ số bị chia cho số chia cho tới khi được số nhỏ hơn số chia, chính là số dư (trước khi trừ đưa số chia vào ô A để sử dụng nhiều lần)

2382001 SHIFT STO A 18901969 - ALPHA A = (16519968)

- ALPHA A = (14137967) ALPHA A = (11755966)

ALPHA A = (9373965) ALPHA A = (6991964) ALPHA

A = (4609963) ALPHA A = (2227962)

Cách giải 3 Chia số bị chia cho số chia để tìm thương, rồi tìm số dư bằng cách trừ số bị chia cho tích của phần nguyên của thương và số chia (Phần nguyên của 7.93532101 bằng 7)

2382001 SHIFT STO A 18901969 ÷ (7.93532101) × ALPHA

A - 7 × ALPHA A = (2227962)

Nhận xét 2 Cách giải 2 sử dụng kĩ thuật trừ liên tiếp: lấy số bị chia liên tiếp trừ cho số chia, cho tới khi được số nhỏ hơn số chia, đó chính là số dư. Số lần trừ liên tiếp chính là thương. Cách này chỉ áp dụng được khi thương tương đối nhỏ (ví dụ, thương là 7 trong câu a) và phải nhớ đã trừ bao nhiêu lần.

Cách giải 3 sử dụng công thức a = b×c = b×[c] +r, trong đó[c] là phần nguyên của c. Trước tiên ta đưa b vào ô nhớ B để sử dụng nhiều lần, sau đó chia a cho b để được c (và nhìn màn hình để được phần nguyên [c]), rồi nhân lại với b (bằng cách gọi số b từ ô nhớ B nhờ ALPHA B ) để được lại a, sau đó trừ đi b×[c] (bấm phím [c] × ALPHA B ) để

được số dư r .

b) 2047 SHIFT STO B 3523127 ÷ ALPHA B = (1721.117245)

× ALPHA B - 1721 × ALPHA B = (240).

Nhận xét 3Trong thực tế, không thể áp dụng cách 2 (đã thực hiện tốt để giải câu a vào câu b) vì số lần bấm phím quá lớn (1721 lần - ALPHA

B = ).

Nhận xét 4 Có thể sử dụng Vinacal 570ES Plus II trợ giúp giải các bài toán chia hết (bài tập khó của Toán Trung học cơ sở) như ví dụ dưới đây. Ví dụ 2.2 Tìm a, b, c biết số 21a2b1509c chia hết cho 504.

Cách giải 1 (Toán kết hợp với máy tính) Vì 504 = 7 × 8× 9 nên để

21a2b1509c chia hết cho 8 thì ba số tận cùng 09c phải chia hết cho 8. Để

9c chia hết cho 8 thì c chỉ có thể bằng 6. Số cần tìm có dạng 21a2b15096. Để số đã cho chia hết cho 9 thì 26 + a + b = 18 + 8 + a + b phải chia hết cho 9, tức là a + b + 8 = 9 hoặc a + b+ 8 = 18. Suy ra a+ b = 1

hoặca+b = 10. Thử tất cả các trường hợp trên máy tính, ta có kết quả sau:

a b Số đã cho Thương (÷504) Dư Kết luận

0 1 2102115096 4170863 144 1 0 2112015096 4190506 72 1 9 2112915096 4192291 432 2 8 2122815096 4211934 360 3 7 2132715096 4231577 288 4 6 2142615096 4251220 216 5 5 2152515096 4270833 144 6 4 2162415096 4290506 72 7 3 2172315096 4310149 0 đáp số 8 2 2182215096 4329791 432 9 1 2192115096 4349434 360

Cách giải 2 (Suy luận toán học) Ta có 21a2b15096 = 2102015096 +a0000000 +b00000 = 2102015096 +a×10000000 +b×100000 = (300287870×7 + 6) + a×(1428571×7 + 3) +b×(14285×7 + 5) = (300287870 + 1428571a+ 14285b)×7 + 3a+ 5b+ 6 = (300287870 + 1428571a+ 14286b)×7 + 3a−2b+ 6.

Như vậy, để số đã cho chia hết cho 7 thì 3a−2b+ 6 phải chia hết cho 7. Vì 3a−2b+ 6 ≤3a+ 6 ≤28 nên 3a−2b+ 6 chỉ có thể bằng một trong các số: 0, 7, 14, 21, 28.

Vì số đã cho đồng thời phải chia hết cho 9 nên a và b đồng thời phải thỏa mãn hai điều kiện: a +b = 1 hoặc a+ b = 10 và 3a− 2b+ 6 bằng một trong các số: 0, 7, 14, 21, 28.

Trường hợp 1 Hệ 3a−2b+ 6 = 0 và a+b = 1 không có nghiệm nguyên. Trường hợp 2 Hệ 3a−2b+ 6 = 7 và a+b = 1 không có nghiệm nguyên. Trường hợp 3Hệ 3a−2b+ 6 = 14và a+b = 1 không có nghiệm nguyên. Trường hợp 4Hệ 3a−2b+ 6 = 21và a+b = 1 không có nghiệm nguyên. Trường hợp 5Hệ 3a−2b+ 6 = 28và a+b = 1 không có nghiệm nguyên. Trường hợp 6Hệ 3a−2b+ 6 = 0 vàa+b = 10 không có nghiệm nguyên. Trường hợp 7Hệ 3a−2b+ 6 = 7 vàa+b = 10 không có nghiệm nguyên. Trường hợp 8Hệ3a−2b+6 = 14vàa+b = 10không có nghiệm nguyên. Trường hợp 9 Hệ 3a−2b+ 6 = 21 và a+b = 10 có nghiệm a = 7, b = 3. Trường hợp 10 Hệ 3a − 2b + 6 = 28 và a+ b = 10 không có nghiệm nguyên. (adsbygoogle = window.adsbygoogle || []).push({});

Đáp số: Số cần tìm là 2172315096.

Nhận xét 5 Sử dụng máy tính hay hơn. Ta đã không phải suy nghĩ gì

Cách 2, ta cũng vẫn phải sử dụng máy tính trợ giúp các phép toán trung gian (tìm phần dư của 1108019872 khi chia cho 7,...).

Tìm thương và số dư khi chia một số tự nhiên a cho một số tự nhiên b khi a vượt quá 10 chữ số (vượt quá khả năng hiển thị của máy tính bỏ túi)

Ví dụ 2.3 (Thi học sinh giỏi cấp khu vực, Bộ Giáo dục và Đào tạo. Trung học Cơ sở, 2006)

Tìm số dư trong mỗi phép chia sau: a) 103103103: 2006;

b) 30419753041975: 151975; c) 103200610320061032006: 2010.

Cách giải 1 (trên Vinacal 570ES Plus II) a) Bấm phím

ON SHIFT 6 1 103103103 SHIFT , 2006 =

(Q=51397, R=721)

Như vậy, 103103103 chia cho 2006 được thương là 51397 và số dư là 721. b) Vì số bị chia vượt quá 10 chữ số (vượt quá khả năng hiển thị trên màn hình và có thể, vượt quá số lưu trong ô nhớ) nên nếu làm như câu a)

ON SHIFT 6 1 30419753041975 SHIFT , 151975 =

thì máy sẽ báo lỗi: trên màn hình hiện

Math ERROR (lỗi về toán)

[AC]: Cancel

[/][.]: Go to

Tức là: Bấm phím AC để xóa bỏ lệnh trên (và làm lại từ đầu), hoặc: Bấm một trong hai phím [/][.] để trở về biểu thức đã khai báo và sửa dữ liệu. Với những bài toán (như câu b hoặc câu c) mà số bị chia vượt quá

10 chữ số, ta làm như sau:

Biểu diễn số bị chia dưới dạng tổng một số có 10 chữ số (nhân với lũy thừa của 10) và một số khác (có dưới 10 chữ số như trong câu b và trên 10 chữ số như trong câu c):

30419753041975=30419753040000+1975= 3041975304×10000+1975. Kết hợp tính trên máy và ghi kết quả ra giấy ta được:

ON SHIFT 6 1 3041975304 SHIFT , 151975 = (Q=20016, R=43704) Tức là 30419753041975=30419753040000+1975 =(20016×151975+43704)×10000+1975 =200160000×151975+437041975.

Chia tiếp 437041975 cho 151975:

ON SHIFT 6 1 437041975 SHIFT , 151975 = (Q=2875, R=113850) Vậy cuối cùng ta có 30419753041975=30419753040000+1975 =(20016×151975+43704)×10000+1975 =200160000×151975+437041975 =200160000×151975+(2875×151975+113850) =200162875×151975+113850.

Đáp số: Thương và số dư khi chia 30419753041975 cho 151975 tương ứng là 200162875 và 113850.

c) Số bị chia là số có 21 chữ số. Trước tiên ta viết số bị chia thành tổng một số có 10 chữ số (nhân với 1011) và một số có 11 chữ số

=1032006103 ×100000000000 + 20061032006. Chia 1032006103 cho 1753 ON SHIFT 6 1 1032006103 SHIFT , 1753 = (Q=513435, R=1753) Vậy 103200610320061032006=(103200610300000000000+20061032006) =1032006103 ×100000000000 + 20061032006. =(513435×2010 + 1753) ×100000000000 + 20061032006 =51343500000000000 × 2010 + 175320061032006.

Vì 175320061032006 có 15 chữ số nên ta lại tách nó thành hai số: 175320061032006=175320061000000 + 32006

=1753200610×100000 + 32006

Tìm thương và số dư khi chia 1753200610 cho 2010:

ON SHIFT 6 1 1753200610 SHIFT , 2010 = (Q=872239, R=220) Vậy 103200610320061032006=(103200610300000000000+20061032006) =1032006103 ×100000000000 + 20061032006 =(513435×2010 + 1753) ×100000000000 + 20061032006 =51343500000000000 × 2010 + 175320061032006 =51343500000000000×2010+ (87223900000 × 2010 + 22000000) + 32006=51343587223900000 × 2010 + 22032006 (adsbygoogle = window.adsbygoogle || []).push({});

Tìm thương và số dư khi chia 22032006 cho 2010:

ON SHIFT 6 1 22032006 SHIFT , 2010 =

Vậy 103200610320061032006

=51343587223900000 × 2010 + 22032006

=51343587223900000 × 2010 + 10961 × 2010 + 396 =51343587223910961 × 2010 + 396

Đáp số: Thương và số dư khi chia 103200610320061032006 cho 2010 tương ứng là 51343587223910961 và 396.

Vinacal 570ES Plus II có thể tìm thương và số dư khi chia một số a cho bởi biểu thức cho một số b cũng cho bởi một biểu thức. Thậm chí khia là số lớn hơn 10 chữ số.

Ví dụ 2.4 Tìm thương và phần dư khi chia 5×3494 cho 13×62. Cách giải 1 (trực tiếp) Thực hiện qui trình bấm phím

SHIFT 6 1 5 × x 4 SHIFT , 13 × x2 =

(Q=158498756, R=197)

Cách giải 2 (gián tiếp) Tính 5×3494 = 7.417741801×1010

Sử dụng chương trình tính thương và số dư

SHIFT 6 1 Ans SHIFT , 13 × 6 x2 =

(Q=158498756, R=197)

Giải thích Làm lộ đuôi 5×3494 = 7.417741801×1010 bằng cách bỏ số đầu ta được

5×3494 = 7.417741801×1010 - 7 × 10x = (4177418005). Như vậy, 5×3494 = 74177418005 khi chia cho 13×62 = 468 được thương là 158498756 và số dư là 197.

Mặc dù không hiển thị trên màn hình đầy đủ các chữ số được, vì số 74177418005 có 11 chữ số, nhưng máy vẫn giữ đáp số đúng trong ô nhớ là số 74177418005 và thực hiện các phép toán tiếp theo với các số đúng (ở đây là thực hiện phép tìm thương và số dư).

2.1.2 Lập trình trên chương trình Pascal

Ý tưởng Sử dụng hàm MOD để lấy số dư và sử dụng hàm DIV để lấy phần nguyên.

Program Chuong trinh tim thuong va sodu; uses crt;

var x,y,q,r:integer; Begin

clrscr;

writeln(’ban hay nhap vao 2 so:’); write(’So bi chia: ’); readln(x); write(’So chia: ’); readln(y); q:= x div y ;

r:= x mod y ;

writeln(’Ket qua la:’); write(’Thuong bang: ’); write(q); readln;

write(’So du bang: ’); write(r); readln;

End.

Ví dụ 2.5 Tìm thương và số dư khi chia 13052014 cho 154. Khi chạy chương trình Pascal ta sẽ được kết quả như sau Ban hay nhap vao 2 so

So bi chia: 13052014 So chia: 154

Ket qua la: (adsbygoogle = window.adsbygoogle || []).push({});

Thuong bang: 84753 So du bang: 52

2.1.3 Tính toán trên Maple Cho hai số nguyên A và B. Theo thuật toán Euclid, khi chia A cho B ta được thương q và số dư r. Muốn tìm thương của một số A khi chia cho một số B ta dùng lệnh iquo(A,B). Muốn tìm phần dư của một số A khi chia cho một số B ta dùng lệnh irem(A,B).

Chú ý Nếu A và B là những số nguyên thì iquo(A,B) và irem(A,B) cho

q và r sao cho A = Bq +r,|r| < |B| và 0 ≤ Ar.

Ví dụ 2.6 Tìm thương và số dư của −23 khi chia cho 4:

[> iquo(-23,4);

−5 [> irem(-23,4);

−3

Ví dụ 2.7 Tìm thương và số dư của 23 khi chia cho −4:

[> iquo(23,-4);

−5 [> irem(23,-4);

3

Ví dụ 2.8 Tìm thương và số dư của 23 khi chia cho 4:

[> iquo(23,4);

5 [> irem(-23,4);

3

Nhận xét 6 Ta có thể đồng thời tìm thương và số dư bằng lệnh iquo( A,B,’r’), trong đó r là số dư hoặc irem(A,B,’q’), trong đó q

là thương.

[> iquo(23,4,’r’); 5 [> r; 3 Hoặc [> irem(23,4,’q’); 3 [> q; 5 Một số ví dụ khác

Ví dụ 2.10 Tìm thương và số dư của 1591987 khi chia cho 1988: Cách 1 [> iquo(1591987,1988); 800 [> irem(1591987,1988); 1587 Cách 2 [> iquo(1591987,1988,’r’); 800 [> r; 1587 Hoặc [> irem(1591987,1988,’q’); 1587 [> q; 800

iquo và irem không xác định.

Ví dụ 2.11 Tìm thương của π khi chia cho 2 và phần dư của 1997 cho x

[> iquo(π,2);

iquo(π,2)

[> irem(1997,x);

irem(1997,x) Một số ví dụ áp dụng

Bài 1 (Đề thi Giải toán trên máy tính, Trung học Cơ sở, Sở Giáo dục và Đào tạo Hòa Bình, 2007-2008)

Tìm các số a và b biết 686430a8b chia hết cho 2008.

Bài 2 (Đề thi Giải toán trên máy tính, Trung học Cơ sở, Bộ Giáo dục và Đào tạo, 2009-2010)

Tìm số dư (trình bày cách giải) trong các phép chia sau: 1) 20092010: 2011 ; 2) 22009201020112012: 2020. (adsbygoogle = window.adsbygoogle || []).push({});

Bài 3 (Đề thi Giải toán trên máy tính, Phòng Giáo dục và Đào tạo Sơn Hòa, 2010-2011)

1)Tìm số dư trong phép chia 9876543210123456789 cho 987654 2) Tìm số dư trong phép chia 22010 cho 49.

Bài 4 (Đề thi Giải toán trên máy tính, lớp 9, Phòng Giáo dục và Đào tạo Lâm Thao, 2012-2013)

Tìm số tự nhiên nhỏ nhất, lớn nhất có 9 chữ số khi chia cho 5; 7; 9; 11 thì có số dư lần lượt là 3; 4; 5; 6

Bài 5(Đề thi Giải toán trên máy tính, Sở Giáo dục và Đào tạo Vĩnh Long, 2013-2014)

2.2 Kiểm tra số nguyên tố

2.2.1 Lập trình trên chương trình Pascal

Ý tưởng Số nguyên tố là số chia cho 1 và chính nó. Giả sử số vừa nhập vào là n, ta cho i chạy từ 2 đến n−1, nếu n chia hết cho i trong bất cứ lần lặp nào thì có nghĩa là n không nguyên tố, nếu không chia hết cho bất cứ lần lặp nào thì n là số nguyên tố.

PROGRAM kiem tra so nguyen to; USES crt;

VAR n,i,a:INTEGER; BEGIN

Clrscr;

Write(’nhap vao 1so:’); readln(n);

a:=0;

For i:=2 to n do

if(n mod i=0)then a:=a+1;

if a< 2 then writeln(n,’ la so nguyen to’) else writeln(n,’ khong la so nguyen to’); Readln;

END.

Ví dụ 2.12 Kiểm tra xem số 3041975 có phải là số nguyên tố hay không Chạy chương trình trên Pascal ta có kết quả như sau

Nhap vao 1so:3041975 Ket qua

Ví dụ 2.13 Viết chương trình in ra tất cả các số nguyên tố bé hơn hoặc

Một phần của tài liệu Các thuật toán cơ bản trong lý thuyết số (Trang 33 - 89)