【问题标题】:How to reduce execution time in algorithm by replacing for loop in python如何通过替换python中的for循环来减少算法的执行时间
【发布时间】:2018-11-18 08:48:49
【问题描述】:

我正在尝试解决算法问题,请考虑以下列表:

l =  [100, 20, 50, 70, 45]

在这个问题中,我必须找到索引 i 之前的元素的平均值:

i = 0
100
i = 1
(100 + 20) //2 = 60
i = 2
(100+20+50) // 3 = 56
...

最终结果应该存储在一个列表中:

[100, 60, 56, 60, 57]

这是我目前的代码:

from functools import reduce
def meanScores(l):
      def av(x):
            return reduce(lambda a, b: a+b,x)//len(x)

      return [av(l[:i]) for i in range(1,len(l)+1)] 

它工作正常问题是当我提交它时,我遇到了时间限制执行。我认为问题是for循环,因为len(l)超过一万时需要很多时间。以前我使用sum() 进行平均,但这也花费了很多时间,当我将sum() 转换为reduce(lambda a, b: a+b,x)//len(x) 时,算法变得更快(它解决了更多的测试用例)。我认为 if 而不是 for循环我使用另一个函数(如lambda)然后问题就解决了。那么你认为有办法吗?谢谢你的时间。

【问题讨论】:

  • 您是否反复调用 meanScores?还是每次都用一次?
  • 此代码可能更适合codereview
  • @OrDinari 它是一次性使用
  • @Torxed 感谢您提到这一点,我是这个网站的新手。
  • 你的老师提到过avg()和数组切片吗?

标签: python algorithm execution-time


【解决方案1】:

这是因为你每次都对整个数组求和,所以成本是二次的,但如果你每次都重用之前的结果,它可能是线性的。想一想。

【讨论】:

  • 感谢您的指导,我可以在自己的代码中实现这个想法(我的意思是使用 reduce 和 lambda)还是我必须更改整个算法(就像在为这个问题建议的解决方案中一样) ?
  • 我看不出reduce()如何适应解决方案,因为它将数组减少到单个值,这已经花费了线性时间,我们只有一个值,我们需要一个完整的数组大小相同。将 reduce() 排除在可能的解决方案之外,您的原始代码并不多。所以,是的,最好从零开始。
【解决方案2】:

你可以尝试使用numpy.cumsum,得到平均值除以cumsum列表的index+1。

import numpy as np

l =  [100, 20, 50, 70, 45]

l_cumsum = np.cumsum(l)
l_indices = np.arange(1,len(l)+1,1)
l_average = np.divide(l_cumsum, l_indices).tolist()

print(l_average) # Outputs [100.0, 60.0, 56.666666666666664, 60.0, 57.0]

它应该很快,O(n),因为numpy.cumsum 已经非常优化了。如果你仍然想要它更快,你可以多线程它。

【讨论】:

    【解决方案3】:

    一些想法:

    1. 每次运行av 函数时,它都会减少整个 列表。由于在您的列表理解中调用av,因此您调用av 的次数超出了您的需要。您应该只计算一次总和列表(使用av),并在遍历l 时得出总和。
    2. 由于您只是总结了i,因此您不应该减少整个列表。您应该对第一个列表 l[:i] 进行切片,然后针对缩短的列表运行您的 reduce()

    【讨论】:

      【解决方案4】:

      由于完整的解决方案已经破坏了游戏,这里有一个 O(n) 的有效解决方案。

      请注意,您通常可以避免在 Python 中自己操作索引。在这里,我们可以使用起始值为 1 的enumerate 来跟踪我们求和的值的数量。

      def means(lst):
          sum = 0
          out = []
          for n, new_val in enumerate(lst, 1): # we enumerate starting with n=1
              sum += new_val
              out.append(sum/n)
          return out
      

      一些测试:

      lst =  [100, 20, 50, 70, 45]
      print(means(lst))
      # [100.0, 60.0, 56.666666666666664, 60.0, 57.0]
      
      print(means([]))
      # []
      

      【讨论】:

        【解决方案5】:

        你想要做的只是在列表上迭代一次:

        i = [10,20,30,40,50,60,70]
        
        
        def meanScores(l):
            if len(l) == 0: #Check is its an empty list
                return []
            res = [l[0]] #the first item is the first item in both lists(l)):
                res.append(float((res[i-1]*i + l[i])) / (i+1)) #Use the previous result to calculate the new result
            return [int(x) for x in res]
        

        为了使用之前的结果,我取之前的总和(即 prev avg * i),加上新的数字,然后除以 i+1。

        【讨论】:

        • 我认为应该由他来找出解决方案。
        • @Or Dinari tnx 的解决方案,但它有一个问题:对于100, 20, 50, 70, 45],答案应该是[100, 60, 56, 60, 57],但你的算法给出:[100, 60, 56, 59, 56]。此外,最终答案应四舍五入为最小整数,因此请在您的帖子中将/ 更改为//
        • 你的正确没有考虑浮点,现在应该没问题了。
        【解决方案6】:

        它可以用线性时间复杂度来实现,即 O(n)。一个示例可以如下,只是为了给你一个想法,你怎么做。

        l =  [100, 20, 50, 70, 45]
        
        a = [0] * len(l)
        
        a[0] = l[0]
        
        temp = a[0]
        
        for i in range(1, len(l)):
            a[i] = (temp + l[i]) / (i + 1)
            temp = temp + l[i]
        
        print(a)
        
        输出
        [100, 60.0, 56.666666666666664, 60.0, 57.0] 

        【讨论】:

          猜你喜欢
          • 2013-10-21
          • 2016-07-11
          • 2019-10-07
          • 1970-01-01
          • 2019-06-16
          • 1970-01-01
          • 2018-02-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多