Skip to content

Commit 0a1d93e

Browse files
committed
algo(numberOfDivisors): add algorithm to list and count divisors of a number
1 parent e80e223 commit 0a1d93e

File tree

1 file changed

+82
-0
lines changed
  • src/algorithm_practice/Number_Algorithms/numberOfDivisors

1 file changed

+82
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#include <iostream>
2+
#include <climits>
3+
4+
// Source: Inspired by `betweenTwoSets`
5+
6+
/**
7+
* Number of divisors
8+
* The goal of this algorithm is to compute and output
9+
* the number of divisors for a given number. I always
10+
* knew the most optimized version of this algorithm had
11+
* something to do with the square root of the number you
12+
* were trying to find the divisors of, but without thinking
13+
* too hard, I couldn't figure out why.
14+
*
15+
* The naïve approach to solving this problem is trivial.
16+
* Given a number n, for all numbers [1, n] (inclusive) we
17+
* need to check to see if (n % i) == 0. Each time the previous
18+
* condition is met, `i` is a divisor of (is a factor of) n, and
19+
* we can increment our count of divisors and print `i` out.
20+
*
21+
* Complexity analysis:
22+
* Time complexity: O(n)
23+
* Space complexity: O(1)
24+
*
25+
* Let's take a closer look at a number's divisors. For example, the
26+
* divisors of 20 are 1, 2, 4, 5, 10, and 20. Seeing this, you might
27+
* see that one pattern is that the divisors of a number will never
28+
* exceed half of that number (with the exception of the number itself).
29+
* This could give us a minor optimization, but we ultimately have the same
30+
* complexity as before. It's on the right track though; what we can realize
31+
* is that the divisors come in pairs. When I divide 20 by 1 I get 20. By 2 I
32+
* get 10, by 4 I get 5, by 5 I get 4, and so on. They repeat. 20 has 6 divisors
33+
* and the pairs look like:
34+
* (1, 20)
35+
* (2, 10)
36+
* (4, 5)
37+
* (5, 4)
38+
* (10, 2)
39+
* (20, 1)
40+
*
41+
* Notice that the pairs repeat, but when do they start repeating? They start repeating
42+
* when the second number begins to overtake the first number. For 20, the first occurrence
43+
* of this appears at 5, but on a more concrete level the first number will TRULY start to get
44+
* overtaken by the second when the two numbers are equal of course. In that, the two numbers are
45+
* equal and multiplying them yields `n`. This happens when the numbers are sqrt(n) of course. Therefore
46+
* to avoid repeating these pairs, we can get all divisors by just looking at the divisors that are <= sqrt(n).
47+
*
48+
* For all i <= sqrt(n) such that (n % i) == 0, `i` is a divisor of `n`, so we can count it. `n / i` is also a divisor
49+
* which we'd get to later by iterating past the point where the first number gets overtaken by the second, but we can
50+
* count it immediately instead of waiting. The divisor `n / i` is either equal to `i` (in which case `i` is the integer
51+
* square root of `n`) or it is > to `i`. If it is greater, we want to count it right away, if it is equal, meaning `n` is
52+
* a perfect square, we want to make sure we only count it once!
53+
*
54+
* Complexity analysis:
55+
* Time complexity: O(sqrt(n))
56+
* Space complexity: O(1)
57+
*/
58+
59+
// Can assume n will not overflow a 32-bit integer
60+
void listDivisors(int n) {
61+
int count = 0;
62+
for (int i = 1; i * i <= n; i++) {
63+
if (n % i == 0) {
64+
count++;
65+
std::cout << i << " is a divisor" << std::endl;
66+
67+
if (i != (n / i)) {
68+
count++;
69+
std::cout << n / i << " is a divisor" << std::endl;
70+
}
71+
}
72+
}
73+
74+
std::cout << "The total number of divisors is: " << count << std::endl;
75+
}
76+
77+
int main() {
78+
int n;
79+
std::cin >> n;
80+
listDivisors(n);
81+
return 0;
82+
}

0 commit comments

Comments
 (0)