You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Rust Itertools实现带有可变长度计数的向量组合生成?

搞定指定结构的不重叠组合分组,还支持懒加载!

嘿,你的需求本质是把原集合拆成若干不相交的子集,每个子集的长度和数量都由len_counts指定,还要懒加载对吧?刚好可以基于itertools的组合能力来实现,我给你整了个简洁的方案,完全符合你的要求。

核心思路

首先得先确认输入合法:所有长度×数量的总和必须等于原向量的元素总数,不然根本没法拆分对吧?然后我们就可以分步来:先生成第一个指定长度的组合,再从剩下的元素里生成下一个长度的组合,以此类推。全程用迭代器链式操作,保证懒加载,不用一次性把所有结果都堆在内存里。

代码实现

下面的代码不仅能处理你给出的{3:1, 2:1}的例子,还能扩展到任意的len_counts配置:

use itertools::Itertools;
use std::collections::HashMap;

fn generate_partitions<T: Clone + Eq + std::hash::Hash>(
    values: &[T],
    len_counts: &HashMap<usize, usize>,
) -> impl Iterator<Item = Vec<Vec<T>>> {
    // 先做个输入校验:总元素数得等于所有len*count的和,不然白忙活
    let total_required: usize = len_counts.iter()
        .map(|(len, count)| len * count)
        .sum();
    assert_eq!(values.len(), total_required, "元素总数和要求的len*count总和不匹配");

    // 把len_counts转成有序列表,按长度降序排,能减少重复计算
    let mut len_count_pairs: Vec<_> = len_counts.iter().collect();
    len_count_pairs.sort_by(|a, b| b.0.cmp(a.0));

    // 递归生成分组,全程用迭代器,懒加载拉满
    generate_partitions_recursive(values, &len_count_pairs)
}

fn generate_partitions_recursive<T: Clone + Eq + std::hash::Hash>(
    remaining: &[T],
    len_count_pairs: &[(usize, usize)],
) -> impl Iterator<Item = Vec<Vec<T>>> {
    if len_count_pairs.is_empty() {
        // 没要求了,返回空分组就行
        return std::iter::once(Vec::new());
    }

    let (&(current_len, current_count), rest_pairs) = len_count_pairs.split_first().unwrap();

    // 先生成当前长度的组合,再递归处理剩下的元素
    remaining.iter().combinations(current_len)
        .map(|comb| comb.into_iter().cloned().collect::<Vec<T>>())
        .flat_map(move |first_group| {
            // 算出剩下的元素:把已经选的从剩余集合里去掉
            let mut remaining_set: std::collections::HashSet<_> = remaining.iter().cloned().collect();
            for elem in &first_group {
                remaining_set.remove(elem);
            }
            let remaining_vec: Vec<_> = remaining_set.into_iter().collect();

            // 如果当前长度需要多个分组,就继续处理同长度;否则处理下一个长度
            if current_count > 1 {
                let mut new_pairs = rest_pairs.to_vec();
                new_pairs.insert(0, (current_len, current_count - 1));
                generate_partitions_recursive(&remaining_vec, &new_pairs)
                    .map(move |mut groups| {
                        groups.insert(0, first_group.clone());
                        groups
                    })
            } else {
                generate_partitions_recursive(&remaining_vec, rest_pairs)
                    .map(move |mut groups| {
                        groups.insert(0, first_group.clone());
                        groups
                    })
            }
        })
        // 去重:避免因为组合顺序不同生成重复的分组(比如相同长度的分组调换顺序)
        .unique()
}

fn main() {
    let values = vec![0, 1, 2, 3, 4];
    let mut len_counts = HashMap::new();
    len_counts.insert(3, 1);
    len_counts.insert(2, 1);

    let partitions = generate_partitions(&values, &len_counts);
    partitions.for_each(|p| println!("{:?}", p));
}

为啥这么写?

  • 懒加载:全程返回迭代器,只有当你遍历的时候才会生成对应的分组,内存占用极低。
  • 通用性:不管len_counts是{2:2,1:1}还是其他合法配置,都能正常工作。
  • 去重处理:用itertools的unique()去掉重复的分组,毕竟你说组合顺序无关紧要。
  • 输入校验:先确保输入合法,避免运行时出现莫名其妙的错误。

对应你的示例

运行这段代码,会生成你预期的10种分组结果,顺序可能和你列的有点不一样,但内容完全一致,完全符合你的要求。


内容的提问来源于stack exchange,提问作者bayramkazik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 21:03:15