Skip to content

Commit d8a11e0

Browse files
authored
All permutations (#56)
Adds the ability to easily get permutations of a range of sizes, like #51 did for Combinations.
1 parent d2a1ca5 commit d8a11e0

File tree

3 files changed

+320
-57
lines changed

3 files changed

+320
-57
lines changed

Guides/Permutations.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,31 @@ for perm in numbers2.permutations() {
6060
// [10, 10, 20]
6161
```
6262

63+
Given a range, the `permutations(ofCount:)` method returns a sequence of all the different permutations of the given sizes of a collection’s elements in increasing order of size.
64+
65+
```swift
66+
let numbers = [10, 20, 30]
67+
for perm in numbers.permutations(ofCount: 0...) {
68+
print(perm)
69+
}
70+
// []
71+
// [10]
72+
// [20]
73+
// [30]
74+
// [10, 20]
75+
// [10, 30]
76+
// [20, 10]
77+
// [20, 30]
78+
// [30, 10]
79+
// [30, 20]
80+
// [10, 20, 30]
81+
// [10, 30, 20]
82+
// [20, 10, 30]
83+
// [20, 30, 10]
84+
// [30, 10, 20]
85+
// [30, 20, 10]
86+
```
87+
6388
## Detailed Design
6489

6590
The `permutations(ofCount:)` method is declared as a `Collection` extension,

Sources/Algorithms/Permutations.swift

Lines changed: 165 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,62 +11,93 @@
1111

1212
/// A sequence of all the permutations of a collection's elements.
1313
public struct Permutations<Base: Collection> {
14-
/// The base collection.
14+
/// The base collection to iterate over for permutations.
1515
public let base: Base
1616

17+
@usableFromInline
1718
internal let baseCount: Int
18-
internal let countToChoose: Int
1919

20+
/// The range of accepted sizes of permutations.
21+
/// - Note: This may be empty if the attempted range entirely exceeds the
22+
/// bounds of the size of the `base` collection.
23+
@usableFromInline
24+
internal let kRange: Range<Int>
25+
26+
/// Initializes a `Permutations` for all permutations of `base` of size `k`.
27+
/// - Parameters:
28+
/// - base: The collection to iterate over for permutations
29+
/// - k: The expected size of each permutation, or `nil` (default) to
30+
/// iterate over all permutations of the same size as the base collection.
31+
@usableFromInline
2032
internal init(_ base: Base, k: Int? = nil) {
33+
let kRange: ClosedRange<Int>?
34+
if let countToChoose = k {
35+
kRange = countToChoose ... countToChoose
36+
} else {
37+
kRange = nil
38+
}
39+
self.init(base, kRange: kRange)
40+
}
41+
42+
/// Initializes a `Permutations` for all combinations of `base` of sizes
43+
/// within a given range.
44+
/// - Parameters:
45+
/// - base: The collection to iterate over for permutations.
46+
/// - kRange: The range of accepted sizes of permutations, or `nil` to
47+
/// iterate over all permutations of the same size as the base collection.
48+
@usableFromInline
49+
internal init<R: RangeExpression>(
50+
_ base: Base, kRange: R?
51+
) where R.Bound == Int {
2152
self.base = base
2253
let baseCount = base.count
2354
self.baseCount = baseCount
24-
self.countToChoose = k ?? baseCount
55+
let upperBound = baseCount + 1
56+
self.kRange = kRange?.relative(to: 0 ..< .max)
57+
.clamped(to: 0 ..< upperBound) ??
58+
baseCount ..< upperBound
2559
}
2660

61+
/// The total number of permutations.
62+
@inlinable
2763
public var count: Int {
28-
return baseCount >= countToChoose
29-
? stride(from: baseCount, to: baseCount - countToChoose, by: -1).reduce(1, *)
30-
: 0
64+
return kRange.map {
65+
stride(from: baseCount, to: baseCount - $0, by: -1).reduce(1, *)
66+
}.reduce(0, +)
3167
}
3268
}
33-
69+
3470
extension Permutations: Sequence {
3571
/// The iterator for a `Permutations` instance.
3672
public struct Iterator: IteratorProtocol {
3773
@usableFromInline
3874
internal var base: Base
75+
3976
@usableFromInline
40-
internal var indexes: [Base.Index]
41-
@usableFromInline
42-
internal var hasMorePermutations: Bool
77+
internal let baseCount: Int
78+
79+
/// The current range of accepted sizes of permutations.
80+
/// - Note: The range is contracted until empty while iterating over
81+
/// permutations of different sizes. When the range is empty, iteration is
82+
/// finished.
4383
@usableFromInline
44-
internal var countToChoose: Int = 0
45-
46-
/// `true` if we're generating permutations of the full collection.
84+
internal var kRange: Range<Int>
85+
86+
/// Whether or not iteration is finished (`kRange` is empty)
4787
@usableFromInline
48-
internal var permutesFullCollection: Bool {
49-
countToChoose == indexes.count
88+
internal var isFinished: Bool {
89+
return kRange.isEmpty
5090
}
5191

5292
@usableFromInline
53-
internal init(_ base: Base) {
54-
self.base = base
55-
self.indexes = Array(base.indices)
56-
self.countToChoose = self.indexes.count
57-
self.hasMorePermutations = true
58-
}
93+
internal var indexes: [Base.Index]
5994

6095
@usableFromInline
61-
internal init(_ base: Base, count: Int) {
62-
self.base = base
63-
self.countToChoose = count
64-
65-
// Produce exactly one empty permutation when `count == 0`.
66-
self.indexes = count == 0 ? [] : Array(base.indices)
67-
68-
// Can't produce any permutations when `count > base.count`.
69-
self.hasMorePermutations = count <= indexes.count
96+
internal init(_ permutations: Permutations) {
97+
self.base = permutations.base
98+
self.baseCount = permutations.baseCount
99+
self.kRange = permutations.kRange
100+
self.indexes = Array(permutations.base.indices)
70101
}
71102

72103
/// Advances the `indexes` array such that the first `countToChoose`
@@ -82,72 +113,92 @@ extension Permutations: Sequence {
82113
/// - Complexity: O(*n*), where *n* is the length of the collection.
83114
@usableFromInline
84115
internal mutating func nextState() -> Bool {
116+
let countToChoose = self.kRange.lowerBound
85117
let edge = countToChoose - 1
86-
118+
87119
// Find first index greater than the one at `edge`.
88120
if let i = indexes[countToChoose...].firstIndex(where: { indexes[edge] < $0 }) {
89121
indexes.swapAt(edge, i)
90122
} else {
91-
indexes.reverse(subrange: countToChoose..<indexes.endIndex)
92-
123+
indexes.reverse(subrange: countToChoose ..< indexes.endIndex)
124+
93125
// Find last increasing pair below `edge`.
94126
// TODO: This could be indexes[..<edge].adjacentPairs().lastIndex(where: ...)
95127
var lastAscent = edge - 1
96128
while (lastAscent >= 0 && indexes[lastAscent] >= indexes[lastAscent + 1]) {
97129
lastAscent -= 1
98130
}
99-
if (lastAscent < 0) {
131+
if lastAscent < 0 {
100132
return false
101133
}
102-
134+
103135
// Find rightmost index less than that at `lastAscent`.
104136
if let i = indexes[lastAscent...].lastIndex(where: { indexes[lastAscent] < $0 }) {
105137
indexes.swapAt(lastAscent, i)
106138
}
107-
indexes.reverse(subrange: (lastAscent + 1)..<indexes.endIndex)
139+
indexes.reverse(subrange: (lastAscent + 1) ..< indexes.endIndex)
108140
}
109141

110142
return true
111143
}
112144

113145
@inlinable
114146
public mutating func next() -> [Base.Element]? {
115-
if !hasMorePermutations { return nil }
147+
guard !isFinished else { return nil }
148+
149+
/// Advances `kRange` by incrementing its `lowerBound` until the range is
150+
/// empty, when iteration is finished.
151+
func advanceKRange() {
152+
kRange.removeFirst()
153+
indexes = Array(base.indices)
154+
}
155+
156+
let countToChoose = self.kRange.lowerBound
157+
if countToChoose == 0 {
158+
defer {
159+
advanceKRange()
160+
}
161+
return []
162+
}
116163

164+
let permutesFullCollection = (countToChoose == baseCount)
117165
if permutesFullCollection {
118166
// If we're permuting the full collection, each iteration is just a
119167
// call to `nextPermutation` on `indexes`.
120-
defer { hasMorePermutations = indexes.nextPermutation() }
168+
defer {
169+
let hasMorePermutations = indexes.nextPermutation()
170+
if !hasMorePermutations {
171+
advanceKRange()
172+
}
173+
}
121174
return indexes.map { base[$0] }
122175
} else {
123176
// Otherwise, return the items at the first `countToChoose` indices and
124177
// advance the state.
125-
defer { hasMorePermutations = nextState() }
178+
defer {
179+
let hasMorePermutations = nextState()
180+
if !hasMorePermutations {
181+
advanceKRange()
182+
}
183+
}
126184
return indexes.prefix(countToChoose).map { base[$0] }
127185
}
128186
}
129187
}
130188

131-
@usableFromInline
132-
internal var permutesFullCollection: Bool {
133-
baseCount == countToChoose
134-
}
135-
136189
public func makeIterator() -> Iterator {
137-
permutesFullCollection
138-
? Iterator(base)
139-
: Iterator(base, count: countToChoose)
190+
Iterator(self)
140191
}
141192
}
142193

143194
extension Permutations: LazySequenceProtocol where Base: LazySequenceProtocol {}
144195

145196
//===----------------------------------------------------------------------===//
146-
// nextPermutation(by:)
197+
// nextPermutation()
147198
//===----------------------------------------------------------------------===//
148199

149200
extension MutableCollection
150-
where Self: BidirectionalCollection, Element: Comparable
201+
where Self: BidirectionalCollection, Element: Comparable
151202
{
152203
/// Permutes this collection's elements through all the lexical orderings.
153204
///
@@ -163,8 +214,8 @@ extension MutableCollection
163214
/// - Complexity: O(*n*), where *n* is the length of the collection.
164215
@usableFromInline
165216
internal mutating func nextPermutation() -> Bool {
166-
// ensure we have > 1 element in the collection
167-
if isEmpty { return false }
217+
// Ensure we have > 1 element in the collection.
218+
guard !isEmpty else { return false }
168219
var i = index(before: endIndex)
169220
if i == startIndex { return false }
170221

@@ -178,7 +229,7 @@ extension MutableCollection
178229
formIndex(before: &j)
179230
}
180231
swapAt(i, j)
181-
self.reverse(subrange: ip1..<endIndex)
232+
self.reverse(subrange: ip1 ..< endIndex)
182233
return true
183234
}
184235

@@ -195,6 +246,62 @@ extension MutableCollection
195246
//===----------------------------------------------------------------------===//
196247

197248
extension Collection {
249+
/// Returns a collection of the permutations of this collection with lengths
250+
/// in the specified range.
251+
///
252+
/// This example prints the different permutations of one to two elements from
253+
/// an array of three names:
254+
///
255+
/// let names = ["Alex", "Celeste", "Davide"]
256+
/// for perm in names.permutations(ofCount: 1...2) {
257+
/// print(perm.joined(separator: ", "))
258+
/// }
259+
/// // Alex
260+
/// // Celeste
261+
/// // Davide
262+
/// // Alex, Celeste
263+
/// // Alex, Davide
264+
/// // Celeste, Alex
265+
/// // Celeste, Davide
266+
/// // Davide, Alex
267+
/// // Davide, Celeste
268+
///
269+
/// This example prints _all_ the permutations (including an empty array) from
270+
/// the an array of numbers:
271+
///
272+
/// let numbers = [10, 20, 30]
273+
/// for perm in numbers.permutations(ofCount: 0...) {
274+
/// print(perm)
275+
/// }
276+
/// // []
277+
/// // [10]
278+
/// // [20]
279+
/// // [30]
280+
/// // [10, 20]
281+
/// // [10, 30]
282+
/// // [20, 10]
283+
/// // [20, 30]
284+
/// // [30, 10]
285+
/// // [30, 20]
286+
/// // [10, 20, 30]
287+
/// // [10, 30, 20]
288+
/// // [20, 10, 30]
289+
/// // [20, 30, 10]
290+
/// // [30, 10, 20]
291+
/// // [30, 20, 10]
292+
///
293+
/// - Parameter kRange: The number of elements to include in each permutation.
294+
///
295+
/// - Complexity: O(1) for random-access base collections. O(*n*) where *n*
296+
/// is the number of elements in the base collection, since `Permutations`
297+
/// accesses the `count` of the base collection.
298+
@inlinable
299+
public func permutations<R: RangeExpression>(
300+
ofCount kRange: R
301+
) -> Permutations<Self> where R.Bound == Int {
302+
return Permutations(self, kRange: kRange)
303+
}
304+
198305
/// Returns a collection of the permutations of this collection of the
199306
/// specified length.
200307
///
@@ -237,10 +344,13 @@ extension Collection {
237344
/// sequence, the resulting sequence has no elements.
238345
///
239346
/// - Parameter k: The number of elements to include in each permutation.
240-
/// If `count` is `nil`, the resulting sequence represents permutations
241-
/// of this entire collection.
347+
/// If `k` is `nil`, the resulting sequence represents permutations of this
348+
/// entire collection.
242349
///
243-
/// - Complexity: O(1)
350+
/// - Complexity: O(1) for random-access base collections. O(*n*) where *n*
351+
/// is the number of elements in the base collection, since `Permutations`
352+
/// accesses the `count` of the base collection.
353+
@inlinable
244354
public func permutations(ofCount k: Int? = nil) -> Permutations<Self> {
245355
precondition(
246356
k ?? 0 >= 0,

0 commit comments

Comments
 (0)