По-добър начин за намиране на прости множители на число

Решавам проблем, при който ви се дават няколко тестови случая. За всеки случай ви е даден диапазон (от x до y, включително). В този диапазон трябва да преброя всички числа, чиято сума от прости множители е точно K.

например:

5 15 2

Знаем, че има 5 числа, които имат точно 2 прости множителя (6, 10, 12, 14 и 15).

Сега кодът ми работи перфектно, но е твърде бавен. И търсих по-бърз начин за генериране на прости числа чрез C++. Ето моя код.

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <math.h>
#include <string>
#include <sstream>
#include <vector>
#include <iomanip>
#include <deque>
#include <queue>

#define Fill(s, v) memset(s, v, sizeof(s))
#define skipChar() (scanf("%c", &useless));
#define scan(x) do{while((x=getchar())<'0'); for(x-='0'; '0'<=(_=getchar());x=(x<<3)+(x<<1)+_-'0');}while(0)
#define rekt return false;
#define notrekt return true;
char _, useless;

using namespace std;
typedef pair <int, int> intpair;
vector<int> primes;

void sieve(int n){
bool *prime = new bool[n +1];
fill(prime, prime + n+1, true);
prime[0] = false;
prime[1] = false;
int m = sqrt(n);
for(int i = 2; i <= m; i++)
    if(prime[i])
        for(int k = i*i; k <= n; k+=i){
            prime[k] = false;
            if(prime[k])primes.push_back(k);
        }
for(int i = 0; i <n; i++){
    if(prime[i])
        primes.push_back(i);
     }
}

int main()
{
int t;
int c = 1;
scan(t);
sieve(1000);
while(t--){
    int a, b, k;
    scan(a);
    scan(b);
    scan(k);
    int realCount = 0;
    for(int i = a; i <= b; i++){
        int count = 0;
        for(int j = 0; j < primes.size(); j++){
            if(i % primes[j] == 0){
                    count++;
            }
        }
        if(count == k)realCount++;
    }
    cout << "Case #"<< c << ": "<< realCount <<endl;
    c++;
    }
}

Благодаря за помощта!

Благодарим на всички за приноса! Ето бърз и оптимизиран код!

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <math.h>
#include <string>
#include <sstream>
#include <vector>
#include <iomanip>
#include <deque>
#include <queue>

#define F(a, s, val) fill(a, a + s, val);
#define skipChar() (scanf("%c", &useless));
#define scan(x) do{while((x=getchar())<'0'); for(x-='0'; '0'<=_=getchar());x=(x<<3)+(x<<1)+_-'0');}while(0)
#define rekt return false;
#define notrekt return true;
char _, useless;

using namespace std;
typedef pair <int, int> intpair;

int *omega = new int[10000001];

void omg(){
    for(int i = 2; i < 10000000; i++)
            if(omega[i] == 0)
                    for(int j = i; j < 10000001; j+=i)
                            omega[j]++;
}

int main(){
    int t;
    int c = 1;
    F(omega, 10000001, 0);
    omg();
    scan(t);
    while(t--){
        int a, b, k;
        scan(a);
        scan(b);
        scan(k);
        int cc = 0;
        for(int i = a; i <= b; i++)
                    if(omega[i] == k)
                            cc++;
        printf("Case #%i: %i\n", c, cc);
        c++;
        }
    }

person Boris    schedule 18.01.2015    source източник
comment
Мисля, че искате да преброите всички числа, които имат K различен прост множител. не сбор от прости множители. нали   -  person    schedule 19.01.2015
comment
Колко голям може да стане диапазонът?   -  person mrk    schedule 19.01.2015
comment
#defines ме убеди дори да не поглеждам кода ви.   -  person gnasher729    schedule 19.01.2015
comment
благодаря ви за вашето много ценно мнение!   -  person Boris    schedule 19.01.2015
comment
@gnasher729 това вероятно е шаблон за конкурентно програмиране.   -  person mrk    schedule 19.01.2015
comment
Тъй като вашият код работи, трябва да публикувате този въпрос на [email protected]. Те са добри в оптимизирането и извършването на прегледи на кода.   -  person Thomas Matthews    schedule 19.01.2015
comment
Да, пишех за Facebook Hacker Cup 2015.   -  person Boris    schedule 19.01.2015


Отговори (2)


Правилно предварително изчислявате простите числа в желания диапазон със Ситото на Ератостен, което е добре. Това, което обаче искате да знаете, е броят на отделните прости множители на всяко число във вашия диапазон, а не дали е просто или съставно.

Това изчисление може да се направи и чрез пресяване. Вместо да поддържате масив от булеви стойности, пазете масив от цели числа, които отчитат броя на отделните прости множители, и го увеличавайте за всеки прост множител, намерен по време на пресяването.

Пресяването изглежда така; ние наричаме масива omega, защото това е името, което теоретиците на числата дават на функцията, която връща броя на отделните множители на число:

omega := makeArray(2..limit, 0)

for i from 2 to limit
    if omega[i] == 0
        for j from i to limit step i
            omega[j] := omega[j] + 1

Първите няколко елемента от масива omega са 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 1, 2, 2, 2, 2, 1, 2, 2‌​, 2, 1, 3 (A001221).

След като имате omega, можете да я използвате за всички ваши заявки:

function f(a, b, c)
    count := 0
    for k from a to b
         if omega[k] == c
             count := count + 1
    return count

Например f(5,15,2) = 5 (наборът 6, 10, 12, 14, 15), f(2,10,1) = 7 (наборът 2, 3, 4, 5, 7, 8, 9), f(24,42,3) = 2 (наборът 30, 42) и f(2,10000000,7) = 1716.

Ако диапазонът ви е твърде голям, за да бъде удобно пресетен, ще трябва да разложите всяко число в диапазона и да преброите тези с правилния брой различни фактори.

person user448810    schedule 19.01.2015
comment
Благодаря ви, това наистина помогна. - person Boris; 19.01.2015

вашата ситова функция може да бъде оптимизирана по този начин.

vector<int> siev(int max) {
    vector<int> ret;
    bool isPrime[max];

    for(int i=2; i<max; i++) isPrime[i]=true; // reset all bits

    for(int i=2; i<max; i++) {
        if(isPrime[i]) {
            ret.push_back(i);
            for(int j=i*i; j<max; j+=i) {
                isPrime[j]=false;
            }
        }
    }

    return ret;
}
person user3064869    schedule 18.01.2015