如何迭代 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 中序列的所有唯一排列?的主要内容,如果未能解决你的问题,请参考以下文章

计数和迭代序列的排列

使用迭代器按谓词重新排列

您将如何迭代地计算 0 到 N 的所有可能排列?

如何在rust中迭代str或String的前缀和后缀?

Rust插入排序

如何在 rust 中迭代 vec 或 slice 的前缀或后缀?