diff --git a/src/main/java/com/thealgorithms/maths/Prime/MobiusFunction.java b/src/main/java/com/thealgorithms/maths/Prime/MobiusFunction.java index 3d4e4eff0f03..ec1785a916c7 100644 --- a/src/main/java/com/thealgorithms/maths/Prime/MobiusFunction.java +++ b/src/main/java/com/thealgorithms/maths/Prime/MobiusFunction.java @@ -31,27 +31,28 @@ public static int mobius(int number) { throw new IllegalArgumentException("Number must be greater than zero."); } - if (number == 1) { - // return 1 if number passed is less or is 1 - return 1; - } - int primeFactorCount = 0; + int remaining = number; - for (int i = 1; i <= number; i++) { - // find prime factors of number - if (number % i == 0 && PrimeCheck.isPrime(i)) { - // check if number is divisible by square of prime factor - if (number % (i * i) == 0) { - // if number is divisible by square of prime factor + /* Divide out every prime factor in turn. Trial division only has to run up to the square + root of the remaining value, and the multiplication is widened to long so that the bound + does not overflow for numbers close to Integer.MAX_VALUE. */ + for (int factor = 2; (long) factor * factor <= remaining; factor++) { + if (remaining % factor == 0) { + remaining /= factor; + if (remaining % factor == 0) { + // number is divisible by the square of this prime factor return 0; } - /*increment primeFactorCount by 1 - if number is not divisible by square of found prime factor*/ primeFactorCount++; } } + /* Whatever is left is either 1 or a single prime factor larger than the square root. */ + if (remaining > 1) { + primeFactorCount++; + } + return (primeFactorCount % 2 == 0) ? 1 : -1; } } diff --git a/src/test/java/com/thealgorithms/maths/prime/MobiusFunctionTest.java b/src/test/java/com/thealgorithms/maths/prime/MobiusFunctionTest.java index 734d02477ba2..98ac29e81a5b 100644 --- a/src/test/java/com/thealgorithms/maths/prime/MobiusFunctionTest.java +++ b/src/test/java/com/thealgorithms/maths/prime/MobiusFunctionTest.java @@ -5,6 +5,8 @@ import com.thealgorithms.maths.Prime.MobiusFunction; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; class MobiusFunctionTest { @@ -152,4 +154,15 @@ void testMobiusFunction() { assertEquals(expectedValue, actualValue); } } + + /** + * Large inputs whose smallest square divisor test used to overflow, most notably + * {@code Integer.MAX_VALUE}, whose square wraps around to 1 and made every number look like it + * had a squared prime factor. + */ + @ParameterizedTest + @CsvSource({"2147483647, -1", "2147483646, 0", "2147483645, -1", "2147483644, 0", "2147483629, -1", "2147395600, 0", "1073741824, 0", "1073741789, -1", "999999937, -1", "999999999, 0", "2146689000, 0"}) + void testMobiusForLargeNumbers(int number, int expected) { + assertEquals(expected, MobiusFunction.mobius(number)); + } }