【问题标题】:How to generate cross product of sets in specific order如何按特定顺序生成集合的叉积
【发布时间】:2011-03-01 02:37:40
【问题描述】:

给定一些数字集合(或列表),我想按照返回数字的总和确定的顺序迭代这些集合的叉积。例如,如果给定的集合是{1,2,3},{2,4},{5},那么我想按顺序检索叉积

, , 或 , ,

我不能先计算所有的叉积然后对它们进行排序,因为太多了。有没有什么巧妙的方法可以通过迭代器实现这一点?

(我正在为此使用 Perl,以防有模块可以提供帮助。)

【问题讨论】:

    标签: algorithm perl set combinatorics


    【解决方案1】:

    对于两个集合 A 和 B,我们可以如下使用最小堆。

    1. 排序 A。
    2. B 类。
    3. 将 (0, 0) 推入具有优先级函数 (i, j) |-> A[i] + B[j] 的最小堆 H。打破平局,更喜欢小 i 和 j。
    4. 当 H 不为空时,弹出 (i, j),输出 (A[i], B[j]),插入 (i + 1, j) 和 (i, j + 1) 如果它们存在并且不'不属于 H.

    对于两个以上的集合,使用朴素算法并排序以减少到两个集合。在最好的情况下(当每个集合相对较小时发生),这需要存储 O(√#tuples) 个元组而不是 Ω(#tuples)。


    这里有一些 Python 可以做到这一点。它应该相当直接地转译为 Perl。您需要来自 CPAN 的堆库并将我的元组转换为字符串,以便它们可以成为 Perl 哈希中的键。该集合也可以存储为哈希。

    from heapq import heappop, heappush
    
    def largest_to_smallest(lists):
      """
      >>> print list(largest_to_smallest([[1, 2, 3], [2, 4], [5]]))
      [(3, 4, 5), (2, 4, 5), (3, 2, 5), (1, 4, 5), (2, 2, 5), (1, 2, 5)]
      """
      for lst in lists:
        lst.sort(reverse=True)
      num_lists = len(lists)
      index_tuples_in_heap = set()
      min_heap = []
      def insert(index_tuple):
        if index_tuple in index_tuples_in_heap:
          return
        index_tuples_in_heap.add(index_tuple)
        minus_sum = 0  # compute -sum because it's a min heap, not a max heap
        for i in xrange(num_lists):  # 0, ..., num_lists - 1
          if index_tuple[i] >= len(lists[i]):
            return
          minus_sum -= lists[i][index_tuple[i]]
        heappush(min_heap, (minus_sum, index_tuple))
      insert((0,) * num_lists)
      while min_heap:
        minus_sum, index_tuple = heappop(min_heap)
        elements = []
        for i in xrange(num_lists):
          elements.append(lists[i][index_tuple[i]])
        yield tuple(elements)  # this is where the tuple is returned
        for i in xrange(num_lists):
          neighbor = []
          for j in xrange(num_lists):
            if i == j:
              neighbor.append(index_tuple[j] + 1)
            else:
              neighbor.append(index_tuple[j])
          insert(tuple(neighbor))
    

    【讨论】:

    • 谢谢,看起来很有希望!你能给我一个关于“朴素算法和排序到两个集合”的指针吗?
    • 如果你想要 A x B x C x D,然后计算 A x B,排序,计算 C x D,排序,然后计算 (A x B) x (C x D) .
    • 为了最大限度地减少空间使用,您应该对集合进行分组,以便天真计算的笛卡尔积的大小大致相同。
    • @user635541,我通常有大约 20 个集合,每个集合有 1-5 个成员,因此即使计算其中一半的叉积也是不可行的。但我想我可以一直使用你的最小堆思想:要获得 A x B x C x D,对 A x B 使用最小堆迭代器,对 C x B 使用另一个迭代器;对于主迭代器,两个列表 A 和 B 将被两个调用相关子迭代器或从缓存中检索所需值的函数替换。在该过程结束时,缓存将包含整个交叉产品,但我通常只需要前几千次迭代。听起来对吗?
    • 如果您只需要前几千个,那一切都会改变。将上述方法调整到 k 维很容易;我以前没有这样做的唯一原因是,如果您拉出整个列表,那么就不会节省空间。稍后我会发布一些代码。
    猜你喜欢
    • 2018-09-04
    • 1970-01-01
    • 2019-07-18
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 2012-02-01
    • 1970-01-01
    • 2022-11-28
    相关资源
    最近更新 更多