【问题标题】:Finding all possible value combinations between two arrays查找两个数组之间所有可能的值组合
【发布时间】:2016-05-24 00:12:33
【问题描述】:

我有两个字符串数组,不一定具有相同的长度,我想从数组中找到两个值之间所有可能的组合“集合”,而不是从任何一个数组中重复。
例如,给定数组:
{ "A1", "A2", "A3" }
{ "B1", "B2" }
我想要的结果是以下几组:
{ ("A1", "B1"), ("A2", "B2") }
{ ("A1", "B1"), ("A3", "B2") }
{ ("A1", "B2"), ("A2", "B1") }
{ ("A1", "B2"), ("A3", "B1") }
{ ("A2", "B1"), ("A3", "B2") }
{ ("A2", "B2"), ("A3", "B1") }

我的总体方向是创建递归函数,该函数将两个数组作为参数并一次删除每个“选择”字符串,调用自身直到任一数组为空,但是我有点担心性能问题(我需要在大约 1000 对字符串数组上运行此代码)。
谁能指导我找到一种有效的方法来做到这一点?

【问题讨论】:

  • 答案中的一对是:{("A1", "B1"), ("A2", "B2")};这是另一个有效的对还是重复的:{("A2", "B2"), ("A1", "B1")}
  • “高效”是什么意思?给定两个大小为nmn <= m 的数组,将有m*...*(m-n+1) 集合。
  • @C.Evenhuis 组合之间的顺序无关紧要,我只需要唯一的集合
  • @Mr E 我通常对递归算法持保留态度,它们可以快速实现但性能较差,因此我的评论

标签: c# algorithm combinatorics


【解决方案1】:

将两个数组视为表格的边可能会有所帮助:

        A1      A2      A3
---+-------+-------+-------+
B1 | B1,A1 | B1,A2 | B1,A3 |
---+-------+-------+-------+
B2 | B2,A1 | B2,A2 | B2,A3 |
---+-------+-------+-------+

这意味着一个循环嵌套在另一个循环中,一个循环用于行,另一个循环用于列。这将为您提供初始配对:

{B1,A1} {B1,A2} {B1,A3} {B2,A1} {B2,A2} {B2,A3}

然后就是建立初始集合的组合。您可以类似地使用行和列的一组对来可视化组合:

      B1,A1 B1,A2 B1,A3 B2,A1 B2,A2 B2,A3
-----+-----+-----+-----+-----+-----+-----+
B1,A1|     |  X  |  X  |  X  |  X  |  X  |
-----+-----+-----+-----+-----+-----+-----+
B1,A2|     |     |  X  |  X  |  X  |  X  |
-----+-----+-----+-----+-----+-----+-----+
B1,A3|     |     |     |  X  |  X  |  X  |
-----+-----+-----+-----+-----+-----+-----+
B2,A1|     |     |     |     |  X  |  X  |
-----+-----+-----+-----+-----+-----+-----+
B2,A2|     |     |     |     |     |  X  |
-----+-----+-----+-----+-----+-----+-----+
B2,A3|     |     |     |     |     |     |
-----+-----+-----+-----+-----+-----+-----+

同样,这可以通过一对嵌套循环来完成(提示:你的内循环的范围将由外循环的值决定)。

【讨论】:

  • 据我了解,如果不假设初始两个数组之一的长度为 2,则使用一对嵌套循环无法生成完整的集合。一般来说,解决这个问题的一个更明智的方法是使用深度优先搜索。
  • 另一种解决方案是固定较小数组的顺序,生成较长的所有排列,并将两个数组的相同索引匹配到较小数组的长度。跨度>
  • 请参阅我的回答以获得澄清。
  • @JingjieZheng 两个初始数组可以是任意长度。您可以通过在表格中适当添加行/列来可视化这一点。
【解决方案2】:

你的问题等价于下面的问题:

问题陈述:
给定两个向量A,大小为nB,大小为m,其中n <= m.
A = [0, 1, 2, ..., n - 1].
B = [0, 1, 2, ..., m - 1].
AB 中查找所有可能的injective and non-surjective mappings

解决方案
由于 A 的大小更小,在一个映射中,对应的数量等于 A 的大小,即 n。

然后我们生成B所有可能的排列,使得每个排列中的前n个元素可以与A中的元素一一对应。

前几个排列和映射如下:

实施:

class Helper {
public:
    /**
     * @brief generateArray
     * @param size
     * @return A vector [0, 1, ..., size - 1]
     */
    vector<int> generateArray(int size) {
        vector<int> arr;
        for (int i = 0; i < size; ++i) {
            arr.push_back(i);
        }
        return arr;
    }

    /**
     * @brief generateMatches
     * @param n, cardinality of the vector X, where X = [0,1, ..., n - 1].
     * @param m, cardinality of the vector Y, where Y = [0,1, ..., m - 1].
     * @return All possible injective and non-surjective mappings 
     * from the smaller vector to the larger vector.
     */
    vector<vector<pair<int, int> > > generateMatches(int n, int m) {
        // Deal with n > m. Swap back when generating pairs.
        bool swapped = false;
        if (n > m) {
            swapped = true;
            swap(n, m);
        }
        // Now n is smaller or equal to m
        vector<int> A = generateArray(n);
        vector<int> B = generateArray(m);
        vector<vector<pair<int, int> > > matches;
        // Generate all the permutations of m
        do {
            vector<pair<int, int> > match;
            for (int i = 0; i < n; ++i) {
                pair<int, int> p;
                if (swapped) {
                    // Swap back to the original order.
                    p = make_pair(A[i], B[i]);
                } else {
                    p = make_pair(B[i], A[i]);
                }
                match.push_back(p);
            }
            matches.push_back(match);
                // Generate next permutation.
        } while(next_permutaion(B.begin(), B.end())); 
        return matches;
    }
};

【讨论】:

    【解决方案3】:

    很简单的方法就是

    string[] arr = new string[3];
            string[] arr1 = new string[4];
            string[] jointarr = new string[100];
    
            for (int i = 0; i < arr.Length; i++)
            {
                arr[i] = "A" + (i + 1);
            }
    
            for (int i = 0; i < arr1.Length; i++)
            {
                arr1[i] = "B" + (i + 1);
            }
    
            int k=0;
            for (int i = 0; i < arr.Length; i++)
            {
                for (int j = 0; j < arr1.Length; j++)
                {
                    jointarr[k] = arr[i] + " " + arr1[j];
                    k++;
                }
            }
    

    【讨论】:

    • 虽然这会给我所有可能的字符串对,但我需要的是这些对的独特组合
    【解决方案4】:

    当我看到一个填字游戏风格的谜题时,我研究了这个挑战,其中每个方块都有一个数字,你必须找到哪个字母对应哪个数字才能使单词正确。知道我正在触及已经给出的答案,我将尝试总结问题,并展示如何递归解决。

    在 x-word 的情况下,较小的数组 A 是一系列数字,而大数组 B 包含字母表中的字母。问题是将每个数字分配给一个字母,并找到所有可能的组合。通常我们有:

    A={1,2,...m} and B={1,2,....n}     n>=m
    

    对于A(i)B(j)对,每个可能的结果都可以写成一个包含m个元素的数组C,其中元素i带有值j。排列的总数,即 C 数组,是 n(n-1).....(n-m+1) 或更简洁的写法:n!/(m+1)!

    这个数字源于认为当 A 的第一个元素与 B 中的任何元素配对时,A 的第二个元素可以与任何元素配对除了被第一个,以此类推。

    我们可以通过以下-code来实现:

    for i= 1 to n
       C(1)=B(i)
       for j= 1 to n-1
          C(2)=B'(j)        '  B' is B with element i removed
             .........
              for x = 1 to n-m
                 C(m)=B'''(x)   'B is now reduced with (m-1) elements
              next x
    

    为了直观起见,我使用基于 1 的数组。

    此代码不适用于任意长度的 A,并且对于较大的 m 编写起来会很麻烦,因此我们最好使用可以调用自身的过程 AllPairs 来递归:

       AllPairs (A,B,C)
        if Size(A)>1             ' check no of elements in A        
          for i=1 to Size(B)
           C(Size(C)-Size(A)+1)= B(i)
           A'=Remove element 1 from A
           B'=Remove element i from B
           Call AllPairs(A',B',C)     'recursive call
          Next i
        else                          ' only one element in A
          for j=1 to Size(B)
          C(Size(C)) = B(i)  'looping last element in C through all unused in B
          Collect.ADD(C)      'collect C-arrays here for later use
          Next j                 
      End AllPairs
    

    请注意,C 最初是一个与 A 大小相同的空数组(也可以是 A 的副本)。 C 保持不变,而 A 和 B 依次缩小,直到 A 只包含一个元素,递归调用结束。这样就可以了。也许(恕我直言)这类似于代码 Jingie Zheng 的答案 - (我无法判断)。我的意图是尝试提供一个简单直观的伪代码版本。这个解决方案可以在VB中找到实现here.

    【讨论】:

      【解决方案5】:

      这不是完全相同的问题,但是我对以下问题做了一个解决方案,这可能是一个不错的起点:

      Array of array combinations

      【讨论】:

        【解决方案6】:

        关于本网站上的两个列表的组合有很多问题(和答案)(见边栏)。如果我理解正确,您的用例似乎只是表面上的不同。

        有方法就够了

        IEnumerable<Tuple<string, string>> Combinations(
          IEnumerable<string> list1, 
          IEnumerable<string> list2) {}
        

        (已在“重复”中以各种形式和大小存在)然后按照以下步骤使用它(作业=您填写详细信息):

        遍历列表 1 和列表 2 的所有组合(使用类似上面的方法)和

        • 按当前组合的第一个元素过滤列表 1
        • 按当前组合的第二个元素过滤列表 2
        • 将当前组合与过滤列表的所有可能组合组合(使用类似于上述方法)

        【讨论】:

          【解决方案7】:

          如果我正确理解您的问题,所有组合都可以通过以下方式得出:

          • 从 A 中选择 2 个不同的元素 {A_i, A_j}
          • 从 B 中选择 2 个不同的元素 {B_k, B_l}
          • 用这些元素{ (A_i, B_k), (A_j, B_l) }, { (A_i, B_l), (A_j, B_k) }进行2次组合。

          使用来自 A 和 B 的 2 个元素子集的所有组合,您将获得所需的所有组合。

          |A| * (|A| - 1) * |B| * (|B| - 1) / 2 组合。

          简单的实现是 4 个循环:

          for i = 1 ... |A|
            for j = i+1 ... |A|
              for k = 1 ... |B|
                for l = k+1 ... |B|
                  make 2 combinations {(A_i, B_k),(A_j, B_l)}, {(A_i, B_l), (A_j, B_k)}
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-07
            • 2012-11-02
            • 1970-01-01
            • 2020-09-13
            • 2011-08-10
            • 2011-05-20
            相关资源
            最近更新 更多