【问题标题】:Algorithm to generate 2 combinations from 1 list从 1 个列表中生成 2 个组合的算法
【发布时间】:2016-10-19 14:16:18
【问题描述】:

我需要一个算法,它会从 6 个字符的列表中生成 2 个 3 个字符的列表。例如,我有一个包含 [a,b,c,d,e,f] 的数组,我想创建诸如 [a,b,c][d,e,f] 之类的组合。

我需要得到 2 乘以 3 个字符的所有可能组合。

约束

  • 两个组合不能相同。我的意思是我不能以不同的顺序将 2 次相同的 3 个字符放在一起。因此,如果[a,b,c][d,e,f] 已经生成,我不能将[b,a,c][f,d,e] 作为另一种可能性或[f,d,e][b,a,c]

如果算法是在 javascript 中,我最理想,但我愿意接受任何其他建议(我可以阅读代码,即使我不知道我最有可能理解算法的语言一种我不懂的语言)。我看过很多关于排列和组合的帖子和问题,但似乎没有一个是我想要的,我似乎无法弄清楚如何修改它们以适应我正在寻找的东西。

【问题讨论】:

  • 这听起来很像家庭作业问题。考虑自己如何解决这个问题。你有一个包含 6 个元素的集合。你能把它们分类吗?如果是这样,从 6 中选择的 3 的排列是 120 种组合,这不是极端的。因此,如果您生成一个列表,然后对其进行排序,您可以与现有列表进行比较并获取同构排列集。 (您也可以通过几种方式进一步优化)编辑:您必须扩展这个想法以收集两个列表
  • 我知道这听起来像是家庭作业,但更像是自学作业。我需要这样做以轻松地为运动时间表生成团队,其中 2 个团队永远不会相同。学校或工作不要求我这样做。只是调度的负责人是手工做的,我想知道如何自动生成这样的调度来节省时间。我主要是出于好奇和更好地了解此类算法而询问。
  • 这两个组合是否必须包含唯一字符?也就是可以[a,b,c][c,d,e]吗?

标签: javascript arrays algorithm combinatorics


【解决方案1】:

尝试结合使用树和队列/堆栈数据结构来实现这种行为。 算法应该是简单相关的遍历树的样子。

【讨论】:

  • 我得去读一下树形数据结构,我不是很熟悉
【解决方案2】:

给定一个包含元素 {1, …, 6} 的输入集,您需要 1. 生成 {2, …, 6}2. 的所有不同的 3 子集(余数)与输入集:

function* subsets(input, length, start = 0) {
  if (start >= input.length || length < 1) yield [new Set(), new Set(input)];
  else {
    for (let i = start; i <= input.length - length; ++i) {
      let first = input[i];
      for ([subsubset, remainder] of subsets(input, length - 1, i + 1)) {
        subsubset.add(first);
        remainder.delete(first);
        yield [subsubset, remainder];
      }
    }
  }
}

let input = ['a', 'b', 'c', 'd', 'e', 'f'];

for ([subset, remainder] of subsets(input, 3, 1)) {
  console.log([...subset].join(''), [...remainder].join(''));
}

这与@Oriol 概述的算法非常相似。另请参阅@Dante 对为什么我们不将第一个元素包含在 3-subsets 中的详细解释(为了防止双精度,它包含在其余部分中!)。

如果您的输入元素不是唯一的,您需要在输入的索引而不是其值上运行上述算法。

如果您输入的大小大于 6,您还需要与其余部分的所有子集组合:

for ([subset, remainder] of subsets(input, 3, 1)) {
  for ([remainder_subset, _] of subsets([...remainder], 3)) {
    console.log([...subset], [...remainder_subset]);
  }
}

【讨论】:

    【解决方案3】:

    我能想到的蛮力解决方案:-

    第一个位置可以有 6 个选项中的任何一个。 第二个位置可以有五分之一 第三个可以是四分之一。

    rest 3 将进入另一个列表。所以基本上需要做的是:-

    继续生成所有 3 种尺寸组合并将它们放入哈希图中。在生成新组合时检查它是否存在于地图中。如果是,则跳过它,否则将其添加到地图中。有效的方法可能是存储组合的排序顺序。

    希望这会有所帮助!

    【讨论】:

      【解决方案4】:

      很简单:

      1. 迭代{2, …, 6} 的所有 2 个子集,例如使用回溯。
      2. 对于每个子集S,考虑H1 = S ⋃ {1}H2 = {1, …, 6} ∖ H1
      3. 当前组合包括A 中由H1 中的索引给出的项目,以及A 中由索引H2 给出的项目

      由于这会强制执行 1 ∈ H1,因此您不会获得像 [a,b,c][d,e,f][d,e,f][a,b,c] 这样的重复。

      由于集合没有顺序,因此您不会获得像 [a,b,c][d,e,f][b,a,c][f,d,e] 这样的重复。

      【讨论】:

      • 简洁明了是最好的
      【解决方案5】:

      避免[a,b,c]&lrarr; [a,c,b] 重复,从“a”到“d”选择第一个玩家,第一个玩家之后的第二个玩家到“e”,第二个玩家之后的第三个玩家到“f”。

      避免[a,b,c][d,e,f]&lrarr; [d,e,f][a,b,c] 重复,将玩家“a”留在第一队。

      这是最简单的代码:(它为一个团队挑选人员;第二个团队显然由其他 3 人组成)

      function distribute(n) {
          for (var i = 1; i < 5; i++) {
              for (var j = i + 1; j < 6; j++) {
                  document.write("team 1: " + n[0] + ", " + n[i] + ", " + n[j] + "<br>");
              }
          }
      }
      distribute(["a","b","c","d","e","f"]);

      【讨论】:

        【解决方案6】:

        在这个问题中要观察的主要内容是:我们不需要构建第二个包含三个项目的列表,一旦我们构建了包含三个项目的第一个列表,第二个列表只是第一个列表的补充 .

        示例

        假设我们选择了3个项目为[a , b , c],第二个列表简单地得到了

        [a , b , c , d , e , f] - [a , b , c] = [d , e , f]

        所以我们将只关注第一个列表(list 1)。

        让我们专注于任何单个元素,比如a(随意)。我们观察到:

        每个可能的组合都将始终将 a 作为某个列表的成员,在两个列表中,并且无论哪个包含 a,我们称之为列表 1。让我们选择其他list 1的成员。

        我们选择了第一个成员。第二个成员可以从[b , c , d , e , f] 中选择。

        在我们算法的第一次迭代中,我们将选择第二个成员为b。第三个成员可以有四种可能性。这意味着我们必须从[c , d , e , f] 中选择一个元素。一旦我们选择了第三个成员,我们的list 1 就完成了,list 2 也是如此。当第三个成员考虑了所有 4 种可能性时,第一次迭代就完成了。

        在我们算法的第二次迭代中,我们将选择第二个成员为c。现在第三个成员只能有 3 种可能性,NOT 4,因为我们已经考虑了 b。三种可能性将是[d , e , f]。一旦我们选择了第三个成员,我们的list 1 就完成了,list 2 也是如此。当第三个成员考虑了所有 3 种可能性时,第二次迭代就完成了。

        类似地,在第三次迭代中,我们的第二个成员将是 d,而第三个成员 [e , f] 将有 2 个可能性。

        同样,在第四次迭代中,我们的第二个成员将是e,而第三个成员只有一种可能性,即f

        注意

        我们不必担心我们的算法会有多少次迭代,我们算法的终止条件将是可能的组合总数,即6choose3/2 = 10

        使用的数据结构

        我们将使用长度为 6 的位数组arr,并且对于list 1 的每个组合,我们将1 放置在相应的位置。因为它会被初始化为0,所以对于每一个0,都表示list 2的成员。

        意思是list 1 = [a , b , c],然后arr = [1 , 1 , 1 , 0 , 0 , 0]

        我们算法的伪代码如下:

        Initialize an array of character of length 6 namely set = [a , b , c , d , e , f]
        
        Initialize an array of integer(or boolean to save space cause we will be storing only 0 
        and 1) of length 6 namely arr,initialized to 0.
        
        int x = 10, total = 0,i = 1.
        arr[0] = 1// because element a belongs to list 1
        
        while(total < x)
        {
         arr[i] = 1
        
         for(j = i + 1 to 5)
         {
          arr[j] = 1
          list 1 is made of all elements set[k] such that arr[k] = 1
          list 2 is made of all elements set[k] such that arr[k] = 0
          arr[j] = 0
         }
        
         total = total + 5 - i 
         arr[i] = 0
         i = i + 1
        }
        

        伪代码很容易理解,应该很容易编码。把它当作一个练习。

        【讨论】:

        • 解决了我的问题,非常感谢您的详细解答
        猜你喜欢
        • 1970-01-01
        • 2013-04-14
        • 1970-01-01
        • 2021-01-31
        • 1970-01-01
        • 2013-06-16
        • 1970-01-01
        相关资源
        最近更新 更多