【问题标题】:what is wrong with my factorial code in python我在 python 中的阶乘代码有什么问题
【发布时间】:2015-04-13 01:55:57
【问题描述】:

我有以下代码用于在 python 中计算数字的阶乘。但我不明白为什么我得到的答案是 1。 有人可以纠正我的代码。我想在不使用递归的情况下计算阶乘。

def factorial (n):
        result =1 
        num = n
        while n<1:
            result = result * num
            num = num -1
        return result

    factorial(5)
    1

【问题讨论】:

  • 循环条件取决于n,但您只更改循环中的resultnum。循环将运行 0 次或无限次。
  • 作为FrerichPM 2Ring 的状态,math.factorial 要快得多。
  • @Pureferret 我认为我们必须在这里假设,OP 的目标不仅仅是他有一些数字并且想知道它们的阶乘是什么。

标签: python python-2.7


【解决方案1】:
while n < 1:

应该是

while num > 1:

【讨论】:

    【解决方案2】:

    其他人指出了您的代码有什么问题,但我想指出factorial 函数确实适合于更实用(如:函数式编程)的解决方案;这完全避免了获取 while 循环的条件的问题,因为您根本没有任何循环。洞察力是n 的阶乘是1..n 的乘积,使用Python 的reduce 函数可以很容易地定义乘积。为避免失去性能,这是我的 Python 2.7 解释器为您的(固定)代码提供的内容:

    python -m timeit -s "import original" "original.factorial(10)"
    1000000 loops, best of 3: 1.22 usec per loop
    

    可以使用更具声明性的较短版本(单行):

    def factorial(n):
        return reduce(lambda x,y: x*y, range(1, n+1))
    

    ...唉,它更慢:

    python -m timeit -s "import func1" "func1.factorial(10)"
    1000000 loops, best of 3: 1.98 usec per loop
    

    但是,这可以通过使用 xrange 而不是 rangeoperator.mul 而不是自定义 lambda 来解决:

    import operator
    
    def factorial(n):
        return reduce(operator.mul, xrange(1, n+1))
    

    对我来说,这甚至比原始代码还要快:

    python -m timeit -s "import func2" "func2.factorial(10)"
    1000000 loops, best of 3: 1.14 usec per loop
    

    就个人而言,我会排除reduce 调用以使代码更加清晰(以牺牲一点点性能为代价):

    import operator
    
    def product(it):
        return reduce(operator.mul, it)
    
    def factorial(n):
        return product(xrange(1, n+1))
    

    我喜欢这个版本的快速和明确:阶乘被定义为范围 [1..n+1[ 的乘积(即不包括 n+1)。如果您尝试计算较大数字的阶乘,则性能差异会变得更加明显:

    python -m timeit -s "import original" "original.factorial(30)"
    100000 loops, best of 3: 5.25 usec per loop
    

    对比

    python -m timeit -s "import func3" "func3.factorial(30)"
    100000 loops, best of 3: 3.96 usec per loop
    

    【讨论】:

    • 这如何回答这个问题?
    • @Pureferret:它没有回答问题(问题已经得到回答),它通过显示可以完全避免问题的替代实现来扩展现有答案(因为没有明确的 for 循环是必需的)。
    • @Pureferret 评论没有为我写的内容提供足够的空间(更不用说可读性了)。
    • @Pureferret Links to external content break 和(正如该特定答案引用的那样)“SO 的主要目标之一是提供一个全面的问答存储库(而不仅仅是一个问答论坛)”。如果接受的答案的作者能用我写的内容扩展他的文本,我会很高兴,但只要不是这种情况,添加另一个后退一步的答案就是一个公平的妥协。如果它让你开心,我还可以添加“将n&lt;1 更改为num&gt;1”。 ;-)
    • @FrerichRaabe - 很好的解释。我无法更改接受的答案,因为这不能回答我的问题。但是你的解释给我介绍了很多新概念。我赞成你的解决方案。
    【解决方案3】:

    while 5 &lt; 1 始终为 false,因此返回 result = 1。错了。

    【讨论】:

    • 知道了!!这是一个愚蠢的错误。我是编程新手。
    • 我应该学习递归吗?我真的对递归的概念感到困惑。或者我可以摆脱python中的循环吗?
    • 你当然应该学习递归的概念。哪种方法最好取决于具体情况。
    【解决方案4】:

    让我们看看:

    1. 调用函数时设置n=5
    2. 你告诉 Python,n &lt; 1 做事。
    3. n 已经比1 大,它不会执行while 代码。
    4. 您的代码返回result,在定义的第一行设置为1

    【讨论】:

      【解决方案5】:

      正如 Simon Gibbons 解释的那样,您的代码具有

      while n &lt; 1:

      而不是

      while num &gt; 1:

      所以您有 小于 而不是 大于,因此您的 while 语句中的测试将立即失败。但是,如果您将其更改为 while n &gt; 1:,它将永远循环,因为您永远不会在 while 循环内更改 n 的值。

      Haresh Shyara 发布了您的代码的更正版本,在此处转载:

      def factorial(n):
          result = 1
          while n > 1:
              result = result * n
              n = n - 1
          return result
      

      请注意,此代码不会将n 复制到num - 它只是直接使用n。这不会影响您调用函数的参数,因为

      1. Python 整数是不可变的,并且
      2. n = n - 1 实际上创建了一个名为 n 的新本地对象。

      我受到 Frerich Raabe 的回答的启发,编写了一个程序,以更系统的方式对此处提供的各种解决方案进行计时。我还包含了 math.factorial() 和一个简单的基于循环的 for 函数,我刚刚将它们放在一起。

      我已经通过定义mul = operator.mul 稍微优化了调用operator.mul 的函数,但是我必须为那些使用reduce() 的函数提供一个为1 的initial 参数,这样它们就不会在@ 上失败987654343@(应该返回 1)。

      我大致按从最快到最慢的顺序排列了函数。

      我刚刚增强了这个程序,使其更容易运行多个测试并添加新功能进行测试。此外,它现在使用函数的文档字符串打印每个函数的简要描述。在运行计时测试之前,它会验证每个函数计算的值是否正确。

      #!/usr/bin/env python
      
      ''' Test and time various implementations of the factorial function
      
          From https://stackoverflow.com/q/28475637/4014959
      
          Written by PM 2Ring 2015.02.13
      '''
      
      import operator
      import math
      from timeit import Timer
      
      factorial0 = math.factorial
      
      def factorial0a(n):
          ''' call math.factorial '''
          return math.factorial(n)
      
      def factorial1(n):
          ''' for loop'''
          p = 1
          for i in xrange(2, n+1):
              p *= i
          return p
      
      mul = operator.mul
      
      def product(it):
          return reduce(mul, it, 1)
      
      def factorial2(n):
          ''' reduce with op.mul '''
          return reduce(mul, xrange(1, n+1), 1)
      
      def factorial3(n):
          ''' call product() '''
          return product(xrange(1, n+1))    
      
      def factorial4(n):
          ''' while loop '''
          result = 1
          while n > 1:
              result = result * n
              n = n - 1
          return result
      
      def factorial4a(n):
          ''' while loop with assignment operators '''
          result = 1
          while n > 1:
              result *= n
              n -= 1
          return result
      
      def factorial5(n):
          ''' recursive '''
          if n <= 1:
              return 1;
          else:
              return n*factorial5(n-1)
      
      def factorial6(n):
          ''' reduce with lambda '''
          return reduce(lambda res, val: res*val, xrange(n, 0, -1), 1)
      
      funcs = (
          factorial0,
          factorial0a,
          factorial1,
          factorial2,
          factorial3,
          factorial4,
          factorial4a,
          factorial5,
          factorial6,
      )
      
      def verify(n):
          ''' Check that each function calculates the same result as math.factorial '''
          r = xrange(n)
          fac = [factorial0(i) for i in r]
          rc = True
          for func in funcs[1:]:
              for i in r:
                  v = func(i)
                  if v != fac[i]:
                      print 'Error: %s(%d) returns %d instead of %d' % (func.func_name, i, v, fac[i])
                      rc = False
          return rc
      
      def time_test(arg=10, loops=100000, reps=3):
          ''' Print timing stats for all the factorial functions '''
          print 'Arg = %d, Loops = %d, Repetitions = %d' % (arg, loops, reps)
      
          for func in funcs:
              #Get function name and docstring
              try:
                  fname = func.func_name
                  fdoc = func.__doc__
              except AttributeError:
                  #Math.factorial has no name, and we can't modify its docstring
                  fname = 'factorial0'
                  fdoc = ' math.factorial itself '
      
              print '\n%s:%s' % (fname, fdoc)
              t = Timer('%s(%d)' % (fname, arg), 'from __main__ import %s' % fname)
              r = t.repeat(reps, loops)
              r.sort()
              print r
          print '\n'
      
      def main():
          if not verify(100): exit(1)
          time_test(arg=5, loops=500000, reps=4)
          time_test(arg=10, loops=200000, reps=4)
          time_test(arg=50, loops=100000, reps=4)
      
      if __name__ == '__main__':
          main()
      

      输出

      Arg = 5, Loops = 500000, Repetitions = 4
      
      factorial0: math.factorial itself 
      [0.30838108062744141, 0.3119349479675293, 0.31210899353027344, 0.32166290283203125]
      
      factorial0a: call math.factorial 
      [0.62141299247741699, 0.62747406959533691, 0.63309717178344727, 0.66500306129455566]
      
      factorial1: for loop
      [1.4656128883361816, 1.476855993270874, 1.4897668361663818, 1.5052030086517334]
      
      factorial2: reduce with op.mul 
      [1.5841941833496094, 1.5868480205535889, 1.6007061004638672, 1.6253509521484375]
      
      factorial3: call product() 
      [1.8745129108428955, 1.8750350475311279, 1.8822829723358154, 1.9097139835357666]
      
      factorial4: while loop 
      [1.1264691352844238, 1.1348199844360352, 1.1348659992218018, 1.178135871887207]
      
      factorial4a: while loop with assignment operators 
      [1.1867551803588867, 1.1881229877471924, 1.1893219947814941, 1.2020411491394043]
      
      factorial5: recursive 
      [1.9756920337677002, 1.9862890243530273, 1.9910380840301514, 2.0284240245819092]
      
      factorial6: reduce with lambda 
      [2.8342490196228027, 2.8369259834289551, 2.8390510082244873, 2.8969988822937012]
      
      
      Arg = 10, Loops = 200000, Repetitions = 4
      
      factorial0: math.factorial itself 
      [0.24756813049316406, 0.24919605255126953, 0.26395106315612793, 0.28582406044006348]
      
      factorial0a: call math.factorial 
      [0.3732609748840332, 0.37482404708862305, 0.37592387199401855, 0.38288402557373047]
      
      factorial1: for loop
      [0.88677501678466797, 0.89632201194763184, 0.89948821067810059, 0.90272784233093262]
      
      factorial2: reduce with op.mul 
      [0.89040708541870117, 0.89259791374206543, 0.89863204956054688, 0.90652203559875488]
      
      factorial3: call product() 
      [1.0093960762023926, 1.031667947769165, 1.2325050830841064, 1.7492170333862305]
      
      factorial4: while loop 
      [0.93423891067504883, 0.93978404998779297, 0.94000387191772461, 0.95153117179870605]
      
      factorial4a: while loop with assignment operators 
      [0.97296595573425293, 0.97462797164916992, 0.98288702964782715, 1.0095341205596924]
      
      factorial5: recursive 
      [1.6726200580596924, 1.6786048412322998, 1.691572904586792, 1.6946439743041992]
      
      factorial6: reduce with lambda 
      [1.8484599590301514, 1.8502249717712402, 1.8615908622741699, 1.9228360652923584]
      
      
      Arg = 50, Loops = 100000, Repetitions = 4
      
      factorial0: math.factorial itself 
      [1.6450450420379639, 1.6641650199890137, 1.6790158748626709, 1.7192811965942383]
      
      factorial0a: call math.factorial 
      [1.7563199996948242, 2.0039281845092773, 2.1530590057373047, 2.3621060848236084]
      
      factorial1: for loop
      [2.7895750999450684, 2.8117640018463135, 2.8381040096282959, 3.0019519329071045]
      
      factorial2: reduce with op.mul 
      [2.4697721004486084, 2.4750289916992188, 2.4813871383666992, 2.5051541328430176]
      
      factorial3: call product() 
      [2.4983038902282715, 2.4994339942932129, 2.5271379947662354, 2.5356400012969971]
      
      factorial4: while loop 
      [3.6446011066436768, 3.650169849395752, 3.6579680442810059, 3.7304909229278564]
      
      factorial4a: while loop with assignment operators 
      [3.7421870231628418, 3.7477319240570068, 3.7655398845672607, 3.7749569416046143]
      
      factorial5: recursive 
      [5.523845911026001, 5.5555410385131836, 5.5760359764099121, 6.2132260799407959]
      
      factorial6: reduce with lambda 
      [4.9984982013702393, 5.0106558799743652, 5.0363597869873047, 5.0808289051055908]
      

      与所有timeit 结果一样,每个列表中最快的条目是重要的条目,应忽略较慢的条目。

      来自timeit docs(由Ffisegydd提供):

      ... 最低值给出了您的机器可以运行多快的下限 运行给定的代码sn-p;结果向量中的较高值是 通常不是由 Python 速度的变化引起的,而是由其他 干扰您的计时精度的过程。所以min() 结果可能是您应该感兴趣的唯一数字...

      【讨论】:

      • 是的,即使您的解决方案看起来也很棒。但我无法理解这部分 def factorial(n): return reduce(lambda res, val: res*val, xrange(n, 0, -1), 1)。你能解释一下这个阶乘代码发生了什么吗?
      • @Dangerous:volcano 的代码与使用reduce 的其他版本非常相似。你明白reduce() 是做什么的吗?你明白lambda 是做什么的吗?
      • @dangerous:好的,那你有什么不明白的? xrange() 有点傻,但它只是迭代从 n 到 1 的整数。
      • 我对 reduce 在这种情况下的确切应用感到困惑。 reduce 接受一个函数并将其应用于每个可迭代对象。所以这里 lambda 函数将两个数字相乘。所以如果我想计算 5 的阶乘是如何使用这段代码实现的?
      • @dangerous:对于可迭代对象reduce中的每个元素,向函数传递两个值,一个是当前结果(res),另一个是来自可迭代对象(val)的当前元素,结果函数由reduce 保存,因此它可以在下一步将其传递回函数。我之前链接的reduce 文档中的示例代码说明了该过程。我将发布一些使用 for 循环的代码,它相当于火山的代码,但我会将其编辑成火山的问题,因为我正在解释的是他的代码。
      【解决方案6】:

      您的代码不会进入 while 循环本身。将其从 n&lt;1 更改为 n&gt;1。例如,如果找到 5 的阶乘,n

      【讨论】:

        【解决方案7】:
        def factorial(n):
            result = 1
            while n > 1:
                result = result * n
                n = n - 1
            return result
        
        print factorial(5)
        120
        

        【讨论】:

        • 这种类型的答案不是很有帮助。您只是提供了一个固定的代码 sn-p。请解释原始 sn-p 有什么问题以及为什么您的版本有效。虽然accepted answer只有一小段代码,但它更好,因为它以更清晰的方式解释了问题。
        • (while n
        【解决方案8】:

        试试这个,它更干净:

        def factorial( n ):
            if n <= 1:
                return 1;
            else:
                return n*factorial(n-1)
        

        这是解决问题的递归方式。

        【讨论】:

        • 为什么递归方式更干净?
        • 我没有正确理解递归。这就是我使用循环的原因;)
        • 也许这个词不是“更干净”,但在我看来它更好。您使用更少的代码行,并使用常见或自然的方式来理解阶乘运算:N! = N*(N-1)! , 除非 N = 1 然后 N! = 1. 纯属个人意见,
        • 递归在 Python 中没有优化,并且对递归深度有限制(虽然限制可以修改)。有些问题最好使用递归(例如树处理)来解决,但一般来说,如果您可以使用迭代算法轻松解决问题,则最好避免使用递归。
        • 第二个@PM2Ring - 即使堆栈是无限的,仍然会因大量使用堆栈而受到惩罚 - 没有真正的理由将其用于可以通过迭代轻松解决的问题
        【解决方案9】:

        说到简短的Pythonic代码

        def factorial(n):
            return reduce(lambda res, val: res*val, xrange(n, 0, -1), 1)
        

        对于那些不了解火山代码如何工作的人,这里有一个近似值:

        ''' Equivalent code by PM 2Ring '''
        
        def f(res, val):
            return res * val
        
        def factorial(n):
            res = 1
            for val in xrange(n, 0, -1):
                res = f(res, val)
            return res
        

        【讨论】:

        • 这看起来不像提问者的代码,那么这有什么帮助呢?
        • @Pureferret,拒绝投票的愚蠢理由 - 但请做我的客人。只是出于好奇 - 您是否对该主题中提供替代解决方案的所有答案投了反对票?
        • @volcano-您的解决方案看起来有些不同。你能解释一下函数 reduce(lambda res, val: resval, xrange(n, 0, -1), 1) 是如何工作的吗?我知道 lambda res,val : resval 将两个数字相乘。但我无法理解 reduce(function, xrange(n,0,-1),1) 背后的逻辑。你能详细说明你的答案吗?
        • 实际上,向后范围是没有意义的——我写它的时候并没有想清楚。初始值也是多余的 - 我不经常使用 reduce,所以我最初错过了
        • 初始值_不是_冗余:考虑factorial(0)会发生什么。
        猜你喜欢
        • 2021-01-01
        • 2019-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-26
        • 2017-10-02
        • 2018-03-09
        相关资源
        最近更新 更多