加速必须遍历整个列表的 Python 代码

Posted

技术标签:

【中文标题】加速必须遍历整个列表的 Python 代码【英文标题】:Speeding up Python code that has to go through entire list 【发布时间】:2017-08-14 03:56:30 【问题描述】:

我有一个问题需要(至少可以肯定)遍历整个列表来解决。问题是找出列表中最多的连续数字加起来到该列表中的另一个(更大)元素。如果没有,那么我们只取列表中的最大值作为候选总和,1 作为最大连续元素数。

我的通用代码可以工作,但对于大型列表(>500,000 个元素)来说不太好。我只是在寻找有关如何以不同方式解决问题的提示。我目前的做法:

L = [1,2,3,4,5,6,7,8,9,10]
candidate_sum = L[-1]
largest_count = 1
N = len(L)
i = 0

while i < N - 1:
    s = L[i]
    j = 0
    while s <= (N - L[i + j + 1]):
        j += 1
        s += L[i+j]
        if s in L and (j+1) > largest_count:
             largest_count = j+1
             candidate_sum = s
    i+=1

在这种情况下,答案将是 [1,2,3,4],因为它们加起来是 10,长度是 4(显然这个例子 L 是一个非常简单的例子)。

然后我通过将初始 while 循环条件更改为:

while i < (N-1)/largest_count

不是一个很好的假设,但基本认为数字的分布有点均匀,因此列表后半部分的两个数字平均大于列表中的最终数字,因此被取消资格。

我只是在寻找:

可能的瓶颈 关于尝试不同方法的建议

【问题讨论】:

您需要更准确地定义您的问题。列表总是有序且单调的吗?它们之间会不会有任何空隙?最佳解决方案将根据确切的问题陈述而有所不同。 @ŁukaszRogalski 列表总是排序的,所有元素都是唯一的,所以列表是严格递增的,是的,连续数字之间存在间隙 【参考方案1】:

严格升序:没有重复的元素或子序列,单一可能的解决方案

任意间距:没有算术快捷方式,必须进行蛮力操作

使用指针算术、数字类型的准多态的高效 C 实现:

#define TYPE int

int max_subsum(TYPE arr [], int size) 
   int max_length = 1;

   TYPE arr_fst = * arr;
   TYPE* num_ptr = arr;

   while (size --) 
      TYPE num = * num_ptr++;

      TYPE* lower = arr;
      TYPE* upper = arr;

      TYPE sum = arr_fst;
      int length = 1;

      for (;;) 
         if (sum > num) 
            sum -= * lower++;
            -- length;
         
         else if (sum < num) 
            sum += * ++upper;
            ++ length;
         
         else 
            if (length > max_length) 
               max_length = length;
            

            break;
         
      
   

   return max_length;

nums 上的主循环是可并行化的。使用 arrfor each 循环的动态数组列表类型相对直接地转换为 Python 3:

def max_subsum(arr):
   max_len = 1
   arr_fst = arr[0]

   for n in arr:
      lower = 0
      upper = 0

      sum = arr_fst

      while True:
         if sum > n:
            sum -= arr[lower]
            lower += 1
         elif sum < n:
            upper += 1
            sum += arr[upper]
         else:
            sum_len = upper - lower + 1

            if sum_len > max_len:
               max_len = sum_len

            break

   return max_len

这个max_subsum是一个偏函数; Python 列表可以为空。该算法适用于提供快速索引和静态类型算术的类 C 编译命令式语言。两者在 Python 中都比较昂贵。一种与您的算法非常相似的(完全定义的)算法,使用set 数据类型进行更高性能的通用量化,并避免 Python 的动态类型算术,可以更有效地解释:

def max_subsum(arr):
   size = len(arr)
   max_len = 0

   arr_set = set(arr)

   for i in range(size):
      sum = 0
      sum_len = 0

      for j in range(i, size):
         sum_mem = sum + arr[j]

         if num_mem not in arr_set:
            break

         sum = sum_mem
         sum_len += 1

      if sum_len > max_len:
         max_len = sum_len

   return max_len

【讨论】:

【参考方案2】:

我将忽略目标值发生变化的可能性,让您弄清楚这一点,但要回答您的问题“有没有更快的方法来做到这一点?”是的:通过使用累积和和一些数学来消除您的循环之一。

import numpy as np

L = np.random.randint(0,100,100)
L.sort()
cum_sum = np.cumsum(L)

start = 0
end = 0

target = 200

while 1:
    total = cum_sum [end-1] - (cum_sum [start-1] if start else 0)
    if total == target:
        break
    elif total < target:
        end += 1
    elif total > target:
        start += 1
    if end >= len(L):
        raise ValueError('something informative')

【讨论】:

似乎这段代码无法处理无法解决的情况。您需要确保 start &lt;= endend &lt; len(L) 在每个循环的末尾。 @LakshayGarg start 永远不能大于end,因为如果L 已排序,则cum_sum 已排序。如果它们相等,end 将在下一次迭代中递增。我修复了无解问题和数学错误.. @LakshayGarg 实际上的条件是所有L > 0,而不是L 已排序,但对于我给出的示例来说确实成立.. 这种方法也有点必要工作(无需重大修改)。

以上是关于加速必须遍历整个列表的 Python 代码的主要内容,如果未能解决你的问题,请参考以下文章

第四章:操作列表

在python 3中加速列表连接[重复]

代码学习PYTHON 列表循环遍历及列表常见操作

如何用最简单的Python爬虫采集整个网站

Python 4.1遍历整个列表(动手试一试)

操作列表