Sáng kiến kiến nghiệm – Chuyên đề Số nguyên tố trong công tác bồi dưỡng HSG môn Tin học – Đỗ Xuân Quyền – Giáo Án Điện Tử
Bạn đang xem nội dung tài liệu Sáng kiến kiến nghiệm – Chuyên đề Số nguyên tố trong công tác bồi dưỡng HSG môn Tin học – Đỗ Xuân Quyền, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
CHUYÊN ĐỀ: SỐ NGUYÊN TỐ ĐẶT VẤN ĐỀ Số nguyên tố là một lĩnh vực Toán-Tin, có rất nhiều bài toán xử lí số nguyên tố. Đặc biệt là trong các cuộc thi tin học lập trình của thành phố và quốc gia, đó là một chủ đề hay và được ứng dụng nhiều. Vì lí do đó tôi xin trình bày chuyên đề về số nguyên tố để cùng chia sẻ và cùng đồng nghiệp xây dựng phương pháp giải cho một số dạng bài toán trong chuyên đề này. NỘI DUNG Khái niệm Một số tự nhiên p (p>1) là số nguyên tố nếu p có đúng 2 ước số là 1 và p. Ví dụ các số nguyên: 2, 3, 5, 7, 11, 13 là các số nguyên tố. Kiểm tra tính nguyên tố Để kiểm tra số nguyên dương n (n>1) có là số nguyên tố không, ta kiểm tra xem có tồn tại một số nguyên k (k=2..n-1) mà p chia hết cho k thì p không phải là số nguyên tố, ngược lại là số nguyên tố. Cài đặt Function ngto(n:longint): Boolean; Var i:longint; Begin if (n<1) then exit(false); if (n<=3) then exit(true); For i:=2 to n-1 do If (n mod i = 0) then exit(false) Else exit(true); End; Nhận xét: theo cách trên thì độ phức tạp về thời gian sẽ là O(n). Vậy ta có một cách cải tiến khác là chỉ cho i chạy từ 2 đến (n div 2), sẽ giảm độ phức tạp về O(n/2). Nhưng khi n là rất lớn thì cách này cũng không có ý nghĩa gì so với cách trên. Với độ phức tạp này thì máy tính sẽ phải thực hiện trong thời gian rất lâu (với tốc độ trung bình của một máy tính hiện nay thì một giây có thể xử lí khoảng 108 phép toán). Như vậy bài toán trên nếu n≤1010 phải mất khoảng thời gian là (1010/108) giây. Vận dụng kiến thức toán học cho ta thấy khi n chia hết cho i thì cũng chia hết cho thương của phép chia n/i (ví dụ: 10 chia hết cho 2 thì cũng chia hết cho 5). Theo tính chất đó ta chỉ cần cho i chạy từ 2 đến phần nguyên căn bậc 2 của n. Thuật toán được cải tiến như sau: Function ngto(n:longint): Boolean; Var i:longint; Begin if (n<1) then exit(false); if (n<=3) then exit(true); For i:=2 to trunc(sqrt(n)) do If (n mod i = 0) then exit(false) Else exit(true); End; * Nhận xét độ phức tạp thời gian của thuật toán trên là O(), với độ phức tạp này thì bài toán trên máy tính chi mất dưới 1s để giải quyết. Một số dạng bài toán: Bài toán 1: Cho dãy số a1,a2,, an. (n<=1000; ai là số nguyên dương, ai<=109), hãy cho biết trong dãy có bao nhiêu số nguyên tố trong dãy? * Input: tệp nguyento.inp gồm: Dòng đầu tiên là số nguyên dương n N dòng tiếp theo mỗi dòng là số nguyên dương ai * Output: tệp nguyento.out gồm: Số nguyên duy nhất là số các chữ số nguyên tố trong dãy. * Nhận xét: với bài toán này, chỉ cần duyệt từ phần tử đầu tiên đến phần tử cuối cùng, gọi hàm kiểm tra nguyên tố cho từng phần tử, nếu đúng thì tăng biến đếm, nếu sai thì không tăng. Chương trình được viết như sau: * Chương trình program dem_nguyen_to; const fi='nguyento.inp'; fo='nguyento.out'; var f1,f2:text; dem,n:integer; a:array[1..1000] of longint; procedure doc; var i:integer; begin assign(f1,fi); reset(f1); readln(f1,n); for i:=1 to n do readln(f1,a[i]); close(f1); end; Function ngto(n:longint): Boolean; Var i:longint; Begin if (n<1) then exit(false); if (n<=3) then exit(true); For i:=2 to trunc(sqrt(n)) do If (n mod i = 0) then exit(false) Else exit(true); End; procedure ghi; begin assign(f2,fo); rewrite(f2); writeln(f2,dem); close(f2); end; Procedure xuli; var i:integer; begin dem:=0; for i:=1 to n do if ngto(a[i]) then inc(dem); ghi; end; BEGIN doc; xuli; END. Bài toán 2: Cho dãy số a1,a2,, an. (n<=106; ai là số nguyên dương, ai<=109), hãy cho biết trong đoạn [l,r] của dãy có bao nhiêu số nguyên tố (1<=l<=r<=106). Yêu cầu bài thực hiện trong 1 giây? * Input: tệp nguyento.inp gồm: Dòng đầu tiên là số nguyên dương n N dòng tiếp theo mỗi dòng là số nguyên dương ai Dòng cuối cùng là 2 số l và r. * Output: tệp nguyento.out gồm: Số nguyên duy nhất là số các chữ số nguyên tố trong dãy. * Nhận xét: Với bài toán này độ phức tạp đã tăng lên (n<=106), nếu ta giải quyết bài toán với thuật toán 2 thì độ phức tạp thời gian sẽ tăng thành O(106*), đồng nghĩa với thời gian xử lí sẽ lớn hơn 1 giây. Trong tình huống này ta sẽ phải sử dụng sàng nguyên tố Eratosthene, để liệt kê được các số nguyên tố nhanh, tuy nhiên nhược điểm của phương pháp này là tốn nhiều bộ nhớ. Thuật toán như sau: * Thuật toán: B1: xóa 1 khỏi tập các số nguyên tố B2: Xét I = 2 đến trunc(sqrt(n)) //phần nguyên căn bậc 2 của n Nếu I là số nguyên tố (f[i]=true) thì các bội của I là (j:=i*i; j:=j+i) không là nguyên tố (f[j]=false). B3: Các số còn lại trên dãy từ 1 đến n nhận giá trị true là số nguyên tố. * Chương trình thực hiện như sau: program dem_nguyen_to; const Nmax=100000000; fi='nguyento.inp'; fo='nguyento.out'; var f1,f2:text; dem,n,l,r:integer; a:array[1..1000000] of longint; f:array[1..Nmax] of boolean; procedure doc; var i:integer; begin assign(f1,fi); reset(f1); readln(f1,n); for i:=1 to n do readln(f1,a[i]); readln(f1,l,r); close(f1); end; procedure sang_estr; var i,j:longint; begin fillchar(f,sizeof(f),true); f[1]:=false; for i:=2 to trunc(sqrt(n)) do if f[i] then begin j:=i*i; while j<=n do begin f[j]:=false; j:=j+i; end; end; end; procedure ghi; begin assign(f2,fo); rewrite(f2); writeln(f2,dem); close(f2); end; procedure xuli; var i:longint; begin dem:=0; for i:=l to r do if f[a[i]] then inc(dem); ghi; end; BEGIN doc; sang_estr; xuli; END. ỨNG DỤNG ( Giải các bài toán về số nguyên tố trong một số đề thi chọn HSG thành phố). Bài 1: (Bài 1-Đề thi chọn học sinh giỏi thành phố năm học 2009-2010 bảng A và B). Cho số nguyên dương N (2≤N≤1010) Yêu cầu: Phân tích N thành các thừa số nguyên tố dưới dạng N=a*b*c* (với a,b,c là các số nguyên tố)? Giả sử: cho N = 81 - Vì N=81=34 nên biểu diễn dưới dạng tích các thừa số nguyên tố: 3*3*3*3 * Dữ liệu vào: cho trong tệp bai1.inp gồm - dòng duy nhất chứa số N * Dữ liệu ra: Ghi ra tệp bai1.out gồm các thừa số được phân tích cách nhau dấu cách. Ví dụ: Bai1.inp Bai1.out 1296 2 2 2 2 3 3 3 3 * Ý tưởng: ta sẽ viết một hàm kiểm tra số nguyên tố i (bắt đầu với i=2) để làm số chia cho phép chia n div i, lặp cho đến khi n là số nguyên tố. * Thuật toán B1: Nếu N là số nguyên tố thông báo kết quả và kết thúc B2: i=2 B3: Khi n chưa là nguyên tố thì còn lặp B3.1. nếu i là nguyên tố thì Nếu n mod I =0 thì thêm I vào danh sách n=n div i nếu n là nguyên tố thì dừng lặp, ngược lại tăng I và chuyển đến B3.1 Nếu n mod I 0 thì tăng I và quay lại B3.1 B3.2. nếu I không phải là nguyên tố thì tăng I và quay lại B3.1 B4. Thông báo kết quả là tích dãy I và n * Chương trình Program bai1_2009_2010; const fi='bai1.inp'; fo='bai1.out'; var f:array[1..1000] of longint; n:longint; k:integer; f1,f2:text; procedure doc; begin assign(f1,fi); reset(f1); readln(f1,n); close(f1); end; function nto(x:longint):boolean; var i:longint; begin if x<=1 then exit(false); for i:=2 to trunc(sqrt(x)) do if x mod i = 0 then exit(false); exit(true); end; procedure xuli; var i:longint; begin k:=0; if nto(n) then begin inc(k); f[k]:=n; end else begin i:=2; while not(nto(n)) do begin while not(nto(i)) do inc(i); while n mod i = 0 do begin inc(k); f[k]:=i; n:=n div i; if n=1 then exit; end; inc(i); end; inc(k); f[k]:=n; end; end; procedure ghi; var i:integer; begin assign(f2,fo); rewrite(f2); for i:=1 to k do write(f2,f[i],' '); close(f2); end; BEGIN doc; xuli; ghi; END. Bài 2: (Bài 2.a-Đề thi chọn học sinh giỏi thành phố năm học 2008-2009) Trong một phân xưởng săn xuất, để hoàn thành được một sản phẩm, người quản đốc phải sử dụng n công nhân để gia công n chi tiết. Biết rằng công sức để gia công một chi tiết của mỗi công nhân là khác nhau. Người ta sử dụng một mảng hai chiều C gồm n hàng và n cột biểu diễn chi phí gian công của công nhân cho từng chi tiết cụ thể, trong đó giá trị c[i,j] là số công bỏ ra của công nhân tứ I gia công chi tiết j (1≤ c[i,j]≤20000). Yêu cầu: - Kiểm tra xem với mỗi chi tiết nếu tất cả các công nhân đề tham gia gia công thì tổng số công bỏ ra của mọi nguowif cho chi tiết đó có là số siêu nguyên tố không? (ghi chú: số siêu nguyên tố là số nguyên tố mà khi bỏ một số tùy ý các chữ số bên phải của nó thì phần còn lại vẫn tạo thành một số nguyên tố: ví dụ 537 là một số siêu nguyên tố có 3 chữ số vì 53, 5 là các số nguyên tố). * Dữ liệu vào: cho trong tệp bai2.inp - Dòng đầu tiên là số nguyên n (1≤n≤20) là số lượng công nhân và số chi tiết. - Trong n dòng tiếp theo mỗi dòng biểu diễn n giá trị c[i,j] được ngăn cách nhau bởi dấu cách. Thể hiện số công bỏ ra của công nhân thứ I gia công cho n chi tiết tương ứng. * Kết quả: đưa ra tệp văn bản bai2.out - gồm 1 dòng ghi số 1 hoặc 0 được ngăn cách nhau bởi dấu cách thể hiện tổng số công bỏ ra của tất cả các công nhân khi gia công mỗi chi tiết tương ứng có hoặc không là số siêu nguyên tố. Ví dụ: Bai2.inp Bai2.out 3 4 5 7 3 6 8 7 8 8 0 0 1 Giải thích: - Tổng số công của chi tiết số 1 = 14: không phải số siêu nguyên tố (ghi 0) - Tổng số công của chi tiết số 2 =19: không phải số siêu nguyên tố(ghi 0) - Tổng số công của chi tiết số 3=23: là số siêu nguyên tố (ghi 1). Nhận xét: Trước tiên phải xây dựng một hàm kiểm tra số siêu nguyên tố, trong đó có sử dụng hàm kiểm tra nguyên tố. Tính tổng từng cột và kiểm tra tổng đó có là siêu nguyên tố không. * Chương trình program bai2_08_09; const fi='bai2.inp'; fo='bai2.out'; var f1,f2:text; c:array[1..20,1..20] of integer; n:byte; procedure modoc; var i,j:byte; begin assign(f1,fi); reset(f1); readln(f1,n); for i:=1 to n do begin for j:=1 to n do read(f1,c[i,j]); readln(f1); end; close(f1); assign(f2,fo); rewrite(f2); end; function nto(x:longint):boolean; var i:integer; begin if x<=1 then exit(false); for i:=2 to trunc(sqrt(x)) do if x mod i = 0 then exit(false); exit(true); end; function snto(x:longint):boolean; begin if nto(x) and (x div 10 =0) then exit(true); while x div 10 0 do if nto(x) then x:=x div 10 else exit(false); if nto(x) then exit(true) else exit(false); end; procedure xuli; var i,j:byte; t:longint; begin for j:=1 to n do begin t:=0; for i:=1 to n do t:=t+c[i,j]; if snto(t) then write(f2,1,' ') else write(f2,0,' '); end; close(f2); end; BEGIN modoc; xuli; END. Bài 3: (bài 3- Đề thi chọn học sinh giỏi thành phố bảng A năm 2014-2015) Bạn được cho biết số N và dãy A=(a1,a2,..,an). Để tránh việc phải đọc một lượng dữ liệu quá lơn, dãy A sẽ được cho bởi 3 số nguyên dương p,q,m, trong đó mỗi phần tử ai được xác định bởi công thức: ai=(p*i) mod m + q (1≤ i ≤ n) Có T câu hỏi dạng u,v (u≤v) yêu cầu cho biết trong đoạn au, au+1,,av có bao nhiêu số nguyên tố? * Dữ liệu vào từ file văn bản bai3.inp - Dòng 1 chứa 2 số nguyên dương n và T - Dòng 2 chứa 3 số nguyên dương p,q,m xác định dãy A (p,q,m≤106); - T dòng tiếp theo, dòng thứ I chứa 2 số u,v tương ứng với câu hỏi I là trong đoạn [u,v] có bao nhiêu số nguyên tố? * Kết quả ghi ra file văn bản bai3.out - T dòng, dòng thứ I là câu trả lời của câu hỏi thứ i Ví dụ: Bai3.inp Bai3.out Giải thích 5 4 2 1 9 1 3 2 4 3 5 4 4 3 2 2 0 Dãy A=(3,5,7,9,2) Đoạn [1,3] là (3,5,7) có 3 số nguyên tố Đoạn [2,,4] là (5,7,9) có 2 số nguyên tố Chú ý: (chương trình chạy trong 1 giây) 40% điểm ứng với các test có n≤1000, T=1 40% điểm ứng với các test có n≤1000, T≤1000 20% điểm ứng với các test có n≤1000, T≤10000 * Nhận xét: với bài toán này nếu chỉ viết hàm kiểm tra nguyên tố thì độ phức tạp tối đa là O(104 * 103 * 103), chương trình sẽ chạy khoảng 102s. Vì vậy ta phải sử dụng sàng nguyên tố Eratosthene để giải quyết bài toán này. * Chương trình CONST tfi = 'BAI3.INP'; tfo = 'BAI3.OUT'; TYPE arr1 = array[0..1000] of longint; arr2 = array[1..2000000] of longint; VAR sum : arr1; Prime : arr2; N,m,p,q,kq,t : longint; fi,fo : text; (*--------------------------------*) procedure sang_nguyen_to; Var i,j : longint; Begin Prime[1] := 1; For i := 2 to trunc(sqrt(2000000)) do if prime[i] = 0 then begin j := i*i; while j <=1000000 do begin prime[j] := 1; j := j + i; end; end; end; (*--------------------------------*) Procedure xuli; Var i,x,u,v : longint; Begin sum[0] := 0; assign(fi,tfi); reset(fi); assign(fo,tfo); rewrite(fo); readln(fi,n,t); readln(fi,p,q,m); for i := 1 to n do begin x := (p*i) mod m + q; if prime[x]=0 then sum[i] := sum[i-1] + 1 else sum[i] := sum[i-1]; end; for i := 1 to t do begin readln(fi,u,v); writeln(fo,sum[v]- sum[u-1]); end; close(fo); close(fi); End; (*--------------------------------*) (*--------------------------------*) BEGIN sang_nguyen_to; xuli; END. KẾT LUẬN Trên đây là chuyên đề về xử lí số nguyên tố trong lập trình mà bản thân tôi đã tích lũy được trong quá trình làm việc, khi giảng dạy các lớp và đặc biệt là bồi dưỡng đội tuyển học sinh giỏi đã gây được hứng thú cho các em và mang lại hiệu quả tốt. Nhưng khi viết thành chuyên đề chắc chắn nội dung không thể tránh khỏi thiếu sót mong các đồng chí trong tổ góp ý, bổ sung để chuyên đề hoàn thiện hơn. Tôi xin trân trọng cảm ơn! Người viết Đỗ Xuân Quyền