简体   繁体   中英

Number of distinct prime factors of a number

Q: Given A,B and K. Find all the numbers between A and B(inclusive) that have K DISTINCT prime factors. Here's what I've done.I've implemented the Sieve of Eratosthenes and computed all the primes till the upper bound of A,B. Then I proceed to find which of these primes is a factor of the numbers between A and B. If the number of distinct primes is equal to K, I increment count. The problem I'm running into is that of time. Even after implementing the sieve, it takes 10 seconds to compute the answer to 2,10000,1 (The numbers between 2 and 100000 that have 1 distinct prime factor) here's my code

    import math

    #Sieve of erastothenes
    def sieve(n):
        numbers=range(0,n+1)
        for i in range(2,int(math.ceil(n**0.5))):
            if(numbers[i]):
                for j in range(i*i,n+1,i):
                    numbers[j]=0

        #removing 0 and 1 and returning a list          
        numbers.remove(1)
        prime_numbers=set(numbers)
        prime_numbers.remove(0)

        primes=list(prime_numbers)
        primes.sort()
        return primes

    prime_numbers=[]
    prime_numbers=sieve(100000)
    #print prime_numbers
    def no_of_distinct_prime_factors(n):

        count=0
        flag=0
        #print prime_numbers
        for i in prime_numbers:
            #print i
            if i>n:
                break
            if n%i==0:
                count+=1
                n=n/i
        return count
    t=raw_input()
    t=int(t)
    foo=[]
    split=[]
    for i in range (0,t):
        raw=raw_input()
        foo=raw.split(" ")
        split.append(foo)
    for i in range(0,t):
        count=0
        for k in range(int(split[i][0]),int(split[i][1])+1):
            if no_of_distinct_prime_factors(k)==int(split[i][2]):
                count+=1
        print count

Any tips on how to optimize it further?

This should do what you are trying to do :

max=100000
k=6
nb_factors=[1]*max
for i in range(2,max):
    if nb_factors[i] == 1:
        for j in range(i, max, i):
            nb_factors[j]+=1

print [(i,f) for i,f in enumerate(nb_factors) if f > k]

I haven't checked correctness (especially for edge cases like 0 and 1) but it seems ok (you can replace 1 by 0 on line 3 and 5 depending on whether you want to include 1 in the list of factors).

I don't know python [ ;( ], but I know how to optimize it. We can use a linear sieve here - its a modification of sieve of Erastothenes, which works for O(n) and allows fast factorization ( O(k), where k is the amount of primes in the factorization).

What we're going to do is to have 2 arrays - pr (the array with prime numbers: pr[i] is the i-th prime) and lp (the array with least divisors: lp[i] is the smallest number that is a divisor of i). The initial values in the lp array are zeroes. We're going to iterate through all the numbers in [2, X]. For each number (lets call it i) there are 2 possible variants: 1. lp[i] = 0 means that no number before i is a divisor of i, so i is a prime number. 2. lp[i] != 0 means that i isn't prime (and we've already found its least divisor). Now let's consider all the numbers x[j] = i * pr[j]. If pr[j] satisfies pr[j]<=lp[i], the least divisor of x[j] is pr[j] (quite obvious - ask if it's not).

Then we can write the following code (C++ since I'm not familiar with python):

const int N = 100001; //the maximum possible input
int lp[N+1];
vector<int> pr;

void init()
{
    for (int i=2; i<=N; ++i) 
    {
        if (lp[i] == 0) //i is prime
        {
            lp[i] = i;
            pr.push_back (i);
        }
        for (int j=0; j<pr.size() && pr[j]<=lp[i] && i*pr[j]<=N; ++j)
            lp[i * pr[j]] = pr[j];
    }
}

Now that we have the lp array, we can easily factorize each number n: lp[n] is the divisor of n. Then we can assign n = n / lp[n] and continue this process. Since lp is the least divisor, all the divisors in the factorization can appear only in the increasing order. So counting the amount of distinct prime divisors in quite easy:

int count(int n)
{
    int ans = 0;
    int curprime = 0;
    while (n!=1)
    {
        int minp = lp[n];
        if (minp != curprime) ++ans, curprime = minp;

        n/=minp;
    }
    return ans;
}

Then we can just watch every number in [A,B] and count the amount of dist. prime divisors to answer the question:

int f(int a, int b, int c)
{
    int cnt = 0;
    for (int i = a; i <= b; ++i)
        if (count(i)==c)
            ++cnt;
    return cnt;
}

Runs less than 1 sec even for test (2,1000000,1): http://ideone.com/rMTIBj

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM