【问题标题】:Example of O(n!)?O(n!)的示例?
【发布时间】:2011-04-26 13:52:32
【问题描述】:

O(n!) 函数的示例(代码中)是什么?参考n 应该需要适当数量的操作才能运行;也就是说,我问的是时间复杂度。

【问题讨论】:

  • 为了迂腐,您的意思是 Ω(n!) [渐近增长的下界] 或“与 n 成正比的时间!” [上限和下限],而不是 O(n!) [渐近增长的上限]。由于 O(n!) 只是上限,因此许多算法都是 O(n!) 以无趣的方式,因为它们是 O(n) 或 O(n log n) 或 O(1) 或类似的东西。

标签: java algorithm big-o complexity-theory factorial


【解决方案1】:

给你。这可能是在O(n!) 时间运行的函数的最简单示例(其中n 是函数的参数):

void nFacRuntimeFunc(int n) {
  for(int i=0; i<n; i++) {
    nFacRuntimeFunc(n-1);
  }
}

【讨论】:

  • 鉴于这是对 n! 的一对一计算,这正是 O(n!) 增长顺序的定义
  • 再想一想,递归方法nFac会影响这个算法的时间复杂度吗?
  • @Derek:绝对是O(n!)(更重要的是Θ(n!))。是的,循环中调用的函数的时间复杂度会影响循环的时间复杂度。如果循环执行了n次,循环中的函数执行了(n-1)!个步骤,那么总共会执行n * (n-1)! = n!个步骤。这正是您证明此函数的时间复杂度在Θ(n!) 中的方式。
  • @Derek Long 循环是 O(n),因为它是用 (n-1) 递归调用的,所以你得到 n * (n-1)*(n-2)*...* 1 = n!所以函数是 O(n!)。
  • @AdamRobinson 即使投了赞成票,您的评论也是完全错误的。计算 n!只需要 O(n) 时间——一个带有乘法的 for 循环。类似地,n2 的计算不会花费 O(n2) 时间——它将是 O(1),单次乘法。
【解决方案2】:

一个经典的例子是通过暴力搜索traveling salesman problem

如果有N 城市,蛮力方法将尝试这些N 城市的每一个排列,以找出哪个最便宜。现在,N 城市的排列数量为N!,使其成为复杂性因子 (O(N!))。

【讨论】:

  • 我没有DV,但可能是因为它没有示例代码,并且没有提供大o符号......
  • @aioobe:因为问题是“什么是 O(n!) 问题”而答案是“这是一个”,所以我认为您不必明确地说 O(n!)。 .
  • 想象 3 个城市。要检查任何可能的路线,您必须检查两个城市之间的距离两次。 A->B 和 B->C。你必须从所有 3 个角开始。将到第一个城市的距离相加,总共是 3 次检查,然后将从第二个城市到第三个城市的距离相加,总共 6 次检查。那是3! = 6. 对 4 个城市执行此操作,检查变为 24。
【解决方案3】:

请参阅Big O Wikipedia 文章的Orders of common functions 部分。

根据文章,通过暴力搜索解决traveling salesman problem和找到determinantexpansion by minors都是O(n!)。

【讨论】:

    【解决方案4】:

    任何计算给定数组所有排列的算法都是O(N!)

    【讨论】:

      【解决方案5】:

      我想我来晚了,但我发现snailsort 是 O(n!) 确定性算法的最佳示例。它基本上会找到数组的下一个排列,直到对它进行排序。

      看起来像这样:

      template <class Iter> 
      void snail_sort(Iter first, Iter last)
      {
          while (next_permutation(first, last)) {}
      }
      

      【讨论】:

      • 自 n!可以定义为置换 n 个对象列表的方法数,这是我最喜欢的示例,尽管伪代码会比 c++ 更好。 ;)
      • 这并不明显这需要 O(n!) 时间。 next_permutation 需要线性时间,因此简单计算会给出 O(n*n!) 时间(严格大于 O(n!))。您必须争辩说 next_permutation 平均需要 O(1) 时间才能正常工作。
      • 值得注意的是,这是因为next_permutation 返回按字典顺序排列的下一个排列并返回true,或者返回最小的一个(即排序后的排列)并在下一个排列不存在时返回false .
      【解决方案6】:

      存在问题,即 NP-complete(可在非确定性多项式时间内验证)。这意味着如果输入扩展,那么解决问题所需的计算量就会增加很多。

      一些NP-hard问题是:Hamiltonian path problem( open img )、Travelling salesman problem( open img )
      一些NP-complete问题是:Boolean satisfiability problem (Sat.)( open img )、N-puzzle( open img )、Knapsack problem( open img )、Subgraph isomorphism problem( open img )、 Subset sum problem( open img ),Clique problem( open img ),Vertex cover problem( open img ),Independent set problem( open img ),Dominating set problem( open img @), open img ) 987654345@),

      来源:link 1link 2


      来源:link

      【讨论】:

      • NP 代表 Nondeterministic Polynomial,意思是比指数时间更快(但仅在理论上)。在理论上和实践中,阶乘比指数慢。所以,这完全无关紧要。
      【解决方案7】:

      通过次要扩展找到行列式。

      很好的解释here

      # include <cppad/cppad.hpp>
      # include <cppad/speed/det_by_minor.hpp>
      
      bool det_by_minor()
      {   bool ok = true;
      
          // dimension of the matrix
          size_t n = 3;
      
          // construct the determinat object
          CppAD::det_by_minor<double> Det(n);
      
          double  a[] = {
              1., 2., 3.,  // a[0] a[1] a[2]
              3., 2., 1.,  // a[3] a[4] a[5]
              2., 1., 2.   // a[6] a[7] a[8]
          };
          CPPAD_TEST_VECTOR<double> A(9);
          size_t i;
          for(i = 0; i < 9; i++)
              A[i] = a[i];
      
      
          // evaluate the determinant
          double det = Det(A);
      
          double check;
          check = a[0]*(a[4]*a[8] - a[5]*a[7])
                - a[1]*(a[3]*a[8] - a[5]*a[6])
                + a[2]*(a[3]*a[7] - a[4]*a[6]);
      
          ok = det == check;
      
          return ok;
      }
      

      来自here 的代码。您还将找到必要的.hpp 文件there

      【讨论】:

        【解决方案8】:

        最简单的例子:)

        伪代码:

        input N
        calculate N! and store the value in a vaiable NFac - this operation is o(N)
        loop from 1 to NFac and output the letter 'z' - this is O(N!)
        

        你去吧:)

        作为一个真实的例子 - 生成一组项目的所有排列怎么样?

        【讨论】:

          【解决方案9】:

          在维基百科中

          通过蛮力搜索解决旅行商问题;用未成年人展开寻找行列式。

          http://en.wikipedia.org/wiki/Big_O_notation#Orders_of_common_functions

          【讨论】:

            【解决方案10】:

            printf("Hello World");

            是的,这是 O(n!)。如果你认为不是,我建议你阅读 BigOh 的定义。

            我只添加了这个答案,因为人们不得不总是使用 BigOh,不管他们的实际意思是什么。

            例如,我很确定要问 Theta(n!) 的问题,至少是 cn!步数不超过Cn!一些常数 c, C > 0 的步骤,但选择使用 O(n!) 代替。

            另一个例子:Quicksort is O(n^2) in the worst case,虽然在技术上是正确的(即使堆排序在最坏的情况下也是 O(n^2)!),它们的实际意思是Quicksort is Omega(n^2) in the worst case

            【讨论】:

            • 虽然在数学上是正确的,但 O(n) 表示法几乎一直被松散地使用,即使是那些 确实 更了解的人也是如此。特别是,使用比严格必要的更高的 O 级被认为具有欺骗性;所以没有从业者将 O(n) 算法称为 O(n²),尽管 O(n) 中的任何算法也(根据定义)在 O(n²) 中
            • 这似乎更适合作为评论而不是答案,因为它只是指出问题中的技术性,显然没有抓住问题的重点。
            【解决方案11】:

            在 C# 中

            这不是 O(N!) 的空间复杂度吗?因为,C# 中的字符串是不可变的。

            string reverseString(string orgString) {
                string reversedString = String.Empty;
            
                for (int i = 0; i < orgString.Length; i++) {
                    reversedString += orgString[i];
                }
            
                return reversedString;
            }
            

            【讨论】:

              【解决方案12】:

              你是对的,递归调用应该正好是 n!时间。这是一个类似测试 n 个不同值的阶乘时间的代码。内循环运行 n! j的不同值的时间,所以内循环的复杂度是Big O(n!)

              public static void NFactorialRuntime(int n)
                  {
                      Console.WriteLine(" N   Fn   N!");
                      for (int i = 1; i <= n; i++)  // This loop is just to test n different values
                      {
                          int f = Fact(i);
                          for (int j = 1; j <= f; j++)  // This is Factorial times
                          {  ++x; }
                          Console.WriteLine(" {0}   {1}   {2}", i, x, f);
                          x = 0;
                      }
                  }
              

              这里是 n = 5 的测试结果,它精确地迭代了阶乘时间。

                N   Fn   N!
                1   1   1
                2   2   2
                3   6   6
                4   24   24
                5   120   120
              

              时间复杂度为 n 的精确函数!

              // Big O(n!)
              public static void NFactorialRuntime(int n)
                  {
                      for (int j = 1; j <= Fact(i); j++) {  ++x; }
                      Console.WriteLine(" {0}   {1}   {2}", i, x, f);
                  }
              

              【讨论】:

                【解决方案13】:

                Bogosort 是我遇到的唯一一个冒险进入 O(n!) 领域的“官方”。但这不是保证 O(n!),因为它本质上是随机的。

                【讨论】:

                  【解决方案14】:

                  您可能学到的用于获取矩阵行列式的递归方法(如果您使用线性代数)需要 O(n!) 时间。虽然我并不特别喜欢编码。

                  【讨论】:

                    【解决方案15】:

                    @clocksmith 你是绝对正确的。这不是计算 n!。也不是 O(n!)。我运行它收集了下表中的数据。请比较第 2 列和第 3 列。 (#nF 是调用 nFacRuntimeFunc 的次数)

                    n #nF n!

                    0    0      1
                    1    1      1
                    2    4      2
                    3    15     6
                    4    65     24
                    5    325    120
                    6    1956   720
                    7    13699  5040
                    

                    很明显,如果性能比 O(n!) 差得多。下面是计算 n! 的示例代码!递归地。你会注意到它的 O(n) 顺序。

                    int Factorial(int n)
                    {
                       if (n == 1)
                          return 1;
                       else
                          return n * Factorial(n-1);
                    }
                    

                    【讨论】:

                      【解决方案16】:

                      添加向上k函数

                      这是一个复杂度为 O(n!) 的函数的简单示例,给定一个 int 数组和一个整数 k。如果数组 x+y = k 中有两项,则返回 true,例如:如果 tab 为 [1, 2, 3, 4] 且 k=6,则返回值为 true,因为 2+4=6

                      public boolean addToUpK(int[] tab, int k) {
                      
                              boolean response = false;
                      
                              for(int i=0; i<tab.length; i++) {
                      
                                  for(int j=i+1; j<tab.length; j++) {
                      
                                      if(tab[i]+tab[j]==k) {
                                          return true;
                                      }
                      
                                  }
                      
                              }
                              return response;
                          }
                      

                      作为奖励,这是一个使用 jUnit 的单元测试,它工作正常

                      @Test
                          public void testAddToUpK() {
                      
                              DailyCodingProblem daProblem = new DailyCodingProblemImpl();
                      
                              int tab[] = {10, 15, 3, 7};
                              int k = 17;
                              boolean result = true; //expected result because 10+7=17
                              assertTrue("expected value is true", daProblem.addToUpK(tab, k) == result);
                      
                              k = 50;
                              result = false; //expected value because there's any two numbers from the list add up to 50
                              assertTrue("expected value is false", daProblem.addToUpK(tab, k) == result);
                          }
                      

                      【讨论】:

                        【解决方案17】:

                        在 JavaScript 中:

                        // O(n!) Time Complexity
                        
                        const {performance} = require('perf_hooks');
                        const t0 = performance.now()
                        function nFactorialRuntime(input){
                          let num = input;
                          
                          if (input === 0) return 1;
                        
                          for(let i=0; i< input; i++){
                            num = input * nFactorialRuntime(input-1);
                          }
                          return num;
                        }
                        const t1 = performance.now()
                        console.log("The function took: " + (t1 - t0) + " milliseconds.")
                        
                        nFactorialRuntime(5);
                        

                        对于节点 8.5+,您需要首先包含 perf_hooks 模块的性能。谢谢。

                        【讨论】:

                          猜你喜欢
                          • 2016-04-27
                          • 2013-06-11
                          • 1970-01-01
                          • 2022-11-10
                          • 1970-01-01
                          • 2019-11-03
                          • 2021-07-04
                          • 1970-01-01
                          • 2022-01-13
                          相关资源
                          最近更新 更多