Let’s say you have been given a programming task, such as "Given an integer as input, can you round it to the next (meaning, "higher") 5
?” How many ‘general’ ways to solve this simple problem? I mean the term ‘general’ in a broad sense, like the barometer story. Here are some general solutions. These are from CodeWars Kata. My goal is to help me internalize these general solutions and to approach new problems with a variety of solution types.
Note: I am not testing values 2,147,483,646 and 2,147,483,647 as they don’t have a valid solution in Java Integer values.
Loops
Loops are used for a ton of solutions. They are the first solutions most programmers attempt to solve the problem. Loops are a good place to start. Here is a loop example:
public Integer roundUp5(Integer number) { while (number % 5 != 0) { number++; } return number; }
This is a good simple solution. It uses a simple while loop to increment to the next multiple of five. This is the simple solution and maybe the quickest to code and debug. Loops are a very powerful tool to solve problems, remember to use them
If Statements
If statements are also used for solutions. Here is my solution using an If statement.
public Integer roundUp5(Integer input) { if (0 < input) { input = input + 4; } return (input / 5) * 5; }
This takes longer to think of and debug because it uses a ‘trick’ of integer division. It is not as ‘direct’ or simple as the loop solution. Most programs use If statements, but it may wise to consider other solutions first. In my opinion, If statements are overused.
This is a good solution using the If statement
function roundToNext5(n){ if (Math.abs(n) % 5 === 0) return n; else { let remainder = n % 5; if (n < 0) { return n - remainder; } return n + 5 - remainder; } }
This doesn’t use the integer division trick which makes it easier to understand. It could be cleaned up a bit, such as I think the Math.abs(n) on line 2 is not needed and the else on line 4 is not needed. It is not the greatest code but it is simple and easy to maintain.
Switch Statement
Switch statements are very handy for solutions. Here is a simple JavaScript solution.
function roundToNext5(n){ switch (n % 5){ case 0: return n; break; case 1: return n+4; break; case 2: return n+3; break; case 3: return n+2; break; case 4: return n+1; break; case -1: return n+1; break; case -2: return n+2; break; case -3: return n+3; break; case -4: return n+4; break; } }
This is a simple and easy to debug solution. One item to point out is that this solution does not have special cases. Most of the solutions add a special case where number % 5 == 0
. This is a great first solution because you can see the pattern of what needs to be added to the number. Now that you know the pattern you may be able to move or refactor to a better solution.
Recursion
Recursion has it share of bad press; it is the step-brother of the “goto
” statement. It is a general solution type that is not used to often. For this problem it is a good solution.
public Integer roundUp5(Integer number) { if (number % 5 == 0) { return number; } else { return roundUp5(number + 1); } }
This is a simple way to solve this problem. Remember to think about recursion on new problems.
Look at the API
If a problem has you stumped then look for a solution in the programming language. This is similar to the If statement solution but uses the Math.ceil() (aka ceiling) function.
// public int roundUp5(Integer number) { return (int) (Math.ceil(number.doubleValue() / 5.0) * 5.0); }
The use of the ceiling function does get you a step closer to the solution. This is a good strategy to use. Unfortunately, in this case, the conversion from Integer
to Double
and back to Integer
is a source of bugs. Finding a solution can also include importing third-party modules such as Apache Commons or any of the millions of artifacts in the maven repository.
Functional programming
Functional programming is a good class of solutions to use on arrays and collections.
public int roundUp5_ver4(Integer number) { int[] kRounder = IntStream.rangeClosed(-1_000_000, 1_000_000).map(k -> k * 5).toArray(); return number == 0 ? 0 : IntStream.of(kRounder).filter( n -> n - number == 0 || (number - n < 5 && n > number)).findFirst().orElse(0); }
Unfortunately, this solution, which I did not write, takes a lot of resources, it creates an array of two millionIntegers
. It does not give the correct answer when 5,000,000 < Math.abs(number). This is buggy and difficult to debug. Functional programming can be quite elegant. This solution is a ton of overkill.
Conclusion
There are other ways to solve problems, but these are a quick summary. When facing a new problem try to think what way is the best then try it. If the first way you chose is frustrating, difficult, and bug filled then step back and try a different method. Try to hone your instincts to know when to switch methods.