2
2
//
3
3
// This source file is part of the Swift Algorithms open source project
4
4
//
5
- // Copyright (c) 2020 Apple Inc. and the Swift project authors
5
+ // Copyright (c) 2020-2021 Apple Inc. and the Swift project authors
6
6
// Licensed under Apache License v2.0 with Runtime Library Exception
7
7
//
8
8
// See https://swift.org/LICENSE.txt for license information
@@ -15,16 +15,51 @@ public struct Combinations<Base: Collection> {
15
15
public let base : Base
16
16
17
17
@usableFromInline
18
- internal var k : Int
18
+ internal let baseCount : Int
19
19
20
+ /// The range of accepted sizes of combinations.
21
+ /// - Note: This may be `nil` if the attempted range entirely exceeds the
22
+ /// upper bounds of the size of the `base` collection.
23
+ @usableFromInline
24
+ internal let kRange : Range < Int > ?
25
+
26
+ /// Initializes a `Combinations` for all combinations of `base` of size `k`.
27
+ /// - Parameters:
28
+ /// - base: The collection to iterate over for combinations.
29
+ /// - k: The expected size of each combination.
20
30
@usableFromInline
21
31
internal init ( _ base: Base , k: Int ) {
32
+ self . init ( base, kRange: k... k)
33
+ }
34
+
35
+ /// Initializes a `Combinations` for all combinations of `base` of sizes
36
+ /// within a given range.
37
+ /// - Parameters:
38
+ /// - base: The collection to iterate over for combinations.
39
+ /// - kRange: The range of accepted sizes of combinations.
40
+ @usableFromInline
41
+ internal init < R: RangeExpression > (
42
+ _ base: Base , kRange: R
43
+ ) where R. Bound == Int {
44
+ let range = kRange. relative ( to: 0 ..< . max)
22
45
self . base = base
23
- self . k = base. count < k ? - 1 : k
46
+ let baseCount = base. count
47
+ self . baseCount = baseCount
48
+ let upperBound = baseCount + 1
49
+ self . kRange = range. lowerBound < upperBound
50
+ ? range. clamped ( to: 0 ..< upperBound)
51
+ : nil
24
52
}
25
-
53
+
54
+ /// The total number of combinations.
26
55
@inlinable
27
56
public var count : Int {
57
+ guard let k = self . kRange else { return 0 }
58
+ let n = baseCount
59
+ if k == 0 ..< ( n + 1 ) {
60
+ return 1 << n
61
+ }
62
+
28
63
func binomial( n: Int , k: Int ) -> Int {
29
64
switch k {
30
65
case n, 0 : return 1
@@ -34,9 +69,9 @@ public struct Combinations<Base: Collection> {
34
69
}
35
70
}
36
71
37
- return k >= 0
38
- ? binomial ( n: base . count , k: k )
39
- : 0
72
+ return k. map {
73
+ binomial ( n: n , k: $0 )
74
+ } . reduce ( 0 , + )
40
75
}
41
76
}
42
77
@@ -46,18 +81,26 @@ extension Combinations: Sequence {
46
81
@usableFromInline
47
82
internal let base : Base
48
83
84
+ /// The current range of accepted sizes of combinations.
85
+ /// - Note: The range is contracted until empty while iterating over
86
+ /// combinations of different sizes. When the range is empty, iteration is
87
+ /// finished.
49
88
@usableFromInline
50
- internal var indexes : [ Base . Index ]
89
+ internal var kRange : Range < Int >
51
90
91
+ /// Whether or not iteration is finished (`kRange` is empty)
52
92
@usableFromInline
53
- internal var finished : Bool
93
+ internal var isFinished : Bool {
94
+ return kRange. isEmpty
95
+ }
96
+
97
+ @usableFromInline
98
+ internal var indexes : [ Base . Index ]
54
99
55
100
internal init ( _ combinations: Combinations ) {
56
101
self . base = combinations. base
57
- self . finished = combinations. k < 0
58
- self . indexes = combinations. k < 0
59
- ? [ ]
60
- : Array ( combinations. base. indices. prefix ( combinations. k) )
102
+ self . kRange = combinations. kRange ?? 0 ..< 0
103
+ self . indexes = Array ( combinations. base. indices. prefix ( kRange. lowerBound) )
61
104
}
62
105
63
106
/// Advances the current indices to the next set of combinations. If
@@ -80,24 +123,35 @@ extension Combinations: Sequence {
80
123
/// // so the iteration is finished.
81
124
@usableFromInline
82
125
internal mutating func advance( ) {
126
+ /// Advances `kRange` by incrementing its `lowerBound` until the range is
127
+ /// empty, when iteration is finished.
128
+ func advanceKRange( ) {
129
+ if kRange. lowerBound < kRange. upperBound {
130
+ let advancedLowerBound = kRange. lowerBound + 1
131
+ kRange = advancedLowerBound ..< kRange. upperBound
132
+ indexes. removeAll ( keepingCapacity: true )
133
+ indexes. append ( contentsOf: base. indices. prefix ( kRange. lowerBound) )
134
+ }
135
+ }
136
+
83
137
guard !indexes. isEmpty else {
84
138
// Initial state for combinations of 0 elements is an empty array with
85
139
// `finished == false`. Even though no indexes are involved, advancing
86
140
// from that state means we are finished with iterating.
87
- finished = true
141
+ advanceKRange ( )
88
142
return
89
143
}
90
-
144
+
91
145
let i = indexes. count - 1
92
146
base. formIndex ( after: & indexes[ i] )
93
147
if indexes [ i] != base. endIndex { return }
94
-
148
+
95
149
var j = i
96
150
while indexes [ i] == base. endIndex {
97
151
j -= 1
98
152
guard j >= 0 else {
99
- // Finished iterating over combinations
100
- finished = true
153
+ // Finished iterating over combinations of this size.
154
+ advanceKRange ( )
101
155
return
102
156
}
103
157
@@ -113,7 +167,7 @@ extension Combinations: Sequence {
113
167
114
168
@inlinable
115
169
public mutating func next( ) -> [ Base . Element ] ? {
116
- if finished { return nil }
170
+ guard !isFinished else { return nil }
117
171
defer { advance ( ) }
118
172
return indexes. map { i in base [ i] }
119
173
}
@@ -129,10 +183,78 @@ extension Combinations: Equatable where Base: Equatable {}
129
183
extension Combinations : Hashable where Base: Hashable { }
130
184
131
185
//===----------------------------------------------------------------------===//
132
- // combinations(count :)
186
+ // combinations(ofCount :)
133
187
//===----------------------------------------------------------------------===//
134
188
135
189
extension Collection {
190
+ /// Returns a collection of combinations of this collection's elements, with
191
+ /// each combination having the specified number of elements.
192
+ ///
193
+ /// This example prints the different combinations of 1 and 2 from an array of
194
+ /// four colors:
195
+ ///
196
+ /// let colors = ["fuchsia", "cyan", "mauve", "magenta"]
197
+ /// for combo in colors.combinations(ofCount: 1...2) {
198
+ /// print(combo.joined(separator: ", "))
199
+ /// }
200
+ /// // fuchsia
201
+ /// // cyan
202
+ /// // mauve
203
+ /// // magenta
204
+ /// // fuchsia, cyan
205
+ /// // fuchsia, mauve
206
+ /// // fuchsia, magenta
207
+ /// // cyan, mauve
208
+ /// // cyan, magenta
209
+ /// // mauve, magenta
210
+ ///
211
+ /// The returned collection presents combinations in a consistent order, where
212
+ /// the indices in each combination are in ascending lexicographical order.
213
+ /// That is, in the example above, the combinations in order are the elements
214
+ /// at `[0]`, `[1]`, `[2]`, `[3]`, `[0, 1]`, `[0, 2]`, `[0, 3]`, `[1, 2]`,
215
+ /// `[1, 3]`, and finally `[2, 3]`.
216
+ ///
217
+ /// This example prints _all_ the combinations (including an empty array and
218
+ /// the original collection) from an array of numbers:
219
+ ///
220
+ /// let numbers = [10, 20, 30, 40]
221
+ /// for combo in numbers.combinations(ofCount: 0...) {
222
+ /// print(combo)
223
+ /// }
224
+ /// // []
225
+ /// // [10]
226
+ /// // [20]
227
+ /// // [30]
228
+ /// // [40]
229
+ /// // [10, 20]
230
+ /// // [10, 30]
231
+ /// // [10, 40]
232
+ /// // [20, 30]
233
+ /// // [20, 40]
234
+ /// // [30, 40]
235
+ /// // [10, 20, 30]
236
+ /// // [10, 20, 40]
237
+ /// // [10, 30, 40]
238
+ /// // [20, 30, 40]
239
+ /// // [10, 20, 30, 40]
240
+ ///
241
+ /// If `kRange` is `0...0`, the resulting sequence has exactly one element, an
242
+ /// empty array. The given range is limited to `0...base.count`. If the
243
+ /// limited range is empty, the resulting sequence has no elements.
244
+ ///
245
+ /// - Parameter kRange: The range of numbers of elements to include in each
246
+ /// combination.
247
+ ///
248
+ /// - Complexity: O(1) for random-access base collections. O(*n*) where *n*
249
+ /// is the number of elements in the base collection, since `Combinations`
250
+ /// accesses the `count` of the base collection.
251
+ @inlinable
252
+ public func combinations< R: RangeExpression > (
253
+ ofCount kRange: R
254
+ ) -> Combinations < Self > where R. Bound == Int {
255
+ return Combinations ( self , kRange: kRange)
256
+ }
257
+
136
258
/// Returns a collection of combinations of this collection's elements, with
137
259
/// each combination having the specified number of elements.
138
260
///
@@ -159,7 +281,9 @@ extension Collection {
159
281
///
160
282
/// - Parameter k: The number of elements to include in each combination.
161
283
///
162
- /// - Complexity: O(1)
284
+ /// - Complexity: O(1) for random-access base collections. O(*n*) where *n*
285
+ /// is the number of elements in the base collection, since `Combinations`
286
+ /// accesses the `count` of the base collection.
163
287
@inlinable
164
288
public func combinations( ofCount k: Int ) -> Combinations < Self > {
165
289
assert ( k >= 0 , " Can't have combinations with a negative number of elements. " )
0 commit comments