【问题标题】:How can I compute a Cartesian product iteratively?如何迭代地计算笛卡尔积?
【发布时间】:2011-01-26 00:53:15
【问题描述】:

This question 询问如何计算给定数量向量的笛卡尔积。由于向量的个数是预先知道的,而且相当少,所以用嵌套的for循环很容易得到解。

现在假设给你一个用你选择的语言的向量向量(或列表列表或集合等):

l = [ [1,2,3], [4,5], [6,7], [8,9,10], [11,12], [13] ]

如果我被要求计算它的笛卡尔积,那就是

[ [1,4,6,8,11,13], [1,4,6,8,12,13], [1,4,6,9,11,13], [1,4,6,9,12,13], ... ]

我会继续递归。比如在quick&dirty python中,

def cartesianProduct(aListOfLists):
    if not aListOfLists:
        yield []
    else:
        for item in aListOfLists[0]:
            for product in cartesianProduct(aListOfLists[1:]):
                yield [item] + product

有没有一种简单的方法来迭代地计算它?

(注意:答案不需要在 python 中,无论如何我知道在 python 中 itertools 做得更好,如this question。)

【问题讨论】:

    标签: algorithm language-agnostic iteration cartesian-product


    【解决方案1】:

    1)在各个列表中创建一个索引列表,初始化为0,即:

    indexes = [0,0,0,0,0,0]
    

    2) 从每个列表中产生适当的元素(在本例中为第一个)。

    3) 将最后一个索引加一。

    4) 如果最后一个索引等于最后一个列表的长度,则将其重置为零并进位。重复此操作,直到没有进位。

    5) 返回第 2 步,直到索引回绕到 [0,0,0,0,0,0]

    这类似于计数的工作原理,只是每个数字的基数可以不同。


    这是上述算法在 Python 中的实现:

    def cartesian_product(aListOfList):
        indexes = [0] * len(aListOfList)
        while True:
            yield [l[i] for l,i in zip(aListOfList, indexes)]
            j = len(indexes) - 1
            while True:
                indexes[j] += 1
                if indexes[j] < len(aListOfList[j]): break
                indexes[j] = 0
                j -= 1
                if j < 0: return
    

    这是另一种使用模技巧实现它的方法:

    def cartesian_product(aListOfList):
        i = 0
        while True:
            result = []
            j = i
            for l in aListOfList:
                 result.append(l[j % len(l)])
                 j /= len(l)
            if j > 0: return
            yield result
            i += 1
    

    请注意,这会以与您的示例稍有不同的顺序输出结果。这可以通过以相反的顺序遍历列表来解决。

    【讨论】:

    • 是的。确实比较容易。谢谢。
    • 我认为你的代码实际上比你的算法效率略低..;P
    • 是的...我有另一个与算法非常匹配的版本,但我认为这很令人困惑!也许我可以发布它......
    • @Larry:我现在也发布了我的第一个版本!也许它可以写得更整洁,但它工作......
    • =) 只是说,因为当我提交我的答案并看到你的答案时,你的显然更有效率!
    【解决方案2】:

    既然您要求一种与语言无关的解决方案,这里是 bash 中的一个,但我们可以称它为迭代、递归,它是什么?这只是符号:

    echo {1,2,3},{4,5},{6,7},{8,9,10},{11,12},13
    

    也许足够有趣。

    1,4,6,8,11,13 1,4,6,8,12,13 1,4,6,9,11,13 1,4,6,9,12,13 1,4,6,10,11,13 ...
    

    【讨论】:

      【解决方案3】:

      从 0 到 \Pi a_i_length 迭代所有 i

      for ( int i = 0; i < product; i++ ) {
          // N is the number of lists
          int now = i;
          for ( int j = 0; j < N; j++ ) {
              // This is actually the index, you can get the value easily.
              current_list[j] = now % master_list[j].length;
      
              // shifts digit (integer division)
              now /= master_list[j].length;  
          }
      }
      

      还有一些简单的方法可以写这个,这样你就不必重复做同样的工作了。

      【讨论】:

        【解决方案4】:

        您只需要手动管理您的堆栈。基本上,做你自己做的递归。由于递归将有关每个递归调用的数据放在堆栈上,因此您只需执行相同操作:

        Let L[i] = elements in vector i
        k = 0;
        st[] = a pseudo-stack initialized with 0
        N = number of vectors 
        while ( k > -1 )
        {
          if ( k == N ) // solution, print st and --k
        
          if ( st[k] < L[k].count )
          {
            ++st[k]
            ++k
          }
          else
          {
            st[k] = 0;
            --k;
          }
        } 
        

        未经测试,但这个想法会奏效。希望我没有错过任何东西。

        编辑:好吧,我想为时已晚。这与计数基本相同,只是另一种看待它的方式。

        【讨论】:

          猜你喜欢
          • 2017-09-28
          • 1970-01-01
          • 2012-08-27
          • 2011-03-24
          • 2017-03-07
          • 2013-04-20
          • 2021-11-19
          • 1970-01-01
          • 2021-02-17
          相关资源
          最近更新 更多