Skip to content

Commit 6c18723

Browse files
authored
Add permutation algorithms (TheAlgorithms#487)
1 parent 4c854dd commit 6c18723

File tree

6 files changed

+360
-0
lines changed

6 files changed

+360
-0
lines changed

DIRECTORY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@
7575
* [Mex](https://github.com/TheAlgorithms/Rust/blob/master/src/general/mex.rs)
7676
* [Nqueens](https://github.com/TheAlgorithms/Rust/blob/master/src/general/nqueens.rs)
7777
* [Two Sum](https://github.com/TheAlgorithms/Rust/blob/master/src/general/two_sum.rs)
78+
* Permutations
79+
* [Naive Implementation](https://github.com/TheAlgorithms/Rust/blob/master/src/general/permutations/naive.rs)
80+
* [Heap's algorithm](https://github.com/TheAlgorithms/Rust/blob/master/src/general/permutations/heap.rs)
81+
* [Steinhaus-Johnson-Trotter algorithm](https://github.com/TheAlgorithms/Rust/blob/master/src/general/permutations/steinhaus-johnson-trotter.rs)
7882
* Geometry
7983
* [Closest Points](https://github.com/TheAlgorithms/Rust/blob/master/src/geometry/closest_points.rs)
8084
* Graph

src/general/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ mod huffman_encoding;
55
mod kmeans;
66
mod mex;
77
mod nqueens;
8+
mod permutations;
89
mod two_sum;
10+
911
pub use self::convex_hull::convex_hull_graham;
1012
pub use self::fisher_yates_shuffle::fisher_yates_shuffle;
1113
pub use self::hanoi::hanoi;
@@ -15,4 +17,7 @@ pub use self::kmeans::f64::kmeans as kmeans_f64;
1517
pub use self::mex::mex_using_set;
1618
pub use self::mex::mex_using_sort;
1719
pub use self::nqueens::nqueens;
20+
pub use self::permutations::{
21+
heap_permute, permute, permute_unique, steinhaus_johnson_trotter_permute,
22+
};
1823
pub use self::two_sum::two_sum;

src/general/permutations/heap.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use std::fmt::Debug;
2+
3+
/// Computes all permutations of an array using Heap's algorithm
4+
/// Read `recurse_naive` first, since we're building on top of the same intuition
5+
pub fn heap_permute<T: Clone + Debug>(arr: &[T]) -> Vec<Vec<T>> {
6+
if arr.is_empty() {
7+
return vec![vec![]];
8+
}
9+
let n = arr.len();
10+
let mut collector = Vec::with_capacity((1..=n).product()); // collects the permuted arrays
11+
let mut arr = arr.to_owned(); // Heap's algorithm needs to mutate the array
12+
heap_recurse(&mut arr, n, &mut collector);
13+
collector
14+
}
15+
16+
fn heap_recurse<T: Clone + Debug>(arr: &mut [T], k: usize, collector: &mut Vec<Vec<T>>) {
17+
if k == 1 {
18+
// same base-case as in the naive version
19+
collector.push((*arr).to_owned());
20+
return;
21+
}
22+
// Remember the naive recursion. We did the following: swap(i, last), recurse, swap back(i, last)
23+
// Heap's algorithm has a more clever way of permuting the elements so that we never need to swap back!
24+
for i in 0..k {
25+
// now deal with [a, b]
26+
let swap_idx = if k % 2 == 0 { i } else { 0 };
27+
arr.swap(swap_idx, k - 1);
28+
heap_recurse(arr, k - 1, collector);
29+
}
30+
}
31+
32+
#[cfg(test)]
33+
mod tests {
34+
use quickcheck_macros::quickcheck;
35+
36+
use crate::general::permutations::heap_permute;
37+
use crate::general::permutations::tests::{
38+
assert_permutations, assert_valid_permutation, NotTooBigVec,
39+
};
40+
41+
#[test]
42+
fn test_3_different_values() {
43+
let original = vec![1, 2, 3];
44+
let res = heap_permute(&original);
45+
assert_eq!(res.len(), 6); // 3!
46+
for permut in res {
47+
assert_valid_permutation(&original, &permut)
48+
}
49+
}
50+
51+
#[test]
52+
fn test_3_times_the_same_value() {
53+
let original = vec![1, 1, 1];
54+
let res = heap_permute(&original);
55+
assert_eq!(res.len(), 6); // 3!
56+
for permut in res {
57+
assert_valid_permutation(&original, &permut)
58+
}
59+
}
60+
61+
#[quickcheck]
62+
fn test_some_elements(NotTooBigVec { inner: original }: NotTooBigVec) {
63+
let permutations = heap_permute(&original);
64+
assert_permutations(&original, &permutations)
65+
}
66+
}

src/general/permutations/mod.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
mod heap;
2+
mod naive;
3+
mod steinhaus_johnson_trotter;
4+
5+
pub use self::heap::heap_permute;
6+
pub use self::naive::{permute, permute_unique};
7+
pub use self::steinhaus_johnson_trotter::steinhaus_johnson_trotter_permute;
8+
9+
#[cfg(test)]
10+
mod tests {
11+
use quickcheck::{Arbitrary, Gen};
12+
use std::collections::HashMap;
13+
14+
pub(crate) fn assert_permutations(original: &[i32], permutations: &[Vec<i32>]) {
15+
if original.is_empty() {
16+
assert_eq!(vec![vec![] as Vec<i32>], permutations);
17+
return;
18+
}
19+
let n = original.len();
20+
assert_eq!((1..=n).product::<usize>(), permutations.len()); // n!
21+
for permut in permutations {
22+
assert_valid_permutation(original, permut);
23+
}
24+
}
25+
26+
pub(crate) fn assert_valid_permutation(original: &[i32], permuted: &[i32]) {
27+
assert_eq!(original.len(), permuted.len());
28+
let mut indices = HashMap::with_capacity(original.len());
29+
for value in original {
30+
*indices.entry(*value).or_insert(0) += 1;
31+
}
32+
for permut_value in permuted {
33+
let count = indices.get_mut(permut_value).unwrap_or_else(|| {
34+
panic!(
35+
"Value {} appears too many times in permutation",
36+
permut_value,
37+
)
38+
});
39+
*count -= 1; // use this value
40+
if *count == 0 {
41+
indices.remove(permut_value); // so that we can simply check every value has been removed properly
42+
}
43+
}
44+
assert!(indices.is_empty())
45+
}
46+
47+
#[test]
48+
fn test_valid_permutations() {
49+
assert_valid_permutation(&[1, 2, 3], &[1, 2, 3]);
50+
assert_valid_permutation(&[1, 2, 3], &[1, 3, 2]);
51+
assert_valid_permutation(&[1, 2, 3], &[2, 1, 3]);
52+
assert_valid_permutation(&[1, 2, 3], &[2, 3, 1]);
53+
assert_valid_permutation(&[1, 2, 3], &[3, 1, 2]);
54+
assert_valid_permutation(&[1, 2, 3], &[3, 2, 1]);
55+
}
56+
57+
#[test]
58+
#[should_panic]
59+
fn test_invalid_permutation_1() {
60+
assert_valid_permutation(&[1, 2, 3], &[4, 2, 3]);
61+
}
62+
63+
#[test]
64+
#[should_panic]
65+
fn test_invalid_permutation_2() {
66+
assert_valid_permutation(&[1, 2, 3], &[1, 4, 3]);
67+
}
68+
69+
#[test]
70+
#[should_panic]
71+
fn test_invalid_permutation_3() {
72+
assert_valid_permutation(&[1, 2, 3], &[1, 2, 4]);
73+
}
74+
75+
#[test]
76+
#[should_panic]
77+
fn test_invalid_permutation_repeat() {
78+
assert_valid_permutation(&[1, 2, 3], &[1, 2, 2]);
79+
}
80+
81+
/// A Data Structure for testing permutations
82+
/// Holds a Vec<i32> with just a few items, so that it's not too long to compute permutations
83+
#[derive(Debug, Clone)]
84+
pub(crate) struct NotTooBigVec {
85+
pub(crate) inner: Vec<i32>, // opaque type alias so that we can implement Arbitrary
86+
}
87+
88+
const MAX_SIZE: usize = 8; // 8! ~= 40k permutations already
89+
impl Arbitrary for NotTooBigVec {
90+
fn arbitrary(g: &mut Gen) -> Self {
91+
let size = usize::arbitrary(g) % MAX_SIZE;
92+
let res = (0..size).map(|_| i32::arbitrary(g)).collect();
93+
NotTooBigVec { inner: res }
94+
}
95+
}
96+
}

src/general/permutations/naive.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
use std::collections::HashSet;
2+
use std::fmt::Debug;
3+
use std::hash::Hash;
4+
5+
/// Here's a basic (naive) implementation for generating permutations
6+
pub fn permute<T: Clone + Debug>(arr: &[T]) -> Vec<Vec<T>> {
7+
if arr.is_empty() {
8+
return vec![vec![]];
9+
}
10+
let n = arr.len();
11+
let count = (1..=n).product(); // n! permutations
12+
let mut collector = Vec::with_capacity(count); // collects the permuted arrays
13+
let mut arr = arr.to_owned(); // we'll need to mutate the array
14+
15+
// the idea is the following: imagine [a, b, c]
16+
// always swap an item with the last item, then generate all permutations from the first k characters
17+
// permute_recurse(arr, k - 1, collector); // leave the last character alone, and permute the first k-1 characters
18+
permute_recurse(&mut arr, n, &mut collector);
19+
collector
20+
}
21+
22+
fn permute_recurse<T: Clone + Debug>(arr: &mut Vec<T>, k: usize, collector: &mut Vec<Vec<T>>) {
23+
if k == 1 {
24+
collector.push(arr.to_owned());
25+
return;
26+
}
27+
for i in 0..k {
28+
arr.swap(i, k - 1); // swap i with the last character
29+
permute_recurse(arr, k - 1, collector); // collect the permutations of the rest
30+
arr.swap(i, k - 1); // swap back to original
31+
}
32+
}
33+
34+
/// A common variation of generating permutations is to generate only unique permutations
35+
/// Of course, we could use the version above together with a Set as collector instead of a Vec.
36+
/// But let's try something different: how can we avoid to generate duplicated permutations in the first place, can we tweak the algorithm above?
37+
pub fn permute_unique<T: Clone + Debug + Eq + Hash + Copy>(arr: &[T]) -> Vec<Vec<T>> {
38+
if arr.is_empty() {
39+
return vec![vec![]];
40+
}
41+
let n = arr.len();
42+
let count = (1..=n).product(); // n! permutations
43+
let mut collector = Vec::with_capacity(count); // collects the permuted arrays
44+
let mut arr = arr.to_owned(); // Heap's algorithm needs to mutate the array
45+
permute_recurse_unique(&mut arr, n, &mut collector);
46+
collector
47+
}
48+
49+
fn permute_recurse_unique<T: Clone + Debug + Eq + Hash + Copy>(
50+
arr: &mut Vec<T>,
51+
k: usize,
52+
collector: &mut Vec<Vec<T>>,
53+
) {
54+
// We have the same base-case as previously, whenever we reach the first element in the array, collect the result
55+
if k == 1 {
56+
collector.push(arr.to_owned());
57+
return;
58+
}
59+
// We'll keep the same idea (swap with last item, and generate all permutations for the first k - 1)
60+
// But we'll have to be careful though: how would we generate duplicates?
61+
// Basically if, when swapping i with k-1, we generate the exact same array as in a previous iteration
62+
// Imagine [a, a, b]
63+
// i = 0:
64+
// Swap (a, b) => [b, a, a], fix 'a' as last, and generate all permutations of [b, a] => [b, a, a], [a, b, a]
65+
// Swap Back to [a, a, b]
66+
// i = 1:
67+
// Swap(a, b) => [b, a, a], we've done that already!!
68+
let mut swapped = HashSet::with_capacity(k);
69+
for i in 0..k {
70+
if swapped.contains(&arr[i]) {
71+
continue;
72+
}
73+
swapped.insert(arr[i]);
74+
arr.swap(i, k - 1); // swap i with the last character
75+
permute_recurse_unique(arr, k - 1, collector); // collect the permutations
76+
arr.swap(i, k - 1); // go back to original
77+
}
78+
}
79+
80+
#[cfg(test)]
81+
mod tests {
82+
use crate::general::permutations::naive::{permute, permute_unique};
83+
use crate::general::permutations::tests::{
84+
assert_permutations, assert_valid_permutation, NotTooBigVec,
85+
};
86+
use quickcheck_macros::quickcheck;
87+
use std::collections::HashSet;
88+
89+
#[test]
90+
fn test_3_different_values() {
91+
let original = vec![1, 2, 3];
92+
let res = permute(&original);
93+
assert_eq!(res.len(), 6); // 3!
94+
for permut in res {
95+
assert_valid_permutation(&original, &permut)
96+
}
97+
}
98+
99+
#[test]
100+
fn test_3_times_the_same_value() {
101+
let original = vec![1, 1, 1];
102+
let res = permute(&original);
103+
assert_eq!(res.len(), 6); // 3!
104+
for permut in res {
105+
assert_valid_permutation(&original, &permut)
106+
}
107+
}
108+
109+
#[quickcheck]
110+
fn test_some_elements(NotTooBigVec { inner: original }: NotTooBigVec) {
111+
let permutations = permute(&original);
112+
assert_permutations(&original, &permutations)
113+
}
114+
115+
#[test]
116+
fn test_unique_values() {
117+
let original = vec![1, 1, 2, 2];
118+
let unique_permutations = permute_unique(&original);
119+
let every_permutation = permute(&original);
120+
for unique_permutation in &unique_permutations {
121+
assert!(every_permutation.contains(unique_permutation));
122+
}
123+
assert_eq!(
124+
unique_permutations.len(),
125+
every_permutation.iter().collect::<HashSet<_>>().len()
126+
)
127+
}
128+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/// https://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm
2+
pub fn steinhaus_johnson_trotter_permute<T: Clone>(array: &[T]) -> Vec<Vec<T>> {
3+
let len = array.len();
4+
let mut array = array.to_owned();
5+
let mut inversion_vector = vec![0; len];
6+
let mut i = 1;
7+
let mut res = Vec::with_capacity((1..=len).product());
8+
res.push(array.clone());
9+
while i < len {
10+
if inversion_vector[i] < i {
11+
if i % 2 == 0 {
12+
array.swap(0, i);
13+
} else {
14+
array.swap(inversion_vector[i], i);
15+
}
16+
res.push(array.to_vec());
17+
inversion_vector[i] += 1;
18+
i = 1;
19+
} else {
20+
inversion_vector[i] = 0;
21+
i += 1;
22+
}
23+
}
24+
res
25+
}
26+
27+
#[cfg(test)]
28+
mod tests {
29+
use quickcheck_macros::quickcheck;
30+
31+
use crate::general::permutations::steinhaus_johnson_trotter::steinhaus_johnson_trotter_permute;
32+
use crate::general::permutations::tests::{
33+
assert_permutations, assert_valid_permutation, NotTooBigVec,
34+
};
35+
36+
#[test]
37+
fn test_3_different_values() {
38+
let original = vec![1, 2, 3];
39+
let res = steinhaus_johnson_trotter_permute(&original);
40+
assert_eq!(res.len(), 6); // 3!
41+
for permut in res {
42+
assert_valid_permutation(&original, &permut)
43+
}
44+
}
45+
46+
#[test]
47+
fn test_3_times_the_same_value() {
48+
let original = vec![1, 1, 1];
49+
let res = steinhaus_johnson_trotter_permute(&original);
50+
assert_eq!(res.len(), 6); // 3!
51+
for permut in res {
52+
assert_valid_permutation(&original, &permut)
53+
}
54+
}
55+
56+
#[quickcheck]
57+
fn test_some_elements(NotTooBigVec { inner: original }: NotTooBigVec) {
58+
let permutations = steinhaus_johnson_trotter_permute(&original);
59+
assert_permutations(&original, &permutations)
60+
}
61+
}

0 commit comments

Comments
 (0)