Bài toán số nguyên tố tối ưu

8 1.2K 12
Bài toán số nguyên tố tối ưu

Đang tải... (xem toàn văn)

Thông tin tài liệu

Bài toán số nguyên tố tối ưu

Tối ưu các bài toán về số nguyên tốĐinh Hữu CôngTrong số 11-2001 tácgiả Nguyễn Văn Trường đã giới thiệu các thuật toán về số nguyên tố.Nhưng một số thuật toán này còn bị hạn chế về thời gian và bộ nhớvới n lớn. Tuỳ vào từng bài toán cụ thể ta có thể tối ưu hoá nhữngthuật toán này dựa vào những nhận xét và chứng minh toán học. Bài toán 1: Cho số tựnhiên n. Hãy phân tích số n thành tích các thừa số nguyên tố Input: file Phantich.inp với số n duy nhất (n < 231) Ouput: file Phantich.out. Dòng 1 là k, số thừa số nguyên tố khác nhautrong phân tích. K dòng tiếp theo dòng thứ i gồm 1 cặp số (p,q) cáchnhau 1 dấu trắng trong đó p là thừa số nguyên tố và q là số mũ tươngứng của nó trong phân tích (q > 0). Thuật toán của bài này rất đơn giản: + Cho biến chạy Temp=0, + Lặp: Chừng nào temp≤n thì làm - Temp:=số nguyên tố liền sau Temp; - While (n mod Temp=0) do Begin n:=n div Temp; Tăng số mũ của Temp; End; Hiệuquả của cài đặt chủ yếu phụ thuộc vào công đoạn tìm số nguyên tốliền sau Temp. Cách nhanh nhất là dùng 1 mảng để lưu trữ các số nguyêntố từ 1 đến n. Với n=1 tỷ ta phải lưu (109) = 50.847.534 số nguyên tố nên ta không thể có đủ bộnhớ để thực hiện điều này. Một cách khác là ta tuần tự tăng biếnTemp và kiểm tra Temp có nguyên tố không, cách này không tốn bộ nhớnhưng không khả thi vì thời gian thực hiện quá lâu. Một cách tự nhiên,ta muốn dung hoà cả hai phương pháp trên. Vì vậy ta sẽ tìm cách giảmkhối lượng lưu trữ và số lần kiểm tra nguyên tố: Bước 1: Kiểm tra n. Nếu n nguyên tố hoặc n=1thìvấn đề trở nên quá đơn giản. Nếu n là hợp số thì chuyển sang bước2. Bước 2: Vì n là hợp số nên ta phải có n=a*b(1<a ≤ b) suy ra a ≤ sqrt(n)≤ 215,5 < 1. Vậy ta chỉ cần lưu các số nguyên tố nhỏ hơn 6341 Vìn< 231 nên số thừa số nguyên tố nhỏ hơn 10 (2*3*5* 31 >231), do đó ta chỉ phải kiểm tra nguyên tố khoảng 10 lần Chươngtrình của bài này như sau: {$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q+,R+,S+,T-,V+,X+} {$M65500,0,655360} {Phantich so n<2^31 thanh thua so nguyen to} usescrt; constmaxPrime=46341; {max Prime = } Fin=′phantich.inp′; Fou=′phantich.out′; var p:array[1 10] of longint; q:array[1 10] of integer; { Số thừa số nguyên tố lớn nhất là 10?} Prime:array[0 maxPrime+1] of boolean; n:longint; count:integer; temp:word; f,g:text; procedureEratosten; vari,j:word; begin Fillchar(Prime,sizeof(Prime),true); Prime[0]:=false; Prime[1]:=false; for i:=2 to trunc(sqrt(maxPrime)) do if Prime[i] then for j:=2 to (maxPrime div i) do Prime[i*j]:=false; end; functionisPrime(n:longint):boolean; vari,step:word; begin isPrime:=false; if (nbegin if Prime[n] then isPrime:=true; exit; end; if ((n mod 2=0) or (n mod 3=0)) then Exit; i:=5; step:=2; while (i≤sqrt(n)) do begin if (n mod i=0) then exit; i:=i+step; step:=6-step; end; isPrime:=true; end; procedureSolve; begin Assign(f,Fin); Reset(f); Readln(f,n); Close(f); Assign(g,Fou); Rewrite(g); if n<2 then begin writeln(g,0); Close(g); Halt; end; temp:=2; count:=0; Fillchar(p,sizeof(p),0); Fillchar(q,sizeof(q),0); while (n<>1) do begin if isPrime(n) then begin Inc(count); p[count]:=n; q[count]:=1; exit; end else begin while ((n mod temp<>0) and (temp< i>begin Inc(temp); while not Prime[temp] do Inc(temp); end; Inc(count); p[count]:=temp; while (n mod temp=0) do begin Inc(q[count]); n:=n div temp; end; end; end; end; procedureResult; vari:integer; begin Writeln(g,count); for i:=1 to count do Writeln(g,p[i],' ',q[i]); Close(g); end; BEGIN Eratosten; Solve; Result; END. Saukhi làm bài toán 1 ta sẽ dễ dàng làm được bài toán số 2 trong đềkhối 10 cuộc thi Olympic 30-4 lần VIII. Đề bài như sau: Cho số nguyên dương n (1 < n < 231). Tìm số nguyêndương a nhỏ nhất sao cho a a chia hết cho n. Input: file Chiahet.inpchứa 1 số duy nhất n Output: file Chiahet.out chứa số a Bàitoán 2: Tìm số mũ của số nguyên tố p trong cách phân tích của n! ra thừa sốnguyên tố (n ≤ 231) Cũng như bài toán 1, việc duyệt các số nguyên là bội của p từ1 đến n để chia cho p tìm số mũ rồi lấy tổng các số mũ này là khôngthể thực hiên được. Ví dụ: Nếun=231 còn p = 2 thì ta phải duyệt qua (n div p) số, đại lượngnày bằng 230 =1.073.741.824 tức là hơn 1 tỷ số cần phải duyệt.Nội chỉ việc cho biến i chạy qua 230 số này mà chưa làm gìcả cũng đã mất khoảng 10 giây rồi (nếu không tin bạn hãy viết chươngtrình chỉ gồm 1 lệnh cho i chạy từ 1 đến 1 tỉ không làm gì cả vàviết hàm đếm thời gian xem). Như vậy ta cần phải tìm 1 cách khác để tính số mũ của p. Gọisi là số số chia hết cho pi trong khoảng 1 n. Số này bằng tổng của số các số chia hết cho p, p2, p3…trong khoảng 1 n. Dễ thấy số mũ của p là s1+s2+…+sk với k lớn nhất sao cho pk≤ n Vídụ: n=29 vàp =3 thì k=3, s1 =9, s2 =3, s3 =1 → Số mũ của 3 là s1+s2+s3 =13 Mặtkhác các số chia hết cho pi trong khoảng 1 n là: 1* pi, 2*pi, 3* pi, …, * pi → có số. Đây chính là nội dung củađịnh lý Lơgiăngđrơ: Số mũ của số nguyên tố p trong phân tích n!thành các thừa số nguyên tố là: Đểý rằng (Số số chia hết cho pi+1trong khoảng 1 n bằng số số chia hết cho pi trong khoảng 1 (ndiv p)). áp dụng điều này với i=1 ta có được chươngtrình hàm tính số mũ của p trong phân tích ra thừa số nguyên tố củan! functionSoMu(p:longint;n:longint):integer; varcount:integer; begin count:=0; while n<>0 do begin n:=n div p; Count:=count + n; end; SoMu:=count; end; Vớik đủ lớn ta có hay n < pk. Vì vậy hàm SoMu chỉ phải vào vòng lặp k lầnvới k là số nguyên nhỏ nhất sao cho n < pk Cụthể trong bài này trường hợp lớn nhất là n = 231; p = 2 thì k = log2231 = 31 so với 230 (nhỏ hơn34 triệu lần) ! Bài toán 3: Tìm số chữ số 0 tận cùng và chữ số tận cùng khác 0 của n! (n< 231). Dữ liệu nhập từ bàn phím số n và in kết quả sốchữ số 0 tận cùng và chữ số tận cùng khác 0 của n! ra màn hình Cơsở thuật toán: Ta thấy n! khi phân tích ra thừa số nguyên tố thìcó dạng n! = 2SoMu(2,n) * 5SoMu(5,n) *… Trong công thức Lơgiăngđrơ nếu p1< p2 thìSoMu(p1,n) ≥ SoMu(p2,n) nên số chữ số 0 tận cùng của n! chính làSoMu(5,n). Đặt m = SoMu(2,n) - SoMu(5,n).Bỏ đi các chữ số 0 tận cùng thì ta có: Chữ số tận cùng khác 0 cua n! = [(2m mod 10) *TanCung(n)] mod 10 với TanCung(n) là chữ số tận cùng của n! đã bỏ đihết số mũ của 2 và 5. Dễ thấy 2m mod 10 chỉ phụ thuộc vào(m mod 4), nếu m mod 4 = 0, 1, 2, 3 thì 2m mod 10 = 6, 2, 4, 8 (trừtrường hợp m = 0). Công việc còn lại là tìm hàm TanCung(n) (Nếu bâygiờ chỉ nói tận cùng thì ta hiểu là số đó đã được chia cho 2 và5 đến dư). Ví dụ: Tận cùng của 10 là 1, của 14 là 7 … Nếun tương đối nhỏ thì ta có thể duyệt bằng 1 vòng for để tìm tận cùngcủa n!. Nhưng n có thể lên tới 2 tỷ nên ta xây dựng hàm TanCung(n) nhưsau (với ví dụ n = 27):Nếu n < 10 thì duyệt bình thường. Xét n ≥10, ta tính tận cùng của tích (n mod 10) số liêntiếp từ * 10 + 1 đến n (temp=21*22*23*24*25*26*27=21*11*23*3*1*13*27=1*1*3*3*1*3*7=9) temp:=1; fori:=n+1-n mod 10 to n do begin tg:=i; while not ođ(tg) do tg:=tg shr 1; while (tg mod 5=0) do tg:=tg div 5; temp:=temp*(tg mod 10) mod 10; end; temp:=temp mod 10; n:=n-n mod 10; VậyTanCung(n) = [temp*TanCung(n − n mod 10)] mod 10. Gánlại n:= n − n mod 10(n = 20). Ta phân hoạch các số từ 1 đến n thành 3phần: -Các số không chia hết cho 2 và 5: Các số này có tận cùng là 1, 3,7, 9 ( 1, 3, 7, 9, 11, 13, 17, 19 ) mỗi loại có (n div 10) số nên tận cùngcủa tích các số này bằng tận cùng của (1*3*7*9)n div 10 = 9n div 10. Nếu (n div 10) chẵnthì số này bằng 1, lẻ thì bằng 9 (n=20, số này là 1) if ođ(n div 10) then temp:=temp*9;{else temp:=temp*1} (n=20 → temp=1*9=9) -Các số là bội của 2 có dạng 2, 2*2, 2*3,…, 2*(n div 2) (2, 4, 6, 8, 10,12, 14, 16, 18, 20) Tận cùng của tích này cũng chính là tận cùng của1*2*3*…*(n div 2) =TanCung(n div 2) (Vì đã loại đi tất cả các thừa số2) (n=20ta có 1*2*3*…*10 = TanCung(10)) -Các số là bội của 5 nhưng lẻ (không là bội của 2) có dạng 5, 5*3,5*5…5*t (t lẻ sao cho 5*t < n) (n=20 → t=3;gồm 2 số: 5, 15). Tận cùng của tích này bằng tận cùng của tích các số lẻ 1*3*5*7*…*t (n=20 là 1*3) (vì đã loại đi tất cả các thừa số 5). Ta tính nó bằng hàm TanCungLe(n) (Tận cùng của các số lẻ không quá n) Theo cách phân hoạch ta có (các số n sau là đã gán lại n = n− n mod 10): TanCung(n):=temp*TanCung(n div 2)*TanCungLe(n div 5) mod 10 TanCung(27)=9*TanCung(10)*TanCungLe(4) mod 10 Cách xây dựng hàm TanCungLe(n) cũng tương tự như trên: n <10 duyệt bình thường. Xét n ≥10, tính tận cùng của các số lẻ trong khoảng (n-n mod 10) n lưu vào biến temp. Gán lại n:= n − n mod 10. Ta phân hoạch các số lẻ từ 1 n thành 2 phần: -Các số không chia hết cho 5 có tận cùng la 1, 3, 7, 9 mỗi loại có (ndiv 10) số tính như phần trên if ođ(n div 10) then temp:=temp*9;{else temp:=temp*1} -Các số là bội của 5 có dạng 5, 5*3, 5*5,…5*t (t lẻ sao cho 5*t < n) Tậncùng tích t số này chính là TanCungLe(n div 5). Tacó TanCungLe(n)=temp * TanCungLe((n- n mod 10) div 5) mod 10; Chương trình bài này cài đặt như sau: {Timchu so cuoi cung khac khong cua n!} {$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q+,R+,S+,T-,V+,X+} {$M65500,0,655360} programChu_so_tan_cung_khac_0_cua_n_giai_thua; usescrt; varn,m,tc:longint; functionTanCungLe(n:longint):integer; vartemp,tg,i:longint; begin if n<=10 then begin temp:=1; for i:=1 to n do if (ođ(i) and (i<>5)) then temp:=temp*i; TanCungLe:=temp mod 10; Exit; end; if not ođ(n) then n:=n-1; if ođ(n div 10) then temp:=9 else temp:=1; for i:=n+1-n mod 10 to n do if ođ(i) then begin tg:=i; while (tg mod 5=0) do tg:=tg div 5; temp:=temp*(tg mod 10) mod 10; end; temp:=temp mod 10; n:=n-n mod 10; TanCungLe:=temp*TanCungLe(n div 5) mod 10; end; functionTanCung(n:longint):integer; vartemp,tg,i:longint; begin if n<=10 then begin temp:=1; for i:=1 to n do begin tg:=i; while not ođ(tg) do tg:=tg shr 1; if (tg mod 5 =0) then tg:=tg div 5; temp:=(temp*tg); end; TanCung:=temp mod 10; Exit; end; if ođ(n div 10) then temp:=9 else temp:=1; for i:=n+1-n mod 10 to n do begin tg:=i; while not ođ(tg) do tg:=tg shr 1; while (tg mod 5=0) do tg:=tg div 5; temp:=temp*(tg mod 10) mod 10; end; temp:=temp mod 10; n:=n-n mod 10; TanCung:=temp*TanCung(n div 2)*TanCungLe(n div 5) mod 10; end; functionSoMu(p:longintr;n:longint):longint; varcount:longint; begin count:=0; while n<>0 do begin n:=n div p; Count:=count+n; end; SoMu:=count; end; procedureSolve; begin Write('N=');Readln(n); m:=SoMu(2,n)-SoMu(5,n); if m=0 then tc:=1 else case (m mod 4) of 0 : tc:=6; 1 : tc:=2; 2 : tc:=4; 3 : tc:=8; end; tc:=(tc*TanCung(n)) mod 10; Writeln('Chu so tan cung khac 0 cua n! la : ',tc); end; BEGIN Clrscr; Solve; Readln; END. Với n < 231 bất kì thì thời gian tính toánkhông quá 1 giây. Nếu làm bình thường bằng cách duyệt như với n ≤ 10 ở cài đặt trênthì chạy n = 10 triệu mất khoảng 73 giây (Gấp nhau đến hàng trăm lần).Như đã thấy, với cùng một bài toán vấn đề nhưng thuật toán và cáchcài đặt khác nhau thì hiệu quả của chương trình cài đặt có thể sẽkhác biệt nhau rất rõ ràng. Vì vậy những kiến thức toán học dể chứngminh thuật toán và chương trình là rất cần thiết khi ta muốn tối ưuhoá một thuật toán nào đó. Lần sau tôi sẽ xin trình bày với các bạnvề những kiến thức số học được ứng dụng trong giải các bài toántin. . Tối ưu các bài toán về số nguyên tố inh Hữu CôngTrong số 11-2001 tácgiả Nguyễn Văn Trường đã giới thiệu các thuật toán về số nguyên tố. Nhưng một số. số nguyên tốliền sau Temp. Cách nhanh nhất là dùng 1 mảng để lưu trữ các số nguyêntố từ 1 đến n. Với n=1 tỷ ta phải lưu (109) = 50.847.534 số nguyên tố

Ngày đăng: 07/09/2012, 10:55

Từ khóa liên quan

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

  • Đang cập nhật ...

Tài liệu liên quan