# This type of combination/permutation is missing. What is it called?

Given

``````[0, 1]
``````

What is this called?

``````[[0, 0], [0, 1], [1, 0], [1, 1]]
``````

I don't see any option which does this. Only this subset is provided:

``````[[0, 1], [1, 0]]
``````
1 Like

Try the Cartesian `product(_:_:)` with the same argument twice.

2 Likes

No, that only works when the collection is two elements long.

The combination count needs to scale as a power of two, representing, for example, corner points of "rectangles" of various dimensions.

``````let rect = Rect3D(origin: .zero, size: .one)
let min = rect.min
let max = rect.max
#expect(
Set(rect.cornerPoints) ==
[ .init(x: min.x, y: min.y, z: min.z),
.init(x: min.x, y: min.y, z: max.z),
.init(x: min.x, y: max.y, z: min.z),
.init(x: min.x, y: max.y, z: max.z),
.init(x: max.x, y: min.y, z: min.z),
.init(x: max.x, y: min.y, z: max.z),
.init(x: max.x, y: max.y, z: min.z),
.init(x: max.x, y: max.y, z: max.z),
]
)
``````
1 Like

As @pyrtsa said, it is the cartesian product (in the latter case the n-ary cartesian product). For the example you provided, where the collections are arrays of the same type, you can naively define your own variadic `product` function, e.g.

``````func product<Element>(_ collections: [Element]...) -> [[Element]] {
guard let first = collections.first?.map({ [\$0] }) else { return [] }

return collections[1...].reduce(first) { partialResult, next in
partialResult.flatMap { row in
next.map { element in row + [element] }
}
}
}
``````

Your latter example would then be:

``````product([min.x, max.x], [min.y, max.y], [min.z, max.z])
``````

A better approach would require creating `ProductSequence` and `ProductSequence.Iterator`, following how `product(_:_:)` and all the other Swift Algorithms have been implemented.

Not quite, in the general case, the cartesian product count is the product of the of the counts of the sets involved.

A relevant topic:

2 Likes

In the general case, you would need a variadic version of `product(_:_:)`, but so far there's none in swift-algorithms. (I don't know if parameter packs are feature complete enough that it could be implemented.)

But you don't strictly need that complexity, because at the cost of a few nested trailing closures, you can achieve the same with `Array.flatMap` calls and a single `map`:

``````struct Point3D {
var x, y, z: Int
}
struct Rect3D {
var min, max: Point3D

var cornerPoints: [Point3D] {
let extrema = [min, max]
let xs = extrema.map(\.x)
let ys = extrema.map(\.y)
let zs = extrema.map(\.z)
return xs.flatMap { x in ys.flatMap { y in zs.map { z in
Point3D(x: x, y: y, z: z)
}}}
// return product(xs, ys, zs).map { x, y, z in
//   Point3D(x: x, y: y, z: z)
// }
}
}
``````

I couldn't resist digging up something I have done ages ago. :)

It can compute the cartesian product of integer ranges.

``````// {0, 1, 2} x {3, 4} x {5, 6, 7}

let product = CartesianProduct (0..<3)
product.append (range: 3..<5)
product.append (range: 5..<8)

while let v = product.next () {
print (v)
}

``````
Cartesian Product
``````//
//  CartesianProduct.swift
//
class CartesianProduct {
private let head  : Range <Int>
private var tail  : CartesianProduct?
private var index : Range <Int>.Index

init (_ head: Range <Int>, _ tail: CartesianProduct? = nil) {
self.tail  = tail
}
}

extension CartesianProduct {
func append (range: Range <Int>) {
if let tail {
tail.append (range: range)
}
else {
tail = CartesianProduct (range)
}
}
}

extension CartesianProduct {
func next () -> [Int]? {
guard index != head.endIndex else {
return nil
}

var u = [Int] ()
guard let tail else {
_ = move ()
return u
}
if let v = tail.next () {
u.append (contentsOf: v)
return u
}
else {
tail.restart ()
if move () {
return next ()
}
}
assert (!u.isEmpty)
return u
}
}

// -------------------------------------------------------------
//
extension CartesianProduct {
private func move () -> Bool {
guard self.index != head.endIndex else {
return false
}
return true
}

private func restart () {
if let tail {
tail.restart()
}
}
}
``````
Driver
``````//  Driver.swift

@main
enum Driver {
static func main ()  {
test1 ()
print ()
test2 ()
}
}

private
extension Driver {
static func test1 () {
// {0, 1, 2} x {3, 4} x {5, 6, 7}

let product = CartesianProduct (0..<3)
product.append (range: 3..<5)
product.append (range: 5..<8)

while let v = product.next () {
print (v)
}
}
}

private
extension Driver {
static func test2 () {
// {red, green, blue} x {mint, teal} x {0, 1, 2}

let s0: [String] = ["red", "green", "blue"]
let s1: [String] = ["mint", "teal"]
let s2: [Int]    = [0, 1, 2]

let f0 = {(i: Int) in s0 [i]}
let f1 = {(i: Int) in s1 [i]}
let f2 = {(i: Int) in s2 [i]}

let product = CartesianProduct (0..<s0.count)
product.append (range: 0..<s1.count)
product.append (range: 0..<s2.count)

let fv: [(Int) -> Any] = [f0, f1, f2]

while let v = product.next () {
// map indices to corresponding values
var x = 0
var mv: [Any] = []
for i in v {
mv.append ((fv [x])(i))
x += 1
}
print (mv, separator: ", ", terminator: "")
print ()
mv = []
}
}
}
``````
``````// {0, 1, 2} x {3, 4} x {5, 6, 7}
[0, 3, 5]
[0, 3, 6]
[0, 3, 7]
[0, 4, 5]
[0, 4, 6]
[0, 4, 7]
[1, 3, 5]
[1, 3, 6]
[1, 3, 7]
[1, 4, 5]
[1, 4, 6]
[1, 4, 7]
[2, 3, 5]
[2, 3, 6]
[2, 3, 7]
[2, 4, 5]
[2, 4, 6]
[2, 4, 7]

// {red, green, blue} x {mint, teal} x {0, 1, 2}
["red", "mint", 0]
["red", "mint", 1]
["red", "mint", 2]
["red", "teal", 0]
["red", "teal", 1]
["red", "teal", 2]
["green", "mint", 0]
["green", "mint", 1]
["green", "mint", 2]
["green", "teal", 0]
["green", "teal", 1]
["green", "teal", 2]
["blue", "mint", 0]
["blue", "mint", 1]
["blue", "mint", 2]
["blue", "teal", 0]
["blue", "teal", 1]
["blue", "teal", 2]
``````
2 Likes

Yeah, if efficiency is important, I would implement this as a stream instead of computing n products and throwing out all intermediate results except for the final one.

That is, instead of approaching this problem as one where you generate the entire list upfront, you want an algorithm that given the current element, outputs the next one.

Also, if the order is not relevant, the problem becomes a little easier. To generate the next element in the lexicographic order requires possibly making several updates to the tuple that represents the next element; in the worst case you update all elements (imagine the transition from 01111 to 10000 for example). Instead, you can use a Gray code, which requires one or two updates at each step: Gray code - Wikipedia

Computing all n-tuples of a fixed set of elements is actually a really interesting problem. If youâ€™re really serious about this, Knuth has a whole section about this problem called â€śGenerating all Possibilitiesâ€ť in Volume 4A.

6 Likes

Also, if youâ€™re only generating a tuple of indices, and the total number of combinations fits in a machine word, you can compute the tuple of indices using multiplication, division and modulo. Itâ€™s the same algorithm as converting an integer into a sequence of digits.

1 Like

Here's a parameter pack implementation I cooked up:

``````struct Product<each C: Collection>: IteratorProtocol, Sequence {
var collection: (repeat each C)
var index: (repeat (each C).Index)
var done: Bool

init(_ collection: repeat each C) {
self.collection = (repeat each collection)
self.index = (repeat (each collection).startIndex)
self.done = false

// We're immediately done if any collection is empty.
for (c, i) in repeat (each collection, each index) {
if (i == c.endIndex) {
done = true
}
}
}

mutating func next() -> (repeat (each C).Element)? {
if done { return nil }

defer {
// Advance the index by incrementing the first digit.
var carry = true

index = (repeat {
let c = each collection
var i = each index

// Increment this digit if necessary.
if carry {
c.formIndex(after: &i)
carry = false
}

// Check for wraparound.
if i < c.endIndex {
// Still in range.
return i
} else {
// We wrapped around; increment the next digit.
carry = true
return c.startIndex
}
}())

// If the last digit wrapped around, we're done.
done = carry
}

// Return the current element.
return (repeat (each collection)[each index])
}
}

for elt in Product(["a", "b", "c"], [0, 1, 2], [true, false]) {
print(elt)
}
``````

This needs a Swift 6.0 compiler, because there the closure in next() captures pack element types.

I found one bug while working on this; adding a `break` after the `done = true` in `init()` causes a compiler crash.

8 Likes

Looks like you can avoid the crash by using `Collection.isEmpty`

``````for c in repeat each collection {
if c.isEmpty {
done = true
break
}
}
``````

Interesting approach anyway!

3 Likes

Same implementation using `Parameter Pack` while requiring only `Sequence`

``````
struct ProductX<each S: Sequence>: IteratorProtocol {
var sequence: (repeat each S)
var iterators: (repeat (each S).Iterator)
var nextValue: (repeat (each S).Element)?

init(_ sequence: repeat each S) {
self.sequence = (repeat each sequence)
self.iterators = (repeat (each sequence).makeIterator())
// We're immediately done if any collection is empty.
do {
let packed = try (repeat {
var i = each iterators
if let value = i.next() {
return (value, i)
} else {
throw CancellationError()
}
}())

self.iterators = (repeat {
let i = (each packed)
return i.1
}())
self.nextValue = (repeat {
let e = (each packed)
return e.0
}())
} catch {
self.nextValue = nil
}
}

mutating func next() -> (repeat (each S).Element)? {
guard let valueToReturn = self.nextValue else {
return nil
}
do {
var carry = true
let packed = try (repeat {
let s = each sequence
var i = each iterators
let previousValue = each valueToReturn

if carry {
carry = false
if let partValue = i.next() {
return (i, partValue)
} else {
carry = true
i = s.makeIterator()
if let partValue = i.next() {
return (i, partValue)
}
throw CancellationError()
}
}
return (i, previousValue)
}())
self.iterators = (repeat {
let i = (each packed)
return i.0
}())
self.nextValue = (repeat {
let e = (each packed)
return e.1
}())
if carry == true {
nextValue = nil
}
} catch {
nextValue = nil
}
return valueToReturn
}
}

``````

This could be useful, if you want to make `Product` of none `RandomAccessCollection`. (Accessing Element of non RandomAccess Collection is slow operation)

1 Like

I converted it to a function.

`func product`
``````@inlinable public func product<each Collection: Swift.Collection>(
_ collection: repeat each Collection
) -> UnfoldSequence<
(repeat (each Collection).Element),
(collection: (repeat each Collection), index: (repeat (each Collection).Index))
> {
var done = false
for collection in repeat each collection {
guard !collection.isEmpty else {
done = true
break
}
}

return sequence(
state: (
collection: (repeat each collection),
index: (repeat (each collection).startIndex)
)
) { state in
if done { return nil }

defer {
// Advance the index by incrementing the first digit.
var carry = true

state.index = (repeat {
let collection = each state.collection
var index = each state.index

// Increment this digit if necessary.
if carry {
collection.formIndex(after: &index)
carry = false
}

// Check for wraparound.
if index < collection.endIndex {
// Still in range.
return index
} else {
// We wrapped around; increment the next digit.
carry = true
return collection.startIndex
}
}())

// If the last digit wrapped around, we're done.
done = carry
}

// Return the current element.
return (repeat (each state.collection)[each state.index])
}
}
``````

Is it possible to return an opaque type?

With the above, you can actually use the resultâ€¦

``````let sequence: some Sequence<(Int, Int)> = product([1], [2])
``````

By changing the return type to

``````-> some Sequence<(repeat (each Collection).Element)>
``````

â€¦it compiles, but becomes not usable at all.

Return type of let 'sequence' requires the types 'Array.Element, Array.Element' and '(Int, Int)' be equivalent