Visit the pythagorean-triplet exercise on Exercism to read the full instructions and download the exercise files.
Dig Deeper
for loops
for loops
import java.util.*;
public class PythagoreanTriplet {
private final int a;
private final int b;
private final int c;
private PythagoreanTriplet() {
this.a = 0;
this.b = 0;
this.c = 0;
}
public PythagoreanTriplet(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
public static PythagoreanTripletBuilder makeTripletsList() {
return new PythagoreanTriplet().createBuilder();
}
private PythagoreanTripletBuilder createBuilder() {
return new PythagoreanTripletBuilder();
}
public class PythagoreanTripletBuilder {
private final List < PythagoreanTriplet > triplets = new ArrayList < > ();
private int limit = 0;
private int sum = 0;
public PythagoreanTripletBuilder withFactorsLessThanOrEqualTo(int limit) {
this.limit = limit;
return this;
}
public PythagoreanTripletBuilder thatSumTo(int sum) {
this.sum = sum;
return this;
}
public List < PythagoreanTriplet > build() {
if (limit == 0)
limit = sum / 2;
int start = (int) Math.sqrt(sum);
for (int a = start; a <= limit; a++) {
for (int b = a; b <= limit; b++) {
double c = Math.sqrt(a * a + b * b);
if (c % 1 == 0 && c <= limit && a + b + c == sum) {
triplets.add(new PythagoreanTriplet(a, b, (int) c));
}
}
}
return triplets;
}
}
@Override
public int hashCode() {
return Objects.hash(a, b, c);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PythagoreanTriplet other)
return this.hashCode() == other.hashCode();
return false;
}
}
Many approaches will return an object of some kind of separate “builder” class from the makeTripletsList() method of the PythagoreanTriplet class.
This particular code does this by instantiating a new object from its nested PythagoreanTripletBuilder class.
Since the nested class is not a static class, a new instance of the PythagoreanTriplet class is first created,
and that object returns an instance of the PythagoreanTripletBuilder class.
PythagoreanTripletBuilder makes use of the Fluent API design so that its withFactorsLessThanOrEqualTo() and
thatSumTo() methods can be chained together.
The real work is done in its build() method.
If the limit has not been set by the withFactorsLessThanOrEqualTo() method, it is set to half of the desired sum.
Any higher limit would result in factors whose combined squared values would exceed the desired sum.
The starting value is set to be the square root of the desired sum, since any valid factor will be no less than the square root of the desired sum.
Constraining the starting and limit values instead of using 2 through the desired sum is a way of minimizing unneccesary calculations.
The first for loop starts at the starting value for a and keeps looping while a is less than or equal to the limit.
The nested for loop starts at the value of a for b and keeps looping while b is less than or equal to the limit.
The factor for c is calculated from the square root of the a squared plus b squared.
Then a chain of boolean conditions is used to check if the values are a desired Pythagorean Triplet.
The first check is for determing if c is a whole number. If it is not, then the logical AND operator (&&)
short circuits.
The second check is for determining that c is less than or equal to the limit.
The final check is for determing that the sum of a, b, and c is equal to the desired sum.
If all of the checks pass then a PythagoreanTriplet object is created and added to the output List.
After the loops finish, the output List is returned from the build() method.
IntStream with parallel, flatMap and filter
IntStream with parallel(), flatMap() and filter()
import java.util.List;
import java.util.Objects;
import java.util.stream.IntStream;
public class PythagoreanTriplet {
public final Integer a;
public final int b;
public final double c;
public PythagoreanTriplet(Integer a, int b, double c) {
this.a = a;
this.b = b;
this.c = c;
}
public static Triplets makeTripletsList() {
return new Triplets();
}
@Override
public int hashCode() {
return Objects.hash(a, b, c);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PythagoreanTriplet other)
return this.hashCode() == other.hashCode();
return false;
}
}
class Triplets {
private double num;
private int limit = 0;
public Triplets thatSumTo(double num) {
this.num = num;
return this;
}
public Triplets withFactorsLessThanOrEqualTo(int limit) {
this.limit = limit;
return this;
}
public List < PythagoreanTriplet > build() {
int start = (int) Math.sqrt(num);
if (limit == 0)
limit = (int) num / 2;
return IntStream.rangeClosed(start, limit).parallel().boxed()
.flatMap(a -> IntStream.rangeClosed(a, limit).parallel()
.filter(b -> {
var c = Math.sqrt(a * a + b * b);
return c % 1 == 0 && c <= limit && a + b + c == num;
})
.mapToObj(b -> new PythagoreanTriplet(a, b, Math.sqrt(a * a + b * b))))
.toList();
}
}
Many approaches will return an object of some kind of separate “builder” class from the makeTripletsList() method of the PythagoreanTriplet class.
This particular code does this by instantiating and returning a new object from its sibling Triplets class.
Triplets makes use of the Fluent API design so that its withFactorsLessThanOrEqualTo() and
thatSumTo() methods can be chained together.
The real work is done in its build() method.
If the limit has not been set by the withFactorsLessThanOrEqualTo() method, it is set to half of the desired sum.
Any higher limit would result in factors whose combined squared values would exceed the desired sum.
The starting value is set to be the square root of the desired sum, since any valid factor will be no less than the square root of the desired sum.
Constraining the starting and limit values instead of using 2 through the desired sum is a way of minimizing unneccesary calculations.
The IntStream.rangeClosed() method is used to generate numbers for a from the start value through the limit value.
The IntStream is chained to the parallel() method so that the calculations can take advantage of multiple processors.
The parallel IntStream is boxed() to convert the IntStream to a Stream<Integer> so its mapped Stream
can later be chained to toList().
The Stream of Integers is chained to the flatMap() method that will flatten the a values with the b values into a Stream
of PythagoreanTriplets (instead of being a Stream of a Stream of PythagoreanTriplets).
This takes place in a lambda that is passed the a value.
The b values are generated from an IntStream.rangeClosed() that starts with the value for a and goes through the limit value.
The IntStream for the b values is also chained to the parallel() method.
The b stream is then chained to the filter() method to be run through some checks.
To prepare for the checks, the c value is calculated from the square root of a squared plus b squared.
The first check is for determing if c is a whole number. If it is not, then the logical AND operator (&&)
short circuits.
The second check is for determining that c is less than or equal to the limit.
The final check is for determing that the sum of a, b, and c is equal to the desired sum.
The surviving b values are chained to the mapToObj() method, in which a PythagoreanTripletobject is instantiated
from a, b, and the recalculated value for c, which is computed from the square root of a squared plus b squared.
It is possible that the filtering of the b values and the mapping to a PythagoreanTriplet object could happen in the same iteration
due to loop fusion.
The Stream of PythagoreanTriplet objects is finally chained to toList(), whose result is returned from the build() method.
Source: Exercism java/pythagorean-triplet