Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,15 +242,14 @@ $ java -cp classes com.williamfiset.algorithms.search.BinarySearch
- [[UNTESTED] Chinese remainder theorem](src/main/java/com/williamfiset/algorithms/math/ChineseRemainderTheorem.java)
- [Prime number sieve (sieve of Eratosthenes)](src/main/java/com/williamfiset/algorithms/math/SieveOfEratosthenes.java) **- O(nlog(log(n)))**
- [Prime number sieve (sieve of Eratosthenes, compressed)](src/main/java/com/williamfiset/algorithms/math/CompressedPrimeSieve.java) **- O(nlog(log(n)))**
- [Totient function (phi function, relatively prime number count)](src/main/java/com/williamfiset/algorithms/math/EulerTotientFunction.java) **- O(n<sup>1/4</sup>)**
- [Totient function using sieve (phi function, relatively prime number count)](src/main/java/com/williamfiset/algorithms/math/EulerTotientFunctionWithSieve.java) **- O(nlog(log(n)))**
- [Totient function (phi function, relatively prime number count)](src/main/java/com/williamfiset/algorithms/math/EulerTotientFunction.java) **- O(√n)**
- [Extended euclidean algorithm](src/main/java/com/williamfiset/algorithms/math/ExtendedEuclideanAlgorithm.java) **- ~O(log(a + b))**
- [Greatest Common Divisor (GCD)](src/main/java/com/williamfiset/algorithms/math/GCD.java) **- ~O(log(a + b))**
- [Greatest Common Divisor (GCD)](src/main/java/com/williamfiset/algorithms/math/Gcd.java) **- ~O(log(a + b))**
- [Fast Fourier transform (quick polynomial multiplication)](src/main/java/com/williamfiset/algorithms/math/FastFourierTransform.java) **- O(nlog(n))**
- [Fast Fourier transform (quick polynomial multiplication, complex numbers)](src/main/java/com/williamfiset/algorithms/math/FastFourierTransformComplexNumbers.java) **- O(nlog(n))**
- [Primality check](src/main/java/com/williamfiset/algorithms/math/IsPrime.java) **- O(√n)**
- [Primality check](src/main/java/com/williamfiset/algorithms/math/PrimalityCheck.java) **- O(√n)**
- [Primality check (Rabin-Miller)](src/main/java/com/williamfiset/algorithms/math/RabinMillerPrimalityTest.py) **- O(k)**
- [Least Common Multiple (LCM)](src/main/java/com/williamfiset/algorithms/math/LCM.java) **- ~O(log(a + b))**
- [Least Common Multiple (LCM)](src/main/java/com/williamfiset/algorithms/math/Lcm.java) **- ~O(log(a + b))**
- [Modular inverse](src/main/java/com/williamfiset/algorithms/math/ModularInverse.java) **- ~O(log(a + b))**
- [Prime factorization (pollard rho)](src/main/java/com/williamfiset/algorithms/math/PrimeFactorization.java) **- O(n<sup>1/4</sup>)**
- [Relatively prime check (coprimality check)](src/main/java/com/williamfiset/algorithms/math/RelativelyPrime.java) **- ~O(log(a + b))**
Expand Down
25 changes: 9 additions & 16 deletions src/main/java/com/williamfiset/algorithms/math/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,31 @@ java_binary(
runtime_deps = [":math"],
)

# bazel run //src/main/java/com/williamfiset/algorithms/math:EulerTotientFunctionWithSieve
java_binary(
name = "EulerTotientFunctionWithSieve",
main_class = "com.williamfiset.algorithms.math.EulerTotientFunctionWithSieve",
runtime_deps = [":math"],
)

# bazel run //src/main/java/com/williamfiset/algorithms/math:FastFourierTransform
java_binary(
name = "FastFourierTransform",
main_class = "com.williamfiset.algorithms.math.FastFourierTransform",
runtime_deps = [":math"],
)

# bazel run //src/main/java/com/williamfiset/algorithms/math:GCD
# bazel run //src/main/java/com/williamfiset/algorithms/math:Gcd
java_binary(
name = "GCD",
main_class = "com.williamfiset.algorithms.math.GCD",
name = "Gcd",
main_class = "com.williamfiset.algorithms.math.Gcd",
runtime_deps = [":math"],
)

# bazel run //src/main/java/com/williamfiset/algorithms/math:IsPrime
# bazel run //src/main/java/com/williamfiset/algorithms/math:PrimalityCheck
java_binary(
name = "IsPrime",
main_class = "com.williamfiset.algorithms.math.IsPrime",
name = "PrimalityCheck",
main_class = "com.williamfiset.algorithms.math.PrimalityCheck",
runtime_deps = [":math"],
)

# bazel run //src/main/java/com/williamfiset/algorithms/math:LCM
# bazel run //src/main/java/com/williamfiset/algorithms/math:Lcm
java_binary(
name = "LCM",
main_class = "com.williamfiset.algorithms.math.LCM",
name = "Lcm",
main_class = "com.williamfiset.algorithms.math.Lcm",
runtime_deps = [":math"],
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,76 +1,70 @@
/**
* Generate a compressed prime sieve using bit manipulation. The idea is that each bit represents a
* boolean value indicating whether a number is prime or not. This saves a lot of room when creating
* the sieve. In this implementation I store all odd numbers in individual longs meaning that for
* each long I use I can represent a range of 128 numbers (even numbers are omitted because they are
* not prime, with the exception of 2 which is handled as a special case).
* Generates a compressed prime sieve using bit manipulation. Each bit represents whether an odd
* number is prime or not. Even numbers are omitted (except 2, handled as a special case), so each
* long covers a range of 128 numbers.
*
* <p>Time Complexity: ~O(nloglogn)
* <p>Time: ~O(n log(log(n)))
*
* <p>Compile: javac -d src/main/java
* src/main/java/com/williamfiset/algorithms/math/CompressedPrimeSieve.java
*
* <p>Run: java -cp src/main/java com/williamfiset/algorithms/math/CompressedPrimeSieve
* <p>Space: O(n / 128) longs
*
* @author William Fiset, [email protected]
*/
package com.williamfiset.algorithms.math;

public class CompressedPrimeSieve {

private static final double NUM_BITS = 128.0;
private static final int NUM_BITS_SHIFT = 7; // 2^7 = 128

// Sets the bit representing n to 1 indicating this number is not prime
// Marks n as not prime by setting its bit to 1.
private static void setBit(long[] arr, int n) {
if ((n & 1) == 0) return; // n is even
if ((n & 1) == 0)
return;
arr[n >> NUM_BITS_SHIFT] |= 1L << ((n - 1) >> 1);
}

// Returns true if the bit for n is off (meaning n is a prime).
// Note: do use this method to access numbers outside your prime sieve range!
// Returns true if n's bit is unset (meaning n is prime).
private static boolean isNotSet(long[] arr, int n) {
if (n < 2) return false; // n is not prime
if (n == 2) return true; // two is prime
if ((n & 1) == 0) return false; // n is even
if (n < 2)
return false;
if (n == 2)
return true;
if ((n & 1) == 0)
return false;
long chunk = arr[n >> NUM_BITS_SHIFT];
long mask = 1L << ((n - 1) >> 1);
return (chunk & mask) != mask;
}

// Returns true/false depending on whether n is prime.
/** Returns true if n is prime according to the given sieve. */
public static boolean isPrime(long[] sieve, int n) {
return isNotSet(sieve, n);
}

// Returns an array of longs with each bit indicating whether a number
// is prime or not. Use the isNotSet and setBit methods to toggle to bits for each number.
/**
* Builds a compressed prime sieve for all numbers up to {@code limit}.
*
* @param limit the upper bound (inclusive) for the sieve.
* @return a bit-packed array where each bit indicates whether an odd number is composite.
*/
public static long[] primeSieve(int limit) {
final int numChunks = (int) Math.ceil(limit / NUM_BITS);
final int sqrtLimit = (int) Math.sqrt(limit);
// if (limit < 2) return 0; // uncomment for primeCount purposes
// int primeCount = (int) Math.ceil(limit / 2.0); // Counts number of primes <= limit
int numChunks = (int) Math.ceil(limit / NUM_BITS);
int sqrtLimit = (int) Math.sqrt(limit);
long[] chunks = new long[numChunks];
chunks[0] = 1; // 1 as not prime
chunks[0] = 1; // Mark 1 as not prime.
for (int i = 3; i <= sqrtLimit; i += 2)
if (isNotSet(chunks, i))
for (int j = i * i; j <= limit; j += i)
if (isNotSet(chunks, j)) {
if (isNotSet(chunks, j))
setBit(chunks, j);
// primeCount--;
}
return chunks;
}

/* Example usage. */

public static void main(String[] args) {
final int limit = 200;
long[] sieve = CompressedPrimeSieve.primeSieve(limit);

for (int i = 0; i <= limit; i++) {
if (CompressedPrimeSieve.isPrime(sieve, i)) {
int limit = 200;
long[] sieve = primeSieve(limit);
for (int i = 0; i <= limit; i++)
if (isPrime(sieve, i))
System.out.printf("%d is prime!\n", i);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,81 +1,49 @@
/**
* Computes Euler's totient function phi(n), which counts the number of integers in [1, n] that are
* relatively prime to n.
*
* <p>Uses trial division to find prime factors and applies the product formula:
* phi(n) = n * product of (1 - 1/p) for each distinct prime factor p of n.
*
* <p>Time: O(sqrt(n))
*
* @author William Fiset, [email protected]
*/
package com.williamfiset.algorithms.math;

import java.util.*;

public class EulerTotientFunction {

/**
* Computes Euler's totient phi(n).
*
* @param n a positive integer.
* @return the number of integers in [1, n] that are coprime to n.
* @throws IllegalArgumentException if n is not positive.
*/
public static long eulersTotient(long n) {
for (long p : new HashSet<Long>(primeFactorization(n))) n -= (n / p);
return n;
}

private static ArrayList<Long> primeFactorization(long n) {
ArrayList<Long> factors = new ArrayList<Long>();
if (n <= 0) throw new IllegalArgumentException();
else if (n == 1) return factors;
PriorityQueue<Long> divisorQueue = new PriorityQueue<Long>();
divisorQueue.add(n);
while (!divisorQueue.isEmpty()) {
long divisor = divisorQueue.remove();
if (isPrime(divisor)) {
factors.add(divisor);
continue;
}
long next_divisor = pollardRho(divisor);
if (next_divisor == divisor) {
divisorQueue.add(divisor);
} else {
divisorQueue.add(next_divisor);
divisorQueue.add(divisor / next_divisor);
if (n <= 0)
throw new IllegalArgumentException("n must be positive.");
long result = n;
for (long p = 2; p * p <= n; p++) {
if (n % p == 0) {
while (n % p == 0)
n /= p;
result -= result / p;
}
}
return factors;
}

private static long pollardRho(long n) {
if (n % 2 == 0) return 2;
// Get a number in the range [2, 10^6]
long x = 2 + (long) (999999 * Math.random());
long c = 2 + (long) (999999 * Math.random());
long y = x;
long d = 1;
while (d == 1) {
x = (x * x + c) % n;
y = (y * y + c) % n;
y = (y * y + c) % n;
d = gcf(Math.abs(x - y), n);
if (d == n) break;
}
return d;
}

private static long gcf(long a, long b) {
return b == 0 ? a : gcf(b, a % b);
}

private static boolean isPrime(long n) {

if (n < 2) return false;
if (n == 2 || n == 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;

int limit = (int) Math.sqrt(n);

for (int i = 5; i <= limit; i += 6) if (n % i == 0 || n % (i + 2) == 0) return false;

return true;
// If n still has a prime factor greater than sqrt(original n).
if (n > 1)
result -= result / n;
return result;
}

public static void main(String[] args) {

// Prints 8 because 1,2,4,7,8,11,13,14 are all
// less than 15 and relatively prime with 15
// phi(15) = 8 because 1,2,4,7,8,11,13,14 are coprime with 15.
System.out.printf("phi(15) = %d\n", eulersTotient(15));

System.out.println();

for (int x = 1; x <= 11; x++) {
for (int x = 1; x <= 11; x++)
System.out.printf("phi(%d) = %d\n", x, eulersTotient(x));
}
}
}

This file was deleted.

Loading
Loading