Skip to content

Commit 62fb2f9

Browse files
committed
Implement uniquePermutations(ofCount:) variants
1 parent a6fab1e commit 62fb2f9

File tree

1 file changed

+152
-58
lines changed

1 file changed

+152
-58
lines changed

Sources/Algorithms/Permutations.swift

Lines changed: 152 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,100 @@
99
//
1010
//===----------------------------------------------------------------------===//
1111

12+
//===----------------------------------------------------------------------===//
13+
// nextPermutation()
14+
//===----------------------------------------------------------------------===//
15+
16+
extension MutableCollection
17+
where Self: BidirectionalCollection, Element: Comparable
18+
{
19+
/// Permutes this collection's elements through all the lexical orderings.
20+
///
21+
/// Call `nextPermutation()` repeatedly starting with the collection in
22+
/// sorted order. When the full cycle of all permutations has been completed,
23+
/// the collection will be back in sorted order and this method will return
24+
/// `false`.
25+
///
26+
/// - Returns: A Boolean value indicating whether the collection still has
27+
/// remaining permutations. When this method returns `false`, the collection
28+
/// is in ascending order according to `areInIncreasingOrder`.
29+
///
30+
/// - Complexity: O(*n*), where *n* is the length of the collection.
31+
@inlinable
32+
internal mutating func nextPermutation() -> Bool {
33+
// Ensure we have > 1 element in the collection.
34+
guard !isEmpty else { return false }
35+
var i = index(before: endIndex)
36+
if i == startIndex { return false }
37+
38+
while true {
39+
let ip1 = i
40+
formIndex(before: &i)
41+
42+
if self[i] < self[ip1] {
43+
var j = index(before: endIndex)
44+
while self[i] >= self[j] {
45+
formIndex(before: &j)
46+
}
47+
swapAt(i, j)
48+
self.reverse(subrange: ip1 ..< endIndex)
49+
50+
// check here if i < the prefix, if so, return true, if not, continue
51+
return true
52+
}
53+
54+
if i == startIndex {
55+
self.reverse()
56+
return false
57+
}
58+
}
59+
}
60+
61+
@inlinable
62+
internal mutating func nextPermutation(upperBound: Index) -> Bool {
63+
// Ensure we have > 1 element in the collection.
64+
guard !isEmpty else { return false }
65+
var i = index(before: endIndex)
66+
if i == startIndex { return false }
67+
68+
while true {
69+
let ip1 = i
70+
formIndex(before: &i)
71+
72+
// Find the last ascending pair (ie. ..., a, b, ... where a < b)
73+
if self[i] < self[ip1] {
74+
// Find the last element greater than self[i]
75+
// This is _always_ at most `ip1` due to if statement above
76+
let j = lastIndex(where: { $0 > self[i] })!
77+
78+
// At this point we have something like this:
79+
// 0, 1, 4, 3, 2
80+
// ^ ^
81+
// i j
82+
swapAt(i, j)
83+
self.reverse(subrange: ip1 ..< endIndex)
84+
85+
// Only return if we've made a change within ..<upperBound region
86+
if i < upperBound {
87+
return true
88+
} else {
89+
i = index(before: endIndex)
90+
continue
91+
}
92+
}
93+
94+
if i == startIndex {
95+
self.reverse()
96+
return false
97+
}
98+
}
99+
}
100+
}
101+
102+
//===----------------------------------------------------------------------===//
103+
// struct Permutations<Base>
104+
//===----------------------------------------------------------------------===//
105+
12106
/// A sequence of all the permutations of a collection's elements.
13107
public struct Permutations<Base: Collection> {
14108
/// The base collection to iterate over for permutations.
@@ -194,54 +288,6 @@ extension Permutations: Sequence {
194288

195289
extension Permutations: LazySequenceProtocol where Base: LazySequenceProtocol {}
196290

197-
//===----------------------------------------------------------------------===//
198-
// nextPermutation()
199-
//===----------------------------------------------------------------------===//
200-
201-
extension MutableCollection
202-
where Self: BidirectionalCollection, Element: Comparable
203-
{
204-
/// Permutes this collection's elements through all the lexical orderings.
205-
///
206-
/// Call `nextPermutation()` repeatedly starting with the collection in
207-
/// sorted order. When the full cycle of all permutations has been completed,
208-
/// the collection will be back in sorted order and this method will return
209-
/// `false`.
210-
///
211-
/// - Returns: A Boolean value indicating whether the collection still has
212-
/// remaining permutations. When this method returns `false`, the collection
213-
/// is in ascending order according to `areInIncreasingOrder`.
214-
///
215-
/// - Complexity: O(*n*), where *n* is the length of the collection.
216-
@usableFromInline
217-
internal mutating func nextPermutation() -> Bool {
218-
// Ensure we have > 1 element in the collection.
219-
guard !isEmpty else { return false }
220-
var i = index(before: endIndex)
221-
if i == startIndex { return false }
222-
223-
while true {
224-
let ip1 = i
225-
formIndex(before: &i)
226-
227-
if self[i] < self[ip1] {
228-
var j = index(before: endIndex)
229-
while self[i] >= self[j] {
230-
formIndex(before: &j)
231-
}
232-
swapAt(i, j)
233-
self.reverse(subrange: ip1 ..< endIndex)
234-
return true
235-
}
236-
237-
if i == startIndex {
238-
self.reverse()
239-
return false
240-
}
241-
}
242-
}
243-
}
244-
245291
//===----------------------------------------------------------------------===//
246292
// permutations(ofCount:)
247293
//===----------------------------------------------------------------------===//
@@ -366,11 +412,25 @@ extension Collection {
366412

367413
public struct UniquePermutations<Element: Comparable> {
368414
@usableFromInline
369-
let elements: [Element]
415+
internal let elements: [Element]
370416

417+
@usableFromInline
418+
internal let kRange: Range<Int>
419+
420+
@inlinable
421+
internal init<S: Sequence>(_ elements: S) where S.Element == Element {
422+
self.init(elements, 0..<Int.max)
423+
}
424+
371425
@inlinable
372-
init<S: Sequence>(_ elements: S) where S.Element == Element {
426+
internal init<S: Sequence, R: RangeExpression>(_ elements: S, _ range: R)
427+
where S.Element == Element, R.Bound == Int
428+
{
373429
self.elements = elements.sorted()
430+
431+
let upperBound = self.elements.count + 1
432+
self.kRange = range.relative(to: 0 ..< .max)
433+
.clamped(to: 0 ..< upperBound)
374434
}
375435
}
376436

@@ -380,27 +440,51 @@ extension UniquePermutations: Sequence {
380440
var elements: [Element]
381441

382442
@usableFromInline
383-
var finished = false
443+
enum State {
444+
case start, middle, end
445+
}
446+
447+
@usableFromInline
448+
var state = State.start
449+
450+
@usableFromInline
451+
var lengths: Range<Int>
384452

385453
@inlinable
386-
init(_ elements: [Element]) {
454+
init(_ elements: [Element], lengths: Range<Int>) {
387455
self.elements = elements
456+
self.lengths = lengths
388457
}
389458

390459
@inlinable
391-
public mutating func next() -> [Element]? {
392-
if finished {
460+
public mutating func next() -> ArraySlice<Element>? {
461+
switch state {
462+
case .start:
463+
state = .middle
464+
return elements[..<lengths.lowerBound]
465+
case .middle:
466+
if !elements.nextPermutation(upperBound: lengths.lowerBound) {
467+
lengths = (lengths.lowerBound + 1)..<lengths.upperBound
468+
469+
if lengths.isEmpty {
470+
state = .end
471+
return nil
472+
}
473+
474+
elements.sort()
475+
state = .start
476+
}
477+
return elements[..<lengths.lowerBound]
478+
479+
case .end:
393480
return nil
394-
} else {
395-
defer { finished = !elements.nextPermutation() }
396-
return elements
397481
}
398482
}
399483
}
400484

401485
@inlinable
402486
public func makeIterator() -> Iterator {
403-
Iterator(elements)
487+
Iterator(elements, lengths: kRange)
404488
}
405489
}
406490

@@ -437,4 +521,14 @@ extension Sequence where Element: Comparable {
437521
public func uniquePermutations() -> UniquePermutations<Element> {
438522
UniquePermutations(self)
439523
}
524+
525+
public func uniquePermutations(ofCount k: Int) -> UniquePermutations<Element> {
526+
UniquePermutations(self, k ..< (k + 1))
527+
}
528+
529+
public func uniquePermutations<R: RangeExpression>(ofCount kRange: R) -> UniquePermutations<Element>
530+
where R.Bound == Int
531+
{
532+
UniquePermutations(self, kRange)
533+
}
440534
}

0 commit comments

Comments
 (0)