【问题标题】:How to iterate over all unique permutations of a sequence in Rust?如何迭代 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)。

【问题讨论】:

  • 您的输入值列表是否始终排序?
  • 不,在这种情况下,输入列表中元素的顺序是任意的(因为两者将具有相同的排列集)。

标签: algorithm rust permutation heaps-algorithm


【解决方案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);
    }
}

另见:

【讨论】:

  • 我之前没有注意到这个迭代器方法,所以知道它是可用的很棒 - 感谢分享。 :-) 也就是说,在某些情况下,此解决方案的运行时间可能非常差(与我发布的解决方案相比)。前任。向量只有几个 (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
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-11-04
      • 2012-03-14
      • 1970-01-01
      • 2017-01-16
      • 1970-01-01
      • 1970-01-01
      • 2012-06-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多