【问题标题】:Finding the time complexity of cartesian product of array of arrays查找数组数组的笛卡尔积的时间复杂度
【发布时间】:2018-10-02 16:09:17
【问题描述】:
final class Combination {

    public static void findCombinations(String[][] sets) {
        int combinations = 1;
        for(int i = 0; i < sets.length; combinations *= sets[i].length, i++);
        for(int i = 0; i < combinations; i++) {
            int j = 1;
            for(String[] set : sets) {
                System.out.print(set[(i/j)%set.length] + " ");
                j *= set.length;
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
         findCombinations(new String[][]{{"a","b","c"}, {"d","e","i"}, {"f","g","h"}});
    }
  }

我的答案是这样的

   a d f 
   b d f 
   c d f 
   a e f 
   b e f 
   c e f 
   a i f 
   b i f 
   c i f 
   a d g 
   b d g 
   c d g 
   a e g 
   b e g 
   c e g 
   a i g 
   b i g 
   c i g 
   a d h 
   b d h 
   c d h 
   a e h 
   b e h 
   c e h 
   a i h 
   b i h 
   c i h 

我想知道我的解决方案的时间复杂度以及是否有任何方法可以改进解决方案。

【问题讨论】:

  • 我认识的许多人都认为将combinations *= sets[i].length 移动到循环主体而不是将其放在头部中会更好。
  • @ReazMurshed 对于 n 的任何合理定义,复杂度绝对是 not O(n^2)。这是指数级的。
  • 我知道已经一年多了,但你可能想看看 Guava 的实现:guava.dev/releases/22.0/api/docs/com/google/common/collect/…

标签: java performance time-complexity cartesian-product


【解决方案1】:

它是O(|first| * |second| * |third| * ...),而你无法改进这个界限,它是Theta,而不仅仅是O

仅结果就是那么大(在您的示例中为27 = 3 * 3 * 3),您需要创建每个结果,这样您就不会比结果的大小更好。 Omega 绑定到此结束。

O 部分非常简单,因为您的代码执行的所有子操作都在Theta(1) 中。所以我们只需要考虑循环。您最内层的循环会生成结果,每个结果都会打印一份。所以你的算法是最优的,每个正确结果一次迭代。您不会生成需要丢弃的无用对或在两者之间使用任何非常量操作。由于仅结果的数量就是上述复杂性,因此您的代码也是如此。


对于精确的界限,我们需要包括每个子元素的大小,如前所述。但是,如果您想要一个大小变量,比如说n,您可以通过最大数组的大小来限制其他大小:

n = max(|first|, |second|, |third|, ...)

然后你得到

Theta(n^x)

其中x 是您传入的数组数量。因此在您的示例中为Theta(n^3)

【讨论】:

  • 我理解你的答案的逻辑是正确的,但是对这个解决方案和我的另一个解决方案进行基准测试:guava.dev/releases/22.0/api/docs/com/google/common/collect/… 番石榴脚本的执行时间几乎是恒定的,无论长度如何数组
  • 我在这里没有看到冲突。 Big-O 分析不谈实际的实时性,完全省略任何因素。例如,只有在输入不切实际的大小之后,情况才会变得更糟。 Big-O 根本不在乎。
  • 我知道执行时间不同,因为复杂性符号是在操作数量中定义的,但这就是我对 guava 的实现发表评论的原因。我仍在尝试understand their implementation,因为我对 java 不是很有经验,但是由于我使用它们的函数时运行时间恒定,看起来操作的数量不会根据每个输入数组的长度而改变,只有外部数组总数。
  • 它似乎只是恒定的,因为它们的实现对于您测试的所有输入都非常快。此外,他们的列表是不可变的,因此他们实际上并没有创建新的真实列表,而只是创建了迷你包装器,可以再次加速一切。如果您想要一个深入的答案,我建议您提出一个新问题,包括实现和使用基准框架的基准。尝试大列表,如果有 100 个列表,每个列表有 10 万个条目,现在您会看到性能下降。
猜你喜欢
  • 2011-05-18
  • 2014-03-11
  • 2017-03-05
  • 2011-12-18
  • 2011-01-31
  • 1970-01-01
相关资源
最近更新 更多