47

I wanted to see what the smallest number divisible by all one digit numbers was and instead of looking it up I created this.

public static void main(String[] args) {

    for (int i = 100; i < 10000; i++) {

        if (i % 2 ==0) {

            if (i % 3 ==0) {

                if (i % 4 ==0) {

                    if (i % 5 ==0) {

                        if (i % 6 ==0) {

                            if (i % 7 ==0) {

                                if (i % 8 ==0) {

                                    if (i % 9 ==0) {

                                        System.out.println(i);

                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

As you can see, I have an if statement in an if statement x9. The code worked but I wanted to condense my if statements using an array to make my if statement like this but it didn't work.

 if (i % x[1, 2, 3, 4, 5, 6, 7, 8]) {
 System.out.println(i);
 break;
 }

Any suggestions?

getRect
  • 579
  • 1
  • 4
  • 6

13 Answers13

153

At first you would think you can test all of them at once by placing the product of 2 through 9 on the right side of the % operator.

if (i % (2 * 3 * 4 * 5 * 6 * 7 * 8 * 9) == 0)

But because certain numbers include previous numbers in their factorization, you should use a lower number, specifically, the least common multiple. 8 is a multiple of 2 and 4, 9 is a multiple of 3, and if 8 and 9 are in the product, then 6 (2 * 3) is covered too.

if (i % (5 * 7 * 8 * 9) == 0)

That turns out to be 2520, which is the least common multiple. It would much more readable to use 2520 and explain in a comment why this number is used.

/**
 * The goal is to test if the number is a multiple of all integers
 * from 2 through 9.  Mathematically, the least common multiple to is a
 * multiple of all its input numbers.  Here, the LCM of 2, 3, ..., 9 is 2520.
 */
public static final int LCM_2_THRU_9 = 2520;

I've declared a constant and I'll use it here:

if (i % LCM_2_THRU_9 == 0)
rgettman
  • 176,041
  • 30
  • 275
  • 357
  • 58
    Since `5 * 7 * 8 * 9` is obfuscating the original logic anyway, might as well just write `2520` and then *comment* that it's the [**Least Common Multiple**](https://en.wikipedia.org/wiki/Least_common_multiple) of `2, 3, 4, 5, 6, 7, 8, 9`. – Andreas Apr 30 '18 at 19:59
  • 6
    Suggestion : declare a constant `lcm` and add a comment that it’s the least common multiple of {2, 3, 4, 5, 6, 7, 8, 9}. Same to the compiler, easier for a human to grok. – Davislor Apr 30 '18 at 21:20
  • 87
    You're kind of defeating the point of the exercise. Now you're looking for the smallest number (other than 0) divisible by 2520... spoiler, it's 2520. But in order to get the number 2520 you had to do the computation in the first place. – user253751 May 01 '18 at 01:22
  • 1
    Personally I'd assign the constant with `2 * 3 * 4 ...`. It's clearer IMO, it's more editable (easier to add or remove numbers), the compiler with do the math at compile time so I don't have to. – gman May 01 '18 at 03:50
  • 5
    @gman and then, what would you do with this constant? It's too large. – Eric Duminil May 01 '18 at 08:08
  • 1
    Then, an obvious optimization for this is just multiplying i by LCM_2_THRU_9 for finding only the multiples of 2520. All values between i*2520+1 and (i+1)*2520-1 would be discarded anyway. – Ale May 01 '18 at 16:31
  • 8
    @gman but that would be wrong! Doing `if (x % 2 && x % 4)` is the same as `if (x % 4)`, not the same as `if (x % 8)`. The multiplication only applies when the numbers are not divisible by each other, like `2` and `3`. – Francisco Presencia May 01 '18 at 19:41
  • 15
    you may as well recommend replacing the code with System.out.println(2520); – RiaD May 01 '18 at 21:15
  • Of course 2520 is the answer to all questions. It is after all, the magic number. – Vincent May 01 '18 at 23:59
86

Try this.

for (int i = 100; i < 10000; ++i) {
    int x = i;
    if (IntStream.of(2, 3, 4, 5, 6, 7, 8, 9).allMatch(k -> x % k == 0)) {
        System.out.println(i);
        break;
    }
}

-> 2520

Or you can write this as one statement.

int result = IntStream
    .range(100, 10000)
    .filter(i -> IntStream.of(2, 3, 4, 5, 6, 7, 8, 9).allMatch(k -> i % k == 0))
    .findFirst()
    .getAsInt();

System.out.println(result);

-> 2520
displayName
  • 13,888
  • 8
  • 60
  • 75
  • 47
    This would be a much better answer if it explained how one might come up with this code and what the code is doing. – David Z Apr 30 '18 at 20:53
  • 3
    I like this answer best because it doesn't rely on the specific case of checking multiple modulos. – Quelklef Apr 30 '18 at 21:55
  • 7
    @DavidZ Sequence processing mechanisms are expected to be common knowledge now. Java's streams are just one of the latest comers to the party being thrown by Python's list comprehensions and .NET's LINQ. – jpmc26 May 01 '18 at 07:44
  • 10
    @jpmc26 That may be true but it doesn't mean the answer wouldn't be improved by adding an explanation. – David Z May 01 '18 at 07:49
  • 1
    @jpmc26 lambdas are much more complex than python comprehensions IMHO. What's the point of `x=i` for example? – Eric Duminil May 01 '18 at 08:09
  • 2
    @Oleksandr that was a rhetorical question indeed, to show that you need to grasp more concepts for Java lambdas than for python comprehrensions. – Eric Duminil May 01 '18 at 11:48
  • @saka1029: I edited your answer and felt that this version is slightly more readable. You may undo the changes if you don't like them. – displayName May 02 '18 at 02:19
  • Can't you also just remove 2, 3, and 4 from your checks? If it's divisible by any even number, 2 is valid. If it's divisible by 6 or 9, 3 is valid. If it's divisible by 8, 4 is valid. Should save some time. – Kulahan May 02 '18 at 15:27
  • @jpmc26 It's bad practice to avoid putting in some quick detail because you believe everyone knows something. A short explanation would only help here. – Kulahan May 02 '18 at 15:30
  • @Quelklef It does check multiple modulos, though... It does literally what the original does, just written down in a way that uses fewer letters. – user3067860 May 02 '18 at 16:00
  • @user3067860 No, I'm not saying it doesn't. I'm just saying that it's a better answer because it generalized much more nicely than other answers do. – Quelklef May 02 '18 at 16:10
  • What is the point of using `IntStream`s? It seems like a crazily complicated way to do a simple task. – mackycheese21 May 03 '18 at 02:57
  • Why not use an infinite `IntStream`? You are making an assumption about the magnitude of the answer. – Boris the Spider May 03 '18 at 06:25
46

As previously answered probably the neatest way to write what you are trying to do is to check for the product of 2 through 9.

However, to answer your question on how you can condense if statements; nested if statements are equivalent to the logical operator AND, hence you could also write your if statements in the following manner:

if (i % 2 == 0 && i % 3 == 0 && i % 4 == 0 && i % 5 == 0 && i % 6 == 0 && i % 7 == 0 && i % 8 == 0 && i % 9 == 0) {
System.out.println(i);
}
stoAlias
  • 470
  • 3
  • 6
  • 1
    You can eliminate earlier factors in the same way that optimisation of the sieve of eratosthenes removes later multiples. Anything evenly divisible by 8 is also divisible by 4 and also 2. But a complete answer and would preserve short circuiting. Suggest stepping by 2 as well if starting on even numbers. – mckenzm May 01 '18 at 22:32
  • @mckenzm: It is not a good suggestion. See the footnote 2 in my answer. – displayName May 02 '18 at 12:29
43

Why don't you..

Invert the IFs?


public static void main(String[] args) {

    for (int i = 100; i < 10000; i++) {

        //If value is not valid, continue to next value
        if (i % 2 != 0) continue;
        if (i % 3 != 0) continue;
        if (i % 4 != 0) continue;
        if (i % 5 != 0) continue;
        if (i % 6 != 0) continue;
        if (i % 7 != 0) continue;
        if (i % 8 != 0) continue;
        if (i % 9 != 0) continue;

        //Valid value found. Print and break out of the loop.
        System.out.println(i);
        break;
    }
}

Alternatively, the above code can be refactored further to:

public static void main(String[] args) {
    for (int i = 100; i < 10000; i++) {
        if (isPrintable(i)) {
            System.out.println(i);
            break;
        }
    }
}

private static boolean isPrintable(int value) {
    return value % 2 == 0
           && value % 3 == 0
           && value % 4 == 0
           && value % 5 == 0
           && value % 6 == 0
           && value % 7 == 0
           && value % 8 == 0
           && value % 9 == 0;
}

Further, per @TeePeemm's suggestion, isPrintable() can be reduced to:

private static boolean isPrintable(int value) {
    for (int divisor = 2; divisor < 10; divisor++) {
        if (value % divisor != 0) return false;
    }
    return true;
}

1. There are language based shortcuts too, as suggested in other answers. I agree with them.

2. A lot of answers used the LCM of the numbers for making the code concise, but that is a dormant bug waiting to bite. The loop execution completely changes which can be seen by commenting out the break;. The seemingly simple solution introduces a subtle, potential bug.

displayName
  • 13,888
  • 8
  • 60
  • 75
  • It's basically _goto_ :P. But it's actually very useful if the conditions are too complex to put them in an array and loop through them. +1. – Frax May 01 '18 at 14:07
  • @Frax: Why do you call it a _goto_ my friend? The way I see it, I'm just validating the values upfront, pretty much the way we verify an external input at the top of the method (like checking if the string is null or empty or whitespaces and that the input number is positive, etc.). I edited the answer and added a _validation method_ now. Do you still see my solution the same way? – displayName May 01 '18 at 14:55
  • 1
    I didn't mean anything bad! It's just that, at least used at this scale, this is a very imperative style based on, well, jumping over some parts of code. Nothing wrong with it, it's good and idiomatic. Actually, I write code like this all the time. And it's quite well suited for this question. – Frax May 01 '18 at 15:11
  • 1
    From a purely logic/maths based approach, you can drop from 8 modulo tests to 4, since some of them (or combinations thereof) imply other lower numbers: `9` (also `3`), `8` (also `4`, also `2`. `9` **and** `8` is also `6`), `7` (prime) and `5` (prime). Of course, the actual output is just the product of those 4 numbers... – Chronocidal May 01 '18 at 17:30
  • @Chronocidal: I think I should leave the code be. There is a subtle problem waiting to happen if I implement what you mentioned. The issue is that the code will fail miserably if we change the start value from 100 to something bigger than the LCM of the test numbers. – displayName May 01 '18 at 18:49
  • This is still pretty repetetive. Maybe include an explanation of how you don't only need to check the numbers 5,7,8,9? And include that in your code? – mackycheese21 May 01 '18 at 20:12
  • @mackycheese21: It isn't repetitive since all the code that's written there is necessary. But I have taken your suggestion and added an explanation to the answer. – displayName May 01 '18 at 20:28
  • @LordFarquaad Knuth's "Structured Programming with go to Statements" also was written for a reason: sometimes a goto-like tool *does* improve the code. – Joker_vD May 01 '18 at 21:31
  • 5
    I like this because it isn't a math answer to a code problem, costs peanuts performance-wise, isn't prone to hidden logic/math errors if the problem complexifies, and is explicit enough future-you in six months won't need to read the comments to figure what the heck you did. – AmiralPatate May 02 '18 at 09:08
20

In Java 8 onwards, you can use a Stream approach (specifically using IntStream).

First, we use IntStream.rangeClosed(2, 9) (or equivalently, IntStream.range(2, 10)) to obtain a stream of consecutive Integers starting with 2 and ending with 9 (inclusive). We can turn this stream into a boolean by using .allMatch(...), which returns true if and only if every stream element matches some criteria. The desired criteria is provided in the form of a lambda expression, n -> i % n == 0. This can be written verbosely as (Integer n) -> (i % n == 0), so the lambda expression takes as input an Integer from the stream called n, and returns whether i (the loop counter) is divisible by n. Hence, .allMatch(n -> i % n == 0) returns true if i is divisible by every Integer in the stream.

We need to make one more modification: variables used in lambda expressions (such as i) must be effectively final:

A variable or parameter whose value is never changed after it is initialized is effectively final. (Oracle documentation)

However, the loop counter i is not effectively final, since it gets incremented (thus reassigned) every iteration. The solution is to declare a new variable int x = i; inside the loop, so that x is only assigned once within its scope (that is, one iteration of the loop). Hence, x is effectively final and can be used in the lambda expression.

Here's the final solution:

import java.util.stream.IntStream;

public static void main(String[] args) {
    for (int i = 100; i < 10000; i++) {
        int x = i; // x is effectively final
        if (IntStream.rangeClosed(2, 9).allMatch(n -> x % n == 0)) {
            System.out.println(i);
            break;
        }
    }
}
k_ssb
  • 6,024
  • 23
  • 47
20

Much simpler way:

    public static boolean isDivisible(int number) {
        for (int i = 2; i <= 9; i++) {
            if (num % i != 0) {
                return false;
            }
        }
        return true;
    }

And using the same type of structure, main method becomes:

    public static void main(String[] args) {
        for (int i = 100; i <= 100000; i++) {
            if (isDivisible(i)) {
                System.out.println("Divisible by numbers 2...9: " + i);
                break;
            }
        }
    }
mackycheese21
  • 884
  • 1
  • 9
  • 24
16

What you are basically doing is trying to find the number i which is the LCM of 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9. Initially, you may be intended simply write

if (i % (2 * 3 * 4 * 5 * 6 * 7 * 8 * 9) == 0) {
    System.out.println(i);
    break;
}

It would be true if all the numbers were coprime. That means they didn't have any common factor. But In this case, the numbers are not coprime and have common factors. Like 8 = 2*2*2, 4 = 2*2, 6 = 2*3 all have 2. 3 = 1 * 3 , 6 = 2*3, 9 = 3*9 all have 3. So basically we have to take the LCM of the numbers 2,3,4,5,6,7,8,9. See the following edit for correction of the above formula.

The LCM (Least Common Multiple) of the numbers 2,3,4,5,6,7,8,9 is = 2520. So the correct formula passing all the test cases is following

if ( i % 2520 == 0) { 
  System.out.println(i); 
  break;
}

Another solution to use would be simply to check all the conditions like following:

if(i % 9 == 0 && i % 8 ==0 && i % 7 == 0 && i % 5 == 0) {
   System.out.println(i);
   break;
}
Md Johirul Islam
  • 5,042
  • 4
  • 23
  • 56
  • 1
    OP isn't checking if the number is divisible by `1*2*3*4*5*6*7*8*9`. Please edit the answer to only leave true statements. There's no need to include the original ones. – Eric Duminil May 01 '18 at 02:51
  • 5
    The premise of this answer is false. The question is asking to write code to find the lowest common multiple, not to check if a number is divisible by the lowest common multiple. – Ben May 01 '18 at 07:41
  • 2
    The question says "I wanted to see what the smallest number divisible by all one digit numbers was" - this is equivalent to saying "I wanted to find the LCM of the numbers 2-9". The author of the question then says "instead of looking it up I created this function" - in other words the question assumes that the LCM of 2-9 is unknown. If the loop started at 2521 the code would not find the LCM, and therefore would not achieve its stated purpose. – Ben May 01 '18 at 12:06
12

The question in actually two-fold: first part is, how to compress 9 if statements with similar condition to some more readable form. The other, perhaps unintended question is, how should things like "LCM of single-digit numbers" be added to the code. Let's start with the latter , and go to former below.

Google for the result

If you need this kind of number in your program (instead of the sole purpose of program being computing it), you should just obtain it by the simplest means necessary (in this case, googling for "smallest number divisible by all one digit numbers"), and include in your program as a constant, perhaps with some comment about where the number came from.

If you can't just find it, try computing it yourself (like rgettman did), and again include it as constant. If that fails or takes too much time, write one-off program to compute the number, but don't make it a part of the bigger program using the constant. It's a good idea to store the one-off code somewhere, though. A comment may be the right place.

Iterate over an array

Now that's about compressing the if statement.

There were solutions using streams, though in your case a simple array may be better. This code is also more generic, you can port it to almost any language with minimal effort, and it's not tied in iny way to numbers (you can use an array of anything). Bonus point - anyone should understand it.

static boolean divisibleByAll(int n, int[] divisors) {
    for (int d : divisors) {
        if (n % d != 0) {
            return false;
        }
    }
    return true;
}

static int lcmOfSingleDigits() {
    int[] divisors = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    for (int i = 100; i < 10000; i++) {
        if (divisibleByAll(i, divisors)) {
            return i;
        }
    }
    return -1;  // Perhaps better to throw an exception
}

public static void main(String args[]) {
    System.out.println("Smallest number divisible by all one digit numbers: " +
                       lcmOfSingleDigits());
}

Use Streams

Most Java-ish solution, that's what you should use in practice - unless you need non-Java programmers to read your code. Covered by saka1029's and pkpnd's answers, so I won't repeat it.

Frax
  • 5,015
  • 2
  • 17
  • 19
6

I think you can use LCM (Least Common Multiple) of (1, 2, 3, 4, 5, 6, 7, 8, 9) = 2520, like this:

if (i % 2520 == 0) {
    System.out.println(i);
    break;
}
Yohan Malshika
  • 716
  • 1
  • 11
  • 20
  • 1
    9! is not equal to 2520, not sure where you got that number. – jbch Apr 30 '18 at 21:40
  • 5
    @jbch it's the least common multiple, not the total multiple. if something is divisible by 8, it is also divisible by 4 and 2. – Sdarb Apr 30 '18 at 22:27
  • 1
    If available, use an LCM function to calculate this `2520`, rather than hard coding it. This both serves as documentation as to what this magic number is, and enables easy changes in the future. If there's no LCM function available and you don't feel like introducing a new dependency ust for it, at least write a comment on the right `// LCM(1, 2, 3, 4, 5, 6, 7, 8, 9) = 2520` – Alexander Apr 30 '18 at 22:58
  • 2
    @bigsandwich Yes, I understand that, the answer was edited. Before the answer had the equation `1*2*3*4*5*6*7*8*9=2520`in it, which is obviously wrong. The LCM is precisely what they are trying to calculate without looking it up. They didn't state it explicitly but the smallest number divisible by A, B and C *is* the LCM of A, B and C, so hard-coding the LCM or using an LCM function defeats the point of the exercise. – jbch May 01 '18 at 14:10
3

Your loop will take a very long time if you ever try it with the numbers from 1 to 20 or 1 to 30. You could calculate the least common multiple directly :

package stackOverflow;
import java.util.stream.LongStream;

public class NumberTheory
{

    public static void main(String[] args) {
        System.out.println(gcd(15, 3) == 3);
        System.out.println(gcd(13, 11) == 1);
        System.out.println(gcd(60, 24) == 12);
        System.out.println(gcd(1071, 462) == 21);
        System.out.println(gcd(462, 1071) == 21);
        System.out.println(gcd(new long[] { 10, 12, 24, 60 }) == 2);
        long[] oneToNine = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        System.out.println(gcd(oneToNine) == 1);
        System.out.println(lcm(oneToNine));
        long[] oneToTwenty = LongStream.range(1, 21).toArray();
        System.out.println(lcm(oneToTwenty));
    }

    /**
     * Calculates the greatest common divisor of 2 numbers which are not all zero. (see
     * https://en.wikipedia.org/wiki/Greatest_common_divisor)
     * 
     * Recursive version of Euclidean algorithm (https://en.wikipedia.org/wiki/Euclidean_algorithm)
     * 
     * @param m
     * @param n
     * @return greatest common divisor of m and n
     */
    public static long gcd(long m, long n) {
        if (m == 0 || n == 0) {
            return m + n;
        } else {
            return gcd(n, m % n);
        }
    }

    /**
     * Calculates the greatest common divisor of n numbers. The array should have at least one number which isn't zero.
     * 
     * @param numbers
     * @return greatest common divisor of numbers
     */
    public static long gcd(long[] numbers) {
        long result = numbers[0];
        for (int i = 1; i < numbers.length; i++) {
            result = gcd(result, numbers[i]);
        }
        return result;
    }

    /**
     * Calculates the least common multiple of 2 numbers which are both non zero. see
     * https://en.wikipedia.org/wiki/Least_common_multiple
     * 
     * @param m
     * @param n
     * @return least common multiple of m and n
     */
    public static long lcm(long m, long n) {
        return m * (n / gcd(m, n));
    }

    /**
     * Calculates the least common multiple of n numbers. The array should have at least one number and shouldn't contain
     * any zero.
     * 
     * @param numbers
     * @return least common multiple of numbers
     */
    public static long lcm(long[] numbers) {
        long result = numbers[0];
        for (int i = 1; i < numbers.length; i++) {
            result = lcm(result, numbers[i]);
        }
        return result;
    }
}

It outputs:

true
true
true
true
true
true
true
2520
232792560
Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
3

While this might be suboptimal, it would really condense your statements:

public static void main(String[] args)
{
    int remainder;
    for (int i = 100; i < 10000; i++) 
    { 
       remainder=0;
       for (int j=2; j<10; j++) 
           remainder+=i % j; 
       if (remainder == 0)
            System.out.println(i);
   }
}

For each i we use inner loop j to modulo it with each digit from 2 to 9. We add each modulo result to remainder variable.

At the end of inner loop, only if all modulos for this i were zero, the remainder will still be zero.

Gnudiff
  • 4,297
  • 1
  • 24
  • 25
  • Seems obfuscated. Why are you doing `j<10` when `j<=9` is much more clear? Using brackets will help with reading the code. And it would help if you included an explaination of why `remainder+=i % j` works. That line took me a little while. – mackycheese21 May 01 '18 at 18:11
  • @mackycheese21 I added the explanation. I use the same convention for loop variable as the OP. And I don't consider the code obfuscated. It is simply another way to achieve the result, which in some of more complicated cases avoids expensive and/or long statements. – Gnudiff May 01 '18 at 18:53
  • Great edit. But wouldn't it be easier just to have the bad number condition inside the `for(int j=2;j<19;j++)`? – mackycheese21 May 01 '18 at 20:10
  • @mackycheese21 for loops of unknown or very long length - yes. For a short loop of only 10 runs we are avoiding up to 8 conditionals per loop. I guess I unconsciously tried to avoid branch prediction: https://stackoverflow.com/questions/315306/is-if-expensive Not that it would matter very much in this case. – Gnudiff May 02 '18 at 05:24
2

What you are trying to do, as several other people have mentioned, is compute the least common multiple of the numbers 1, 2, 3, ..., 9.

But how do you do that in a computer? First, you need to know how to compute the greatest common divisor of two numbers:

function gcd2(a, b)
    while b ≠ 0
        t := b; 
        b := a mod b; 
        a := t; 
    return a;

Now, the least common multiple of two numbers can be computed from their greatest common divisor with a simple formula:

function lcm2(a, b)
    if a = 0 and b = 0
        return 0;
    else
        return abs(a*b) / gcd2(a,b);

(The special case for a and b both being zero is necessary to avoid division by zero.)

And finally, LCM(a,b,c) = LCM(LCM(a,b),c), so to compute the LCM of more than two numbers, iterate over a list:

function lcmN(ns)
    let rv := 1;
    for n in ns
        rv := lcm2(rv, n);
    return rv;

Translation of pseudocode to Java is left as an exercise.

zwol
  • 135,547
  • 38
  • 252
  • 361
2

Alright, this may not be the best approach, but I wanted to give it a try with an arbitrary set of numbers.

public interface Divisor {
    boolean isDivisible(Long value);
}

public static main(String[] args) {
    LCMDivisor divisor = LCMDivisor.init()
            .addValue(2L)
            .addValue(3L)
            .addValue(4L)
            .addValue(5L)
            .addValue(6L)
            .addValue(7L)
            .addValue(8L)
            .addValue(9L);

    LongStream.range(1, 10000)
            .filter(divisor::isDivisible)
            .findFirst()
            .ifPresent(System.out::println);
}

So we create an object, Divisor, which has a method that tells you whether a value is divisible by itself or not.

The code is run by creating Stream of Longs from 1 to N, filtering out all values that are not divisible by the divisor, and then taking the first one (per your 'break' statement). It returns an Optional. If a value is present, that value will be printed to the stdout.

For this example, per comments made above, I introduced an implementation of Divisor which store the least common multiple of all values added in. When initialized, it has a value of one; but everytime a new value is added, it returns a new instance of the Divisor with the least common multiple. The implementation looks like this:

public class LCMDivisor implements Divisor {

    public final Long lcmValue;

    private LCMDivisor(Long lcmValue) {
        this.lcmValue = lcmValue;
    }

    public static LCMDivisor init() {

        return new LCMDivisor(1L);
    }

    public Boolean isDivisible(final Long value) {
        return value % lcmValue == 0;
    }

    public LCMDivisor addValue(final Long newValue) {

        return new LCMDivisor(lcm(newValue));
    }

    private Long lcm(final Long newValue) {
        return newValue * (lcmValue / gcd(newValue));
    }

    private Long gcd(final Long newValue) {

        Long greater = newValue < lcmValue ? lcmValue : newValue;
        Long lesser = newValue > lcmValue ? lcmValue : newValue;

        while (lesser > 0)
        {
            long temp = lesser;
            lesser = greater % lesser;
            greater = temp;
        }
        return greater;
    }
}
JRogerC
  • 598
  • 6
  • 17
  • The LCM approach is inferior, but the design of your solution is impressive. – displayName May 02 '18 at 16:27
  • @displayName, why is it inferior? I like this approach because it doesn't have to go through each modulo for each number it tests. Both our approaches on the order of N, but yours has to do the modulo calculation for each test value, while this one does some intensive work up front, and then reduces the work on the back end. – JRogerC May 02 '18 at 19:27
  • See the second footnote of my answer. – displayName May 02 '18 at 19:30
  • I see that the above solution fails fast, but it will still do, in at least half of all situations do more than one modulo operation. This solution will only ever do at most one modulo operation per checked value. (btw, enjoying the discussion :) ) – JRogerC May 02 '18 at 19:37
  • Gerald Weinberg tells the story of a programmer who was flown to Detroit to help debug a troubled program. The programmer worked with the team who had developed the program and concluded after several days that the situation was hopeless. On the flight home, he mulled over the situation and realized what the problem was. By the end of the flight, he had an outline for the new code. He tested the code for several days and was about to return to Detroit when he got a telegram saying that the project had been canceled because the program was impossible to write. (1/3) – displayName May 02 '18 at 20:08
  • He headed back to Detroit anyway and convinced the executives that the project could be completed. Then he had to convince the project’s original programmers. They listened to his presentation, and when he’d finished, the creator of the old system asked, “And how long does your program take?” “That varies, but about ten seconds per input.” “Aha! But my program takes only one second per input.” The veteran leaned back, satisfied that he’d stumped the upstart. The other programmers seemed to agree, but the new programmer wasn’t intimidated. (2/3) – displayName May 02 '18 at 20:08
  • “Yes, but your program doesn’t work. If mine doesn’t have to work, I can make it run instantly.” (3/3) – displayName May 02 '18 at 20:08
  • I still don't understand the "inferior" statement. Both solutions work. – JRogerC May 03 '18 at 14:03
  • yes they both work but the LCM solution is fragile and can break if the start of variable _i_ is changed. – displayName May 03 '18 at 14:06
  • What value of i could break this? – JRogerC May 03 '18 at 15:17
  • I stand corrected. I misinterpreted your answer. It is a good answer on all three aspects - design, correctness, and maintainability. – displayName May 03 '18 at 17:02
  • Hope you enjoyed the story anyway. :) . It is from the book `Code Complete 2`'s performance tuning chapter. – displayName May 03 '18 at 17:15
  • I did enjoy it :) – JRogerC May 03 '18 at 17:56