-7

The assignment was to write a program which reads in an integer k, and prints out the number of positive integers between 1 and 100000 (inclusive) which have exactly k divisors. As an example, the number 24 has 8 divisors: 1, 2, 3, 4, 6, 8, 12, and 24.

I have a running programme, but is there anyway i could make the search faster??

#include <stdio.h>
#include <math.h>


int main(void)
{   int a; //user input//
    int divisors; //running total of number of divisors//
    int sum; //running total of numbers with the required number of divisors//

    printf("Enter the target number of divisors:");
    scanf("%d", &a);
    printf("\n");

    int i;
    for (i=1; i<=100000; i++)
    {
            divisors=2;
            int p;
            for(p=2; p<i; p++)
            {if (i%p==0)
            divisors++;}

        if (divisors==a)  
        sum++;}

    printf("There are %d numbers between 1 and 100000 inclusive which have exactly %d divisors.", sum, a);

return 0;
}
pvg
  • 2,673
  • 4
  • 17
  • 31
  • 1
    that's a classic. loop until sqrt(i) only and if you find a divisor, add the other one unless it's the same (case of perfect square). – Jean-François Fabre Nov 06 '17 at 19:37
  • whenever I implement the loop until the square root of i, it gives me a incorrect and different number of divisors everytime I run the programme – Rebecca Meagher Nov 06 '17 at 19:54
  • check the duplicate link and accept that your question is a duplicate. to speed this up you need to loop only until int(sqrt(n)) included but there's a special case if n is a perfect square, in that case, you have to sub 1 because you counted the square root twice. – Jean-François Fabre Nov 06 '17 at 20:00
  • @Jean-FrançoisFabre - the comments in the linked to answer include suggested optimizations which were never incorporated into that answer. The answer I've posted below includes those optimizations. – rcgldr Nov 07 '17 at 18:00
  • @Jean-FrançoisFabre - I added a second seive like example to my answer, which is significantly different than the linked to answer. – rcgldr Nov 11 '17 at 03:29

2 Answers2

-1

Rather than checking for the value of p uptil i we can optimize by checking until sqrt(i) and instead of incrementing divisors by 1, we increament it by 2, one for the number say k divided by i and second for the number i/k.

n=1000000;

for (i=1; i<=10000; i++)
    {
            divisors=2;
            int p;
            for(p=2; p<=sqrt(i); p++)
            {
                if (i%p==0)
                {
                    if(p != (i/p)
                        divisors = divisors + 2;
                    else 
                      divisors++;
                }
            }

        if (divisors==a)  
        sum++;
}
Aditi Rawat
  • 784
  • 1
  • 12
  • 15
-1

Example code. Moved declarations for i and p to be compatible with old C type compilers (I'm using Microsoft / Visual Studio). Uses ceil(sqrt(i)) outer loops. The code handles an input of 1 (only the number 1 has 1 divisor). An input of 2 will output the number of prime numbers less than 100,000 (there are 9592 prime numbers less than 100,000).

This method takes a bit over 21 million iterations. Number of iterations ~= .67 n sqrt(n).

#include <stdio.h>

int main(void)
{
    int a;          /* user input */
    int divisors;   /* total number of divisors */
    int sum;        /* count of numbers with required number of divisors */
    int i;          /* moved here for C compiler */
    int p;          /* moved here for C compiler */ 
    int sqrti;      /* ceil(sqrt(i)) */

    printf("Enter the target number of divisors: ");
    scanf("%d", &a);
    printf("\n");
    sum = 0;                            /* init sum */
    sqrti = 1;                          /* init sqrti */
    for (i = 1; i <= 100000; i++)
    {
        divisors = 0;                   /* init number of divisors */
        if(sqrti*sqrti < i)             /* update sqrti as needed */
            sqrti += 1;
        for(p = 1; p < sqrti; p++)
            if(i%p == 0)                /* if p is a divisor, count it and i/p */
                divisors += 2;
        if(p*p == i)                    /* if p*p == i, count it */
            divisors += 1;
        if (divisors == a)              /* if i has a divisors, increment sum */
            sum += 1;
    }
    printf("There are %d numbers from 1 to 100000 inclusive which have exactly %d divisors.\n", sum, a);
    return 0;
}

If an array can be used similar to sieve method for primes, this method takes a bit over 1 million iterations. Number of iterations ~= n ln(n).

#include <stdio.h>
#define n 100000
int main(void)
{
int * cnt = (int *)calloc(n+1, sizeof(int));
int d;
    printf("Enter the target number of divisors: ");
    scanf("%d", &d);
    /* time complexity O(n log(n)) */
    {
    int i, j;
        for (i = 1; i <= n; i++) {
            for(j = i; j <= n; j += i) {
                cnt[j]++;
            }
        }
    }
    {
    int i;
    int sum = 0;
        for (i = 1; i <= n; i++)
            sum += (cnt[i] == d) ? 1 : 0;
        printf("excactly %d numbers have %d divisors\n", sum, d); 
    }
    free(cnt);
    return 0;
}
rcgldr
  • 27,407
  • 3
  • 36
  • 61
  • A downvote wtih no comment (not very helpful), and after it was already marked as accepted answer. The answer was obviously provided before it was marked as a duplicate, and although it is similar to a prior question and answer, it's not quite a duplicate. – rcgldr Nov 07 '17 at 18:32
  • hi just wondering why youre setting sqrti as 1? It surely doesnt remain as the square root, or does this even matter? – Rebecca Meagher Nov 08 '17 at 15:02
  • @RebeccaMeagher - You could think of sqrt(1) as 1, so sqrti is initialized to 1. This is part of the reason if the input is 1, the program will detect that only 1 has 1 divisor (with the `if(p*p == 1`) check). As commented, sqrti is the ceiling (it's rounded up if not an exact integer) of sqrt(i), and the inner for loop condition is `p < sqrti` and not `p <= sqrti`, eliminating the need to check for `p*p == i` inside the inner loop, allowing a single check for `p*p == i` just after the inner loop. – rcgldr Nov 08 '17 at 15:17
  • @RebeccaMeagher - continuing, divisors is initialized to `divisors = 0` instead of `divisors = 2`. The inner loop starts at `p = 1` instead of `p = 2`, so the first inner loop does a `divisors += 2` for the pair of numbers 1 and i, except when `sqrti == 1` where the inner loop is not run and only the check for `p*p == i` is done. – rcgldr Nov 08 '17 at 15:23
  • Okay thanks. So is the programme itself calculating the square root with the sqrti integer? Sorry im just a beginner to computer science and find this part confusing – Rebecca Meagher Nov 08 '17 at 16:00
  • @RebeccaMeagher - sqrti is the ceiling (rounded up) of sqrt(i). A list of values for (i, sqrti) as i gets incremented: (1,1) (2,2) (3,2) (4,2) (5,3) (6,3) (7,3) (8,3) (9,3) (10,4) ... (15,4) (16,4) (17,5) ... (24,5) (25,5) (26,6) ... . sqrti gets incremented each time i == (1 + a perfect square). Perfect squares are 1,4,9,16,25,36, ... so i gets incremented at 2,5,10,17,26,37, ... . As I commented before, I'm using sqrti = ceiling(sqrt(i)), so that the inner loop condition can be `p < sqrti` instead of `p <= sqrti` . – rcgldr Nov 08 '17 at 16:12
  • @RebeccaMeagher - I could have used | `i = (int) ceil(sqrt(i));` | , but | `if(sqrti*sqrti < i)` | `sqrti += 1;` |, should be faster as i gets incremented from 1 to 100,000. Technical aspect: branch prediction and/or branch table buffer (cache) will be good for all but small values of i, since the increment after the if is skipped more often as i increases in value. – rcgldr Nov 08 '17 at 17:49
  • @RebeccaMeagher - I updated my answer with a much faster version that uses an array similar to the seive method for finding primes, if array usage is allowed. – rcgldr Nov 11 '17 at 03:28