在程式設計競賽中,如果要求確定某一範圍內的素數,乙個常用的方法是使用素數篩。在使用素數篩的過程中,需要記錄某個數是否是素數,常見的方法是使用乙個整數陣列來記錄,例如:
在上述**中,使用整數陣列const
int maxn =
10000010
;int primes[maxn]
, cnt;
void
sieve()
}
primes
來標記某個數 x
xx 是否為素數,如果 x
xx 不是素數,則標記primes[x] = 1
,否則primes[x] = 0
。與此同時,為了節省儲存空間,篩選得到的素數也存放在 pri
me
sprimes
primes
中,因為不是所有整數都是素數,因此這樣的做法沒有問題。
可以看到,使用上述的方法來標記是否為素數,與所求素數的範圍有關,由於 32
3232
位整數佔 4
44 位元組儲存空間,故所需的儲存空間為:maxn * 4
位元組。
可以將標記陣列換成bool
型別的陣列,比如:
由於從 1const
int maxn =
10000010
;bool flag[maxn]
;int primes[
700000
], cnt;
void
sieve()
}
11 到 10000010
10000010
100000
10之間,素數的個數不超過 70
7070
萬個,這樣空間花費約為maxn
位元組(當maxn
較大時,儲存素數所使用的空間基本可以忽略不計)。
進一步地,可以使用位來標記某個整數是否為素數。例如,利用 c++
\operatorname
c++ 提供的bitset
序列容器:
const
int maxn =
10000010
;bitset flag;
int primes[
700000
], cnt;
void
sieve()
}
bitset
在內部實現中是採用整數陣列的方式,整數陣列可以視為一長串的位組合,bitset
通過位運算判斷序號為 i
ii 的位是否為 1
11 來確定flag.test(i)
的值。那麼我們可以進一步地予以簡化,因為在素數篩中,只應用了bitset
的判斷和標記兩項功能,其他功能並未應用。
要簡化bitset
的功能,關鍵是將序號 x
xx 對映為整數陣列中某個元素的某個特定位。舉個例子,乙個整數陣列中,每個元素都是乙個整數,佔 4
44 個位元組,即乙個陣列元素有 32
3232
個位,可以標記 32
3232
個整數。
也就是說,b[0const
int maxb =
100000010
;int b[maxb >>5]
;
]b[0]
b[0]
中的 32
3232
個二進位制位可以用於標記整數 0
00 至 31
3131
是否為素數,b[1
]b[1]
b[1]
中的 32
3232
個二進位制位可以用於標記整數 32
3232
至 63
6363
是否為素數……依此類推。為了方便的將序號 x
xx 對映到整數陣列 b
bb 中的某個位,定義以下的兩個巨集:
由於每 32#define get(x) (b[x >> 5] & (1 << (x & 0x1f)))
#define set(x) (b[x >> 5] |= (1 << (x & 0x1f)))
3232
個整數對應 b
bb 中的乙個元素,因此整數 x
xx 所對應的 b
bb 中的元素為b[x / 32]
,即b[x >> 5]
,接下來需要確定 x
xx 在b[x >> 5]
對應著哪乙個二進位制位。由於是每 32
3232
個整數「一段」,整數 x
xx 在第 x/32
x / 32
x/32
段,xx
x 在段內的序號就是 x
xx 除以 32
3232
後所得的餘數,使用位運算來表示的話,就是 x
xx 對應二進位制數的最後 5
55 個二進位制位的值,因此,x & 0x1f
表示整數 x
xx 在段內的序號,結合獲取單個二進位制位值的方法,容易理解(b[x >> 5] & (1 << (x & 0x1f)))
就可以獲取 x
xx 所對應的二進位制位值。
對一位讀者留言的回覆,並在此重申我的原則。
一位讀者對我的翻譯提出了一些比較嚴肅的建議。當然,我也知道 忠言逆耳,良藥苦口 但是有些原則想在這裡再次重申一下。譯者你不要生氣,我只是客觀地說一下我看您的譯作的感受。如果您看過錢歌川先生的 翻譯的技巧 或者讀過任何一本大學英漢翻譯教科書,我想您的作品不至於這麼晦澀和支離破碎。翻譯是一項系統工程,您...