|
| 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