以减少的总和顺序生成笛卡尔积

时间:2018-03-21 22:30:24

标签: algorithm sorting generator combinatorics cartesian-product

给定n个排序列表A1,A2,...,按降序排列整数,是否有算法以递减元组和顺序有效地生成其笛卡尔积的所有元素?

例如,n = 3

A1 = [9, 8, 0]
A2 = [4, 2]
A3 = [5, 1]

预期输出将是A1xA2xA3的笛卡尔积,其顺序如下:
combination sum
9, 4, 5 18
8, 4, 5 17
9, 2, 5 16
8, 2, 5 15
9, 4, 1 14
8, 4, 1 13
9, 2, 1 12
8, 2, 1 11
0, 4, 5 9
0, 2, 5 7
0, 4, 1 5
0, 2, 1 3

3 个答案:

答案 0 :(得分:2)

如果问题实例有N个要交叉,那么您可以将产品中的元组视为N维"矩形" grid,其中每个元组对应一个网格元素。您将首先发出最大和元组[9,4,5],它位于网格的一个角落。

您将跟踪候选集"未发射的元组相对于已经发射的至少一个,在每个维度上小一个。如果它有所帮助,你可以将已经发射的元组可视化为" solid"在网格中。候选集是触及固体表面的所有元组。

您将重复选择要从候选集中发出的下一个元组,然后使用新发出的元组的邻居更新该集合。当该集合为空时,您已完成。

在发出[9,4,5]后,候选集是

[8,4,5]  (one smaller on first dimension)
[9,2,5]  (one smaller on second dimension)
[9,4,1]  (one smaller on third dimension) 

接下来以最大的总和发出其中一个。那是[8,4,5]。与此相邻的是

[0,4,5], [8,2,5], [8,4,1]

将这些添加到候选集中,所以我们现在有

[9,2,5], [9,4,1], [0,4,5], [8,2,5], [8,4,1]

再次选择最高金额。那是[9,2,5]。相邻的是

[8,2,5], [9,2,1]. 

所以新候选人集是

[9,4,1], [0,4,5], [8,2,5], [8,4,1], [9,2,1]

注意[8,2,5]再次出现。不要复制它。

这次最高金额是[8,2,5]。相邻的是

[0,2,5], [8,2,1]

此时你应该有这个想法。

为候选集使用最大堆。然后找到具有最大和的元组需要O(log | C |),其中C是候选集。

套装有多大?有趣的问题。我会让你考虑一下。对于您的示例中的3个输入集,它是

|C| = O(|A1||A2| + |A2||A3| + |A1||A3|)

所以发射每个元组的成本是

O(log(|A1||A2| + |A2||A3| + |A1||A3|))

如果集合的大小最多为N,那么这是O(log 3 N ^ 2)= O(log 3 + 2 log N)= O(log N)。

有| A1 || A2 || A3 |要发射的元组,即O(N ^ 3)。

生成所有元组和排序的更简单的算法是O(log N ^ 3)= O(3 log N)= O(log N)。它大约只有50%慢,渐近相同。更复杂的算法的主要优点是它节省了O(N)空间。堆/优先级队列大小仅为O(N ^ 2)。

这是一个快速的Java实现,旨在保持代码大小。

import java.util.Arrays;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;

public class SortedProduct {
  final SortedTuple [] tuples;
  final NoDupHeap candidates = new NoDupHeap();

  SortedProduct(SortedTuple [] tuple) {
    this.tuples = Arrays.copyOf(tuple, tuple.length);
    reset();
  }

  static class SortedTuple {
    final int [] elts;

    SortedTuple(int... elts) {
      this.elts = Arrays.copyOf(elts, elts.length);
      Arrays.sort(this.elts);
    }

    @Override
    public String toString() {
      return Arrays.toString(elts);
    }
  }

  class RefTuple {
    final int [] refs;
    final int sum;

    RefTuple(int [] index, int sum) {
      this.refs = index;
      this.sum = sum;
    }

    RefTuple getSuccessor(int i) {
      if (refs[i] == 0) return null;
      int [] newRefs = Arrays.copyOf(this.refs, this.refs.length);
      int j = newRefs[i]--;
      return new RefTuple(newRefs, sum - tuples[i].elts[j] + tuples[i].elts[j - 1]);
    }

    int [] getTuple() {
      int [] val = new int[refs.length];
      for (int i = 0; i < refs.length; ++i) 
        val[i] = tuples[i].elts[refs[i]];
      return val;
    }

    @Override
    public int hashCode() {
      return Arrays.hashCode(refs);
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof RefTuple) {
        RefTuple t = (RefTuple) o;
        return Arrays.equals(refs, t.refs);
      }
      return false;
    }
  }

  RefTuple getInitialCandidate() {
    int [] index = new int[tuples.length];
    int sum = 0;
    for (int j = 0; j < index.length; ++j) 
      sum += tuples[j].elts[index[j] = tuples[j].elts.length - 1];
    return new RefTuple(index, sum);
  }

  final void reset() {
    candidates.clear();
    candidates.add(getInitialCandidate());
  }

  int [] getNext() {
    if (candidates.isEmpty()) return null;
    RefTuple next = candidates.poll();
    for (int i = 0; i < tuples.length; ++i) {
      RefTuple successor = next.getSuccessor(i);
      if (successor != null) candidates.add(successor);
    }
    return next.getTuple();
  }

  /** A max heap of indirect ref tuples that ignores addition of duplicates. */
  static class NoDupHeap {
    final PriorityQueue<RefTuple> heap = 
        new PriorityQueue<>((a, b) -> Integer.compare(b.sum, a.sum));
    final Set<RefTuple> set = new HashSet<>();

    void add(RefTuple t) {
      if (set.contains(t)) return;
      heap.add(t);
      set.add(t);
    }

    RefTuple poll() {
      RefTuple t = heap.poll();
      set.remove(t);
      return t;
    }

    boolean isEmpty() {
      return heap.isEmpty();
    }

    void clear() {
      heap.clear();
      set.clear();
    }
  }

  public static void main(String [] args) {
    SortedTuple [] tuples = {
      new SortedTuple(9, 8, 0),
      new SortedTuple(4, 2),
      new SortedTuple(5, 1),
    };
    SortedProduct product = new SortedProduct(tuples);
    for (;;) {
      int[] next = product.getNext();
      if (next == null) break;
      System.out.println(Arrays.toString(next));
    }
  }
}

答案 1 :(得分:0)

这是一些Python。 (效率不高 - 只需生成整个列表然后对其进行排序可能会更好。)

#! /usr/bin/env python
import heapq

def decreasing_tuple_order(*lists):
    # Each priority queue element will be:
    #    (-sum, indices, incrementing_index, sliced)
    # The top element will have the largest sum.
    if 0 < min((len(l) for l in lists)):
        indices = [0 for l in lists]
        sliced = [lists[i][indices[i]] for i in range(len(indices))]
        queue = [(-sum(sliced), indices, 0, sliced)]
        while 0 < len(queue):
            #print(queue)
            (_, indices, indexable, sliced) = heapq.heappop(queue)
            yield sliced

            # Can we increment this index?
            if indices[indexable] + 1 < len(lists[indexable]):
                new_indices = indices[:]
                new_indices[indexable] = indices[indexable] + 1
                sliced = [lists[i][new_indices[i]] for i in range(len(indices))]
                heapq.heappush(queue, (-sum(sliced), new_indices, indexable, sliced))

            # Start indexing the next index?
            while indexable + 1 < len(lists):
                indexable = indexable + 1
                if 1 < len(lists[indexable]):
                    # Start incrementing here.
                    indices[indexable] = 1
                    sliced = [lists[i][indices[i]] for i in range(len(indices))]
                    heapq.heappush(queue, (-sum(sliced), indices, indexable, sliced))



a1 = [9, 8, 0]
a2 = [4, 2]
a3 = [5, 1]

for x in decreasing_tuple_order(a1, a2, a3):
    print((x,sum(x)))

答案 2 :(得分:0)

在python中你可以使用itertools.product

>>> A1 = [9, 8, 0]
>>> A2 = [4, 2]
>>> A3 = [5, 1]
>>> from itertools import product
>>> for p in product(A1, A2, A3):
...     print sum(p)
... 
18
14
16
12
17
13
15
11
9
5
7
3