線性篩法的應用

         線性篩法最基礎的功能就是求[1,n]中的素數,以此爲基礎,可以對他進行一些變形。變形後的線性

篩法可以實現許多其他的功能。(下文中的tot均指一定區間內的質數個數)

         先看一道簡單的問題:求[1,n]中的m個數的最大質因子的序數(hdoj2136)
         這個問題可以利用線性篩法打一個質數表,然後二分答案,複雜度爲O(n+mlog(tot))。
         然而,如果m很大,算法必然會超時。我們可以對線性篩法進行一些變形,每次篩掉一個合數時,
         可以通過他的兩個約數推出他的最大質因子。這樣可以得到一個O(maxn+m)的算法。
Code:
program hdoj_2136;
type
        int=int64;
var
        i,j:longint;
        k,m,n:int;
        a,ans:array[1..1001000]of int;
        prime:array[1..100000]of int;
        tot:int;


function max(x,y:int):int;
begin
        if x>y then max:=x
                else max:=y;
end;


begin
        assign(input,'input.txt');reset(input);
        assign(output,'bb.out');rewrite(output);
        ans[1]:=0;
        n:=1000100;tot:=0;
        for i:=2 to n do begin
                if a[i]=0 then begin
                        inc(tot);prime[tot]:=i;
                        ans[i]:=tot;
                end;
                for j:=1 to tot do begin
                        if(prime[j]*i<=n)then begin
                                k:=prime[j]*i;
                                a[k]:=1;
                                ans[k]:=max(ans[i],j);
                        end else break;
                        if(i mod prime[j]=0)then break;
                end;
        end;
        while not eof do begin
                read(n);
                if n=0 then break;
                writeln(ans[n]);
        end;
        close(output);
end.




         同樣的,每次篩掉一個合數時,也可以記下他的最小質因子,這樣就可以在log(n)以內的時間內
給出一個數的質因數分解,也就可以同時求出phi(n)的值。
         再看一道問題:求出n!的質因數分解,輸出爲若干行,每行第一個數爲一個質數,第二個數爲它的次
數,時間限制爲兩秒。(n<=10^7)
         這道題很容易想到非線性的做法,即按以上的方法篩一遍,再一個一個的求質因數分解,最後加起
來。原題的數據範圍是3*10^6,用好一點的實現的話完全是可以過的。但是,這道題數據範圍更大,經測試
這種算法在我的電腦上需要2.5s以上才跑得出來,我們需要一個更優秀的算法。
         篩法已經是線性的了,算法的瓶頸在於分解質因數。
         分析一下算法一分解質因數的過程:對一個數k分解質因數時,每次找到他的最小質因數s,然後
k div s,繼續分解。我們發現,每次k都變成一個更小的合數,而這個合數本身在後面還要分解一次!!!
也就是說,對於k和2k,分解2k的時候把k也分解了一次,這是冗餘的!!!
         由於k*(s*k)的質因數分解等於k^2的質因數分解加上s的質因數分解,因此我們可以構造一個線性
算法。開一個數組b,初值賦爲1。令指針i從後往前掃,每次掃到一個數k,若k是質數,跳過他。否則我們
可以知道k=s*(k div s),b[s]=b[k]+b[s];b[k div s]=b[k div s]+b[k]。這樣,我們就可以在線性時間內
求出n!的質因數分解了。
算法一的程序:
program al_1;
type
        int=int64;
var
        i,j:longint;
        k,m,n:int;
        a,b:array[1..10000000]of longint;
        prime:array[1..1000000]of longint;
        tot:longint;
begin
        assign(output,'ans.txt');rewrite(output);
        read(n);
        tot:=0;
        for i:=2 to n do begin
                if a[i]=0 then begin
                        inc(tot);prime[tot]:=i;a[i]:=i;
                end;
                for j:=1 to tot do begin
                        k:=prime[j]*i;
                        if k<=n then a[k]:=prime[j]
                                else break;
                        if i mod prime[j]=0 then break;
                end;
        end;
        for i:=2 to n do begin
                if a[i]=i then continue;
                k:=i;
                while k<>1 do begin
                        inc(b[a[k]]);
                        k:=k div a[k];
                end;
        end;
        for i:=1 to tot do writeln(prime[i],' ',b[prime[i]]+1);
        close(output);
end.


算法二的程序:
program al_2;
type
        int=int64;
var
        i,j:longint;
        k,m,n,tot:int;
        a,b:array[1..10000000]of longint;
        prime:array[1..1000000]of int;
begin
        assign(output,'ans.txt');rewrite(output);
        n:=10000000;
        tot:=0;
        for i:=2 to n do begin
                if a[i]=0 then begin
                        inc(tot);prime[tot]:=i;
                end;
                for j:=1 to tot do begin
                        k:=prime[j]*i;
                        if k<=n then a[k]:=i
                                else break;
                        if i mod prime[j]=0 then break;
                end;
                b[i]:=1;
        end;
        for i:=n downto 2 do begin
                if a[i]=0 then continue;
                k:=b[i];
                inc(b[a[i]],k);inc(b[i div a[i]],k);
        end;
        for i:=1 to tot do writeln(prime[i],' ',b[prime[i]]);
        close(output);
end.


我用來測時間的程序:
program time;
uses dos;
var
        h1,h2,m1,m2,s1,ss1,s2,ss2:word;
procedure get(filename:string);
begin
        gettime(h1,m1,s1,ss1);
        exec(filename,'');
        gettime(h2,m2,s2,ss2);
        write(filename,' ',(3600*(h2-h1)+60*(m2-m1)+s2-s1+(ss2-ss1)/100):0:2);
        writeln('s');
end;
begin
        get('a.exe');
        get('b.exe');
end.

2012-11-15

線篩預處理約束個數:

        for i:=2 to m do begin
                if v[i]=0 then begin
                        inc(tot);Prime[tot]:=i;
                        mi[i]:=1;g[i]:=2;
                end;
                for j:=1 to tot do begin
                        k:=Prime[j]*i;
                        if k>m then break;
                        if i mod Prime[j]=0 then begin
                                mi[k]:=mi[i]+1;
                                g[k]:=(g[i]div mi[k])*(mi[k]+1);
                                v[k]:=1;
                        end else begin
                                g[k]:=g[i]*2;
                                mi[k]:=1;
                                v[k]:=1;
                        end;
                end;
        end;


poj 3421線性篩法+排列組合,O(n)預處理,O(1)回答

Code:

program poj3421;
type int=longint;
const maxn=1<<20;
var
        i,j,k,m,n:int;
        f,v,prime,a,b:array[1..maxn]of int;
        ans:array[1..maxn]of int64;

procedure push(x,y:int);
begin
        v[y]:=x;f[y]:=f[y div x]+1;
        ans[y]:=ans[y div x]*f[y];
        if y mod(x*x)=0 then begin
                b[y]:=b[y div x]+1;
                ans[y]:=ans[y]div b[y];
        end else b[y]:=1;;
end;

begin
        f[1]:=0;ans[1]:=1;
        for i:=2 to maxn do begin
                if v[i]=0 then begin
                        inc(m);Prime[m]:=i;
                        f[i]:=1;ans[i]:=1;
                        b[i]:=1;
                end;
                for j:=1 to m do begin
                        k:=prime[j];
                        if i*k>maxn then break;
                        push(k,i*k);
                        if i mod k=0 then break;
                end;
        end;
        while not seekeof do begin
                read(n);
                writeln(f[n],' ',ans[n]);
        end;
end.



BY QW

轉載請註明出處

發佈了45 篇原創文章 · 獲贊 5 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章