如何迭代 Rust 中序列的所有唯一排列?
Posted
技术标签:
【中文标题】如何迭代 Rust 中序列的所有唯一排列?【英文标题】:How to iterate over all unique permutations of a sequence in Rust? 【发布时间】:2020-05-13 08:10:29 【问题描述】:给定一个值列表,例如vec![0, 0, 1, 2]
,我想创建一个迭代器来生成其所有唯一排列。也就是说,
[0, 0, 1, 2]
[0, 0, 2, 1]
[0, 1, 0, 2]
[0, 1, 2, 0]
[0, 2, 0, 1]
[0, 2, 1, 0]
[1, 0, 0, 2]
[1, 0, 2, 0]
[1, 2, 0, 0]
[2, 0, 0, 1]
[2, 0, 1, 0]
[2, 1, 0, 0]
(请注意,有 12 种不同的排列,而如果我们有 4 个 distinct 元素,则将有 24 种不同的排列)。
已经有一种方法可以使用itertools package 生成排列(以及其他迭代器,如组合或没有替换的组合),但对于排列,没有办法将排列限制为唯一的排列。
有一种相当有效的算法来生成排列,通常称为Heap's Algorithm,但是这没有考虑值的相等性/重复性。
这个问题在带有生成器的语言中实现起来并不太棘手,such as Python,但我觉得这在 Rust 中更棘手(至少与上面的解决方案相比),因为它需要使用迭代器(必须维护内部状态),或使用生成器(当前为unstable)。
【问题讨论】:
您的输入值列表是否始终排序? 不,在这种情况下,输入列表中元素的顺序是任意的(因为两者将具有相同的排列集)。 【参考方案1】:使用来自 itertools 的更多工具,即Itertools::unique
:
use itertools::Itertools; // 0.8.2
fn main()
let items = vec![0, 0, 1, 2];
for perm in items.iter().permutations(items.len()).unique()
println!(":?", perm);
另见:
How can I add new methods to Iterator?【讨论】:
我之前没有注意到这个迭代器方法,所以知道它是可用的很棒 - 感谢分享。 :-) 也就是说,在某些情况下,此解决方案的运行时间可能非常差(与我发布的解决方案相比)。前任。向量只有几个 (1-3) 不同的项目,例如 [1, 0, 0, 0, 0, 0, 0, 0];那么这段代码将尝试计算所有 8 个!非显着排列(然后按唯一性过滤)与预先计算 8 种可能的不同排列(如果我没记错的话)。【参考方案2】:Python解决方案可以转化为迭代器:
use std::collections::btree_set::BTreeSet, IntoIter;
enum UniquePermutations
Leaf
elements: Option<Vec<i32>>,
,
Stem
elements: Vec<i32>,
unique_elements: IntoIter<i32>,
first_element: i32,
inner: Box<Self>,
,
impl UniquePermutations
fn new(elements: Vec<i32>) -> Self
if elements.len() == 1
let elements = Some(elements);
Self::Leaf elements
else
let mut unique_elements = elements
.clone()
.into_iter()
.collect::<BTreeSet<_>>()
.into_iter();
let (first_element, inner) = Self::next_level(&mut unique_elements, elements.clone())
.expect("Must have at least one item");
Self::Stem
elements,
unique_elements,
first_element,
inner,
fn next_level(
mut unique_elements: impl Iterator<Item = i32>,
elements: Vec<i32>,
) -> Option<(i32, Box<Self>)>
let first_element = unique_elements.next()?;
let mut remaining_elements = elements;
if let Some(idx) = remaining_elements.iter().position(|&i| i == first_element)
remaining_elements.remove(idx);
let inner = Box::new(Self::new(remaining_elements));
Some((first_element, inner))
impl Iterator for UniquePermutations
type Item = Vec<i32>;
fn next(&mut self) -> Option<Self::Item>
match self
Self::Leaf elements => elements.take(),
Self::Stem
elements,
unique_elements,
first_element,
inner,
=> loop
match inner.next()
Some(mut v) =>
v.insert(0, *first_element);
return Some(v);
None =>
let (next_fe, next_i) =
Self::next_level(&mut *unique_elements, elements.clone())?;
*first_element = next_fe;
*inner = next_i;
,
fn main()
let items = vec![0, 0, 1, 2];
for perm in UniquePermutations::new(items)
println!(":?", perm);
生成器解决方案隐藏了许多分配和复杂性,这在此处被带到了最前沿。调用堆栈变成了inner
字段的链表,我们必须进行大量的克隆。
我没有执行任何微优化,例如使用VecDeque
插入列表的头部,或者添加一个包装迭代器适配器,在最终将Vec
返回给调用者之前反转它。我还使用了BTreeSet
来专注于确保集合是独一无二的;随意将其更改为基于 Vec
的纯解决方案。
我也没有进行任何分析或基准测试。这可能会也可能不会更快。
在 crates.io 上有许多排列板条箱可用。我鼓励您看看是否可以将这段代码添加到其中的一个或多个中,以防止人们将来不得不解决这个问题。
【讨论】:
【参考方案3】:如果您愿意放弃使用迭代器或生成器,可以使用以下代码编写一个输出列表所有可能唯一排列的函数。但是,由于即使在小情况下(例如,包含两个项目的向量),它也分配了很多向量,因此实现效率并不高。
fn unique_permutations<T: Clone>(items: Vec<T>) -> Vec<Vec<T>>
where
T: Ord,
if items.len() == 1
vec![items]
else
let mut output: Vec<Vec<T>> = vec![];
// Obtain a list of the unique elements.
// Sorting and deduping should be faster than using a hashset for most small n.
let mut unique_items = items.clone();
unique_items.sort();
unique_items.dedup();
for first in unique_items
let mut remaining_elements = items.clone();
// this feature is unstable
// remaining_elements.remove_item(first);
let index = remaining_elements.iter().position(|x| *x == first).unwrap();
remaining_elements.remove(index);
for mut permutation in unique_permutations(remaining_elements)
permutation.insert(0, first.clone());
output.push(permutation);
output
【讨论】:
以上是关于如何迭代 Rust 中序列的所有唯一排列?的主要内容,如果未能解决你的问题,请参考以下文章