Мой лучший выстрел в исполнении, не выходя за борт с особыми альгосами.
The Erathostenes' seive - сложность ниже является O (N * журнала (журнал (N))) - потому что внутренний j
цикл начинается с i*i
вместо i
.
#include <vector>
using std::vector;
void erathostenes_sieve(size_t upToN, vector<size_t>& primes) {
primes.clear();
vector<bool> bitset(upToN+1, true); // if the bitset[i] is true, the i is prime
bitset[0]=bitset[1]=0;
// if i is 2, will jump to 3, otherwise will jump on odd numbers only
for(size_t i=2; i<=upToN; i+=((i&1) ? 2 : 1)) {
if(bitset[i]) { // i is prime
primes.push_back(i);
// it is enough to start the next cycle from i*i, because all the
// other primality tests below it are already performed:
// e.g:
// - i*(i-1) was surely marked non-prime when we considered multiples of 2
// - i*(i-2) was tested at (i-2) if (i-2) was prime or earlier (if non-prime)
for(size_t j=i*i; j<upToN; j+=i) {
bitset[j]=false; // all multiples of the prime with value of i
// are marked non-prime, using **addition only**
}
}
}
}
Теперь факторизуя на основе primes
(набор в отсортирован вектор). До этого давайте рассмотрим миф о sqrt
, являющийся дорогостоящим, но большого количества умножений нет.
Прежде всего, отметим, что sqrt is not that expensive anymore: на старых CPU-эс (x86/32b) это было в два раза дороже, чем разделение (и modulo
операции является деление), на новых архитектурах CPU затраты равны. Так как факторизация - это все о %
операциях снова и снова, все равно можно рассматривать sqrt
сейчас (например, если и когда он используется, это экономит процессорное время).
Для примера рассмотрим следующий код для N=65537
(который является 6553-й прайм) предполагая, что primes
имеет 10000 entries
size_t limit=std::sqrt(N);
size_t largestPrimeGoodForN=std::distance(
primes.begin(),
std::upper_limit(primes.begin(), primes.end(), limit) // binary search
);
// go descendingly from limit!!!
for(int i=largestPrimeGoodForN; i>=0; i--) {
// factorisation loop
}
Мы имеем:
- 1 SQRT (равный 1
modulo
) ,
- 1 поиск по
10000
записей - при максимальных 14 шагах, каждый из которых включает в себя 1 сравнение, 1 сменное смещение на 2 и 1 приращение/декремент - с o допустим, что стоимость равна 14-20 умножениям (если когда-либо)
- 1 разница из-за
std::distance
.
Итак, максимальная стоимость - 1 div и 20 muls? Я щедр.
С другой стороны:
for(int i=0; primes[i]*primes[i]<N; i++) {
// factorisation code
}
выглядит гораздо проще, но, как N=65537
первична, мы будем проходить через весь цикл до i=64
(где мы находим первое простое число, которое вызывает цикл сломать) - всего 65 умножений.
Попробуйте это с более высоким простым номером, и я гарантирую, что стоимость 1 sqrt + 1binary search лучше использовать процессорный цикл, чем все умножения на пути в более простой форме цикла. рекламируется как лучшее решение производительности.
Итак, вернемся к коду факторизации:
#include <algorithm>
#include <math>
#include <unordered_map>
void factor(size_t N, std::unordered_map<size_t, size_t>& factorsWithMultiplicity) {
factorsWithMultiplicity.clear();
while(!(N & 1)) { // while N is even, cheaper test than a '% 2'
factorsWithMultiplicity[2]++;
N = N >> 1; // div by 2 of an unsigned number, cheaper than the actual /2
}
// now that we know N is even, we start using the primes from the sieve
size_t limit=std::sqrt(N); // sqrt is no longer *that* expensive,
vector<size_t> primes;
// fill the primes up to the limit. Let's be generous, add 1 to it
erathostenes_sieve(limit+1, primes);
// we know that the largest prime worth checking is
// the last element of the primes.
for(
size_t largestPrimeIndexGoodForN=primes.size()-1;
largestPrimeIndexGoodForN<primes.size(); // size_t is unsigned, so after zero will underflow
// we'll handle the cycle index inside
) {
bool wasFactor=false;
size_t factorToTest=primes[largestPrimeIndexGoodForN];
while(!(N % factorToTest)) {
wasFactor=true;// found one
factorsWithMultiplicity[factorToTest]++;
N /= factorToTest;
}
if(1==N) { // done
break;
}
if(wasFactor) { // time to resynchronize the index
limit=std::sqrt(N);
largestPrimeIndexGoodForN=std::distance(
primes.begin(),
std::upper_bound(primes.begin(), primes.end(), limit)
);
}
else { // no luck this time
largestPrimeIndexGoodForN--;
}
} // done the factoring cycle
if(N>1) { // N was prime to begin with
factorsWithMultiplicity[N]++;
}
}
Не могли бы вы дать примеры того, что вы ищете? Например, если «x = 12», то число уникальных факторов равно «6» (1, 2, 3, 4, 6, 12) и «n = 12 * 6 = 72'? Или вы хотите считать простые коэффициенты ('2 * 2 * 3'), и в этом случае' n = 12 * 3 = 36'? – Holt
Что вы подразумеваете под большим количеством? Можете ли вы рассказать о диапазоне N? – vish4071
[Почему «использование пространства имен std» считается плохой практикой?] (Http://stackoverflow.com/q/1452721/995714). И вы должны включить ['' и ''] (http://stackoverflow.com/q/15656434/995714) вместо 'stdio.h' и' math.h' –