Java Top

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

> CHECK OUT THE COURSE

1. Overview

In this tutorial, we'll write a Java program to find all the factors of a given integer.

2. Introduction to the Problem

Before we start writing the Java code, let's understand what an integer's factors are.

Given an integer n, the integer i is n‘s factor if it can completely divide the number i. Completely divisible here means when we divide n by i, we get zero as the remainder.

A few examples may explain it quickly:

  • n = 10, its factors: 1, 2, 5, and 10
  • n = 13, its factors: 1 and 13
  • n = 1, n has only one factor: 1
  • n = 0, zero has no factor

As the example shows, usually, an integer n‘s factors always contain 1 and n, even if n is a prime number, for example, 13. However, zero is a special integer. It has no factor.

Now that we understand the concept of factors, let's create a Java program to find all the factors of a given integer.

For simplicity, we'll use unit test assertions to verify if our solution works as expected.

3. Creating a Method to Find All Factors of an Integer

The most straightforward way to find all the factors of an integer n is by looping from 1 to n and testing which number can completely divide n. We can store those numbers that can completely divide n in a Set. When the looping finishes, this Set will hold all the factors of n.

Implementing this idea in Java isn't a challenging job for us:

static Set<Integer> getAllFactorsVer1(int n) {
    Set<Integer> factors = new HashSet<>();
    for (int i = 1; i <= n; i++) {
        if (n % i == 0) {
            factors.add(i);
        }
    }
    return factors;
}

Next, let's write some tests to check if our method works as expected. First, let's create a Map to prepare some numbers to test and their expected factors:

final static Map<Integer, Set<Integer>> FACTOR_MAP = ImmutableMap.of(
    0, ImmutableSet.of(),
    1, ImmutableSet.of(1),
    20, ImmutableSet.of(1, 2, 4, 5, 10, 20),
    24, ImmutableSet.of(1, 2, 3, 4, 6, 8, 12, 24),
    97, ImmutableSet.of(1, 97),
    99, ImmutableSet.of(1, 3, 9, 11, 33, 99),
    100, ImmutableSet.of(1, 2, 4, 5, 10, 20, 25, 50, 100)
);

Now, for each number in the FACTOR_MAP above, we call the getAllFactorsVer1() method that we've implemented to see if it can find the desired factors:

FACTOR_MAP.forEach((number, expected) -> assertEquals(expected, FactorsOfInteger.getAllFactorsVer1(number)));

The test passes if we run it. So, the method solves the problem, great!

Sharp eyes may spot that we named the method with Ver1. Usually, it implies we'll introduce different versions in the tutorial. In other words, the solution still has room for improvement.

Next, let's see how to optimize the version 1 implementation.

4. Optimization – Version 2

Let's review the primary logic in the method:

for (int i = 1; i <= n; i++) {
   if (n % i == 0) {
       factors.add(i);
   }
}

As the code above shows, we'll execute the n % i calculation n times. Now, if we examine the factors of an integer, we'll see that factors always come in pairs. Let's take n =100 as an example to understand this factor characteristic:

   1    2    4    5    10    20    25    50    100
   │    │    │    │    |      │     │     │     │
   │    │    │    │  [10,10]  │     │     │     │
   │    │    │    │           │     │     │     │
   │    │    │    └──[5, 20] ─┘     │     │     │
   │    │    │                      │     │     │
   │    │    └───────[4, 25]────────┘     │     │
   │    │                                 │     │
   │    └────────────[2, 50]──────────────┘     │
   │                                            │
   └─────────────────[1, 100]───────────────────┘

As we've seen, all factors of 100 are in pairs. Therefore, if we've found one factor i of n, we can get the paired one i'= n/i. That is to say, we don't need to loop n times. Instead, we check from 1 to the square root of the number n and find all i and i' pairs. In this way, given n=100, we loop only ten times.

Next, let's optimize our version 1 method:

static Set<Integer> getAllFactorsVer2(int n) {
    Set<Integer> factors = new HashSet<>();
    for (int i = 1; i <= Math.sqrt(n); i++) {
        if (n % i == 0) {
            factors.add(i);
            factors.add(n / i);
        }
    }
    return factors;
}

As the code above shows, we've used the Math.sqrt() method from the Java standard library to calculate the square root of n.

Now, let's test our second version's implementation with the same testing data:

FACTOR_MAP.forEach((number, expected) -> assertEquals(expected, FactorsOfInteger.getAllFactorsVer2(number)));

If we run the test, it passes. So the optimized version 2 works as expected.

We've successfully reduced the factor determination times from n to n‘s square root. It's a significant improvement. However, there is still room for further optimization. So, next, let's analyze it further.

5. Further Optimization – Version 3

First, let's do some simple math analysis.

As we know, the given integer n can be either even or odd. If n is an even number, we cannot predicate whether its factors are even or odd. For example, 20's factors are 1, 2, 4, 5, 10, and 20. So there are even and odd numbers.

However, if n is an odd number, all its factors must be odd numbers too. For example, 99's factors are 1, 3, 9, 11, 33, and 99. Therefore, all of them are odd numbers.

So, we can adjust the loop's increment step depending on whether n is odd. As our loop begins from i = 1, if we're given an odd number, we can set the increment step = 2 to skip checks on all even numbers.

Next, let's implement this idea in version 3:

static Set<Integer> getAllFactorsVer3(int n) {
    Set<Integer> factors = new HashSet<>();
    int step = n % 2 == 0 ? 1 : 2;
    for (int i = 1; i <= Math.sqrt(n); i += step) {
        if (n % i == 0) {
            factors.add(i);
            factors.add(n / i);
        }
    }
    return factors;
}

With this optimization, if n is an even number, the loop gets executed sqrt(n) times, the same as version 2.

However, if is an odd integer, the loop gets executed sqrt(n)/2 times in total.

Finally, let's test our version 3 solution:

FACTOR_MAP.forEach((number, expected) -> assertEquals(expected, FactorsOfInteger.getAllFactorsVer3(number)));

The test passes if we give it a run. So, it does the job correctly.

6. Conclusion

In this article, we've created a Java method to find all factors of an integer. Further, we've discussed two optimizations to the initial solution.

As usual, all code snippets presented here are available over on GitHub.

Java bottom

Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:

>> CHECK OUT THE COURSE
Generic footer banner
Comments are closed on this article!