にエラトステネスのふるいを最適化された移植私はここで見つけるPythonで(高速連射)primesieve使用前にいくつかの時間:今は、PythonからC++
def primes2(n):
""" Input n>=6, Returns a list of primes, 2 <= p < n """
n, correction = n-n%6+6, 2-(n%6>1)
sieve = [True] * (n/3)
for i in xrange(1,int(n**0.5)/3+1):
if sieve[i]:
k=3*i+1|1
sieve[ k*k/3 ::2*k] = [False] * ((n/6-k*k/6-1)/k+1)
sieve[k*(k-2*(i&1)+4)/3::2*k] = [False] * ((n/6-k*(k-2*(i&1)+4)/6-1)/k+1)
return [2,3] + [3*i+1|1 for i in xrange(1,n/3-correction) if sieve[i]]
:Fastest way to list all primes below N
は、正確には、この実装を私は自動的に2,3の倍数をスキップして最適化するという考え方を少しは理解することができますが、このアルゴリズムをC++に移植することになると固くなります(私はPythonとC /しかし、ロックンロールには十分です)。
私は現在、自分自身を巻いたことは、この(isqrt()
が単純な整数平方根関数である)である:
template <class T>
void primesbelow(T N, std::vector<T> &primes) {
T sievemax = (N-3 + (1-(N % 2)))/2;
T i;
T sievemaxroot = isqrt(sievemax) + 1;
boost::dynamic_bitset<> sieve(sievemax);
sieve.set();
primes.push_back(2);
for (i = 0; i <= sievemaxroot; i++) {
if (sieve[i]) {
primes.push_back(2*i+3);
for (T j = 3*i+3; j <= sievemax; j += 2*i+3) sieve[j] = 0; // filter multiples
}
}
for (; i <= sievemax; i++) {
if (sieve[i]) primes.push_back(2*i+3);
}
}
この実装はまともですし、自動的に2の倍数をスキップし、私はポートPython実装をすることができれば私はそれがはるかに速く(50%〜30%程度)することができると思う。
Q6600 Ubuntu 10.10でのN=100000000
,g++ -O3
の現在の実行時間は1230msです(この質問にはうまく答えられるはずです)。
私は、上記のPython実装が何をしているのか、それとも私のために移植するのかを理解するのに役立つでしょう。
EDIT
私が難しいものについてのいくつかの追加情報。
訂正変数のように使われている手法や一般的にどのようになっているのかよくわかりません。さまざまなEratosthenesの最適化を説明しているサイトへのリンク(「2,3,5の倍数をスキップして1000行のCファイルでスラムする」という単純なサイトを除いて)はすばらしいでしょう。
私は100%直接ポートとリテラルポートに問題はないと思っていますが、結局これはまったく役に立たない学習のためです。
EDIT
オリジナルnumpyのバージョンのコードを見ていたら、実際にはかなり実装が容易と理解することはそれほど難しくなく、いくつかの考え方です。これは私が思いついたC++のバージョンです。私は200万行のコードではない非常に効率的なprimesieveが必要な場合に備えて、読者の皆様の助けになるよう完全版でここに掲載しています。この素数は、上記と同じマシン上で約415ミリ秒で100000000未満のすべての素数を実行します。これは3倍のスピードアップです。
#include <vector>
#include <boost/dynamic_bitset.hpp>
// http://vault.embedded.com/98/9802fe2.htm - integer square root
unsigned short isqrt(unsigned long a) {
unsigned long rem = 0;
unsigned long root = 0;
for (short i = 0; i < 16; i++) {
root <<= 1;
rem = ((rem << 2) + (a >> 30));
a <<= 2;
root++;
if (root <= rem) {
rem -= root;
root++;
} else root--;
}
return static_cast<unsigned short> (root >> 1);
}
// https://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
// https://stackoverflow.com/questions/5293238/porting-optimized-sieve-of-eratosthenes-from-python-to-c/5293492
template <class T>
void primesbelow(T N, std::vector<T> &primes) {
T i, j, k, l, sievemax, sievemaxroot;
sievemax = N/3;
if ((N % 6) == 2) sievemax++;
sievemaxroot = isqrt(N)/3;
boost::dynamic_bitset<> sieve(sievemax);
sieve.set();
primes.push_back(2);
primes.push_back(3);
for (i = 1; i <= sievemaxroot; i++) {
if (sieve[i]) {
k = (3*i + 1) | 1;
l = (4*k-2*k*(i&1))/3;
for (j = k*k/3; j < sievemax; j += 2*k) {
sieve[j] = 0;
sieve[j+l] = 0;
}
primes.push_back(k);
}
}
for (i = sievemaxroot + 1; i < sievemax; i++) {
if (sieve[i]) primes.push_back((3*i+1)|1);
}
}
Pythonコードは単一の要素を配列にプッシュしていません。それを要素のブロックで初期化し、その場で変更します。それはC++でもずっと速いでしょう。あなたが持っているように 'dynamic_bitset'で始めて、それを記入してから、ふるいにひとつのループで書き出します。 Pythonバージョンからテクニックをコピーしようとしても、あなたのバージョンはずっと速く動くでしょう。 –
Python実装のどの部分を理解できないのですか?この情報を質問に追加してください。また、あなたが言ったように、あなたの理解ではあまり役に立ちませんので、人々は本当にあなたのためにポートに答えるべきではありません。 –
@Jeremiah Willcock:正しくありません。ふるい分けの間、それはインプレースで修正されていますが、最終的には、配列(list) 'xrange(1、n)のiに対してreturn [2,3] + [3 * i + 1 | 1]/3補正)sieve [i]]ならば。そしてそれは私がやっていることとほとんど同じです。最初のループはふるい分けていて、すでにわかっている素数を押しています.2番目のループは、sievelimitの根の後ろにある素数を押しています。 – orlp