Tải bản đầy đủ (.pdf) (28 trang)

SKKN Ứng dụng lý thuyết toán để giải các bài toán tin Phần 2 Số nguyên tố

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (296.67 KB, 28 trang )

M ỤC L ỤC
NỘI DUNG

TRANG

MỤC LỤC

1

I. PHẦN ĐẶT VẤN ĐỀ

2

I.1/ Lý do chọn đề tài

2

I.2/ Mục tiêu nghiên cứu

2

I.3/ Nhiệm vụ nghiên cứu

2

I.4/ Đối tượng nghiên cứu

2

I.5/ Các phương pháp nghiên cứu


2

II. PHẦN NỘI DUNG

4

II.1/ Lịch sử của vấn đề nghiên cứu

4

II.2/ Cơ sở lý luận của đề tài

4

II.3/ Thực trạng của vấn đề nghiên cứu

4

II.4/ Nội dung nghiên cứu và kết quả nghiên cứu

4

A/ NỘI DUNG NGHIÊN CỨU

4

A.1) Số nguyên tố

4


A.2) Các bài tập áp dụng.

11

A.3) Một số bài tập đề nghị.

17

A.4) Kết luận.

18

B/ KẾT QUẢ NGHIÊN CỨU

18

III. PHẦN KẾT LUẬN

19

III.1/ Kết luận

19

III.2/ Tài liệu tham khảo

19

1



I. PHẦN ĐẶT VẤN ĐỀ
I.1/ Lý do chọn đề tài
Để tiếp tục hoàn chỉnh chuyên đề bồi dưỡng HSG chuyên Tin “ỨNG DỤNG
LÝ THUYẾT TOÁN ĐỂ GIẢI CÁC BÀI TOÁN TIN”; qua quá trình nghiên cứu,
giảng dạy, tham khảo ý kiến đồng nghiệp, tôi thấy rằng đa số các bài toán trong tin
học về số thường đề cập nhiều đến số nguyên tố, xử lí trên các số nguyên tố. Chính
vì vậy tôi tiếp tục chọn viết tiếp đề tài về chuyên đề “ỨNG DỤNG LÝ THUYẾT
TOÁN ĐỂ GIẢI CÁC BÀI TOÁN TIN” (Phần 2: Số nguyên tố).
I.2/ Mục tiêu nghiên cứu
Như đã biết, toán học có ảnh hưởng rất lớn đến mọi lĩnh vực của cuộc sống.
Các bài toán tin nếu có được thuật toán dựa trên cơ sở lý thuyết toán học vững chắc
sẽ đem lại kết quả tốt hơn rất nhiều so với các thuật toán khác. Giúp các em học sinh
có kiến thức tốt, tư duy tốt về lập trình; số nguyên tố và xử lí trên các số nguyên tố là
một trong những vấn đề về số mà bất cứ người lập trình tin học đều cần phải xử lý.
I.3/ Nhiệm vụ nghiên cứu
Trước hết là thực hiện đổi mới phương pháp giảng dạy Tin học làm cho học
sinh sáng tạo tìm những kết quả, lời giải hay trên một “dạng bài toán tin có sử dụng
toán học”; giúp bản thân nắm vững hơn nữa về lập trình, tư duy thuật toán, số
nguyên tố, xử lí số nguyên tố, đồng thời trao đổi và học tập kinh nghiệm ở Quý Thầy
Cô ở Tổ Tin học.
I.4/ Đối tượng nghiên cứu
Trong nghiên cứu này, các học sinh được chọn là các em học lớp chuyên Tin
học khối 10, 11, 12 thuộc một trường THPT Chuyên Tiền Giang và một số giáo viên
đứng lớp dạy tin học ở trường THPT đó.
I.5/ Các phương pháp nghiên cứu
* Phương pháp suy luận, tổng hợp: kết hợp từ nhiều nguồn tài liệu tham khảo
của các tác giả và tra cứu trên mạng internet với các đề thi Học sinh Giỏi rút ra
những kinh nghiệm, hệ thống lại kiến thức, mở ra các hướng mới.
2



* Phương pháp trò chuyện – phỏng vấn: trao đổi tâm tình với nhiều học sinh
giỏi để nắm tình hình trong việc giải các bài toán tin về số.
* Phương pháp khảo sát: bản thân được tham gia giảng dạy các lớp, đội tuyển
HSG, các kỳ tập huấn, ra đề; tham khảo đồng nghiệp, quý Thầy Cô đã giảng dạy đội
tuyển nhiều năm nên có nắm được tình hình sử dụng các phương pháp làm bài của
các em học sinh.
* Phương pháp phân tích lý luận: phân tích giúp học sinh nắm thật rõ bản chất
vấn đề, lựa chọn được phương pháp giải cho phù hợp.

3


II. PHẦN NỘI DUNG
II.1/ Lịch sử của vấn đề nghiên cứu
Trong những năm liên tiếp dạy bồi dưỡng HSG lớp 10, 11, 12 và đội tuyển thi
HSG cấp Tỉnh, cấp Quốc Gia môn Tin học và bản thân tôi được tham dự lớp “BỒI
DƯỠNG TẬP HUẤN GIẢNG DẠY CHUYÊN ĐỀ MÔN CHUYÊN TIN HỌC
THPT” nhiều năm do Bộ GD&ĐT tổ chức. Tôi nhận thấy kiến thức về Tin học của
mình được nâng lên rõ rệt. Các bài giảng của các Thầy NGUYỄN THANH TÙNG,
Thầy ĐỖ ĐỨC ĐÔNG, Thầy LÊ MINH HOÀNG, cũng như tham khảo ý kiến các
đồng nghiệp chuyên dạy bồi dưỡng đội tuyển ở các Tỉnh bạn, ở các trường THPT
Chuyên, tôi rút ra một điều là “KIẾN THỨC TOÁN HỌC RẤT QUAN TRỌNG
DẠY LẬP TRÌNH TRONG TIN HỌC”. Được sự động viên khuyến khích của quý
Thầy Cô trong tổ, cũng như thầy LÊ NGỌC LINH, tôi mạnh dạng tiếp tục chọn viết
phần hai cho đề tài này là “Số nguyên tố”;
II.2/ Cơ sở lý luận của đề tài
Kết hợp bài giảng và các tài liệu tham khảo để phân tích, tổng hợp, hệ thống.
II.3/ Thực trạng của vấn đề nghiên cứu

Đa số học sinh chuyên tin rất ngại, sợ khi giải các bài toán tin về số có ứng
dụng toán; rất lúng túng trong quá trình phân tích, tổ chức dữ liệu, thuật toán để tìm
ra bản chất và vận dụng kiến thức một cách thích hợp.
II.4/ Nội dung nghiên cứu và kết quả nghiên cứu
A/ NỘI DUNG NGHIÊN CỨU
A.1) Số nguyên tố
A.1.1) Kiến thức cơ bản trong toán học
A.1.1.1) Định nghĩa số nguyên tố
Một số nguyên p (p > 1) là số nguyên tố nếu p có đúng hai ước số 1 và p. Một
số nguyên lớn hơn 1 mà không là số nguyên tố được gọi là hợp số.
Ví dụ: các số nguyên tố là: 2, 3, 5, 7, 11, 13, 17,…
A.1.1.2) Các định lí cơ bản về số nguyên tố
4


 Bổ đề 1: Mọi số nguyên lớn hơn 1 đều chia hết cho ít nhất một số nguyên
tố
Chứng minh bổ đề 1: Ta dể dàng chứng minh bằng phương pháp quy nạp.
 Bổ đề 2: Mọi hợp số có ước thực sự nhỏ hơn hoặc bằng căn bậc hai của nó
(ước thực sự là ước khác 1 và khác chính nó)
Chứng minh bổ đề 2: Vì n là hợp số nên ta có: n = a.b với 1 < a, b < n
Nếu đồng thời a, b >

n thì n =

n . n < a.b = n (mâu thuẩn)

Vậy có ít nhất một trong hai số a, b phải nhỏ hơn hoặc bằng

n


Nhận xét: từ bổ đề trên ta có nhận xét sau:
Mỗi hợp số phải có ước nguyên tố nhỏ hơn hoặc bằng căn bậc hai của nó.
 Định lý (Định lý Fecma nhỏ): Nếu p là số nguyên tố và a là số tự nhiên thì
ap mod p = a.
 Từ những lý thuyết toán cơ sở trên, ta có thể ứng dụng chúng vào các giải
thuật kiểm tra số nguyên tố trong tin học.
A.1.2) Giải thuật kiểm tra số nguyên tố trong tin học:
Bài toán: Kiểm tra số nguyên dương n có phải là số nguyên tố không?
Ý tưởng: Nếu n > 1 không chia hết cho số nguyên nào trong tất cả các số k
chạy từ 2 đến

n thì n là số nguyên tố.

 Câu hỏi đầu tiên học sinh sẽ hỏi các thầy cô là: “Tại sao chỉ xét mọi k chạy
từ 2 đến n ”?
 Thật vậy, theo bổ đề 2, nếu n (n > 1) không phải là số nguyên tố, ta có:
n = k1k2 mà 2  k1  k2  n  1
Vì k12  k1k1  k1k2  n nên k1  n
Giải thuật 1: (Pascal)
Function is_prime(n: longint): boolean;
Var k: longint;
Begin
5


if n = 1 then exit (false);
for k := 2 to trunc(sqrt(n)) do
if (n mod k = 0) then exit (false);
exit (true);

End;
Nhận xét: Hàm is_prime(n) tiến hành lần lượt kiểm tra số k trên đoạn [2, n ].
Để cải tiến, ta giảm số lần kiểm tra, ta kiểm tra xem có tồn tại một số nguyên
tố k (2  k 

n ) mà k là ước của n thì n không phải là số nguyên tố, ngược

lại n là nguyên tố. Thay vì kiểm tra k là số nguyên tố trên đoạn [2, n ] ta kiểm
tra số k có tính chất giống với tính chất của số nguyên tố trong đoạn [2, n ]:
 Tc1. Trừ số 2 và các số nguyên tố là số lẻ
 Tc2. Trừ số 2 và 3, các số nguyên tố có dạng 6k  1 (vì các số dạng
6k  2;6k  3 chia hết cho 2;3)

Giải thuật 2: (Pascal)
Function is_prime2(n: longint): boolean;
Var k, sqrt_n: longint;
Begin
if (n = 2) or (n = 3) then exit (true);
if (n = 1) or (n mod 2 = 0) or (n mod 3 = 0) then exit (false);
sqrt_n := trunc(sqrt(n));
k := -1;
repeat
inc(k,6);
if (n mod k = 0) or (n mod (k+2) = 0) then break;
until k > sqrt_n;
exit (k > sqrt_n));
End;

6



Nhận xét: Với hai giải thuật trên, ta có thể chạy chương trình với n = 106, khi
n lớn (khoảng 107 trở đi) chương trình chạy chậm. Muốn kiểm tra những số nguyên
lớn có nguyên tố, người ta chuyển sang hướng kiểm tra xác suất. Có nhiều thuật toán
xây dựng theo hướng này: dựa vào định lý Fermat nhỏ có kiểm tra Fermat và kiểm
tra Miller-Rabin là tiêu biểu.
Giải thuật 3: Kiểm tra nguyên tố dùng định lý Fecmat nhỏ
Ý tưởng:
Lặp k lần {
 Chọn giá trị ngẫu nhiên a, 2 ≤ a ≤ p-1
Nếu ap-1  1 (mod p) thì tăng biến đếm c (số lần thừa nhận p có thể là



nguyên tố), ngược lại thì p là hợp số và thoát.
}
Nếu tỉ số c/k > xacsuat thì có thể chấp nhận p là nguyên tố (với xác suất sai nhỏ)

Giải thuật:
function is_prime3(p : int64): boolean;
var test, dem, a : longint;
begin
if (p=2) or (p=3) then exit(true);
if (p<2) or ( p mod 2 = 0) or (p mod 3 = 0) then exit(false);
randomize; dem := 0;
for test:=1 to ntest do
begin
a := random(p-2) + 2;
if luythua(a,p-1,p) <> 1 then exit(false)
else inc(dem);

if (dem/ntes t> 0.6) then exit(true);
end;
7


exit(false);
end;

Trong giải thuật prime_3 trên, ta có sử dụng hàm luythua(x,y,n). Hàm
luythua(x,y,n) được viết như sau:

  2y   2
   
y  2k
 x 




y
x (mod n)  
(mod n)
   2y   2
    
y  2k  1
x x 

 

 

function luythua(x,y,n: int64): int64;
var z1 : longint;
begin
if y=0 then exit(1)
else begin
z := luythua(x, y div 2, n);
z1 := ((z mod n)*(z mod n)) mod n;
if (y mod 2 = 1) then
z1:= ((x mod n)*(z1 mod n)) mod n;
if z1 > n then z1:= z1 mod n;
exit(z1); end;
end;

Giải thuật 4: Kiểm tra nguyên tố Miller-Rabin
Mệnh đề Miller-Rabin:

8


Ý tưởng:
{ Phân tích số lẻ n: n-1=d.2s (d lẻ)
Lặp: ntest lần {
Chọn a thuộc{2,...,n-1};
Tính x = ad (mod n);
Nếu (x=1) or (x=n-1) về đầu lặp;
k=1;
Lặp: Trong khi kx = x2 (mod n);
Nếu x =1 then n là hợp số, kết thúc;
Nếu x =n-1: Thoát lặp trong;

k = k+1;

}

Nếu x <> n-1: n là hợp số, kết thúc;

}

n là nguyên tố
}
Giải thuật:
function is_prime4(n : int64): boolean;
var dem, r, a, x : int64; k: longint;
begin
if n=2 then exit(true);
if (n<2) or (n mod 2=0) then exit(false);
phantich(n-1,d,s);
9


randomize;
for k:=1 to ntest do begin
a := random(n-2)+2;
x := luythua(a,d,n);
if (x=1) or (x=n-1) then continue;
r := 1;
while r < s do begin
x:= luythua(x,2,n);
if x=1 then exit(false);
if x=n-1 then break;

inc(r);
end;
if x <> n-1 then exit(false);
end;
exit(true);
end;

A.1.3) Sàn số nguyên tố:
Bài toán: In ra các số nguyên tố trong [1; n].
Ý tưởng: Với bài toán này, ta có thể thử lần lượt các số m trong đoạn [1; n], rồi
kiểm tra tính nguyên tố của m dựa vào các giải thuật trên.
Thuật toán:
Procedure generate(N: longint);
Var m: longint;
Begin
For m:=2 to N do
If is_prime(m) then writeln(m);
10


End;
Nhận xét: Cách này đơn giản nhưng chạy chậm; để cải tiến, ta sử dụng tính
chất của số nguyên tố để loại bỏ trước những số không phải là nguyên tố và không
cần kiểm tra các số này; ta sử dụng sàn nguyên tố như sàn Eratosthene.
Ý tưởng: (sàn nguyên tố Eratosthene)
Trước tiên xóa bỏ số 1 ra khỏi tập các số nguyên tố; Số tiếp theo số 1 là số 2, là
số nguyên tố, xóa tất cả các bội của 2 ra khỏi bảng. Số đầu tiên không bị xóa sau số
2 (số 3) là số nguyên tố, xóa bội của 3,…. Thuật toán tiếp tục cho đến khi gặp số
nguyên tố lớn hơn


n thì dừng lại. Tất cả các số chưa bị xóa là số nguyên tố.

Thuật toán:
Procedure Eratosthene (N: Longint);
Const Max = 1000000;
Var i, j : longint;
prime: array[1..Max] of byte;
Begin
Fillchar(prime, sizeof(prime),0);
For i:=2 to trunc(sqrt(N)) do
If prime[i] = 0 then
Begin
j := i*i;
While j <= N do
begin
Prime[j] := 1;
j := j + i;
end;
End;
For i:=2 to N do
If prime[i] = 0 then writeln(i);
End;
11


A.2) Một số bài tập áp dụng
Bài 1: Nhập một mảng 2 chiều m dòng, n cột từ file BANGSO.TXT. Cấu trúc
file như sau: dòng đầu là 2 số m và n, cách nhau bằng dấu cách, m dòng sau, mỗi
dòng n số nguyên.
Hãy in ra những số là số nguyên tố của mảng.

Chương trình:
var m,n : integer;
a : array[1..100,1..100] of integer;
(* Nhập dữ liệu *)
procedure nhap;
var f : text;
i, j : integer;
begin
assign(f,'BANGSO.TXT'); reset(f);
readln(f,m,n);
for i := 1 to m do
for j := 1 to n do read(f,a[i,j]);
close(f);
end;
function ngto(k : integer): boolean;
var i : integer;
begin
ngto := false;
if k < 2 then exit;
for i := 2 to trunc(sqrt(k)) do
12


if k mod i = 0 then exit;
ngto := true;
end;
procedure inngto;
var i,j : integer;
begin
writeln('Cac phan tu nguyen to cua mang:');

for i := 1 to m do
for j := 1 to n do
if ngto(a[i,j]) then write(a[i,j],' ');
writeln;
end;
BEGIN
nhap;
inngto;
END.
Bài 2: (Sinh số nguyên tố) Tạo ra mọi số nguyên tố giữa hai số đã cho.
Input. Tệp SINH_NT.INP Dòng đầu tiên ghi số t là số test (t ≤ 10). Trong mỗi
t dòng tiếp theo là hai số m và n (1 ≤ m ≤ n ≤ 109, n-m ≤ 105) cách nhau một dấu
cách.
Output. Tệp SINH_NT.OUT với mỗi test ghi mọi số nguyên tố p sao cho
m ≤ p ≤ n, mỗi số một dòng, mỗi test cách nhau một dòng trống.
uses sysutils;
const ntest = 10;
fi = 'sinh_nt.inp';
fo = 'sinh_nt.out';
var m, n, p, d, s : int64;
i, t : integer;
13


start, stop : tdatetime;
f,g : text;

procedure phantich(x: int64; var d, s : int64);
begin
s := 0; d:=1;

while (x mod 2 =0) do begin
x := x div 2;
inc(s);
end;
d := x;
end;
function exponent1(x,y,n: int64): int64;
var z : int64;
begin
z := 1;
while (y>0) do begin
if (y mod 2=1) then z := (z* (x mod n)) mod n;
x := (x*x) mod n;
y := y div 2;
end;
exit(z);
end;
function exponent(x,y,n: int64): int64;
var z, z1 : int64;
begin
if y=0 then exit(1);
14


z := exponent(x, y div 2,n);
z := z*z mod n;
if (y mod 2=1) then
z := x*z mod n;
if z>n then z := z mod n;
exit(z);

end;
function prime(n : int64): boolean;
var dem, r, a, x : int64; k: longint;
begin
if n=2 then exit(true);
if (n<2) or (n mod 2=0) then exit(false);
phantich(n-1,d,s);
randomize;
for k:=1 to ntest do begin
a := random(n-2)+2;
x := exponent1(a,d,n);
if (x=1) or (x=n-1) then continue;
r := 1;
while rx:= exponent1(x,2,n);
if x=1 then exit(false);
if x=n-1 then break;
inc(r);
end;
if x<>n-1 then exit(false);
end;
exit(true);
end;
15


BEGIN
assign(g,fo); rewrite(g);
assign(f,fi); reset(f);
readln(f,t);

start := now;
for i:=1 to t do begin
readln(f,m,n);
p:=m;
while p<=n do begin
if prime(p) then writeln(g,p);
p := p+1;
end;
writeln(g);
end;
close(f); close(g);
stop := now;
writeln('Miller-Rabin, Time: ',(stop-start)*3600*24:0:5);
readln;
END.
Bài 3: (Kiểm tra tính nguyên tố) Số đó có là số nguyên tố hay không?
Input. Tệp YESNO.INP Dòng đầu tiên ghi số t là số các test, sau đó là t test
(t≤500): mỗi test trên một dòng chứa một số nguyên N (2 ≤ N < 231)
Output. Tệp YESNO.OUT với mỗi test, ghi trên một dòng một xâu “YES” nếu
số đã cho là nguyên tố, hoặc xâu “NO” trong trường hợp ngược lại.
uses sysutils;
const ntest = 10;
fi = 'yesno.inp';
fo = 'yesno.out';
var n, d, s : int64;
16


i, t : integer;
start, stop : tdatetime;

f,g : text;

procedure phantich(x: int64; var d, s : int64);
begin
s := 0; d:=1;
while (x mod 2 =0) do begin
x := x div 2;
inc(s);
end;
d := x;
end;
function luythua(x,y,n: int64): int64;
var z : int64;
begin
z := 1;
while (y>0) do begin
if (y mod 2 =1) then z := ((z mod n)* (x mod n)) mod n;
y := y div 2;
x := ((x mod n)*(x mod n)) mod n;
end;
exit(z);
end;
function prime(n : int64): boolean;
var dem, r, a, x : int64; k: longint;
begin
17


if n=2 then exit(true);
if (n<2) or (n mod 2=0) then exit(false);

phantich(n-1,d,s);
randomize;
for k:=1 to ntest do begin
a := random(n-2)+2;
x := luythua(a,d,n);
if (x=1) or (x=n-1) then continue;
r := 1;
while rx:= luythua(x,2,n);
if x=1 then exit(false);
if x=n-1 then break;
inc(r);
end;
if x<>n-1 then exit(false);
end;
exit(true);
end;
BEGIN
assign(g,fo); rewrite(g);
assign(f,fi); reset(f);
readln(f,t);
start := now;
for i:=1 to t do begin
readln(f,n);
if prime(n) then writeln(g,'YES ')
else writeln(g,'NO');
end;
18



close(f); close(g);
stop := now;
writeln('Miller-Rabin, Time: ',(stop-start)*3600*24:0:5);
readln;
END.

Bài 4: (Số nguyên tố vòng) số 993319 là nguyên tố vòng vì các số 993319,
933199, 331999, 319993, 199933, 999331, đều là số nguyên tố.
Cho trước hai số nguyên dương a và b. Tìm xem trong đoạn [a;b] có bao nhiêu
số nguyên tố vòng. Hạn chế: aInput. Tệp nt_vong.inp gồm một dòng ghi hai số a và b
Output. Tệp nt_vong.out ghi số lượng số nguyên tố vòng trong [a; b].
uses crt;
const fi = 'nt_vong.inp';
fo = 'nt_vong.out';
var

a, b, n, x : longint;

procedure chuyen(var s: string);
var code : integer;
begin
s := copy(s,2,length(s))+s[1];
end;
function prime(a: int64): boolean;
var i, sqrt_a: longint;
begin
if ((a=2) or (a=3)) then exit(true);
if ((a<2) or (a mod 2=0) or (a mod 3=0)) then exit(false);
sqrt_a := trunc(sqrt(a));

i:=5;
while (i<=sqrt_a) do begin
19


if ((a mod i = 0) or (a mod (i+2)=0)) then exit(false) else
inc(i,6);
end;
exit(true);
end;
function vong(x : longint): boolean;
var i, l : integer; s: string; code: integer;
begin
str(x,s);
l := length(s);
for i:=1 to l do begin
chuyen(s);
val(s,x,code);
if not prime(x) then exit(false);
end;
exit(true);
end;
BEGIN
for x:=10000000 to 100000000 do
if vong(x) then write(x: 16);
writeln;
writeln('xong');
readln;
END.


Bài 5: Cho số tự nhiên n  480.000. Hãy phân tích n! ra tích của các thừa số
nguyên tố theo trật tự tăng dần. Thí dụ, 13! = 210.35.52.7.11.13. Kết quả hiển thị
20


dưới dạng các dòng, mỗi dòng một số nguyên tố tiếp đến là số mũ tương ứng. Các số
trên cùng dòng cách nhau qua dấu cách. Thí dụ trên cho ta kết quả hiển thị như sau
2 10
3 5
5 2
7 1
11 1
13 1
Thuật toán
Nhận xét: Cho số tự nhiên N và một số nguyên tố p. Khi đó,
Nếu viết dãy thừa số 1, 2, ..., N vào một bảng có p cột thì ta thấy có n1 = N div
p dòng chứa p, 2p,...,n1.p (ở cột cuối cùng). Nhóm các phần tử này lại ta được,
1p.2p....n1p = (1.2...n1).pn1. Thực hiện tương tự với tích 1.2...n1 ta thu được n2 =
n1 div p dòng chứa p, 2p,...,n2.p... Từ đây ta suy ra lũy thừa k của p, pk trong dạng
phân tích của N! sẽ là k = n1+n2+...+nv, trong đó ni = ni-1 div p, n1 = N div p, nv = 0, i
= 2..v. Hàm tính lũy thừa của p trong dạng phân tích của N! bằng các phép chia liên
tiếp khi đó sẽ như sau,
function Power(n,p: longint): byte;
var k: byte;
begin
k := 0;
while (n <> 0) do
begin
n := n div p;
k := k + n;

end;
Power := k;
end;
Ta dùng hàm NextPrime để sinh lần lượt các số nguyên tố p trong khoảng 2..N
và tính Power(N,p). Nếu giá trị này lớn hơn 0 thì ta hiển thị kết quả.
21


procedure Fac(n: longint);
const bl = #32; { Dau cach }
var p: longint; k: byte;
begin
writeln;
p := 2;
while p <= n do
begin
k := Power(n,p);
if (k > 0) then writeln(p,bl,k);
p := NextPrime(p);
end;
end;
Hai hàm phụ trợ.
Hàm IsPrime(p) kiểm tra p có phải là số nguyên tố hay không bằng cách xét
xem trong khoảng từ 2 đến

p có ước nào không.

function IsPrime(p: longint): Boolean;
var i: longint;
begin

IsPrime := false;
if p < 2 then exit;
for i := 2 to round(sqrt(p)) do
if p mod i = 0 then exit;
IsPrime := True;
end;
Hàm NextPrime(p) sinh số nguyên tố sát sau p bằng cách duyệt tuần tự các số
lẻ sau p là p+2k nếu p lẻ và (p-1) + 2k, nếu p chẵn.
function NextPrime(p: longint): longint;
22


begin
if p < 2 then
begin
NextPrime := 2;
exit;
end;
if not odd(p) then p := p-1;
repeat
p := p+2;
until IsPrime(p);
NextPrime := p;
end;
Bài 6: Số nguyên tố cùng độ cao
Độ cao của một số tự nhiên là tổng các chữ số của số đó. Với mỗi cặp số tự
nhiên n và h cho trước hãy liệt kê các số nguyên tố không vượt quá n và có độ cao h,
10  n  1000000; 1  h  54.

hprimes.inp hprimes.out

nh

mỗi dòng 1 số

Dữ liệu test
n = 1000, h = 16. Kết quả 15 số nguyên tố độ cao 16: 79, 97, 277, 349, 367, 439,
457, 547, 619, 673, 691, 709, 727, 853, 907.
Thuật toán

Thuật toán liệt kê các số nguyên tố độ cao h trong khoảng 1..n
1. Gọi thủ tục Sang(n) (do Eratosthenes)
xác định các số nguyên tố trong khoảng 1..n
23


và đánh dấu vào mảng byte p: p[i] = 0 khi và chỉ khi i là số nguyên tố.
2. Duyệt lại các số nguyên tố i trong danh sách p, tính độ cao của số i.
Nếu Height(i) = h thì ghi nhận.
3. end.

Để tính độ cao của số i ta tách dần các chữ số hàng đơn của i bằng phép chia dư
cho 10 rồi cộng dồn vào một biến tổng.
Độ phức tạp
Thủ tục sàng duyệt

n lần, mỗi lần lại duyệt n phần tử do đó cần cỡ n n thao

tác cơ sở (phép nhân, phép gán).
Chương trình


(* Hprimes.pas – So nguyen to cung do cao *)
uses crt;
const fn = 'hprimes.inp'; gn = 'hprimes.out';
nl = #13#10; bl = #32; mn = 1000000;
type mb1 = array[0..mn] of byte;
var p: mb1;
n,h: longint;
procedure Sang(n: longint);
var i,j: longint;
begin
fillchar(p,sizeof(p),0);
for i := 2 to round(sqrt(n)) do
if (p[i] = 0) then
for j := i to (n div i) do p[i*j] := 1;
end;
function Height(x: longint): integer;
var sum : integer;
24


begin
sum := 0;
while x <> 0 do
begin
sum := sum + (x mod 10);
x := x div 10;
end;
Height := sum;
end;
procedure Ghi;

var i: longint;
g: text;
begin
assign(g,gn); rewrite(g);
for i := 2 to n do
if p[i] = 0 then
if Height(i) = h then writeln(g,i);
close(g);
end;
procedure Doc;
var f: text;
begin
assign(f,fn); reset(f);
readln(f,n,h); close(f);
end;
BEGIN
Doc; Sang(n); Ghi;
writeln(nl,' Fini'); readln;
END.

25