【问题标题】:How would you write a non-recursive algorithm to calculate factorials?您将如何编写非递归算法来计算阶乘?
【发布时间】:2008-10-23 20:01:25
【问题描述】:

您将如何编写一个非递归算法来计算n!

【问题讨论】:

  • 为什么?因为计算 n!与循环相比,递归的速度慢得惊人。
  • @BradC:其实不是,如果你使用动态编程的话。
  • 我一直认为它取决于语言。
  • 大多数编译器优化了尾递归,因此您的里程可能会有所不同

标签: algorithm recursion factorial


【解决方案1】:

因为 Int32 会在大于 12 的任何东西上溢出!无论如何,就这样做:

public int factorial(int n) {
  int[] fact = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 
                362880, 3628800, 39916800, 479001600};
  return fact[n];
}

【讨论】:

  • 正如所写的那样,这实际上会更慢,因为每次调用初始化数组将比约 12 次乘法运算(大约 2 倍)更昂贵。一个好的查找表应该使用静态数据。另外,当我将 13 或 -20 传递给这个函数时会发生什么?
  • 另外,倒数第二个数字应该是 39916800,而不是 3991800(这可能说明查找表存在问题)。
  • 不在例如Ruby,它会自动切换到大整数表示。
  • @Wedge - 我的编译器说查找速度快了两倍(除非我将一个常量传递给函数,在这种情况下编译器只返回一个预先计算的常量)
  • @Wedge 从 2021 程序员到 2008 程序员,它在可执行文件中作为静态数据内联,因此不需要初始化数组
【解决方案2】:

伪代码

ans = 1
for i = n down to 2
  ans = ans * i
next

【讨论】:

    【解决方案3】:
    public double factorial(int n) {
        double result = 1;
        for(double i = 2; i<=n; ++i) {
            result *= i;
        }
        return result;
    }
    

    【讨论】:

    • 我认为你需要
    • 应该是 for(int i = 2; i
    • 允许提出家庭作业问题。今天早些时候有一篇关于这个的帖子。但是,应为响应发布的代码提供伪代码。
    【解决方案4】:

    为了科学,我对计算阶乘的各种算法实现进行了一些分析。我在 C# 和 C++ 中创建了每个迭代、查找表和递归实现。我将最大输入值限制为 12 或更少,因为 13!大于 2^32(能够保存在 32 位 int 中的最大值)。然后,我运行每个函数 1000 万次,循环遍历可能的输入值(即,将 i 从 0 增加到 1000 万,使用 i 模 13 作为输入参数)。

    以下是标准化为迭代 C++ 图的不同实现的相对运行时间:

                C++    C#
    ---------------------
    Iterative   1.0   1.6
    Lookup      .28   1.1
    Recursive   2.4   2.6
    

    为了完整起见,以下是使用 64 位整数并允许输入值最大为 20 的实现的相对运行时间:

                C++    C#
    ---------------------
    Iterative   1.0   2.9
    Lookup      .16   .53
    Recursive   1.9   3.9
    

    【讨论】:

    • 也许更多的是对 C# 和 .Net 的要求;)
    • 那么您是在 64 位 CPU 上运行这些测试的吗?如果不是,那我很好奇为什么第二次测试中有一半的测试会比第一次更快。
    • 每个表中的数字都被规范化为该表的 C++ 迭代时间,表之间不按比例缩放。请注意,这些运行是在 32 位操作系统上执行的,并且 64 位测试所用的时间比 32 位测试要长。
    【解决方案5】:

    将递归解决方案重写为循环。

    【讨论】:

      【解决方案6】:

      除非你有像 Python 那样的任意长度的整数,否则我会将 factorial() 的预计算值存储在一个大约 20 个 long 的数组中,并使用参数 n 作为索引。 n 的增长率!相当高,计算20!或 21!无论如何,即使在 64 位机器上,您也会遇到溢出。

      【讨论】:

        【解决方案7】:

        这是预先计算的函数,但实际上是正确的。如前所述,13!溢出,因此计算这么小的值范围没有意义。 64 位更大,但我希望范围仍然相当合理。

        int factorial(int i) {
            static int factorials[] = {1, 1, 2, 6, 24, 120, 720, 
                    5040, 40320, 362880, 3628800, 39916800, 479001600};
            if (i<0 || i>12) {
                fprintf(stderr, "Factorial input out of range\n");
                exit(EXIT_FAILURE); // You could also return an error code here
            }
            return factorials[i];
        } 
        

        来源:http://ctips.pbwiki.com/Factorial

        【讨论】:

          【解决方案8】:
          long fact(int n) {
              long x = 1;
              for(int i = 1; i <= n; i++) {
                  x *= i;
              }
              return x;
          }
          

          【讨论】:

            【解决方案9】:
            int total = 1
            loop while n > 1
                total = total * n
                n--
            end while
            

            【讨论】:

              【解决方案10】:

              我喜欢 pythonic 解决方案:

              def fact(n): return (reduce(lambda x, y: x * y, xrange(1, n+1)))
              

              【讨论】:

                【解决方案11】:
                fac = 1 ; 
                for( i = 1 ; i <= n ; i++){
                   fac = fac * i ;
                }
                

                【讨论】:

                  【解决方案12】:
                  public int factorialNonRecurse(int n) {
                      int product = 1;
                  
                      for (int i = 2; i <= n; i++) {
                          product *= i;
                      }
                  
                      return product;
                  }
                  

                  【讨论】:

                    【解决方案13】:

                    在运行时这是非递归的。在编译时它是递归的。运行时性能应该是 O(1)。

                    //Note: many compilers have an upper limit on the number of recursive templates allowed.
                    
                    template <int N>
                    struct Factorial 
                    {
                        enum { value = N * Factorial<N - 1>::value };
                    };
                    
                    template <>
                    struct Factorial<0> 
                    {
                        enum { value = 1 };
                    };
                    
                    // Factorial<4>::value == 24
                    // Factorial<0>::value == 1
                    void foo()
                    {
                        int x = Factorial<4>::value; // == 24
                        int y = Factorial<0>::value; // == 1
                    }
                    

                    【讨论】:

                      【解决方案14】:

                      对于非递归方法,没有比这更简单的了

                      int fac(int num) {
                          int f = 1;
                          for (int i = num; i > 0; i--)
                              f *= i;
                          return f;
                      }
                      

                      【讨论】:

                        【解决方案15】:

                        伪代码

                        total = 1
                        For i = 1 To n
                            total *= i
                        Next
                        

                        【讨论】:

                          【解决方案16】:

                          假设您希望能够处理一些非常大的数字,我将其编码如下。如果您希望在常见情况下(小数字)获得相当大的速度,但又希望能够处理一些超大量的计算,则此实现将适用。我认为这是理论上最完整的答案。在实践中,我怀疑您是否需要为作业问题以外的任何事情计算如此大的阶乘

                          #define int MAX_PRECALCFACTORIAL = 13;
                          
                          public double factorial(int n) {
                            ASSERT(n>0);
                            int[MAX_PRECALCFACTORIAL] fact = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 
                                          362880, 3628800, 39916800, 479001600};
                            if(n < MAX_PRECALCFACTORIAL)
                              return (double)fact[n];
                          
                            //else we are at least n big
                            double total = (float)fact[MAX_PRECALCFACTORIAL-1]
                            for(int i = MAX_PRECALCFACTORIAL; i <= n; i++)
                            {
                              total *= (double)i;  //cost of incrimenting a double often equal or more than casting
                            }
                            return total;
                          
                          }
                          

                          【讨论】:

                          • 您的查找部分中有两个错误。你的支票应该是
                          • (这是一个教训,告诉你永远不要盲目地复制你的 CS 作业而不进行测试:))已更正。添加了断言,修复了 MAX 值以包含新数字(因此
                          • 优秀。不过,您可能还想对太大的输入值进行断言。使用阶乘,您甚至可以很容易地溢出双倍。
                          【解决方案17】:

                          我会使用记忆。这样,您可以将方法编写为递归调用,并且仍然可以获得线性实现的大部分好处。

                          【讨论】:

                          • 您可能会想到斐波那契数列 - 阶乘不会从记忆化中受益。
                          【解决方案18】:
                          long fact(int n)
                          {
                              long fact=1;
                              while(n>1)
                                fact*=n--;
                              return fact;
                          }
                          
                          long fact(int n)
                          {
                             for(long fact=1;n>1;n--)
                                fact*=n;
                             return fact;
                          }
                          

                          【讨论】:

                            【解决方案19】:

                            迭代:

                            int answer = 1;
                            for (int i = 1; i <= n; i++){
                                answer *= i;
                            }
                            

                            或者...在 Haskell 中使用尾递归:

                            factorial x =
                                tailFact x 1
                                where tailFact 0 a = a
                                    tailFact n a = tailFact (n - 1) (n * a)
                            

                            在这种情况下,尾递归的作用是使用累加器来避免堆栈调用堆积。

                            参考:Tail Recursion in Haskell

                            【讨论】:

                              【解决方案20】:

                              Java 中的非递归阶乘。此解决方案使用自定义迭代器(以演示迭代器的使用:))。

                              /** 
                               * Non recursive factorial. Iterator version,
                               */
                              package factiterator;
                              
                              import java.math.BigInteger;
                              import java.util.Iterator;
                              
                              public class FactIterator
                              {   
                                  public static void main(String[] args)
                                  {
                                      Iterable<BigInteger> fact = new Iterable<BigInteger>()
                                      {
                                          @Override
                                          public Iterator<BigInteger> iterator()
                                          {
                                              return new Iterator<BigInteger>()
                                              {
                                                  BigInteger     i = BigInteger.ONE;
                                                  BigInteger total = BigInteger.ONE;
                              
                                                  @Override
                                                  public boolean hasNext()
                                                  {
                                                      return true;
                                                  }
                              
                                                  @Override
                                                  public BigInteger next()
                                                  {                        
                                                      total = total.multiply(i);
                                                      i = i.add(BigInteger.ONE);
                                                      return total;
                                                  }
                              
                                                  @Override
                                                  public void remove()
                                                  {
                                                      throw new UnsupportedOperationException();
                                                  }
                                              };
                                          }
                                      };
                                      int i = 1;
                                      for (BigInteger f : fact)
                                      {
                                          System.out.format("%d! is %s%n", i++, f);
                                      }
                                  }
                              }
                              

                              【讨论】:

                                【解决方案21】:
                                int fact(int n){
                                    int r = 1;
                                    for(int i = 1; i <= n; i++) r *= i;
                                    return r;
                                }
                                

                                【讨论】:

                                  【解决方案22】:

                                  递归地使用带有缓存的 JavaScript。

                                  var fc = []
                                  function factorial( n ) {
                                     return fc[ n ] || ( ( n - 1 && n != 0 ) && 
                                            ( fc[ n ] = n * factorial( n - 1 ) ) ) || 1;
                                  }
                                  

                                  【讨论】:

                                  • 是的,但问题是没有递归
                                  • 查看问题文本。 “非递归”
                                  猜你喜欢
                                  • 1970-01-01
                                  • 2015-04-19
                                  • 2017-04-19
                                  • 1970-01-01
                                  • 2015-01-08
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2020-02-10
                                  • 2013-04-28
                                  相关资源
                                  最近更新 更多