Lluís Josep Martínez
Posted on October 26, 2023
Just for fun, I asked chatGPT to give me a Java implementation of the sieve of Eratosthenes to find the first 100 prime numbers.
It gave me this:
public class SieveOfEratosthenes {
public static void main(String[] args) {
int n = 100; // Change this to the desired limit to find prime numbers up to n.
boolean[] isPrime = new boolean[n + 1];
Arrays.fill(isPrime, true);
isPrime[0] = isPrime[1] = false;
for (int p = 2; p * p <= n; p++) {
if (isPrime[p]) {
for (int i = p * p; i <= n; i += p) {
isPrime[i] = false;
}
}
}
System.out.println("Prime numbers up to " + n + ":");
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
System.out.print(i + " ");
}
}
}
}
So far so good. Now I asked for an implementation with lambdas. That pesky for loops are just too old.
public class SieveOfEratosthenesWithLambdas {
public static void main(String[] args) {
int n = 100; // Change this to the desired limit to find prime numbers up to n.
List<Integer> primes = IntStream.rangeClosed(2, n)
.boxed()
.filter(SieveOfEratosthenesWithLambdas::isPrime)
.collect(Collectors.toList());
System.out.println("Prime numbers up to " + n + ":");
primes.forEach(System.out::print);
}
static boolean isPrime(int number) {
return IntStream.rangeClosed(2, (int) Math.sqrt(number))
.allMatch(i -> number % i != 0);
}
}
Looks good. Compact and efficient.
Now it's when the fun starts. I ask for a "clean code" implementation, whatever that means. Here's the result:
public class SieveOfEratosthenesCleanCode {
public static void main(String[] args) {
int limit = 100; // Change this to the desired limit to find prime numbers up to 'limit'.
List<Integer> primes = findPrimesUpTo(limit);
System.out.println("Prime numbers up to " + limit + ":");
for (int prime : primes) {
System.out.print(prime + " ");
}
}
public static List<Integer> findPrimesUpTo(int limit) {
boolean[] isPrime = new boolean[limit + 1];
List<Integer> primes = new ArrayList<>();
// Initialize the array
for (int i = 2; i <= limit; i++) {
isPrime[i] = true;
}
// Sieve of Eratosthenes
for (int p = 2; p * p <= limit; p++) {
if (isPrime[p]) {
for (int i = p * p; i <= limit; i += p) {
isPrime[i] = false;
}
}
}
// Collect the prime numbers
for (int i = 2; i <= limit; i++) {
if (isPrime[i]) {
primes.add(i);
}
}
return primes;
}
}
I don't think it's "clean" enough. Methods are too large. I ask for a "cleaner" version:
public class PrimeNumberFinder {
public static void main(String[] args) {
int upperLimit = 100; // Change this to the desired limit to find prime numbers up to 'upperLimit'.
List<Integer> primeNumbers = findPrimeNumbers(upperLimit);
displayPrimeNumbers(primeNumbers);
}
public static List<Integer> findPrimeNumbers(int upperLimit) {
List<Integer> primeNumbers = new ArrayList<>();
boolean[] isComposite = new boolean[upperLimit + 1];
for (int number = 2; number * number <= upperLimit; number++) {
if (!isComposite[number]) {
for (int multiple = number * number; multiple <= upperLimit; multiple += number) {
isComposite[multiple] = true;
}
}
}
for (int number = 2; number <= upperLimit; number++) {
if (!isComposite[number]) {
primeNumbers.add(number);
}
}
return primeNumbers;
}
public static void displayPrimeNumbers(List<Integer> primeNumbers) {
System.out.println("Prime numbers found:");
for (int prime : primeNumbers) {
System.out.print(prime + " ");
}
System.out.println();
}
}
Methods are still too large. Refactor till you die.
public class PrimeNumberFinder {
public static void main(String[] args) {
int upperLimit = 100; // Change this to the desired limit to find prime numbers up to 'upperLimit'.
List<Integer> primeNumbers = findPrimeNumbers(upperLimit);
displayPrimeNumbers(primeNumbers);
}
public static List<Integer> findPrimeNumbers(int upperLimit) {
boolean[] isComposite = new boolean[upperLimit + 1];
sieveOfEratosthenes(isComposite, upperLimit);
return collectPrimes(isComposite);
}
private static void sieveOfEratosthenes(boolean[] isComposite, int upperLimit) {
for (int number = 2; number * number <= upperLimit; number++) {
if (!isComposite[number]) {
markMultiplesAsComposite(isComposite, number, upperLimit);
}
}
}
private static void markMultiplesAsComposite(boolean[] isComposite, int number, int upperLimit) {
for (int multiple = number * number; multiple <= upperLimit; multiple += number) {
isComposite[multiple] = true;
}
}
private static List<Integer> collectPrimes(boolean[] isComposite) {
List<Integer> primeNumbers = new ArrayList<>();
for (int number = 2; number < isComposite.length; number++) {
if (!isComposite[number]) {
primeNumbers.add(number);
}
}
return primeNumbers;
}
public static void displayPrimeNumbers(List<Integer> primeNumbers) {
System.out.println("Prime numbers found:");
for (int prime : primeNumbers) {
System.out.print(prime + " ");
}
System.out.println();
}
}
Well, that's it! A perfectly good algorithm has been "cleaned" until it is an incomprehensible garbage that needs much more cognitive load to understand than the original version.
Thanks Clean Code!
PS: the version in the book is even worse.
Posted on October 26, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.