素數尋找問題由來已久,一直是一些數學家追求的目的。關於素數的定義及性質,我就不在這裡多敘了,相信大家都對此了如指掌。素數的尋找思路比較的簡單,根據素數的性質(素數應該不能被除了1和它自身的其他數整除)我們可以從最小的素數2開始,一直到比它小1的數為止,用這些數去整除它,如果它能被整除則它必定不是素數,這是判斷單個素數的方法(這個算法思想最簡單,時間復雜度最大)。對於尋找比某一個給定的整數值小的所有素數也可以采用這種方法,不過我們會發現,采用這種單個判斷的方法所耗的時間比較多。比如查找不大於10的素數,我們必須從2開始一個個判斷,共需判斷9個數,事實上按照我們後面講述的方法,只需循環2次就可以了。因此,下面的兩種方法都將基於刪除法來做。
我們來看看刪除法的思想:
1.將小於給定整數值n的所有正整數加到一個數組中;
2.刪除能夠被一些整數整除的數;
3.數組中遺留的元素就是最後要得到的素數序列。
對於第二步,我們將給出兩種方法來實現。我們先來看看算法:
算法一:
class prime
{
public static int[] PrimeList;
public static void FindPrime(int n)
{
int[] IntList;
IntList=new int[n];
for (int p=2;p<=n;p++) IntList[p-1]=p;
for (int p=2;p<Math.Sqrt(n);p++)
{
int j=p+1;
while (j<=n)
{
if ((IntList[j-1]!=0 ) && ((IntList[j-1]% p)==0) ) IntList[j-1]=0;
j=j+1;
}
}
int i=0;
for (int p=2;p<=n;p++)
{
if (IntList[p-1]!=0) i=i+1;
}
PrimeList=new int[i];
i=0;
for (int p=2;p<=n;p++)
{
if (IntList[p-1]!=0)
{
PrimeList[i]=IntList[p-1];
i=i+1;
}
}
}
}
這這個算法中,刪除的數是那些被從2開始直到n的平方根的整數整除的數。這個算法比起前面介紹的單個素數的尋找方法要好,它的循環次數減少了一多半,但是這個算法還不是最理想的:
1.例如,6既能被2整除,也能被3整除,那麼當p=2時,6被刪掉了一次;當p=3時,6又被刪除了一次,雖然按照我們設定的算法規則,這不會導致沖突(通過判斷IntList數組元素是否為0,若為0就不必重復刪除),但是這會使得算法的效率低下。
2.還有計算素數序列元素個數時,我們也走了彎路。第一步,我們先計算出了數組元素大小,第二步才開始賦值,事實上這兩步我們可以減去計算數組大小這一步,可以把它放在前面完成。
3.已經被刪除了的元素,也就是那些不是素數的元素,可以不用拿他們去整除整數,例如4不用拿去整除8,因為能被4整除的數肯定能被2整除,已經在前面循環中被刪除了。
基於上述考慮,我們得到了一個效率更加高的算法:
class primegood
{
public static int[] PrimeList;
public static void FindPrime(int n)
{
int[] IntList;
int len=n-1;
IntList=new int[n];
for (int p=2;p<=n;p++) IntList[p-1]=p;
for (int p=2;p<Math.Sqrt(n);p++)
{
if (IntList[p-1]==0) continue;
int j=p*p;
while (j<=n)
{
if (IntList[j-1]!=0 )
{
IntList[j-1]=0;
len=len-1;
}
j=j+p;
}
}
PrimeList=new int[len];
int i=0;
for (int p=2;p<=n;p++)
{
if (IntList[p-1]!=0)
{
PrimeList[i]=IntList[p-1];
i=i+1;
}
}
}
}
這個算法思想和前面的算法完全一樣,不過改正了上面算法中不完善的一些內容。
為了說明這兩個算法的效率區別,我們編制了如下的主程序來比較一下他們的差異:
static void Main()
{
Console.WriteLine("Start!");
DateTime mytime5=DateTime.Now;
primegood.FindPrime(100000);
/*for (int i=0;i<=primegood.PrimeList.Length-1;i++)
{
Console.WriteLine(primegood.PrimeList[i]);
}*/
DateTime mytime6=DateTime.Now;
TimeSpan timeadd3=mytime6-mytime5;
Console.WriteLine(timeadd3.Ticks);
DateTime mytime1=DateTime.Now;
prime.FindPrime(100000);
DateTime mytime2=DateTime.Now;
TimeSpan timeadd=mytime2-mytime1;
DateTime mytime3=DateTime.Now;
primegood.FindPrime(100000);
DateTime mytime4=DateTime.Now;
TimeSpan timeadd2=mytime4-mytime3;
Console.WriteLine(timeadd.Ticks);
Console.WriteLine(timeadd2.Ticks);
}
}
通過運行這個程序,可以發現他們的差別是如此的大(前面的算法所耗時間幾乎是後面算法的30-60倍)
事實上,這兩個算法的時間復雜度近似為:⊙(n1.5);⊙(n);可見,對於同一個問題有著多種不同復雜性的算法實現,算法設計是一門十分重要的學問。