A small, classroom-friendly Java library for combinations, permutations, derangements, power sets, Cartesian products, and more—built for teaching and tinkering.
Combinations.of(12).choose(3) → all 3-subsets of {0..11}Permutations.of(5).take(2) → all ordered pairs from {0..4}PowerSet.of(4) → all subsets of {0..3}size()/sizeExact() so you can discuss how many without enumerating.A fluent API is a style where you chain method calls so code reads like a sentence:
Combinations.of(12).choose(3) // “choose 3 from 12 (no repetition)”
Combinations.of(8).choose(3).inGrayOrder() // “same set, but Gray (minimal-change) order”
Combinations.of(6).withRepetition().choose(4) // “choose 4 from 6 with repetition”
Permutations.of(5).take(3) // “take ordered 3-tuples without repetition”
PowerSet.of(5) // “all subsets of a 5-element set”
CartesianProduct.of(2,3,2) // “mixed-radix tuples with dims 2×3×2”
Derangements.of(5).all() // “all permutations with no fixed points”
Each call returns an iterable view of int[] index tuples (e.g., [0,2,5]). Use IndexingAdapter to map those indices onto real objects like strings, cards, toppings, etc.
What is Gray order for combinations? For
Combinations.of(n).choose(k).inGrayOrder(), consecutive k-subsets differ by toggling exactly one element (one leaves, one enters). You get the same combinations as lexicographic order, just in a minimal-change sequence—handy for animations, incremental updates, or bitset diffs.
import java.util.List;
import org.loudouncodes.combinatorics.Combinations;
import org.loudouncodes.combinatorics.IndexingAdapter;
public class PizzaDemo {
public static void main(String[] args) {
List<String> toppings = List.of(
"Pepperoni","Sausage","Mushrooms","Onions","Green Peppers","Black Olives",
"Spinach","Bacon","Ham","Pineapple","Tomatoes","Extra Cheese"
);
int n = toppings.size();
// 1) 3-topping pizzas (no repetition, lexicographic)
var combosLex = Combinations.of(n).choose(3);
var pizzasLex = new IndexingAdapter<>(combosLex, toppings);
System.out.println("Three-topping pizzas (lex order):");
for (var pizza : pizzasLex) System.out.println(pizza);
// 2) Same combinations but in Gray (minimal-change) order
var combosGray = Combinations.of(n).choose(3).inGrayOrder();
var pizzasGray = new IndexingAdapter<>(combosGray, toppings);
System.out.println("\nThree-topping pizzas (Gray order):");
for (var pizza : pizzasGray) System.out.println(pizza);
// 3) 3 scoops from 6 flavors (with repetition — like ice cream)
var scoops = Combinations.of(6).withRepetition().choose(3);
System.out.println("\nThree scoops from 6 flavors (with repetition):");
for (int[] pick : scoops) System.out.println(java.util.Arrays.toString(pick));
}
}
What’s going on?
Combinations.of(n).choose(k) yields k-element index sets like [0,4,7] in lexicographic order.…choose(k).inGrayOrder() yields the same k-subsets in minimal-change order (one index toggled per step).IndexingAdapter<>(Iterable<int[]>, List<E>) turns those indices into real lists (here, topping names).[0,0,2]).| Concept | Fluent entry | Returns | Notes |
|---|---|---|---|
| Combinations (no rep, lex) | Combinations.of(n).choose(k) |
Iterable<int[]> |
Lexicographic order; size() = C(n,k) |
| Combinations (no rep, Gray) | Combinations.of(n).choose(k).inGrayOrder() |
Iterable<int[]> |
Minimal-change sequence (same set as lex); size() = C(n,k) |
| Combinations (with repetition) | Combinations.of(n).withRepetition().choose(k) |
Iterable<int[]> |
Nondecreasing arrays; size() = C(n+k−1,k) |
| Permutations (k-tuples) | Permutations.of(n).take(k) |
Iterable<int[]> |
Lexicographic; size() = P(n,k) |
| Derangements | Derangements.of(n).all() |
Iterable<int[]> |
No fixed points; size() = subfactorial |
| Power set | PowerSet.of(n) |
Iterable<int[]> |
Size-then-lex order; count() = 2^n |
| Cartesian product | CartesianProduct.of(d0,d1,...) |
Iterable<int[]> |
Rightmost coordinate varies fastest |
| Index → object | new IndexingAdapter<>(tuples, data) |
Iterable<List<E>> |
Defensive copies each step |
| Binary Gray codes (bitstrings) | BinaryGray.of(n).asBits() |
Iterable<int[]> |
Minimal-change bit patterns (for demos and visualizations) |
Availability: Gray order is provided for combinations without repetition. (With-repetition uses lexicographic nondecreasing tuples.)
IllegalArgumentExceptions; iterators follow the hasNext()/next() contract and throw on exhaustion.size()/sizeExact() so you can reason about feasibility first.PMD (static analysis):
int[]) so you can remap to any domain (names, cards, colors) using IndexingAdapter.size() or sizeExact() first.k > n without repetition).