Skip to content

Commit

Permalink
Add next_permutation and prev_permutation onto MutableOrdVector<T>.
Browse files Browse the repository at this point in the history
Unlike ImmutableClonableVector::permutations() which returns an iterator,
cloning the entire array each iteration, these methods mutate the vector in-place.
For that reason, these methods are much faster; between 35-55 times faster,
depending on the benchmark. They also generate permutations in lexicographical order.
  • Loading branch information
exscape committed Jun 3, 2014
1 parent 918dbfe commit 3b5d6fd
Showing 1 changed file with 142 additions and 0 deletions.
142 changes: 142 additions & 0 deletions src/libstd/slice.rs
Expand Up @@ -712,13 +712,103 @@ pub trait MutableOrdVector<T> {
/// assert!(v == [-5, -3, 1, 2, 4]);
/// ```
fn sort(self);

/// Mutates the slice to the next lexicographic permutation.
///
/// Returns `true` if successful, `false` if the slice is at the last-ordered permutation.
///
/// # Example
///
/// ```rust
/// let v = &mut [0, 1, 2];
/// v.next_permutation();
/// assert_eq!(v, &mut [0, 2, 1]);
/// v.next_permutation();
/// assert_eq!(v, &mut [1, 0, 2]);
/// ```
fn next_permutation(self) -> bool;

/// Mutates the slice to the previous lexicographic permutation.
///
/// Returns `true` if successful, `false` if the slice is at the first-ordered permutation.
///
/// # Example
///
/// ```rust
/// let v = &mut [1, 0, 2];
/// v.prev_permutation();
/// assert_eq!(v, &mut [0, 2, 1]);
/// v.prev_permutation();
/// assert_eq!(v, &mut [0, 1, 2]);
/// ```
fn prev_permutation(self) -> bool;
}

impl<'a, T: Ord> MutableOrdVector<T> for &'a mut [T] {
#[inline]
fn sort(self) {
self.sort_by(|a,b| a.cmp(b))
}

fn next_permutation(self) -> bool {
// These cases only have 1 permutation each, so we can't do anything.
if self.len() < 2 { return false; }

// Step 1: Identify the longest, rightmost weakly decreasing part of the vector
let mut i = self.len() - 1;
while i > 0 && self[i-1] >= self[i] {
i -= 1;
}

// If that is the entire vector, this is the last-ordered permutation.
if i == 0 {
return false;
}

// Step 2: Find the rightmost element larger than the pivot (i-1)
let mut j = self.len() - 1;
while j >= i && self[j] <= self[i-1] {
j -= 1;
}

// Step 3: Swap that element with the pivot
self.swap(j, i-1);

// Step 4: Reverse the (previously) weakly decreasing part
self.mut_slice_from(i).reverse();

true
}

fn prev_permutation(self) -> bool {
// These cases only have 1 permutation each, so we can't do anything.
if self.len() < 2 { return false; }

// Step 1: Identify the longest, rightmost weakly increasing part of the vector
let mut i = self.len() - 1;
while i > 0 && self[i-1] <= self[i] {
i -= 1;
}

// If that is the entire vector, this is the first-ordered permutation.
if i == 0 {
return false;
}

// Step 2: Reverse the weakly increasing part
self.mut_slice_from(i).reverse();

// Step 3: Find the rightmost element equal to or bigger than the pivot (i-1)
let mut j = self.len() - 1;
while j >= i && self[j-1] < self[i-1] {
j -= 1;
}

// Step 4: Swap that element with the pivot
self.swap(i-1, j);

true
}
}

/// Unsafe operations
Expand Down Expand Up @@ -1229,6 +1319,58 @@ mod tests {
}
}

#[test]
fn test_lexicographic_permutations() {
let v : &mut[int] = &mut[1, 2, 3, 4, 5];
assert!(v.prev_permutation() == false);
assert!(v.next_permutation());
assert_eq!(v, &mut[1, 2, 3, 5, 4]);
assert!(v.prev_permutation());
assert_eq!(v, &mut[1, 2, 3, 4, 5]);
assert!(v.next_permutation());
assert!(v.next_permutation());
assert_eq!(v, &mut[1, 2, 4, 3, 5]);
assert!(v.next_permutation());
assert_eq!(v, &mut[1, 2, 4, 5, 3]);

let v : &mut[int] = &mut[1, 0, 0, 0];
assert!(v.next_permutation() == false);
assert!(v.prev_permutation());
assert_eq!(v, &mut[0, 1, 0, 0]);
assert!(v.prev_permutation());
assert_eq!(v, &mut[0, 0, 1, 0]);
assert!(v.prev_permutation());
assert_eq!(v, &mut[0, 0, 0, 1]);
assert!(v.prev_permutation() == false);
}

#[test]
fn test_lexicographic_permutations_empty_and_short() {
let empty : &mut[int] = &mut[];
assert!(empty.next_permutation() == false);
assert_eq!(empty, &mut[]);
assert!(empty.prev_permutation() == false);
assert_eq!(empty, &mut[]);

let one_elem : &mut[int] = &mut[4];
assert!(one_elem.prev_permutation() == false);
assert_eq!(one_elem, &mut[4]);
assert!(one_elem.next_permutation() == false);
assert_eq!(one_elem, &mut[4]);

let two_elem : &mut[int] = &mut[1, 2];
assert!(two_elem.prev_permutation() == false);
assert_eq!(two_elem, &mut[1, 2]);
assert!(two_elem.next_permutation());
assert_eq!(two_elem, &mut[2, 1]);
assert!(two_elem.next_permutation() == false);
assert_eq!(two_elem, &mut[2, 1]);
assert!(two_elem.prev_permutation());
assert_eq!(two_elem, &mut[1, 2]);
assert!(two_elem.prev_permutation() == false);
assert_eq!(two_elem, &mut[1, 2]);
}

#[test]
fn test_position_elem() {
assert!([].position_elem(&1).is_none());
Expand Down

9 comments on commit 3b5d6fd

@bors
Copy link
Contributor

@bors bors commented on 3b5d6fd Jun 4, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors
Copy link
Contributor

@bors bors commented on 3b5d6fd Jun 4, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging exscape/rust-fork/master = 3b5d6fd into auto

@bors
Copy link
Contributor

@bors bors commented on 3b5d6fd Jun 4, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exscape/rust-fork/master = 3b5d6fd merged ok, testing candidate = 67aa8c70

@bors
Copy link
Contributor

@bors bors commented on 3b5d6fd Jun 4, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors
Copy link
Contributor

@bors bors commented on 3b5d6fd Jun 4, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging exscape/rust-fork/master = 3b5d6fd into auto

@bors
Copy link
Contributor

@bors bors commented on 3b5d6fd Jun 4, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exscape/rust-fork/master = 3b5d6fd merged ok, testing candidate = 7645982

@bors
Copy link
Contributor

@bors bors commented on 3b5d6fd Jun 4, 2014

@bors
Copy link
Contributor

@bors bors commented on 3b5d6fd Jun 4, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = 7645982

Please sign in to comment.