#组合 #排列 #笛卡尔积 #多个 #k-排列

permutator

从数据中获取任意特定索引的字典序笛卡尔积和字典序排列。从单个或多个数据集中生成完整的字典序笛卡尔积。从数据中生成完整的字典序组合。生成非字典序排列和 k-排列。

16 个版本

0.4.3 2022 年 1 月 22 日
0.4.2 2022 年 1 月 22 日
0.4.0 2019 年 12 月 24 日
0.3.3 2018 年 12 月 7 日
0.1.6 2018 年 10 月 24 日

#238算法

Download history 188/week @ 2024-03-11 211/week @ 2024-03-18 191/week @ 2024-03-25 308/week @ 2024-04-01 135/week @ 2024-04-08 209/week @ 2024-04-15 197/week @ 2024-04-22 176/week @ 2024-04-29 143/week @ 2024-05-06 176/week @ 2024-05-13 223/week @ 2024-05-20 188/week @ 2024-05-27 192/week @ 2024-06-03 146/week @ 2024-06-10 175/week @ 2024-06-17 160/week @ 2024-06-24

694 每月下载量
用于 16 个 crate(10 个直接使用)

BSD-3-Clause

645KB
8K SLoC

排列器

它提供了多种获取数据排列的方法。

TLDR

最简单的泛型用法

use permutator::{CartesianProduct, Combination, Permutation};
let domains : &[&[i32]] = &[&[1, 2], &[3, 4, 5], &[6], &[7, 8], &[9, 10, 11]];
domains.cart_prod().for_each(|cp| {
    // each cp will be &[&i32] with length equals to domains.len() which in this case 5

    // It's k-permutation of size 3 over data.
    cp.combination(3).for_each(|mut c| { // need mut
        // each `c` is not &[&&i32]
        // print the first 3-combination over data
        // No longer need this line from verion 0.4.0 onward
        // println!("{:?}", c);

        // start permute the 3-combination
        c.permutation().for_each(|p| {
            // each `p` is not &[&&&i32]
            // print each permutation of the 3-combination.
            println!("{:?}", p);
        });

        // It'll print the last 3-permutation again because permutation permute the value in place.
        println!("{:?}", c);
    })
});

注意,每个嵌套级别都会获得更深的引用。如果不希望这种行为,请使用 copy 模块。以下是一个示例

use permutator::copy::{CartesianProduct, Combination, Permutation};
let domains : &[&[i32]] = &[&[1, 2], &[3, 4, 5], &[6], &[7, 8], &[9, 10, 11]];
domains.cart_prod().for_each(|cp| {
    // each cp will be &[i32] with length equals to domains.len() which in this case 5

    // It's k-permutation of size 3 over data.
    cp.combination(3).for_each(|mut c| { // need mut
        // each `c` is not &[i32]
        // print the first 3-combination over data
        // No longer need this line from verion 0.4.0 onward
        // println!("{:?}", c);

        // start permute the 3-combination
        c.permutation().for_each(|p| {
            // each `p` is not &[i32]
            // print each permutation of the 3-combination.
            println!("{:?}", p);
        });

        // It'll print the last 3-permutation again because permutation permute the value in place.
        println!("{:?}", c);
    })
});

copy 模块

此 crate 分为两个模块。一个是根模块,在大多数情况下都可以使用。另一个是 copy 模块,它要求类型实现 Copy 特性。根模块返回值作为 &T 的集合,除了所有堆排列家族之外。 copy 模块总是返回值作为 T 的集合。在 copy 模块中没有堆排列,因为它在原地排列。没有复制或创建任何引用。

获取特定点的排列,而不是迭代器风格。

此 crate 提供了两个函数来获取笛卡尔积或 k-排列

  • get_cartesian_for
  • get_permutation_for 它执行数学计算并在该位置返回结果。它不会跳过迭代。当域非常大时非常有用。否则,简单地跳过迭代可能更快。例如,在不可控测试环境中,使用 --release 标志编译的 heap_permutation 函数每秒可以排列约 8800 万种排列。

此 crate 还提供了以下实用函数

  • get_cartesian_size
  • get_permutation_size

在集合本身上多次获取笛卡尔积

有两种不同的方法来获取笛卡尔积。

  • 返回产品的迭代器
  • 调用回调函数以返回产品的函数

迭代器

此 crate 提供 SelfCartesianProductIteratorSelfCartesianProductCellIterSelfCartesianProductRefIter 结构体,它们实现了 IteratorIteratorResetExactSizeIterator 特性。每个结构体适用于不同的用例:-

  • SelfCartesianProductIterator 可在任何性能不是首要关注的情况下使用。
  • SelfCartesianProductCellIter 可在性能和安全性都很重要的场合使用。
  • SelfCartesianProductRefIter 可在性能至关重要且安全性由调用者处理的情况下使用。每个结构体都实现了 IteratorReset 特性。
  • 使用 reset 函数而不是每次需要重新迭代时创建一个新的迭代器。

特性

此crate在根模块和 copy 模块中都提供了 CartesianProduct 特性,它添加了一个名为 cart_prod 的函数,该函数返回一个迭代器,用于生成集合自身多次的笛卡尔积。目前支持的类型有

  • (&'a [T], usize) - 对 '第二个参数' 生成 '第一个参数' 的笛卡尔积。
  • (&'a [T], usize, Rc<RefCell<&'a mut [&'a T]>>) - 与上面类似,但将积覆盖到 '第三个参数' 中。 此类型需要根模块的特性。
  • (&'a [T], usize, *mut [&'a T]) - 与上面类似,但使用不安全的指针来存储值。 此类型需要根模块的特性。 上述每种类型返回不同的迭代器。例如,(&'a [T], usize) 返回 SelfCartesianProductIterator,但在 (&'a [T], usize, *mut [&'a T]) 返回 SelfCartesianProductRefIter
  • (&'a [T], usize, Rc<RefCell<&'a mut [T]>>) - 与上面类似,但将积覆盖到 '第三个参数' 中。 此类型需要 copy 模块的特性。
  • (&'a [T], usize, *mut [T]) - 与上面类似,但使用不安全的指针来存储值。 此类型需要来自 copy 模块的特征。 上面的每个类型都返回不同的迭代器。例如 (&'a [T], usize) 返回 copy::SelfCartesianProductIterator,但在 (&'a [T], usize, *mut [T]) 返回 copy::SelfCartesianProductRefIter

回调函数

此包提供了4个函数,用于不同的用例。

  • self_cartesian_product 函数,将乘积作为回调参数返回
  • self_cartesian_product_cell 函数,将乘积放入函数参数中给出的 Rc<RefCell>
  • self_cartesian_product_sync 函数,将乘积放入函数参数中给出的 Arc<RwLock>
  • unsafe_self_cartesian_product 不安全函数,将乘积放入函数参数中给出的可变指针

获取多个集合的笛卡尔积

获取笛卡尔积有三种不同的实现。

  • 返回产品的迭代器
  • 调用回调函数以返回产品的函数
  • CartesianProduct 特征,将 &[&[T]] 中的 cart_prod 函数添加到 (&[&[T]], Rc<RefCell<&mut[&T]>>)

迭代器

此包提供了实现 IteratorIteratorResetExactSizeIterator 特征的 CartesianProductIteratorCartesianProductCellIterCartesianProductRefIter 结构体。每个结构体适用于不同的用例:

  • CartesianProductIterator 可用于任何性能不是最关键的情况。
  • CartesianProductCellIter 可用于性能和安全性都很重要的情况。
  • CartesianProductRefIter 可用于性能至关重要,且安全性由调用者处理的情况。每个结构体都实现了 IteratorReset 特征。
  • 使用 reset 函数而不是每次需要重新迭代时创建一个新的迭代器。

特性

此软件包在根模块和copy模块中都提供了CartesianProduct特质。它在各种类型上实现,例如切片的切片、切片的泛型Vec、元组(&'a [&'a [T]], Rc<RefCell<&'a mut[&'a T]>>)以及元组(&'a [&'a [T]], *mut [&'a T])等。它向其中添加了cart_prod()函数,并根据数据类型返回所需的迭代器。例如,对于切片的泛型Vec,返回CartesianProductIterator,而对于(&'a [&'a [T]], *mut [&'a T])则返回CartesianProductRefIter

回调函数

此软件包在两个模块中提供了4个类似的功能,用于不同的用例。根模块中的这4个函数:

  • cartesian_product函数,将乘积作为回调参数返回
  • cartesian_product_cell函数,将乘积放入函数参数中给出的Rc<RefCell<>>
  • cartesian_product_sync函数,将乘积放入函数参数中给出的Arc<RwLock<>>
  • unsafe_cartesian_product不安全函数,将乘积放入函数中给出的可变指针,以及copy模块中的所有4个函数,这些函数执行完全相同的功能,除了每个元素是T而不是&T

从数据中获取组合

有三种不同的方法可以获取n集合的k组合。

  • 迭代器在每个迭代中返回每个组合
  • 特质为切片、VecRc<RefCell<&mut[&mut[&mutT]>>等添加了函数。
  • 调用回调函数以返回产品的函数

迭代器

这个crate在两个模块中提供了LargeCombinationIteratorLargeCombinationCellIterLargeCombinationRefIter结构体,这些结构体实现了IteratorIteratorResetExactSizeIterator特性。每个结构体服务于不同的用例:

  • LargeCombinationIterator可以在任何性能不是最关键的情况下使用。
  • LargeCombinationCellIter可以在性能和安全都很重要的情况下使用。
  • LargeCombinationRefIter可以在性能至关重要且安全性由调用者处理的情况下使用。这两个模块中的所有三个结构体仅在返回类型上有所不同。根模块的结果元素是&T,而copy模块的结果元素是复制的T。每个结构体都实现了IteratorReset特性。
  • 使用 reset 函数而不是每次需要重新迭代时创建一个新的迭代器。

特性

这个crate在根模块和copy模块中都提供了Combination特性。它为各种类型提供了基本实现,例如泛型切片、泛型Vec、元组(&'a [T], Rc<RefCell<&'a mut[&'a T]>>)和元组(&'a [T], * mut[&'a T])。它向其中添加了combination(usize)函数,并根据数据类型返回所需的迭代器。例如,对于泛型Vec返回LargeCombinationIterator,但对于(&'a [T], * mut[&'a T])返回LargeCombinationRefIter

回调函数

这个crate在两个模块中提供了4个函数,用于不同的用例。

  • large_combination函数,该函数将乘积作为回调参数返回
  • large_combination_cell函数,该函数将乘积放入函数参数中给出的Rc<RefCell<>>
  • large_combination_sync函数,该函数将乘积放入函数参数中给出的Arc<RwLock<>>
  • unsafe_large_combination不安全函数,该函数将乘积放入函数参数中给出的可变指针。根模块和copy模块之间的不同之处在于,根模块中的乘积包含&T,而copy模块包含复制的T

从数据中获取排列

此crate提供了两种不同的算法。一种生成字典序排列。另一种生成非字典序排列,但速度更快。

获取排列有三种不同的实现方式。

  • 对数据进行排列的迭代器
  • 为slice、Vec等添加功能的trait
  • 调用回调函数以返回每个排列的函数

迭代器

此crate提供了以下结构体:HeapPermutationIteratorHeapPermutationCellIterHeapPermutationRefIterXPermutationIteratorXPermutationCellIter、和XPermutationRefIter,这些结构体在根模块和copy模块中都实现了IteratorIteratorResetExactSizeIterator traits。每个结构体适用于不同的场景:

  • HeapPermutationIterator可以在顺序不重要且性能最不关心的情况下使用。这个迭代器不会以原始值作为第一个值返回。
  • XPermutationIterator可以在顺序重要且性能最不关心的情况下使用。
  • HeapPermutationCellIter可以在顺序不重要且性能和安全都重要的场景中使用。这个迭代器不会以原始值作为第一个值返回。
  • XPermutationCellIter可以在顺序重要且性能和安全都重要的场景中使用。
  • HeapPermutationRefIter可以在顺序不重要且性能至关重要,安全由调用者处理的情况下使用。这个迭代器不会以原始值作为第一个值返回。
  • XPermutationRefIter可以在顺序重要且性能至关重要,安全由调用者处理的情况下使用。根模块和copy模块的不同之处在于,在copy模块中,类型T需要实现Copy trait。每个结构体都实现了IteratorReset trait。
  • 使用 reset 函数而不是每次需要重新迭代时创建一个新的迭代器。

特性

此crate在根模块和copy模块中提供了Permutation特质。它提供了对各种类型的实现,如泛型切片、泛型Vec、元组(&'a mut[T], Rc<RefCell<&'a mut[T]>>等,用于k-排列。它添加了permutation()函数,并返回基于数据类型的所需迭代器。例如,对于泛型Vec返回HeapPermutationIterator,而对于(&'a mut [T], Rc<RefCell<&'a mut[T]>>)返回HeapPermutationCellIter。这个特质永远不会返回字典序排列的迭代器。从版本0.4.0开始,它还增加了一个好处。与构建迭代器不同,它返回一个链式迭代器。链式迭代器只是将两个迭代器链接在一起。第一个迭代器只返回一个值,原始值。第二个迭代器返回所有剩余的排列。

回调函数

此crate在根模块中提供了3个函数,它们返回非字典序排列的结果,服务于不同的用例。

  • heap_permutation函数,该函数将乘积作为回调参数返回
  • heap_permutation_cell函数,该函数将乘积返回到由函数参数给出的Rc>>
  • heap_permutation_sync函数,该函数将乘积返回到由函数参数给出的Arc>>

copy模块中没有堆排列函数族。

从版本0.4.0开始,所有堆排列族(包括所有迭代器样式)都不在copy模块中。 这是因为迭代器需要返回所有值,而HeapPermutationIterator直接使用T,而不是&T,因此T需要实现Clone。这使得在copy模块中的实现与根模块中的实现重复。

此crate在根模块和copy模块中都提供了4个函数,它们返回字典序排列的结果,服务于不同的用例。

  • x_permutation函数,该函数将字典序排列的乘积作为回调参数返回
  • x_permutation_cell函数,该函数将字典序排列的乘积返回到由函数参数给出的Rc>>
  • x_permutation_sync函数,该函数将字典序排列的乘积返回到由函数参数给出的Arc>>
  • unsafe_x_permutation不安全函数,该函数将乘积返回到由函数参数给出的可变指针

从数据中获取k-排列

有三种实现可以获取k-排列。

  • 返回产品的迭代器
  • 特质,它为某些特定元组添加功能。
  • 调用回调函数以返回产品的函数

迭代器

此crate在根模块和copy模块中提供了KPermutationIteratorKPermutationCellIterKPermutationRefIter结构体,这些结构体实现了IteratorIteratorResetExactSizeIterator特性。每个结构体适用于不同的场景:

  • KPermutationIterator在性能不是最关心的情况下可以使用。
  • KPermutationCellIter在性能和安全性都重要的情况下可以使用。
  • KPermutationRefIter在性能至关重要且安全性由调用者处理的情况下可以使用。与根模块产生的&T集合不同,copy模块产生的是复制后的T集合。每个结构体都实现了IteratorReset特性。
  • 使用 reset 函数而不是每次需要重新迭代时创建一个新的迭代器。

特性

此软件包在根模块中提供了Permutation特质,可用于对类型为(&'a [T], usize)的元组进行k-排列,对类型为(&'a [T], usize, Rc<RefCell<&'a mut [&'a T]>>)的元组进行k-排列,以及对类型为(&'a [T], usize, *mut [&'a T])的元组进行k-排列,以创建不同类型的迭代器。在copy模块中的Permutation特质可以用于对类型为(&'a [T], usize)、类型为(&'a [T], usize, Rc<RefCell<&'a mut [T]>>)和类型为(&'a [T], usize, *mut [&'a T])的元组进行k-排列,以创建不同类型的迭代器。它向其中添加了permutation()函数,并根据数据类型返回所需的迭代器。例如,对于(&'a [T], usize)返回KPermutationIterator,而对于(&'a [T], usize, *mut [&'a T])返回KPermutationRefIter

回调函数

此软件包在根模块和copy模块中提供了4个函数,用于不同的用例。

  • k_permutation函数,将乘积作为回调参数返回
  • k_permutation_cell函数,将乘积放入函数参数中给出的Rc
  • k_permutation_sync函数,将乘积放入函数参数中给出的Arc
  • unsafe_k_permutation 是一个不安全的函数,它将乘积返回到函数参数中给出的可变指针。与根模块和 copy 模块的区别在于,根模块返回一个 &T 的集合,而 copy 模块返回一个 T 的集合。

注意

带有 RefIterCellIter 后缀的结构体在每次迭代时都会返回空的项。

类似于 CartesianProductIteratorCombinationIteratorHeapPermutationIteratorKPermutationIterator 的结构体在每次迭代时都会返回一个新的 Vec。所有其他以其他方式返回值的结构体在每次迭代时都会返回一个空元组。例如,CartesianProductCellIterCombinationRefIterHeapPermutationCellIter、和 KPermutationRefIter 在每次迭代时都会返回一个空元组。它通过在实例化对象时指定的参数返回结果。例如,根模块中的 CartesianProductCellIternew 方法需要一个 Rc<RefCell<&mut [&T]>> 参数,该参数将用于存储每个笛卡尔积。需要注意的是,这些带有 RefIterCellIter 后缀的结构体在每次迭代时都会**覆盖**之前迭代的結果。如果需要保留每个迭代的每个结果,请考虑使用非后缀版本。例如,而不是使用 KPermutationRefIter 并将每个结果克隆/复制到 Vec 中,请考虑使用 KPermutationIterator

性能问题

  • 对于原始数据类型,copy 模块和根模块的性能大致相当。
  • 对于复杂数据类型,copy 模块的性能将取决于 Copy 特性的实现。
  • 一般来说,标准回调函数提供最高的吞吐量,但返回的结果是仅在回调作用域内有效的借用数据。
  • 该软件包提供了三种内置方法来共享结果。
    1. 带有 "_cell" 后缀的回调函数。
    2. 带有 CellIterRefIter 后缀的结构体。
    3. 返回自有值的迭代器。带有 "_cell" 后缀的回调函数方法比使用 CellIter 后缀方法慢 10%-20%。返回自有值的方法是最慢的,但最灵活。它比使用 CellIter 后缀对象慢 700%-1000%。然而,它仍然比使用标准回调函数然后将结果转换为自有值来共享结果要快。
  • 该软件包提供了两种内置方法将结果发送到其他线程。
    1. 带有 "_sync" 后缀的回调函数。
    2. 返回自有值的迭代器。将结果发送到其他线程最快且最安全的方法是使用返回自有值的迭代器。它比使用带有 "_sync" 后缀的回调函数快 50%-200%。

修改结果很危险

大多数共享结果使用内部可变性,因此函数/结构体只借用共享结果。它仅在将要修改结果时才会进行可变借用,并在调用回调或从迭代返回结果之前立即释放借用。这意味着结果在用户端也是可变的。然而,这样做可能会导致不可预想的行为。例如:heap_permutation_cell 函数会在 Rc<RefCell<>> 中就地交换一对元素。如果用户在结果中交换值,未来的某些排列可能重复已经返回的一个。如果用户从结果中删除一些值,它将引发恐慌,因为在 heap_permutation_cell 函数中无法识别大小变化。

将结果发送到其他线程很复杂

此crate提供了两种内置方法来跨线程发送结果。这两个用例在性能方面存在强烈对立。带有 "_sync" 后缀的回调将借用结果存储到 Arc > 中,从而降低了分配额外内存和复制/克隆结果到其中的成本。每个读取借用内容的线程可能需要额外的通信开销,特别是如果它不能错过发送给它的任何数据。在这种情况下,应用以下场景

  1. 函数生成新结果
  2. 函数通过通道通知每个线程新结果已可用。
  3. 函数阻塞,直到每个线程发送通知,表示它们都已处理完数据。

另一种方法是使用返回所有者值的迭代器,然后在每个线程上克隆该值。这更容易实现,但需要更多的内存。它将上述场景简化为

  1. 迭代器返回新结果。
  2. 它通过通道发送新数据通知每个线程。在不受控制的测试环境中观察到的性能表明,迭代器方式比回调快至少50%。

不安全的方式最快但最困难

这是因为所有以 "unsafe_" 前缀的函数和带有 RefIter 后缀的结构都通过可变指针返回结果,这使得它具有发送结果回最低的成本。它将所有其他工作留给用户去做。要使用它,确保在不再使用时释放内存,同步和初始化都做得恰当。原始变量所有者比用户和生成器都长寿。

示例

获取特定点的排列示例

要进入 'n' 个特定的字典序排列,

use permutator::get_cartesian_size;

get_cartesian_size(3, 2); // return 9.
get_cartesian_size(3, 3); // return 27.

use permutator::get_cartesian_for;

let nums = [1, 2, 3];
get_cartesian_for(&nums, 2, 0); // Return Ok([1, 1])
get_cartesian_for(&nums, 2, 3); // Return Ok([2, 1])
get_cartesian_for(&nums, 2, 8); // Return Ok([3, 3])
get_cartesian_for(&nums, 2, 9); // Return Err("Parameter `i` is out of bound")
get_cartesian_for(&nums, 4, 0); // Return Err("Parameter `degree` cannot be larger than size of objects")

use permutator::get_permutation_size;

get_permutation_size(3, 2); // return = 6
get_permutation_size(4, 2); // return = 12

use permutator::get_permutation_for;

let nums = [1, 2, 3, 4];
get_permutation_for(&nums, 2, 0); // return Result([1, 2])
get_permutation_for(&nums, 3, 0); // return Result([1, 2, 3])
get_permutation_for(&nums, 2, 5); // return Result([2, 4])
get_permutation_for(&nums, 2, 11); // return Result([4, 3])
get_permutation_for(&nums, 2, 12); // return Err("parameter x is outside a possible length")
get_permutation_for(&nums, 5, 0); // return Err("Insufficient number of object in parameters objects for given parameter degree")

多个数据集的笛卡尔积

从3个数据集获取笛卡尔积。

    use permutator::cartesian_product;

    cartesian_product(&[&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]], |product| {
        println!("{:?}", product);
    });

或者以迭代方式执行

    use permutator::CartesianProductIterator
    use std::time::Instant;
    let data : &[&[usize]] = &[&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]];
    let cart = CartesianProductIterator::new(&data);
    let mut counter = 0;
    let timer = Instant::now();

    for p in cart {
        // println!("{:?}", p);
        counter += 1;
    }

    assert_eq!(data.iter().fold(1, |cum, domain| {cum * domain.len()}), counter);
    println!("Total {} products done in {:?}", counter, timer.elapsed());

导入特质,然后跳过所有对象实例化。

    use std::time::Instant;
    use permutator::CartesianProduct;
    let data : &[&[usize]] = &[&[1, 2], &[3, 4, 5, 6], &[7, 8, 9]];
    let mut counter = 0;
    let timer = Instant::now();

    data.cart_prod.for_each(|p| {
        // println!("{:?}", p);
        counter += 1;
    });

    assert_eq!(data.iter().fold(1, |cum, domain| {cum * domain.len()}), counter);
    println!("Total {} products done in {:?}", counter, timer.elapsed());

组合迭代器示例

该结构提供两种获取组合的方法。首先它可以作为迭代器使用。其次,可以通过借用一个将存储下一个组合的可变变量手动调用 next。

// Combination iterator
use permutator::LargeCombinationIterator;
use std::time::{Instant};
let data = [1, 2, 3, 4, 5];
let combinations = LargeCombinationIterator::new(&data, 3);
let mut counter = 0;
let timer = Instant::now();

for combination in combinations {
    // uncomment a line below to print each combination
    println!("{}:{:?}", counter, combination);
    counter += 1;
}

println!("Total {} combinations in {:?}", counter, timer.elapsed());
use permutator::Combination;
use std::time::{Instant};

let data = [1, 2, 3, 4, 5];
let mut counter = 0;

let timer = Instant::now();

data.combination(3).for_each(|combination| {
    // uncomment a line below to print each combination
    println!("{}:{:?}", counter, combination);
    counter += 1;
}

println!("Total {} combinations in {:?}", counter, timer.elapsed());

迭代器风格排列示例

存在 HeapPermutationIteratorKPermutationIterator 结构,可以进行排列。下面是 HeapPermutationIterator 的示例。

use permutator::HeapPermutationIterator;
use std::time::{Instant};
let data = &mut [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
println!("0:{:?}", data);
let mut permutator = HeapPermutationIterator::new(data);
let timer = Instant::now();
let mut counter = 1;

for permutated in permutator {
    println!("{}:{:?}", counter, permutated);
    counter += 1;
}

// or use iterator related functional approach like line below.
// permutator.into_iter().for_each(|permutated| {counter += 1;});

println!("Done {} permutations in {:?}", counter, timer.elapsed());

迭代到 Rc>

存在 HeapPermutationCellIterKPermutationCellIter 结构,提供这种功能。下面是 HeapPermutationCellIter 的示例。

use permutator::HeapPermutationCellIter;
use std::cell::RefCell;
use std::rc::Rc;
use std::time::{Instant};

let data = &mut [1, 2, 3, 4];
let result = Rc::new(RefCell::new(data));
// print original data before permutation
println!("0:{:?}", &*result.borrow());
let mut permutator = HeapPermutationCellIter::new(Rc::clone(&result));
let timer = Instant::now();
let mut counter = 1;

for _ in permutator {
    // uncomment the line below to print all possible permutation
    println!("{}:{:?}", counter, &*result.borrow());
    counter += 1;
}

println!("Done {} permutations in {:?}", counter, timer.elapsed());

下面的 KPermutationCellIter 示例

use permutator::KPermutationCellIter;
use std::cell::RefCell;
use std::rc::Rc;

let k = 3;
let data = &[1, 2, 3, 4, 5];
let mut result = vec![&data[0]; k];
let shared = Rc::new(RefCell::new(result.as_mut_slice()));

let mut kperm = KPermutationCellIter::new(data, k, Rc::clone(&shared));
for _ in kperm {
    // each permutation will be stored in `shared`
    println!("{:?}", &*shared.borrow());
}

字典序排列操作

生成[1, 2, 3]、[4, 5]、[6, 7]、[8, 9]和[10]之间的有序笛卡尔积,然后从每个笛卡尔积中生成有序的k-排列,其中k=3。

use permutator::{CartesianProduct, LargeCombinationIterator, x_permutation};

let data : &[&[u8]] = &[&[1, 2, 3], &[4, 5], &[6, 7], &[8, 9], &[10]];
let k = 3;

data.cart_prod().for_each(|cp| {
    // lexicographically ordered cartesian product in `cp`
    LargeCombinationIterator::new(&cp, k).for_each(|co| {
        // lexicographically ordered combination of length 3
        x_permutation(&co, |_| true, |p| {
            // lexicographically ordered permutation
            println!("{:?}", p);
        });
    });
});

生成[1, 2, 3]、[4, 5]、[6, 7]、[8, 9]和[10]之间的有序笛卡尔积,然后从每个笛卡尔积中生成有序的k-排列,其中k=3。此外,过滤掉所有以奇数为第一个元素的排列。

use permutator::{CartesianProduct, LargeCombinationIterator, x_permutation};

let data : &[&[u8]] = &[&[1, 2, 3], &[4, 5], &[6, 7], &[8, 9], &[10]];
let k = 3;

data.cart_prod().for_each(|cp| {
    // lexicographically ordered cartesian product in `cp`
    LargeCombinationIterator::new(&cp, k).for_each(|co| {
        // lexicographically ordered combination of length 3
        x_permutation(
            &co, 
            // first bit == 1 mean it's odd number
            // notice *** in front of v ?
            // that's because the root module always return borrowed value.
            // to get rid of this, use all operation from `copy` module
            |v| ***v[0] & 1 != 1, 
            |p| 
        {
            // lexicographically ordered permutation
            println!("{:?}", p);
        });
    });
});

为各种类型添加新功能的特点

CartesianProduct特质添加了cart_prod函数。该函数没有参数。该函数返回与通过提供的结构体返回的相同迭代器,因此可以像这个示例一样使用。

use permutator::CartesianProduct;
let data : &[&[i32]]= &[&[1, 2, 3], &[4, 5]];

data.cart_prod().for_each(|p| {
    // print all product like [1, 4], [1, 5], ...
    println!("{:?}", p);
});

use permutator::CartesianProduct;
let data : &[&[i32]]= &[&[1, 2, 3], &[4, 5]];
let mut result = vec![&data[0][0]; data.len()];
let shared = Rc::new(RefCell::new(result.as_mut_slice()));
// shared can be owned by anyone who want to get cartesian product.
(&data, Rc::clone(&shared)).cart_prod().for_each(|_| {
    // print all product like [1, 4], [1, 5], ...
    println!("{:?}", &*shared.borrow());
    // and notify all owner of shared object so they know that new product is available.
});

Combination特质添加了combination函数。该函数接受1个参数。它是组合框的大小,称为kr等。该函数返回与通过提供的结构体返回的相同迭代器,因此可以像这个示例一样使用。

use permutator::Combination;
let data = [1, 2, 3, 4, 5];
data.combination(3).for_each(|comb| {
    // print all combination like [1, 2, 3], [1, 2, 4], ...
    println!("{:?}", comb);
});

use permutator::Combination;
let data = [1, 2, 3, 4, 5];
let k = 3;
let mut result = vec![&data[0]; k];
let shared = Rc::new(RefCell::new(result.as_mut_slice()));
// shared can be owned by anyone who want to get combinations.
(&data, Rc::clone(&shared)).combination(k).for_each(|_| {
    // print all combination like [1, 2, 3], [1, 2, 4], ...
    println!("{:?}", &*shared.borrow());
    // and notify all owner of shared object so they know that new combination is available.
});

Permutation特质添加了permutation函数。它原地排列[T]Vec<T>或Rc<RefCell<&mut [T]>>。该函数返回与通过此提供的结构体或此提供的结构体返回的相同迭代器,具体取决于此方法调用的类型,因此可以像这个示例这个示例或以下示例一样使用。

use permutator::Permutation;
let mut data = [1, 2, 3];
data.permutation().for_each(|p| {
    // print all the permutation.
    println!("{:?}", p);
});
// The `data` at this point will also got permuted.
// It'll print the last permuted value twice.
println!("{:?}", data);
use permutator::Permutation;
let mut data = [1, 2, 3];
let shared = Rc::new(RefCell::new(&mut data));
// shared can be owned by anyone that want to get a permutation
Rc::clone(&shared).permutation().for_each(|_| {
    // print all the permutation.
    println!("{:?}", &*shared.borrow());
    // and notify all owner of shared object so they know that new permutation is available.
});
// The same goes as previous example, the data inside shared on every owner will now has last permuted value.

或k-排列到Rc<RefCell<>>

use permutator::KPermutationCellIter;
use std::cell::RefCell;
use std::rc::Rc;

let k = 3;
let data = &[1, 2, 3, 4, 5];
let mut result = vec![&data[0]; k];
let shared = Rc::new(RefCell::new(result.as_mut_slice()));

(data, k, Rc::clone(&shared)).permutation().for_each(|_| {
    // each permutation will be stored in `shared`
    println!("{:?}", &*shared.borrow());
});

更快地共享结果的不安全方式

在某些情况下,组合结果需要共享,但安全的函数不允许您共享结果,除非为每个共享复制/克隆结果。在这种情况下,使用迭代器可能可以解决这个问题。

另一种方法是使用后缀为CellIer的结构体或具有后缀为_cell的回调函数。只要每次迭代不重复使用先前结果,并且结果所有者将结果视为不可变数据,则可以使用此方法。

另一种方法,如果安全性不如性能重要,则有一个不安全侧实现,它接受一个可变指针来存储结果。与使用后缀为CellIter的结构体和后缀为_cell的回调函数相比,需要考虑更多的事情。例如

  1. 指针需要超出整个操作
  2. 指针指向的对象需要被释放。
  3. 结果同步,包括单线程和多线程。
  4. ...
  5. 所有其他不安全的Rust条件都可能适用

示例

  • 不安全的回调函数
    use permutator::unsafe_combination;
    let data = [1, 2, 3, 4, 5];
    let r = 3;
    let mut counter = 0;
    let mut result = vec![&data[0]; r];
    let result_ptr = result.as_mut_slice() as *mut [&usize];

    unsafe {
        unsafe_combination(&data, r, result_ptr, || {
            println!("{:?}", result);
            counter += 1;
        });
    }

    assert_eq!(counter, divide_factorial(data.len(), data.len() - r) / factorial(r));
  • 不安全的迭代器对象
    use permutator::LargeCombinationRefIter;
    let data = [1, 2, 3, 4, 5];
    let r = 3;
    let mut counter = 0;
    let mut result = vec![&data[0]; r];
    let result_ptr = result.as_mut_slice() as *mut [&usize];

    unsafe {
        let comb = LargeCombinationRefIter::new(&data, r, result_ptr);
        for _ in comb {
            println!("{:?}", result);
            counter += 1;
        });
    }

    assert_eq!(counter, divide_factorial(data.len(), data.len() - r) / factorial(r));

从回调函数中与多个对象共享

一个示例展示了将新的笛卡尔积保存到Rc<RefCell<>>中,以便可以轻松地与其他对象共享。此示例使用两个工作对象,它们读取每个笛卡尔积并将其打印出来。

    use std::fmt::Debug;
    use std::rc::Rc;
    use std::cell::RefCell;

    use permutator::cartesian_product_cell;

    trait Consumer {
        fn consume(&self);
    }
    struct Worker1<'a, T : 'a> {
        data : Rc<RefCell<&'a mut[&'a T]>>
    }
    impl<'a, T : 'a + Debug> Consumer for Worker1<'a, T> {
        fn consume(&self) {
            println!("Work1 has {:?}", self.data);
        }
    }
    struct Worker2<'a, T : 'a> {
        data : Rc<RefCell<&'a mut[&'a T]>>
    }
    impl<'a, T : 'a + Debug> Consumer for Worker2<'a, T> {
        fn consume(&self) {
            println!("Work2 has {:?}", self.data);
        }
    }

    fn start_cartesian_product_process<'a>(data : &'a[&'a[i32]], cur_result : Rc<RefCell<&'a mut [&'a i32]>>, consumers : Vec<Box<Consumer + 'a>>) {
        cartesian_product_cell(data, cur_result, || {
            consumers.iter().for_each(|c| {
                c.consume();
            })
        });
    }

    let data : &[&[i32]] = &[&[1, 2], &[3, 4, 5], &[6]];
    let mut result = vec![&data[0][0]; data.len()];

    let shared = Rc::new(RefCell::new(result.as_mut_slice()));
    let worker1 = Worker1 {
        data : Rc::clone(&shared)
    };
    let worker2 = Worker2 {
        data : Rc::clone(&shared)
    };
    let consumers : Vec<Box<Consumer>> = vec![Box::new(worker1), Box::new(worker2)];
    start_cartesian_product_process(data, shared, consumers);

将数据发送到其他线程的迭代器

此示例通过使用KPermutation迭代器生成k-排列并将其发送到多个线程。

主线程会持续生成新的 k-排列并发送给每个线程,同时其他所有线程通过通道读取新的 k-排列。在这个示例中,它使用了大小为 0 的 sync_channel。缓冲区内部不保留任何内容。发送者将在接收者读取数据之前阻塞。

    use permutator::KPermutation;
    use std::sync::mpsc;
    let k = 5;
    let data : &[i32] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // workter thread 1
    let (t1_send, t1_recv) = mpsc::sync_channel::<Option<Vec<&i32>>>(0);

    thread::spawn(move || {
        while let Some(c) = t1_recv.recv().unwrap() {
            let result : Vec<&i32> = c;
            println!("Thread1: {:?}", result);
        }
        println!("Thread1 is done");
    });

    // worker thread 2
    let (t2_send, t2_recv) = mpsc::sync_channel::<Option<Vec<&i32>>>(0);
    thread::spawn(move || {
        while let Some(c) = t2_recv.recv().unwrap() {
            let result : Vec<&i32> = c;
            println!("Thread2: {:?}", result);
        }
        println!("Thread2 is done");
    });

    let channels = vec![t1_send, t2_send];
    // main thread that generate result
    thread::spawn(move || {
        use std::time::Instant;
        let timer = Instant::now();
        let mut counter = 0;
        let kperm = KPermutation::new(data, k);
        
        kperm.into_iter().for_each(|c| {
            channels.iter().for_each(|t| {t.send(Some(c.to_owned())).unwrap();});
            counter += 1;
        });
        channels.iter().for_each(|t| {t.send(None).unwrap()});
        println!("Done {} combinations in {:?}", counter, timer.elapsed());
    }).join().unwrap();

回调函数将数据发送到其他线程

本示例通过使用回调方法 k_permutation_sync 函数生成 k-排列并将其发送到多个线程。

主线程会持续生成新的 k-排列并发送给每个线程,同时其他所有线程通过通道读取新的 k-排列。在这个示例中,它使用了大小为 0 的 sync_channel。缓冲区内部不保留任何内容。发送者将在接收者读取数据之前阻塞。

    use std::sync::{Arc, RwLock};
    use std::sync::mpsc;
    use std::sync::mpsc::{Receiver, SyncSender};
    fn start_k_permutation_process<'a>(data : &'a[i32], cur_result : Arc<RwLock<Vec<&'a i32>>>, k : usize, notifier : Vec<SyncSender<Option<()>>>, release_recv : Receiver<()>) {
        use std::time::Instant;
        let timer = Instant::now();
        let mut counter = 0;
        k_permutation_sync(data, k, cur_result, || {
            notifier.iter().for_each(|n| {
                n.send(Some(())).unwrap(); // notify every thread that new data available
            });

            for _ in 0..notifier.len() {
                release_recv.recv().unwrap(); // block until all thread reading data notify on read completion
            }

            counter += 1;
        });

        notifier.iter().for_each(|n| {n.send(None).unwrap()}); // notify every thread that there'll be no more data.

        println!("Done {} combinations with 2 workers in {:?}", counter, timer.elapsed());
    }
    let k = 5;
    let data = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    let result = vec![&data[0]; k];
    let result_sync = Arc::new(RwLock::new(result));

    // workter thread 1
    let (t1_send, t1_recv) = mpsc::sync_channel::<Option<()>>(0);
    let (main_send, main_recv) = mpsc::sync_channel(0);
    let t1_local = main_send.clone();
    let t1_dat = Arc::clone(&result_sync);
    thread::spawn(move || {
        while let Some(_) = t1_recv.recv().unwrap() {
            let result : &Vec<&i32> = &*t1_dat.read().unwrap();
            // println!("Thread1: {:?}", result);
            t1_local.send(()).unwrap(); // notify generator thread that reference is no longer neeed.
        }
        println!("Thread1 is done");
    });

    // worker thread 2
    let (t2_send, t2_recv) = mpsc::sync_channel::<Option<()>>(0);
    let t2_dat = Arc::clone(&result_sync);
    let t2_local = main_send.clone();
    thread::spawn(move || {
        while let Some(_) = t2_recv.recv().unwrap() {
            let result : &Vec<&i32> = &*t2_dat.read().unwrap();
            // println!("Thread2: {:?}", result);
            t2_local.send(()).unwrap(); // notify generator thread that reference is no longer neeed.
        }
        println!("Thread2 is done");
    });

    // main thread that generate result
    thread::spawn(move || {
        start_k_permutation_process(data, result_sync, k, vec![t1_send, t2_send], main_recv);
    }).join().unwrap();

从 0.3.x 到 0.4 的破坏性更改

特质 Permutation、函数 heap_permutationheap_permutation_cellheap_permutation_sync 现在首先返回未排列的值,而不是首先返回排列好的值。

    use permutator::{
        heap_permutation,
        Permutation
    };
    let arr = &[1, 2, 3];
    // no longer need to `println!("{:?}", arr);` first

    heap_permutation(arr, |perm| {
        // now it print [1, 2, 3], [2, 1, 3], ...
        println!("{:?}", perm);
    });

    arr.permutation().for_each(|perm| {
        // now it print [1, 2, 3], [2, 1, 3], ...
        println!("{:?}", perm);
    });

所有对 permutator::copy::::HeapPermutationIterator 的使用应变为 permutator::HeapPermutationIterator

从 0.2.x 到 0.3.x 的破坏性更改

根模块中的 combinationcopy 模块现在返回 "Large" 组合家族。

理由

所有的 "Gosper" 组合家族都被 "Large" 组合家族取代。它还没有标记那些家族为已弃用。只有 Rust 文档声明它已被弃用。这是因为弃用的原因是这个包中的实现效率低下。每次 gosper 算法生成新值时,都会复制所有值或为该组合创建新的引用。相比之下,"Large" 家族只在组合位置发生变化时复制或创建新的引用。这使得 "Large" 家族的组合速度比 "Gosper" 快 10 倍以上。因此,除非有更高效的实现,否则在某些时候,"Gosper" 家族函数可能会正式标记为已弃用。还有 "Gosper" 组合家族的限制,它可以生成与支持快速位操作变量的位数一样多的组合,而 Rust 目前受限于 128 位,源数据大小最多为 128 个元素切片。在实践中,这在大多数情况下已经足够。但在某些边缘情况下,"Large" 组合家族允许在数据上生成最多为 usize 最大值的组合,32 位平台上为 2^32,64 位平台上为 2^64。如果源数据是字典序排列的,"Large" 组合家族的结果也将是字典序排列的。

内部,所有 k-排列家族都已迁移到使用 "Large" 组合家族,而不是 "Gosper" 家族。

从 0.2.x 到 0.3.x 的迁移指南

  • combination* 函数变为 large_combination* 函数。
  • GosperCombination* 结构体变为 LargeCombination* 结构体。例如
    // This line will be error in 0.3.0
    let combinations : GosperCombinationIterator = [1, 2, 3, 4, 5].combination(3);

变为

    let combinations : LargeCombinationIterator = [1, 2, 3, 4, 5].combination(3);

从 0.1.6 到 0.2.0 的破坏性更改

0.2.0 对整个包进行了重大改进,以使各个功能的使用案例更加一致。现在只有两种主要的不同风格。1. 回调函数 2. 迭代器对象。迭代器对象有两种子风格。1. 平凡 Iterator 风格。2. 共享 Iterator 风格。共享 Iterator 风格具有类似回调风格的对等品的安全和不安全共享。需要重命名每个结构体。添加了一个额外的特质和一些其他类型。更多关于破坏性更改的详细信息

  • 迭代器风格 next_into_cell 已重构为其自己的结构体。现在它可以像简单的迭代器一样使用,只是返回值的方式略有不同。
  • 将接受&mut[&T]参数的模拟迭代器风格的next重构为独立的struct。现在可以使用类似于简单迭代器的模式,以略不同的方式返回值。
  • CartesianProduct struct已重命名为CartesianProductIterator
  • HeapPermutation struct已重命名为HeapPermutationIterator
  • GosperCombination struct已重命名为GosperCombinationIterator
  • KPermutation struct已重命名为KPermutationIterator
  • CombinationPermutation traits现在分别使用关联类型combinatorpermutator来定义用于在切片/数组/Vec和Rc>>上执行组合/排列的struct,而不是使用固定的返回类型。现在,所有特性都返回一个实现了Iterator特质的对象。它并不约束在Iterator中定义的关联类型Item。现在特质采用<'a>生命周期参数,不再采用泛型类型T。函数combination的签名从combination(&mut self)更改为combination(&'a mut self)。函数permutation的签名从permutation(&mut self)更改为permutation(&'a mut self)

从0.1.6迁移到0.2.0的指南

  • 模拟迭代器风格的函数现在移动到它自己的迭代器struct中,其名称后缀为"RefIter"。所有这些现在都成为unsafe来使用。以下是这样一些struct的列表。
    • CartesianProductRefIter
    • CombinationRefIter
    • GosperCombinationRefIter
    • HeapPermutationRefIter
    • KPermutationRefIter
  • 所有next_into_cell函数现在移动到它自己的迭代器struct中,其名称后缀为"CellIter"。以下是这样一些struct的列表。
    • CartesianProductCellIter
    • CombinationCellIter
    • GosperCombinationCellIter
    • HeapPermutationCellIter
    • KPermutationCellIter
  • 重命名所有struct。以下是被重命名的struct。
    • CartesianProduct struct已重命名为CartesianProductIterator
    • HeapPermutation struct已重命名为HeapPermutationIterator
    • GosperCombination struct已重命名为GosperCombinationIterator
    • KPermutation struct已重命名为KPermutationIterator
  • CombinationPermutation traits的其他类型实现需要定义关联类型,以及将combinationpermutation函数签名从接受&self更改为&'a self,并将&mut self更改为&'a mut self

示例:新的Permutation trait现在看起来像这样。

// instead of this old implementation
// impl Permutation<T> for [T] {
//     fn permutation(&mut self) -> HeapPermutation<T> {
//          HeapPermutation {
//              c : vec![0; self.len],
//              data : self,
//              i : 0
//          }
//     }
// }
// now it become..
impl<'a, T> Permutation<'a> for [T] where T : 'a {
    type Permutator = HeapPermutation<'a, T>; // This struct implement `Iterator`

    fn permutation(&'a mut self) -> HeapPermutation<T> {
        HeapPermutation {
            c : vec![0; self.len()],
            data : self,
            i : 0
        }
    }
}

新增的复杂性使这个特性适用于更广泛的类型。这里是对 Rc<RefCell<&mut [T]>> 的新实现,它返回 HeapPermutationCell

impl<'a, T> Permutation<'a> for Rc<RefCell<&'a mut[T]>> where T :'a {
    type Permutator = HeapPermutationCell<'a, T>; // This struct also implement `Iterator`

    fn permutation(&'a mut self) -> HeapPermutationCell<T> {
        HeapPermutationCell {
            c : vec![0; self.borrow().len()],
            data : Rc::clone(self),
            i : 0
        }
    }
}

依赖项

~475KB