给定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
答案 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