延伸:
1. 為什麼在程式工作過程中,如果sieve[i]為KEPT時,
2i+3就是一個質數?
2. 請分析一下程式一共查過了多少個元素。
3. 連3的倍數都不處理的話,每6個數剩下的就只有6n+1, 6n+5而已,
換句話說只要處理原來的三分之一的元素就行了。
請把這個觀念寫成程式。
額外(個人討論):
4.將程式改寫成只篩到N/2就結束,同樣要輸出質數及總數目。
個人解答:
1. 在查到第i個的時候,表示已經用所有前面的數篩過了,
既然到這裡沒有被篩掉,那麼就表示其沒有質因數,
所以2i+3就是一個質數。
2. 首先程式要每一個數經過(共n次),
再來每次濾掉2i+3的倍數,
就是n/3 + n/5 + n/7 + ...也就是n*(n以內質數的倒數和-1/2)。
兩者相加就剛好是n*(n以內的質數的倒數和)。
n越大的時候其實它會發散就是了= =......
3.
對於前一個程式而言,x[i]=2i+1,
我們每一次移動1單位的index跳的值是2。
如果我們要直接去用6i+1和6i+5來記錄,
單位的移動會變得很難掌握。
我們可以改成,
一開始將全部都設為KEPT,
接著把所有3的倍數再設為DELETED。
除3以外剩下為KEPT的是5、7、11、13、17、...
也就是從x[1]開始每次值是2、4、2、4的移動,
index則是1、2、1、2的移動。
換句話說,
在x[]中的初始樣態應該長這樣:
index: 0 1 2 3 4 5 6 7 8 9 10 11
代表值: 3 5 7 9 11 13 15 17 19 21 23 25
K/D: K K K D K K D K K D K K
接下來,在迴圈中的移動,
我們就直接跳著來做篩選就好,
比照之前的dist,這次初始的i是1,
dist是1,做完後要以dist = 3-dist來改變位移量。
同時參照4.來控制範圍,節省的更多。
Code部分的變化:
int range = n/2, dist = 1, cnt = 1, KEPT = 1, DELETED = 0; for(i = 0; i <= n; i++) x[i] = KEPT; for(i = 3; i <= n; i+=3) x[i] = DELETED; i = 1; while( i <= range ) { if(x[i]) // If KEPT -> start to sieve { tmp = i+i+3; // Each time index moves 2i+3 for(j = i+tmp; j <= n ; j += tmp) x[j] = DELETED; } i += dist; dist = 3 - dist; }
4. 宣告一個變數range = n/2,並且將for迴圈條件改成for(i = 0; i <= range; i++)。
篩完以後,再使用迴圈加總count。
相較於原程式,多了一次走訪全陣列,少去了篩大於n/2的倍數,
整體而言,當n越大,這個方法就越節省時間。
額外增加的Code:
for(i = 0; i <= n; i++) if(x[i]) cnt++;
第4的話
回覆刪除我認為特地在外面加一個i<=range的迴圈沒有什麼必要也加快不了什麼速度
因為原本的Seive()的13行: for(j = i+tmp; j <= n ; j += tmp)
若是j>n/2的情況,只要簡單加一次,在程式碼中j<=n就會被判定跳離迴圈...也就是迴圈不會執行,只是一個if的作用
因此你第四題那樣的寫法只是節省一些if的時間,應該是差不了多少
這是我的想法
---------------------
若要加快速度,我想到了一個點
在篩7的倍數的時候,7x3,7x5都不用篩了,因為他們分別是3和5的倍數,之前一定篩過,所以從7x7開始篩即可
即,篩x的倍數的時候,從x倍的x開始篩
如果沒有要算count的話,
刪除就會省掉一半的全走訪XD
因為原本的code對每個if(x[i])成立的狀況下,
都最少會做一次tmp = i+i+3以及j = i+tmp的動作,
儘管第一次判斷完>n就會跳出去,前面的加法做掉了還是很傷。
篩倍數時省掉前面的倍數是一個的點,
而且這個想法其實已經含有一點後面準備要提的線性篩法的概念了XD
每次篩的首項為(2i+3)(2i+3),只要把j的初始值改過來就可以了喵!
註:
刪除相較於(2i+3)來說,
移動了(2i+2)(2i+3),也就是(i+1)(2i+3)個index。
篩的首項"值" = i+(i+1)*tmp
恩的確是可以省你說的那兩個加法!(不過我是覺得差不了多少...XDD 但這似乎就要實驗了)
回覆刪除還有改該篩的首項也是那樣沒錯~!