生成所有多集大小为 n 的分区的算法

2024-05-15

我一直在试图找出一种方法来生成多重集的所有不同的大小为 n 的分区,但到目前为止却空手而归。首先让我展示一下我想要实现的目标。

假设我们有一个输入向量uint32_t:

std::vector<uint32_t> input = {1, 1, 2, 2}

假设我们想要创建所有不同的 2 大小分区。其中只有两个,即:

[[1, 1], [2, 2]], [[1, 2], [1, 2]]

请注意,顺序并不重要,即以下所有内容都是重复的、不正确的解决方案。

  • 重复,因为排列组内的顺序并不重要:

    [[2, 1], [1, 2]]
    
  • 重复,因为组的顺序并不重要:

    [[2, 2], [1, 1]]
    

顺便说一句,不是某种家庭作业。我在工作中编码时遇到了这个问题,但现在出于个人兴趣,我想知道如何处理这个问题。与工作相关的问题的参数足够小,生成几千个重复的解决方案并不重要。

当前解决方案(生成重复项)

为了说明我不仅仅是在没有尝试提出解决方案的情况下提出问题,让我尝试解释我当前的算法(与多重集一起使用时会生成重复的解决方案)。

它的工作原理如下:状态有一个位集,每个分区块的 n 位设置为 1。位集的长度是size(input) - n * index_block(),例如如果输入向量有 8 个元素且 n = 2,则第一个分区块使用 8 位位集,其中 2 位设置为 1,下一个分区块使用 6 位位集,其中 2 位设置为 1,依此类推。

通过按顺序迭代每个位集并提取索引等于当前位集中 1 位位置的输入向量的元素,从这些位集创建分区。

为了生成下一个分区,我以相反的顺序迭代位集。计算下一个位集排列(使用与 Gosper 的 hack 相反的方法)。如果当前位集中的第一位未设置(即未选择向量索引 0),则该位集将重置为其起始状态。强制始终设置第一位可以防止在创建大小为 n 的集合分区时生成重复项(上面显示的第二种重复项)。如果当前位集等于其起始值,则对前一个(较长)位集重复此步骤。

这对于集合来说效果很好(而且非常快)。然而,当与多重集一起使用时,它会生成重复的解决方案,因为它不知道两个元素在输入向量中出现多次。这是一些示例输出:

std::vector<uint32_t> input = {1, 2, 3, 4};
printAllSolutions(myCurrentAlgo(input, 2));
=> [[2, 1], [4, 3]], [[3, 1], [4, 2]], [[4, 1], [3, 2]]

std::vector<uint32_t> input = {1, 1, 2, 2};
printAllSolutions(myCurrentAlgo(input, 2));
=> [[1, 1], [2, 2]], [[2, 1], [2, 1]], [[2, 1], [2, 1]]

最后一个(重复)解决方案的生成仅仅是因为算法不知道输入中的重复项,它在两个示例中生成完全相同的内部状态(即选择哪些索引)。

想要解决方案

我想现在我想要得到的结果已经很清楚了。为了完整起见,它看起来有点如下:

std::vector<uint32_t> multiset = {1, 1, 2, 2};
MagicClass myGenerator(multiset, 2);
do {
  std::vector<std::vector<uint32_t> > nextSolution = myGenerator.getCurrent();
  std::cout << nextSolution << std::endl;
} while (myGenerator.calcNext());
=> [[1, 1], [2, 2]]
   [[1, 2], [1, 2]]

IE。代码的工作原理有点像std::next_permutation,通知它已经生成了所有解决方案,并回到了“第一个”解决方案(对于您想要使用的第一个定义,可能按字典顺序,但不需要如此)。

我发现的最相关的算法是 Knuth 的《计算机编程艺术》第 4 卷第 1 部分第 7.2.1.5 节(第 430 页)中的算法 M。但是,这会生成所有可能的多重集分区。书中还有一个关于如何修改Alg的练习(7.2.1.5.69,第778页的解决方案)。 M,以便仅生成最多具有 r 个分区的解决方案。但是,这仍然允许不同大小的分区(例如[[1, 2, 2], [1]]r = 2 时将是有效输出)。

关于如何解决这个问题有什么想法/技巧/现有算法吗?请注意,该解决方案应该是高效的,即跟踪所有先前生成的解决方案,确定当前生成的解决方案是否是一种排列,如果是,则跳过它,这是不可行的,因为解决方案空间会随着更长的输入而爆炸。重复。


逐一分配元素的递归算法可以基于一些简单的规则:

  • 首先对不同元素进行排序或计数;它们不必按任何特定顺序,您只需将相同的元素分组在一起即可。 (此步骤将简化以下一些步骤,但可以跳过。)
   {A,B,D,C,C,D,B,A,C} -> {A,A,B,B,D,D,C,C,C}  
  • 从一个空的解决方案开始,并使用以下规则一一插入元素:
   { , , } { , , } { , , }  
  • 在插入元素之前,找到重复的块,例如:
   {A, , } { , , } { , , }  
                    ^dup^

   {A, , } {A, , } {A, , }  
            ^dup^   ^dup^
  • 将元素插入到每个具有可用空间的非重复块中:
   partial solution: {A, , } {A, , } { , , }  
                              ^dup^

   insert element B: {A,B, } {A, , } { , , }  
                     {A, , } {A, , } {B, , }  
  • 如果相同的元素已存在,请勿将新元素放在它之前:
   partial solution:  {A, , } {B, , } { , , }  
   insert another B:  {A,B, } {B, , } { , , }  <- ILLEGAL  
                      {A, , } {B,B, } { , , }  <- OK
                      {A, , } {B, , } {B, , }  <- OK
  • 当插入一个有另外 N 个相同元素的元素时,请确保在当前元素后面留下 N 个空位:
   partial solution:  {A, , } {A, , } {B,B, }  
   insert first D:    {A,D, } {A, , } {B,B, }  <- OK  
                      {A, , } {A, , } {B,B,D}  <- ILLEGAL (NO SPACE FOR 2ND D)  
  • 最后一组相同的元素可以一次性插入:
   partial solution:  {A,A, } {B,B,D} {D, , }  
   insert C,C,C:      {A,A,C} {B,B,D} {D,C,C}  

所以算法会是这样的:

// PREPARATION  
Sort or group input.              // {A,B,D,C,C,D,B,A,C} -> {A,A,B,B,D,D,C,C,C}  
Create empty partial solution.    // { , , } { , , } { , , }  
Start recursion with empty partial solution and index at start of input.  

// RECURSION  
Receive partial solution, index, group size and last-used block.  
If group size is zero:  
    Find group size of identical elements in input, starting at index.  
    Set last-used block to first block.  
Find empty places in partial solution, starting at last-used block.  
If index is at last group in input:  
    Fill empty spaces with elements of last group.
    Store complete solution.
    Return from recursion.
Mark duplicate blocks in partial solution.  
For each block in partial solution, starting at last-used block:  
    If current block is not a duplicate, and has empty places,  
    and the places left in current and later blocks is not less than the group size:
        Insert element into copy of partial solution.
        Recurse with copy, index + 1, group size - 1, current block.

我测试了该算法的简单 JavaScript 实现,它给出了正确的输出。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

生成所有多集大小为 n 的分区的算法 的相关文章

  • 如何提高洪水填充例程的性能?

    我正在我的应用程序中实现四路洪水填充 伪代码如下 Flood fill node target color replacement color 1 If the color of node is not equal to target co
  • 按步长值变化对数组中的数字进行分组

    我有一个像 101 107 106 199 204 205 207 306 310 312 312 314 317 318 380 377 379 382 466 469 471 472 557 559 562 566 569 在这个数组中
  • 字符串的渐进单词组合

    我需要获得字符串的渐进单词组合 例如 这是字符串 输出 这是字符串 这是 这个字符串 是字符串 这 是 细绳 你知道类似的算法吗 我需要php语言 谢谢 这是解决您问题的简单代码 我将每个字符串递归地连接到数组中的其余字符串 string
  • 整数除法性质

    下面的整数算术性质成立吗 m n l m n l 起初我以为我知道答案 不成立 但现在不确定 它适用于所有数字还是仅适用于某些条件 即n gt l 该问题涉及计算机算术 即q n m q m n 忽略溢出 Case1 assume m kn
  • 逐字遍历句子

    如何逐字遍历任何给定的句子 java中有内置函数吗 我不知道如何开始 像这样的事情 String sentence Your sentence here String words sentence split s splits by whi
  • O(n^2) 与 O (n(logn)^2)

    时间复杂度是O n 2 or O n logn 2 better 我知道当我们简化它时 它就变成了 O n vs O logn 2 and logn lt n 但是关于logn 2 n is only less than log n 2 f
  • 将数字 n 拆分为 k 个不同数字的总和

    我有一个数字 n 我必须将它分成 k 个数字 使得所有 k 个数字都是不同的 k 个数字的总和等于 n 并且 k 最大 例如 如果 n 为 9 则答案应为 1 2 6 如果 n 为 15 则答案应为 1 2 3 4 5 这就是我尝试过的 v
  • 比较两棵树的伪代码

    这是我遇到过几次的问题 并且不确信我使用了最有效的逻辑 例如 假设我有两棵树 一棵是文件夹结构 另一棵是该文件夹结构的内存 模型 我希望比较两棵树 并生成一棵树中存在的节点列表 而不是另一棵树中存在的节点列表 反之亦然 是否有公认的算法来处
  • 带有元数据的 scipy kdtree

    我目前正在寻找一种方法来构建几个 kd 树以快速查询一些 n 维数据 但是 我对 scipy KD 树算法有一些问题 我的数据包括id gt data somedata coordinate x y 我希望能够基于坐标和 k 最近邻居的 i
  • 从关系数据库中“区分”对象

    我们的 win32 应用程序根据 MySQL 关系数据库中多个表中的数据组装对象 对于这样的对象 多个修订版本存储在数据库中 当存储某些内容的多个修订版本时 迟早您会问自己这样的问题 您是否可以想象两个修订版本之间的差异 所以我的问题是 比
  • 如何确定字符串的最小公约数?

    我在面试时被问到以下问题 并被它难住了 我遇到的部分问题是要下定决心要解决什么问题 起初我并不认为这个问题在内部是一致的 但后来我意识到它要求你解决两个不同的问题 第一个任务是弄清楚一个字符串是否包含另一个字符串的倍数 但第二个任务是在两个
  • Yegge 的原型模式示例如何处理实例变量?

    我喜欢史蒂夫 耶吉的原型模式示例 http steve yegge blogspot com 2008 10 universal design pattern html并决定快速制作一个概念验证示例 不过 我并没有真正考虑清楚 虽然它非常适
  • 查找两个大小为 n 的数组中第 n 大数的算法

    我有这个问题 给定两个大小为 n 的排序列表 存储在数组中 找到 O log n 计算并集中第 n 大元素的算法 两个列表 我可以看到这里可能有一个技巧 因为它需要第 n 个最大的元素 并且数组的大小也是 n 但我不知道它是什么 我在想我可
  • 打印从 1 到 100 的质数

    此 C 代码打印出以下素数 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 但我不认为这就是我的书所希望的写作方式 它提到了一些关于数字的平方根的内容
  • 让电脑实现360度=0度,旋转炮塔

    我正在制作一个游戏 其中有一个计算机控制的炮塔 炮塔可360度旋转 它使用 trig 找出枪瞄准所需的角度 obj deg 并将枪的当前角度存储在 gun deg 下面的代码以设定的速度旋转枪 if objdeg gt gundeg gun
  • RNG 技术的可移植性和可重复性

    我可以使用两种方法之一来创建一个伪随机数序列 该序列具有两个重要特征 1 它可以在不同的机器上重现 2 该序列永远不会重复范围内的数字 直到所有数字都被发出 我的问题是 这两种方法在可移植性 操作系统 Python 版本等 方面是否存在潜在
  • 如何从列中创建对称矩阵?

    例如 我想转动以下列 90 175 600 650 655 660 代入矩阵 90 175 600 650 655 660 175 600 650 655 660 655 600 650 655 660 655 650 650 655 66
  • 插入排序 - 如何接受输入并打印排序后的数组

    我试图做一个插入排序程序 它接受任何数据类型 Int Double String 然后打印排序后的数组 我知道我的代码可以工作 但我无法找出真正的问题 import java util public class MyInsertionSor
  • Swift 中计算只读属性与函数

    在 Swift WWDC 简介会话中 只读属性description被证明 class Vehicle var numberOfWheels 0 var description String return numberOfWheels wh
  • 获取Windows下新线程/删除线程的通知

    创建 DLL 时 您可以在 DllMain 函数 DLL THREAD ATTACH DLL THREAD DETACH 中获取有关新线程 退出线程的通知 有没有办法在 非托管 可执行文件中从 Windows 获取这些或等效通知 是的 在您

随机推荐