Skip to content

Add uniquePermutations as wrapper for nextPermutation #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 35 additions & 13 deletions Guides/Permutations.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/Permutations.swift) |
[Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/PermutationsTests.swift)]

A type that computes permutations of a collection’s elements, or of a subset of
Methods that compute permutations of a collection’s elements, or of a subset of
those elements.

The `permutations(ofCount:)` method, when called without the `ofCount`
Expand Down Expand Up @@ -60,7 +60,18 @@ for perm in numbers2.permutations() {
// [10, 10, 20]
```

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.
To generate only unique permutations, use the `uniquePermutations(ofCount:)` method:

```swift
for perm in numbers2.uniquePermutations() {
print(perm)
}
// [20, 10, 10]
// [10, 20, 10]
// [10, 10, 20]
```

Given a range, the methods return a sequence of all the different permutations of the given sizes of a collection’s elements in increasing order of size.

```swift
let numbers = [10, 20, 30]
Expand All @@ -87,28 +98,40 @@ for perm in numbers.permutations(ofCount: 0...) {

## Detailed Design

The `permutations(ofCount:)` method is declared as a `Collection` extension,
and returns a `Permutations` type:
The `permutations(ofCount:)` and `uniquePermutations(ofCount:)` methods are declared as `Collection` extensions,
and return `Permutations` and `UniquePermutations` instances, respectively:

```swift
extension Collection {
public func permutations(ofCount k: Int? = nil) -> Permutations<Self>
public func permutations<R>(ofCount kRange: R) -> Permutations<Self>
where R: RangeExpression, R.Bound == Int
}

extension Collection where Element: Hashable {
public func uniquePermutations(ofCount k: Int? = nil) -> UniquePermutations<Self>
public func uniquePermutations<R>(ofCount kRange: R) -> UniquePermutations<Self>
where R: RangeExpression, R.Bound == Int
}
```

Since the `Permutations` type needs to store an array of the collection’s
indices and mutate the array to generate each permutation, `Permutations` only
has `Sequence` conformance. Adding `Collection` conformance would require
Since both result types need to store an array of the collection’s
indices and mutate the array to generate each permutation, they only
have `Sequence` conformance. Adding `Collection` conformance would require
storing the array in the index type, which would in turn lead to copying the
array at every index advancement. `Combinations` does conform to
`LazySequenceProtocol` when the base type conforms.
array at every index advancement. The `Permutations` type
conforms to `LazySequenceProtocol` when its base type conforms.

### Complexity

Calling `permutations()` is an O(1) operation. Creating the iterator for a
`Permutations` instance and each call to `Permutations.Iterator.next()` is an
O(_n_) operation.

Calling `uniquePermutations()` is an O(_n_) operation, because it preprocesses the
collection to find duplicate elements. Creating the iterator for and each call to
`next()` is also an O(_n_) operation.

### Naming

See the ["Naming" section for `combinations(ofCount:)`](Combinations.md#naming) for detail.
Expand All @@ -117,9 +140,8 @@ See the ["Naming" section for `combinations(ofCount:)`](Combinations.md#naming)

**C++:** The `<algorithm>` library defines a `next_permutation` function that
advances an array of comparable values through their lexicographic orderings.
This function is tricky to use and understand, so while it’s included in
`swift-algorithms` as an implementation detail of the `Permutations` type, it
isn’t public.
This function is very similar to the `uniquePermutations(ofCount:)` method.

**Rust/Ruby/Python:** Rust, Ruby, and Python all define functions with
essentially the same semantics as the method described here.
essentially the same semantics as the `permutations(ofCount:)` method
described here.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Read more about the package, and the intent behind it, in the [announcement on s

- [`combinations(ofCount:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Combinations.md): Combinations of particular sizes of the elements in a collection.
- [`permutations(ofCount:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Permutations.md): Permutations of a particular size of the elements in a collection, or of the full collection.
- [`uniquePermutations(ofCount:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Permutations.md): Permutations of a collection's elements, skipping any duplicate permutations.

#### Mutating algorithms

Expand Down
Loading