From 61428f31868fa48d6127113d7a1851efe7180b88 Mon Sep 17 00:00:00 2001 From: Lars Fischer Date: Fri, 8 Mar 2024 18:05:02 +0100 Subject: [PATCH 1/6] wip --- src/lib.rs | 53 ++++++++++++ src/multiset_permutations.rs | 163 +++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 src/multiset_permutations.rs diff --git a/src/lib.rs b/src/lib.rs index 71c8234f5..8417f5d13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,6 +117,8 @@ pub mod structs { #[cfg(feature = "use_alloc")] pub use crate::permutations::Permutations; #[cfg(feature = "use_alloc")] + pub use crate::multiset_permutations::MultisetPermutations; + #[cfg(feature = "use_alloc")] pub use crate::powerset::Powerset; pub use crate::process_results_impl::ProcessResults; #[cfg(feature = "use_alloc")] @@ -205,6 +207,8 @@ mod peeking_take_while; #[cfg(feature = "use_alloc")] mod permutations; #[cfg(feature = "use_alloc")] +mod multiset_permutations; +#[cfg(feature = "use_alloc")] mod powerset; mod process_results_impl; #[cfg(feature = "use_alloc")] @@ -1720,6 +1724,55 @@ pub trait Itertools: Iterator { permutations::permutations(self, k) } + /// Return an iterator adaptor that iterates over all unique multiset permutations + /// of the elements from an iterator. + /// + /// Iterator element type is `Vec` with equal length to the source iterator. + /// The iterator produces a new Vec per iteration, and clones the iterator elements. + /// + /// + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut it = vec![1, 4, 2, 1].into_iter().multiset_permutations(); + /// assert_eq!(it.next(), Some(vec![4, 2, 1, 1])); + /// assert_eq!(it.next(), Some(vec![1, 4, 2, 1])); + /// assert_eq!(it.next(), Some(vec![4, 1, 2, 1])); + /// assert_eq!(it.next(), Some(vec![1, 4, 1, 2])); + /// assert_eq!(it.next(), Some(vec![1, 1, 4, 2])); + /// assert_eq!(it.next(), Some(vec![4, 1, 1, 2])); + /// assert_eq!(it.next(), Some(vec![2, 4, 1, 1])); + /// assert_eq!(it.next(), Some(vec![1, 2, 4, 1])); + /// assert_eq!(it.next(), Some(vec![2, 1, 4, 1])); + /// assert_eq!(it.next(), Some(vec![1, 2, 1, 4])); + /// assert_eq!(it.next(), Some(vec![1, 1, 2, 4])); + /// assert_eq!(it.next(), Some(vec![2, 1, 1, 4])); + /// assert_eq!(it.next(), None); + /// ``` + /// + /// If the source iterator is empty, the resultant iterator adaptor + /// will only contain an empty vector. + /// + /// ``` + /// use itertools::Itertools; + /// use itertools::MultisetPermutations; + /// + /// let mut it: MultisetPermutations = vec![].into_iter().multiset_permutations(); + /// assert_eq!(it.next(), Some(vec![])); + /// assert_eq!(it.next(), None); + /// ``` + /// + /// Note: The source iterator is collected completely + #[cfg(feature = "use_alloc")] + fn multiset_permutations(self) -> MultisetPermutations + where + Self: Sized, + Self::Item: Ord, + { + multiset_permutations::multiset_permutations(self) + } + /// Return an iterator that iterates through the powerset of the elements from an /// iterator. /// diff --git a/src/multiset_permutations.rs b/src/multiset_permutations.rs new file mode 100644 index 000000000..467f40798 --- /dev/null +++ b/src/multiset_permutations.rs @@ -0,0 +1,163 @@ +use std::iter::FromIterator; + +/// TODO! +#[derive(Debug, Clone)] +pub struct MultisetPermutations { + buffer: Vec>, + start: bool, + head: usize, + index: usize, +} + +#[derive(Debug, Clone)] +struct Node { + value: I, + next: Option, +} + +pub fn multiset_permutations(iter: I) -> MultisetPermutations +where + I: Iterator, + I::Item: Ord, +{ + let mut buffer = Vec::from_iter(iter); + buffer.sort_unstable_by(|a, b| b.cmp(a)); + let length = buffer.len(); + MultisetPermutations { + buffer: buffer + .into_iter() + .enumerate() + .map(|(curr, v)| Node { + value: v, + next: if curr + 1 < length { + Some(curr + 1) + } else { + None + }, + }) + .collect(), + start: true, + head: 0, + index: length.saturating_sub(2), + } +} + +impl Iterator for MultisetPermutations +where + I: Ord, +{ + type Item = Vec; + + fn next(&mut self) -> Option { + if self.start { + self.start = false; + let mut permutation = Vec::with_capacity(self.buffer.len()); + let mut curr = self.head; + for _ in 0..self.buffer.len() { + permutation.push(self.buffer[curr].value); + match self.buffer[curr].next { + Some(next) => curr = next, + None => break, + } + } + + return Some(permutation); + } + + // In case of empty buffer + if self.buffer.len() <= self.index { + return None; + } + + let next = match self.buffer[self.index].next { + Some(next) => next, + None => return None, + }; + + if self.buffer[next].next.is_none() { + if self.buffer[self.head].value <= self.buffer[next].value { + return None; + } + } else { + let next_next = self.buffer[next].next.unwrap(); + let shift_index = if self.buffer[next_next].value <= self.buffer[self.index].value { + next_next + } else { + next + }; + + + + } + + return None; + // // [0,1,2,3,4,5,6,7,8,9,10,0] 4.15s 239500800 base + // // [0,1,2,3,4,5,6,7,8,9,10,0] 4.44s 239500800 opt1 + // // [0,1,2,3,4,5,6,7,8,9,10,0] 3.4 239500800 opt1 + // let elem = self.buffer[shift_index]; + // let mut i = shift_index; + // while i > 0 { + // self.buffer[i] = self.buffer[i - 1]; + // i -= 1; + // } + // // for i in (0..shift_index).rev() { + // // self.buffer[i + 1] = self.buffer[i] + // // } + // self.buffer[0] = elem; + + // // let shift_element = self.buffer.remove(shift_index); + // // self.buffer.insert(0, shift_element); + + // if self.buffer[0] < self.buffer[1] { + // self.index = 0; + // } else { + // self.index += 1; + // } + + // Some(self.buffer.clone()) + } +} + +#[cfg(test)] +mod tests { + use crate::Itertools; + + #[test] + fn test1() { + let mut iter = vec![1, 4, 2, 1].into_iter().multiset_permutations(); + assert_eq!(iter.next(), Some(vec![4, 2, 1, 1])); + assert_eq!(iter.next(), Some(vec![1, 4, 2, 1])); + assert_eq!(iter.next(), Some(vec![4, 1, 2, 1])); + assert_eq!(iter.next(), Some(vec![1, 4, 1, 2])); + assert_eq!(iter.next(), Some(vec![1, 1, 4, 2])); + assert_eq!(iter.next(), Some(vec![4, 1, 1, 2])); + assert_eq!(iter.next(), Some(vec![2, 4, 1, 1])); + assert_eq!(iter.next(), Some(vec![1, 2, 4, 1])); + assert_eq!(iter.next(), Some(vec![2, 1, 4, 1])); + assert_eq!(iter.next(), Some(vec![1, 2, 1, 4])); + assert_eq!(iter.next(), Some(vec![1, 1, 2, 4])); + assert_eq!(iter.next(), Some(vec![2, 1, 1, 4])); + assert_eq!(iter.next(), None); + } + + #[test] + fn test7() { + let mut iter: crate::MultisetPermutations = vec![].into_iter().multiset_permutations(); + assert_eq!(iter.next(), Some(vec![])); + assert_eq!(iter.next(), None); + } + + #[test] + fn timing() { + use std::time::Instant; + let now = Instant::now(); + + let iter = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0] + .iter() + .multiset_permutations(); + let count = iter.count(); + + let elapsed = now.elapsed(); + println!("Elapsed: {:.2?} {count}", elapsed); + } +} From ff2809bacfe0077a760eca7ff556a5b3cf4cc6ff Mon Sep 17 00:00:00 2001 From: Lars Fischer Date: Fri, 8 Mar 2024 23:55:08 +0100 Subject: [PATCH 2/6] prefix shift with linked list --- src/multiset_permutations.rs | 98 ++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 56 deletions(-) diff --git a/src/multiset_permutations.rs b/src/multiset_permutations.rs index 467f40798..1a402fc18 100644 --- a/src/multiset_permutations.rs +++ b/src/multiset_permutations.rs @@ -6,7 +6,8 @@ pub struct MultisetPermutations { buffer: Vec>, start: bool, head: usize, - index: usize, + next: usize, + next_next: usize, } #[derive(Debug, Clone)] @@ -38,7 +39,8 @@ where .collect(), start: true, head: 0, - index: length.saturating_sub(2), + next: length.saturating_sub(2), + next_next: length.saturating_sub(1), } } @@ -51,70 +53,54 @@ where fn next(&mut self) -> Option { if self.start { self.start = false; - let mut permutation = Vec::with_capacity(self.buffer.len()); - let mut curr = self.head; - for _ in 0..self.buffer.len() { - permutation.push(self.buffer[curr].value); - match self.buffer[curr].next { - Some(next) => curr = next, - None => break, - } - } - - return Some(permutation); + return Some(self.get_permutation()) } - // In case of empty buffer - if self.buffer.len() <= self.index { + // Special cases + if self.buffer.len() <= 1 { return None; } - let next = match self.buffer[self.index].next { - Some(next) => next, - None => return None, - }; + // Finish condition + let is_last = self.buffer[self.next_next].next.is_none(); + if is_last && self.buffer[self.head].value <= self.buffer[self.next_next].value { + return None; + } - if self.buffer[next].next.is_none() { - if self.buffer[self.head].value <= self.buffer[next].value { - return None; - } + // Prefix shift + let shift = if !is_last + && self.buffer[self.buffer[self.next_next].next.unwrap()].value + <= self.buffer[self.next].value + { + self.next_next } else { - let next_next = self.buffer[next].next.unwrap(); - let shift_index = if self.buffer[next_next].value <= self.buffer[self.index].value { - next_next - } else { - next - }; - - + self.next + }; + let shift_next = self.buffer[shift].next.unwrap(); + self.buffer[shift].next = self.buffer[shift_next].next; + self.buffer[shift_next].next = Some(self.head); + // Update pointers + if self.buffer[shift_next].value < self.buffer[self.head].value { + self.next = shift_next; } + self.next_next = self.buffer[self.next].next.unwrap(); + self.head = shift_next; - return None; - // // [0,1,2,3,4,5,6,7,8,9,10,0] 4.15s 239500800 base - // // [0,1,2,3,4,5,6,7,8,9,10,0] 4.44s 239500800 opt1 - // // [0,1,2,3,4,5,6,7,8,9,10,0] 3.4 239500800 opt1 - // let elem = self.buffer[shift_index]; - // let mut i = shift_index; - // while i > 0 { - // self.buffer[i] = self.buffer[i - 1]; - // i -= 1; - // } - // // for i in (0..shift_index).rev() { - // // self.buffer[i + 1] = self.buffer[i] - // // } - // self.buffer[0] = elem; - - // // let shift_element = self.buffer.remove(shift_index); - // // self.buffer.insert(0, shift_element); - - // if self.buffer[0] < self.buffer[1] { - // self.index = 0; - // } else { - // self.index += 1; - // } - - // Some(self.buffer.clone()) + Some(self.get_permutation()) + } +} + +impl MultisetPermutations { + fn get_permutation(&self) -> Vec { + let mut permutation = Vec::with_capacity(self.buffer.len()); + let mut curr = Some(self.head); + while curr.is_some() { + let Node {value, next } = self.buffer[curr.unwrap()]; + permutation.push(value); + curr = next; + } + permutation } } From a51d408abdcbd062d8e3473e279e778c1700dd41 Mon Sep 17 00:00:00 2001 From: Lars Fischer Date: Sat, 9 Mar 2024 12:02:32 +0100 Subject: [PATCH 3/6] removed linked list --- src/multiset_permutations.rs | 91 ++++++++++++------------------------ 1 file changed, 30 insertions(+), 61 deletions(-) diff --git a/src/multiset_permutations.rs b/src/multiset_permutations.rs index 1a402fc18..45383ef50 100644 --- a/src/multiset_permutations.rs +++ b/src/multiset_permutations.rs @@ -3,17 +3,9 @@ use std::iter::FromIterator; /// TODO! #[derive(Debug, Clone)] pub struct MultisetPermutations { - buffer: Vec>, + buffer: Vec, start: bool, - head: usize, - next: usize, - next_next: usize, -} - -#[derive(Debug, Clone)] -struct Node { - value: I, - next: Option, + index: usize, } pub fn multiset_permutations(iter: I) -> MultisetPermutations @@ -25,22 +17,9 @@ where buffer.sort_unstable_by(|a, b| b.cmp(a)); let length = buffer.len(); MultisetPermutations { - buffer: buffer - .into_iter() - .enumerate() - .map(|(curr, v)| Node { - value: v, - next: if curr + 1 < length { - Some(curr + 1) - } else { - None - }, - }) - .collect(), + buffer: buffer, start: true, - head: 0, - next: length.saturating_sub(2), - next_next: length.saturating_sub(1), + index: length.saturating_sub(2), } } @@ -51,56 +30,46 @@ where type Item = Vec; fn next(&mut self) -> Option { + // Start iteration with buffer itself if self.start { self.start = false; - return Some(self.get_permutation()) + return Some(self.buffer.clone()); } - // Special cases - if self.buffer.len() <= 1 { - return None; - } - - // Finish condition - let is_last = self.buffer[self.next_next].next.is_none(); - if is_last && self.buffer[self.head].value <= self.buffer[self.next_next].value { + // Exhausted iteration + let has_two_next = self.index + 2 < self.buffer.len(); + if !has_two_next + && (self.buffer.len() <= self.index + 1 + || self.buffer[0] <= self.buffer[self.index + 1]) + { return None; } - // Prefix shift - let shift = if !is_last - && self.buffer[self.buffer[self.next_next].next.unwrap()].value - <= self.buffer[self.next].value + // Determine shift index + let shift_index = if has_two_next && self.buffer[self.index + 2] <= self.buffer[self.index] { - self.next_next + self.index + 2 } else { - self.next + self.index + 1 }; - let shift_next = self.buffer[shift].next.unwrap(); - self.buffer[shift].next = self.buffer[shift_next].next; - self.buffer[shift_next].next = Some(self.head); - // Update pointers - if self.buffer[shift_next].value < self.buffer[self.head].value { - self.next = shift_next; + // Prefix shift + let shift_elem = self.buffer[shift_index]; + let mut swap_index = shift_index; + while swap_index > 0 { + self.buffer[swap_index] = self.buffer[swap_index - 1]; + swap_index -= 1; } - self.next_next = self.buffer[self.next].next.unwrap(); - self.head = shift_next; + self.buffer[0] = shift_elem; - Some(self.get_permutation()) - } -} - -impl MultisetPermutations { - fn get_permutation(&self) -> Vec { - let mut permutation = Vec::with_capacity(self.buffer.len()); - let mut curr = Some(self.head); - while curr.is_some() { - let Node {value, next } = self.buffer[curr.unwrap()]; - permutation.push(value); - curr = next; + // Update index + if self.buffer[0] < self.buffer[1] { + self.index = 0; + } else { + self.index += 1; } - permutation + + Some(self.buffer.clone()) } } From f5815d412e17c3dc8562a782d07661c821e8435b Mon Sep 17 00:00:00 2001 From: Lars Fischer Date: Sat, 9 Mar 2024 20:43:54 +0100 Subject: [PATCH 4/6] tests --- src/multiset_permutations.rs | 55 ++++++++++++++++++++++++++++++++++++ tests/quick.rs | 17 +++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/multiset_permutations.rs b/src/multiset_permutations.rs index 45383ef50..98efd679e 100644 --- a/src/multiset_permutations.rs +++ b/src/multiset_permutations.rs @@ -95,6 +95,61 @@ mod tests { assert_eq!(iter.next(), None); } + #[test] + fn test3() { + use rand::Rng; + use std::time::Instant; + + let n = 12; + let mut rng = rand::thread_rng(); + let vec: Vec = (0..n).map(|_| rng.gen_range(0, 10)).collect(); + + println!("{:?}", vec); + + let now = Instant::now(); + let mut permutations1 = vec + .clone() + .into_iter() + .permutations(n) + .unique() + .collect_vec(); + println!("permutations: {:.2?}", now.elapsed()); + + let now = Instant::now(); + let mut permutations2 = vec.into_iter().multiset_permutations().collect_vec(); + println!("multiset_permutations: {:.2?}", now.elapsed()); + + permutations1.sort(); + permutations2.sort(); + // println!("{:?}", permutations1); + + // println!("{:?}", permutations2); + + assert_eq!(permutations1, permutations2); + } + + #[test] + fn test4() { + let mut iter = vec![0, 0, 1].into_iter().multiset_permutations(); + assert_eq!(iter.next(), Some(vec![1, 0, 0])); + assert_eq!(iter.next(), Some(vec![0, 1, 0])); + assert_eq!(iter.next(), Some(vec![0, 0, 1])); + assert_eq!(iter.next(), None); + } + + #[test] + fn test5() { + let mut iter = vec![1, 1].into_iter().multiset_permutations(); + assert_eq!(iter.next(), Some(vec![1, 1])); + assert_eq!(iter.next(), None); + } + + #[test] + fn test6() { + let iter = "MISSISSIPPI".chars().multiset_permutations(); + assert_eq!(iter.count(), 34650); // 34650 = 11! / (1! * 2! * 4! * 4!) + } + #[test] fn test7() { let mut iter: crate::MultisetPermutations = vec![].into_iter().multiset_permutations(); diff --git a/tests/quick.rs b/tests/quick.rs index 7b163d8dc..bf8b0df41 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -729,6 +729,23 @@ quickcheck! { assert_eq!(expected, actual); } + + fn correct_multiset_permutations(a: usize, b: usize, c: usize, d: usize) -> () { + let n1 = a % 2; + let n2 = b % 4; + let n3 = c % 2; + let n4 = d % 5; + let mut vec = vec![0; n1]; + vec.append(&mut vec![1; n2]); + vec.append(&mut vec![2; n3]); + vec.append(&mut vec![3; n4]); + let mut perm1 = vec.clone().into_iter().permutations(vec.len()).unique().collect_vec(); + perm1.sort(); + let mut perm2 = vec.into_iter().multiset_permutations().collect_vec(); + perm2.sort(); + assert_eq!(perm1, perm2); + } + } quickcheck! { From cae12eecfc5aee5714303e59db92dc38d2bf90c7 Mon Sep 17 00:00:00 2001 From: Lars Fischer Date: Mon, 5 Aug 2024 23:32:56 +0200 Subject: [PATCH 5/6] updated quickcheck --- tests/quick.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/quick.rs b/tests/quick.rs index d0ad35a99..c4041af33 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -750,11 +750,10 @@ quickcheck! { vec.append(&mut vec![1; n2]); vec.append(&mut vec![2; n3]); vec.append(&mut vec![3; n4]); - let mut perm1 = vec.clone().into_iter().permutations(vec.len()).unique().collect_vec(); - perm1.sort(); - let mut perm2 = vec.into_iter().multiset_permutations().collect_vec(); - perm2.sort(); - assert_eq!(perm1, perm2); + let n = vec.len(); + let unique_permutations = vec.iter().permutations(n).unique().sorted().collect_vec(); + let multiset_permutations = vec.iter().multiset_permutations().sorted().collect_vec(); + assert_eq!(unique_permutations, multiset_permutations); } } From d9347c86a8586b92d6fd19a11eec3b700088b464 Mon Sep 17 00:00:00 2001 From: Lars Fischer Date: Fri, 13 Dec 2024 15:35:16 +0100 Subject: [PATCH 6/6] cleanup --- src/multiset_permutations.rs | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/src/multiset_permutations.rs b/src/multiset_permutations.rs index 98efd679e..1b4c0fa84 100644 --- a/src/multiset_permutations.rs +++ b/src/multiset_permutations.rs @@ -1,6 +1,10 @@ use std::iter::FromIterator; -/// TODO! +/// An iterator adaptor that iterates through all the unique multiset permutations of the iterator. +/// The supplied iterator is fully consumed, so it must be finite. +/// +/// See [`.multiset_permutations()`](crate::Itertools::multiset_permutations) for +/// more information. #[derive(Debug, Clone)] pub struct MultisetPermutations { buffer: Vec, @@ -98,33 +102,20 @@ mod tests { #[test] fn test3() { use rand::Rng; - use std::time::Instant; - let n = 12; let mut rng = rand::thread_rng(); let vec: Vec = (0..n).map(|_| rng.gen_range(0, 10)).collect(); - println!("{:?}", vec); - - let now = Instant::now(); let mut permutations1 = vec .clone() .into_iter() .permutations(n) .unique() .collect_vec(); - println!("permutations: {:.2?}", now.elapsed()); - - let now = Instant::now(); let mut permutations2 = vec.into_iter().multiset_permutations().collect_vec(); - println!("multiset_permutations: {:.2?}", now.elapsed()); permutations1.sort(); permutations2.sort(); - // println!("{:?}", permutations1); - - // println!("{:?}", permutations2); - assert_eq!(permutations1, permutations2); } @@ -156,18 +147,4 @@ mod tests { assert_eq!(iter.next(), Some(vec![])); assert_eq!(iter.next(), None); } - - #[test] - fn timing() { - use std::time::Instant; - let now = Instant::now(); - - let iter = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0] - .iter() - .multiset_permutations(); - let count = iter.count(); - - let elapsed = now.elapsed(); - println!("Elapsed: {:.2?} {count}", elapsed); - } }