Java 高效生成三重态组合

Java 高效生成三重态组合,java,algorithm,Java,Algorithm,给定一组三元组S,其中对于S中的每个三元组S,S[1]>=S[2]>=S[3],其中S[i]是三元组S的第i个元素。对于s中的任何s,t,v,让函数F(s,t,v)生成一个新的三元组:F(s,t,v)=(max{s[1],t[1],v[1]},max{s[2],t[2],v[2]},max{s[3],t[3],v[3]})。目标:高效地生成集T={F(s,T,v)| s,T,v\in s} 两个例子: S = [(9,4,3),(8,6,2),(6,6,4)] T = [(9,4,3),(8,6

给定一组三元组S,其中对于S中的每个三元组S,S[1]>=S[2]>=S[3],其中S[i]是三元组S的第i个元素。对于s中的任何s,t,v,让函数F(s,t,v)生成一个新的三元组:F(s,t,v)=(max{s[1],t[1],v[1]},max{s[2],t[2],v[2]},max{s[3],t[3],v[3]})。目标:高效地生成集T={F(s,T,v)| s,T,v\in s}

两个例子:

S = [(9,4,3),(8,6,2),(6,6,4)]
T = [(9,4,3),(8,6,2),(6,6,4),(9,6,3),(9,6,4),(8,6,4)]

S = [(9,4,3),(8,6,2),(6,5,4)]
T = [(9,4,3), (9,6,3), b(9,5,4), b(9,6,4), b(8,6,2), b(8,6,4), b(6,5,4)]

下面是一个实现上述功能的简单但相对低效的实现。这段代码在O(n^3)中运行,其中| S |=n。问题是:如何更有效地实现这一点?这将涉及到建立一个有效的数据结构来保存S的排序版本。例如,我们可以观察到F(S,t,v)=S如果t[1],v[1]我怀疑还有很多可以获得的。我提出我的尝试

  • 考虑只由两个三元组组成的类似组合的函数F2(s,t)。现在F(s,t,v)可以写为F2(s,F2(t,v)),通过这种方式将F2(t,v)的结果用于不同的s来计算,可能会有性能增益
  • 通过估计结果
    HashSet
    的容量,可能会有轻微的改进,因此不需要进行扩展和重新灰化
代码:

public static Set<Triple> gen(List<Triple> s) {
    // Deduplicate s
    s = new ArrayList<>(new HashSet<>(s));
    
    int n = s.size();
    
    // Combine pairs of triplets first
    int maxSizeOfT2 = (n * n - 1) / 2;
    int capacityForT2 = (maxSizeOfT2 * 4 + 2) / 3;
    Set<Triple> t2AsSet = new HashSet<>(capacityForT2);
    // For the pairs only pair two *different* triples
    for (int i = 0; i < s.size(); i++) {
        for (int j = i + 1; j < s.size(); j++) {
            Triple newTriplet = f2(s.get(i), s.get(j));
            t2AsSet.add(newTriplet);
        }
    }
    List<Triple> t2 = new ArrayList<>(t2AsSet);
    
    // For the combinations of three original triplets
    // combine every pair with ever original triplet
    int maxSizeOfT = (t2AsSet.size() + 1) * (n + 1) - 1;
    int capacityForT = (maxSizeOfT * 4 + 2) / 3;
    Set<Triple> t = new HashSet<>(capacityForT);
    for (int i = 0; i < t2.size(); i++) {
        for (int j = 0; j < s.size(); j++) {
            Triple newTriplet = f2(t2.get(i), s.get(j));
            t.add(newTriplet);
        }
    }
    
    // Instead of generating F(s, s, s) just add every s to the result
    t.addAll(s);
    
    return t;
}
在评论中,您期望最好的案例可以得到改进,数字可能表明这是事实。在最坏的情况下,似乎只有轻微的改善


正如我在评论中所说的,结果集的大小是O(n^3),因此生成它的任何算法都不会比O(n^3)快。我们可能希望在n^3上有一个更小的常数因子。

在我看来,新集合中可能有O(n^3)个元素。如果是这样,那么生成它的速度就不能超过O(n^3)。@OleV.V。我同意,最坏情况下的运行时复杂性不会提高,但平均/最佳情况下的复杂性肯定会提高。特别是使用正确的数据结构来保持三元组的排序。您可能需要澄清这个问题。我来自首都O,这意味着最糟糕的情况。另外,如果有人可以减少一些运行时间的因素,使其更短,但仍然是O(n^3),您有兴趣吗?
public static Set<Triple> gen(List<Triple> s) {
    // Deduplicate s
    s = new ArrayList<>(new HashSet<>(s));
    
    int n = s.size();
    
    // Combine pairs of triplets first
    int maxSizeOfT2 = (n * n - 1) / 2;
    int capacityForT2 = (maxSizeOfT2 * 4 + 2) / 3;
    Set<Triple> t2AsSet = new HashSet<>(capacityForT2);
    // For the pairs only pair two *different* triples
    for (int i = 0; i < s.size(); i++) {
        for (int j = i + 1; j < s.size(); j++) {
            Triple newTriplet = f2(s.get(i), s.get(j));
            t2AsSet.add(newTriplet);
        }
    }
    List<Triple> t2 = new ArrayList<>(t2AsSet);
    
    // For the combinations of three original triplets
    // combine every pair with ever original triplet
    int maxSizeOfT = (t2AsSet.size() + 1) * (n + 1) - 1;
    int capacityForT = (maxSizeOfT * 4 + 2) / 3;
    Set<Triple> t = new HashSet<>(capacityForT);
    for (int i = 0; i < t2.size(); i++) {
        for (int j = 0; j < s.size(); j++) {
            Triple newTriplet = f2(t2.get(i), s.get(j));
            t.add(newTriplet);
        }
    }
    
    // Instead of generating F(s, s, s) just add every s to the result
    t.addAll(s);
    
    return t;
}
List  Element     Result   Your time      My time     Improvement
size  range        size   milliseconds  milliseconds      %
-----------------------------------------------------------------
  3   1–9              6       0.038       0.015         60
  3   1–10 000         7       0.046       0.016         66
400   1–9            159    4736        4740              0
400   1–10 000   858 897    1079        1067              1