-3

I have been given x and k, where x is the number of factors of a number A, and k is the number of prime factors of A. Given x and k, I have to find out whether such an A exists.

For example:

INPUT : 4 2 
OUTPUT : 1

Since 6 is a number that has 4 factors 1, 2, 3, 6 out of which 2 are prime(2, 3). Also it is given that x and k can have any values between 1 and 109.

Here is my code for the this:

long long int x, k;
scanf("%lld%lld", &x, &k);

int ans = 0;

bool stop = false;

for (long long int numbers = 1; numbers < pow(10, 9) && !stop; numbers++) {
    long long int noOfFactors = 0, noOfPrimes = 0;
    for (long long int a = 1; a <= numbers && !stop; a++) {
        if (numbers % a == 0) {
            noOfFactors += 1;
            if ((isprime(a)) == 1) {
                noOfPrimes += 1;
            }
         }
     }
     if (noOfFactors == x && noOfPrimes == k) {
         ans = 1;
         stop = true;
     } else
         ans = 0;
}   
printf("%d\n", ans);

Where isprime(x) returns 1 if x is prime else 0.

But while running the program, it shows TLE error. Can anyone help me out in optimising this algorithm, or if any other method exists, can you just explain it to me ? Any help in optimising this algorithm or using another method would be kindly appreciated.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Shrey Tripathi
  • 210
  • 2
  • 7
  • Uhm. Split this code into smaller functions; it's unclear to read. Ask yourself what a for loop is doing, take it out of there and give it a clear name, so we can help. The ````stop```` Boolean could have been a ````return```` for example. –  Apr 06 '20 at 06:36
  • 2
    Next to that, you're constantly re-calculating the stop condition ````pow(10, 9)````, which you could put in a variable instead. –  Apr 06 '20 at 06:44
  • The question is really, "can `x` be written as the product of `k` factors?" So with `x=4` and `k=2`, the answer is yes because `x` can be written as `2*2`. If `x=12` and `k=3`, the answer is yes because `x` can be written as `2*2*3`. On the other hand, if `x=14` and `k=3`, the answer is no because `x` can only be factored as `2*7`. – user3386109 Apr 06 '20 at 06:49
  • @BlayerBond, I think the for loop is necessary to check numbers starting from 1, if they have x factors of which k are prime. The `stop` boolean is to break out of the two for loops. And, I tried storing `pow(10, 9)` in a variable; it doesn't help. – Shrey Tripathi Apr 06 '20 at 07:05
  • You don't have to remove the for-loop, you have to put it into another function (e.g. ````isNumberCorrect()````) and call that function when you need it. And storing that variable might not fix the time, but it at least prevents extra over-head. You should keep that variable. Also, why did you make the algorithm global? Use ````main()```` :P –  Apr 06 '20 at 07:19
  • On top of that, checking whether a number is prime is time consuming (because for the possible primeNum, it has to check all lower numbers (or just the square!)). So if you check all the numbers between 0 and 10^99, it's quite obvious the program takes a long time - I think. –  Apr 06 '20 at 07:25
  • @BlayerBond, yeah you're right. I can put the for loop in another function. But still the program would take a long time. I guess we have to search for another algorithm that doesn't check all the numbers between 1 and 10^9 – Shrey Tripathi Apr 06 '20 at 07:34
  • See the post below: are you calculating all dividers, or only till the root? –  Apr 06 '20 at 07:36
  • 4
    you are going about this all wrong. Given `k` (the number of prime factors) you can start computing the set of possible `x` (number of all factors). You don't need to start from the number, you need to start from `k`. It doesn't even matter which prime numbers. – bolov Apr 06 '20 at 07:52
  • 1
    can you post a link to the original problem? – bolov Apr 06 '20 at 07:56
  • 1
    This question is from an ongoing contest on codeChef – Viswa Apr 09 '20 at 03:18
  • 1
    This question is a part of Ongoing Codechef April Long Challenge 2020. Question Link : [Question][1] And Hence it should not be asked directly in this portal.This violated the Codechef Code of Conduct and User may be banned by Codechef. In future ,Please avoid asking question from an Ongoing Coding Challenge. – Chandra Shekhar Apr 12 '20 at 22:35

3 Answers3

4

Let p1, p2, p3, … pk be the prime factors of some positive integer n. By the fundamental theorem of arithmetic, n can be represented as p1e1p2e2p3e3• … pkek for some positive integers e1, e2, e3, … ek. Furthermore, any such set of such positive integers represents one positive integer in this way.

Every factor of n can be represented as p1f1p2f2p3f3• … pkfk for some integers fi where 0 ≤ fiei.

Each fi can have ei+1 values from 0 to ei, so the number of factors of n is (e1+1)•(e2+1)•(e3+1)• … (ek+1).

Since each ei must be positive, each ei+1 must be at least 2. Now we can see that, if n has x factors, then x is expressible as a product of k integers each at least 2. Conversely, if x is expressible as a product of k integers each at least 2, that product gives us values for ei, which gives us a positive integer n that has x factors and k prime factors.

Therefore, a number with x factors and k prime factors exists if and only if x is expressible as a product of k integers each at least 2.

To test this, simply divide x by prime numbers, e.g., divide by 2 as many times as possible without a remainder, then by 3, then by 5, and so on. Once x has been divided by k−1 factors and the result is greater than 1, then we know x is expressible as a product of k integers each at least 2 (the last may be composite rather than a prime, such as 2•3•3•3•35). If x reaches 1 before or just as we have divided it by k−1 factors, then no such number exists.

Addendum

Thinking about it further, it is not necessary to use prime numbers as candidates when testing x for factors. We can simply iterate through the integers, testing whether each candidate f is a factor of x. Trying to filter these by first testing whether f is prime would take more time than simply testing whether f is a factor of x. (This is not to say some more sophisticated method of testing x for prime factors, such as using a prepared table of many primes, would not be faster.) So the following code suffices.

#include <stdint.h>


/*  Return true if and only if there exists a positive integer A with exactly
    x factors and exactly k prime factors.
*/
static _Bool DoesAExist(uintmax_t x, uintmax_t k)
{
    /*  The only positive integer with no prime factors is 1.  1 has exactly
        one factor.  So, if A must have no prime factors (k is zero), then an A
        exists if and only if x is one.
    */
    if (k == 0) return x == 1;

    /*  A number with exactly one prime factor (say p) has at least two factors
        (1 and p) and can have any greater number of factors (p^2, p^3,...).
        So, if k is one, then an A exists if and only if x is greater than one.
    */
    if (k == 1) return 1 < x;

    /*  Otherwise, an A exists only if x can be expressed as the product of
        exactly k factors greater than 1.  Test this by dividing x by factors
        greater than 1 until either we have divided by k-1 factors (so one
        more is needed) or we run out of possible factors.

        We start testing factors with two (f = 2).

        If we find k factors of x during the loop, we return true.

        Otherwise, we continue as long as there might be more factors.  (When
        f*f <= x is false, we have tested the current value of x by every
        integer from two to the square root of x.  Then x cannot have any more
        factors less than x, because if there is any factorization x = r*s,
        either r or s must be less than or equal to the square root of x.)

        For each f, as long as it is a factor of the current value of x and we
        need more factors, we divide x by it and decrement k to track the
        number of factors still required.
    */
    for (uintmax_t f = 2; f*f <= x; ++f)
        while (x % f == 0)
        {
            /*  As long as f is a factor of x, remove it from x and decrement
                the count of factors still needed.  If that number reaches one,
                then:

                    If the current value of x exceeds one, it serves as the
                    last factor, and an A exists, so we return true.

                    If the current value of x exceeds one, there is no
                    additional factor, but we still need one, so no A exists,
                    so we return false.
            */
            x /= f;
            if (--k <= 1) return 1 < x;
        }

    /*  At this point, we need k more factors for x, and k is greater than one
        but x is one or prime, so x does not have enough factors.  So no A with
        the required properties exists, and we return false.
    */
    return 0;
}


#include <stdio.h>


int main(void)
{
    printf("False test cases:\n");
    printf("%d\n", DoesAExist(0, 0));   //  False since each positive integer has at least one factor (1).
    printf("%d\n", DoesAExist(2, 0));   //  False since no number has two factors and no prime factors.

    printf("%d\n", DoesAExist(0, 1));   //  False since each positive integer has at least one factor (1).
    printf("%d\n", DoesAExist(1, 1));   //  False since each positive integer with a prime factor has at least two factors (one and the prime).
    printf("%d\n", DoesAExist(2, 2));   //  False since each number with two prime factors (p and q) has at least four factors (1, p, q, and pq).
    printf("%d\n", DoesAExist(3, 2));   //  False since each number with two prime factors (p and q) has at least four factors (1, p, q, and pq).

    printf("%d\n", DoesAExist(8, 4));

    printf("%d\n", DoesAExist(6, 3));
    printf("%d\n", DoesAExist(22, 3));

    printf("%d\n", DoesAExist(24, 5));
    printf("%d\n", DoesAExist(88, 5));
    printf("%d\n", DoesAExist(18, 4));
    printf("%d\n", DoesAExist(54, 5));
    printf("%d\n", DoesAExist(242, 4));
    printf("%d\n", DoesAExist(2662, 5));

    printf("True test cases:\n");
    printf("%d\n", DoesAExist(1, 0));   //  True since 1 has one factor and zero prime factors.
    printf("%d\n", DoesAExist(2, 1));   //  True since each prime has two factors.
    printf("%d\n", DoesAExist(3, 1));   //  True since each square of a prime has three factors.
    printf("%d\n", DoesAExist(4, 1));   //  True since each cube of a prime has three factors.
    printf("%d\n", DoesAExist(4, 2));   //  True since each number that is the product of two primes (p and q) has four factors (1, p, q, and pq).

    printf("%d\n", DoesAExist(8, 2));
    printf("%d\n", DoesAExist(8, 3));

    printf("%d\n", DoesAExist(6, 2));
    printf("%d\n", DoesAExist(22, 2));

    printf("%d\n", DoesAExist(24, 2));
    printf("%d\n", DoesAExist(24, 3));
    printf("%d\n", DoesAExist(24, 4));
    printf("%d\n", DoesAExist(88, 2));
    printf("%d\n", DoesAExist(88, 3));
    printf("%d\n", DoesAExist(88, 4));
    printf("%d\n", DoesAExist(18, 2));
    printf("%d\n", DoesAExist(18, 3));
    printf("%d\n", DoesAExist(54, 2));
    printf("%d\n", DoesAExist(54, 3));
    printf("%d\n", DoesAExist(54, 4));
    printf("%d\n", DoesAExist(242, 2));
    printf("%d\n", DoesAExist(242, 3));
    printf("%d\n", DoesAExist(2662, 2));
    printf("%d\n", DoesAExist(2662, 3));
    printf("%d\n", DoesAExist(2662, 4));
}
Community
  • 1
  • 1
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Thank you very much for your answer. I understood the method, but I am not able to implement it. My method still takes a lot of time. Any help on how to implement it would be appreciated very much. – Shrey Tripathi Apr 08 '20 at 06:59
0

First your implementation is too inefficient

  • Don't call a function inside the for loop like this

    for (long long int numbers = 1; numbers < pow(10, 9) && !stop; numbers++)
    

    pow will be called unnecessarily on each iteration. Save every constant to a variable before the loop. But in this case just use numbers < 1e9

  • To get all factors of n you just need to loop until sqrt(n). You're doing for (long long int a = 1; a <= numbers && !stop; a++) { if (numbers % a == 0) { so for example instead of only 104 loops for 108 you'll need 108 loops. If numbers % a == 0 then both a and numbers/a are factors of numbers

But the real issue here is that your algorithm is flawed. You can get all factors of A if you know the prime factors of A and their exponents. If A = p1m p2n ... pkp then A will have the following factors:

  • p1i for i = 1..m
  • p2i for i = 1..n
  • ...
  • pki for i = 1..p
  • Factors from 2 prime factors: p1ip2j, p1ip3j,... p1ipkj, p2ip3j, p2ip4j,... p2ipkj,... pk-1ipkj
  • Factors from 3 prime factors...
  • Factors from k prime factors

This is purely a combination counting issue that can be done easily without even knowing A. Notice that the number of factors from k prime factors has some relation to the number of factors from k-1 prime factors

phuclv
  • 37,963
  • 15
  • 156
  • 475
-1

Your algorithm is very inefficient. Here are a few ideas:

  • reject obvious fails: if x < 2 or k <= 0 or x < k the answer is 0 without further testing.
  • do not mix floating point and integer arithmetics. Use 1000000000 instead of pow(10, 9).
  • the inner loop can be stopped much earlier than you do: only run it while a * a < numbers and add 2 divisors for each match. Add another divisor if a * a == numbers.
  • also stop the inner loop if noOfFactors > x or noOfPrimes > k.

The program will run faster, but it is still not the right approach as the solution A might be much larger than the range of the integer types. For example x = 1000, k = 1 has an infinite number of solutions A = p999 for any prime p. Finding this solution via enumeration is impossible.

Analyzing the problem mathematically: the total number of factors for a number A that has k prime factors is (e1+1)(e2+1)...(ek+1), where ei is the power of its i-th prime factor, ie: A = Prod(piei).

For a solution to exist, x must be the product of at least k factors. A modified version of a factoring loop can determine if at least k factors can be found whose product equals x.

Here is a simple program using this approach, that completes as soon as k factors have been found:

#include <stdio.h>

int test(unsigned int x, unsigned int k) {
    if (k == 0) {
        /* k is not supposed to be 0, but there is a single number A
           with no prime factors and a single factor: A = 1 */
        return x == 1;
    }
    if (x > k && k > 1) {
        for (unsigned int p = 2; x / p >= p;) {
            if (x % p == 0) {
                x /= p;
                if (--k == 1)
                    break;
            } else {
                /* skip to the next odd divisor */
                p += 1 + (p & 1);
            }
        }
    }
    return k == 1 && x > 1;
}

int main() {
    unsigned int x, k;

    while (scanf("%u%u", &x, &k) == 2)
        printf("%d\n", test(x, k));

    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Please avoid giving code solution to an online coding challenge as It may hamper the Rank of the Contestant who are participating in it. – Chandra Shekhar Apr 13 '20 at 19:38
  • Copying code found on the Internet is common place. Other contestants do it as well. The goal here is to teach programmers new skills, make them curious about new approaches... If they just copy without understanding, they will not help themselves. If you are interested in real challenges, try https://projecteuler.net/ . Whenever you have solved a problem, you get to see other people's code, such a humbling experience. – chqrlie Apr 13 '20 at 19:45
  • Agree, But my concern was to enable the code to them after the contest as the contest is having some rules and it hampers the rating of other who are trying their best to crack the problem.Thanks – Chandra Shekhar Apr 13 '20 at 19:49
  • @ChandraShekhar: I understand, but you did not vote to close the question. Did you at least flag it for the moderators? The contest is over now, so I will leave this answer active. How many contestants used this approach? How many used this exact code? Will you penalise them? – chqrlie Apr 13 '20 at 20:01
  • Maybe Codechef will.Because they are having an internal algorithm for that.Also I marked the question down and also flagged it as comment. – Chandra Shekhar Apr 14 '20 at 09:21