更好的python逻辑,防止在嵌套循环中比较数组时超时
我试图解决一个编程难题,我编写的程序正确地解决了这个问题的小测试数据。但当他们在更大的数据集上运行时,我的程序在某些情况下超时。我主要是自学成才的程序员,如果有比我的逻辑更好的算法/实现,你们能告诉我吗。谢谢 问题 给定一个整数数组a,返回任意 一对数字,使得该对中较大的整数出现在 索引(在数组中)大于较小的整数<代码>返回-1如果需要 找不到满足此条件的对 我的Python函数更好的python逻辑,防止在嵌套循环中比较数组时超时,python,arrays,python-2.7,Python,Arrays,Python 2.7,我试图解决一个编程难题,我编写的程序正确地解决了这个问题的小测试数据。但当他们在更大的数据集上运行时,我的程序在某些情况下超时。我主要是自学成才的程序员,如果有比我的逻辑更好的算法/实现,你们能告诉我吗。谢谢 问题 给定一个整数数组a,返回任意 一对数字,使得该对中较大的整数出现在 索引(在数组中)大于较小的整数返回-1如果需要 找不到满足此条件的对 我的Python函数 def maxDifference( a): diff=0 find=0 leng = len(a)
def maxDifference( a):
diff=0
find=0
leng = len(a)
for x in range(0,leng-1):
for y in range(x+1,leng):
if(a[y]-a[x]>=diff):
diff=a[y]-a[x]
find=1
if find==1:
return diff
else:
return -1
约束条件:
1 <= N <= 1,000,000
-1,000,000 <= a[i] <= 1,000,000 i belongs to [1,N]
Array { 2,3,10,2,4,8,1}
8
样本输出:
1 <= N <= 1,000,000
-1,000,000 <= a[i] <= 1,000,000 i belongs to [1,N]
Array { 2,3,10,2,4,8,1}
8
这是一个线性时间解。它跟踪列表中每个索引之前的最小值。这些最小值存储在列表中。最后,通过压缩原始列表和最小列表的对应元素,将它们之间的差异计算到另一个差异列表中。此差异列表中的最大值应为所需答案
def get_max_diff(lst):
min_lst = []
running_min = lst[0]
for item in lst:
if item < running_min:
running_min = item
min_lst.append(running_min)
val = max(x-y for (x, y) in zip(lst, min_lst))
if not val:
return -1
return val
>>> get_max_diff([5, 6, 2, 12, 8, 15])
13
>>> get_max_diff([2, 3, 10, 2, 4, 8, 1])
8
>>> get_max_diff([5, 4, 3, 2, 1])
-1
def get_max_diff(lst):
最小值=[]
正在运行的\u min=lst[0]
对于lst中的项目:
如果项目>>获取最大差异([5,6,2,12,8,15])
13
>>>获取最大差异([2,3,10,2,4,8,1])
8.
>>>获取最大差异([5,4,3,2,1])
-1
程序耗时过长的原因是嵌套循环本身就意味着二次时间
外循环通过N-1
索引。内部循环每次通过不同数量的索引,但平均值明显向上取整。因此,通过内环的总次数为(N-1)*(N-1)/2
,即O(N^2)
。对于最大N=1000000
,这意味着4999990001次迭代。那要花很长时间
诀窍是找到一种在线性时间内实现这一点的方法
这里有一个解决方案(作为一个模糊的描述,而不是实际的代码,所以当有人面临与您相同的测试时,他们不能复制并粘贴它):
- 在每个索引之前列出最小值。每一个都是
,显然您可以通过min(最小的_值[-1],arr[i])
步骤来完成N
- 列出每个索引后的最大值。最简单的方法是反转列表,执行与上面完全相同的循环(但使用
而不是max
),然后再次反转。(当然,反转列表需要min
步骤。)N
- 现在,对于列表中的每个元素,您只需与
和最小值[i]
进行比较,而不是与其他每个元素进行比较。由于您只对每个最大值[i]
值进行两次比较,因此这需要N
时间2N
N+3N+2N
步骤,也就是O(N)
。如果N=1000000
,这意味着6000000个步骤,比4999990001快了很多
显然,您可以看到如何删除这两个反转,以及如何跳过第一个和最后一个比较。如果你很聪明,你可以看到如何将整个
最大值
从等式中完全去掉。最后,我认为你可以把它归结为2N-3步,或者1999997步。但这只是一个小小的持续改进;远没有解决基本算法问题那么重要。通过使用PyPy而不是CPython运行简单的代码,或者通过转换为NumPy,您可能会获得比3x(可能是20x)更大的改进,但除了更改算法之外,您不会以任何方式获得83333x的改进。,我认为,由于有人在同一个问题上可以复制您的代码并使用它运行,我不会因为他们复制一些更优化的代码而失眠:
import time
import random
def max_difference1(a):
# your function
def max_difference2(a):
diff = 0
for i in range(0, len(a)-1):
curr_diff = max(a[i+1:]) - a[i]
diff = max(curr_diff, diff)
return diff if diff != 0 else -1
my_randoms = random.sample(range(100000), 1000)
t01 = time.time()
max_dif1 = max_difference1(my_randoms)
dt1 = time.time() - t01
t02 = time.time()
max_dif2 = max_difference2(my_randoms)
dt2 = time.time() - t02
print("The maximum difference is", max_dif1)
print("Time taken by your method:", dt1)
print("Time taken by my method:", dt2)
print("My method is", dt1/dt2, "times faster.")
The maximum difference is 99895
Time taken by your method: 0.5533690452575684
Time taken by my method: 0.08005285263061523
My method is 6.912546237558299 times faster.
与@abarnert所说的类似(我发誓,他总是在这些事情上挖苦我),你不想在列表上重复两次。你可以利用这样一个事实:你知道你的大价值必须在小价值之前。您还可以利用这样一个事实:除了最大的数字之外,您不关心任何事情,也就是说,在列表[1,3,8,5,9]
中,最大的差异是8(9-1),您不关心其中是否有3、8和5。因此:max(a[i+1:])-a[i]
是给定索引的最大差异
然后将其与diff
进行比较,并将其中较大的一个与max
进行比较,因为如果curr\u diff>diff:diff=curr\u diff
(或等效值),则调用默认内置python函数的速度略快于
return
行只是您在1行中的(固定)行,而不是4行
如您所见,在1000个示例中,此方法快了约6倍(注意:使用了Python3.4,但在Python2.x上不会出现任何问题)那么。。。由于您只需要在最小的数字后面找到最大的数字,只要差异是目前为止最大的,就没有理由在数组的某个切片上进行多次传递或使用max()
:
def f1(a):
smallest = a[0]
result = 0
for b in a:
if b < smallest:
smallest = b
if b - smallest > result:
result = b - smallest
return result if result > 0 else -1
有嵌套的for循环,所以它是O(n^2)
,这在任何在线判断中都不容易接受。这里有一个提示:你可以在线性时间内找到每个索引之前的最大值。可以在线性时间内找到每个索引后的最小值。一旦你有了这两个列表,你怎么能避免内环,使整个算法的时间是线性的而不是二次的?同时,你声称这段代码给出了正确的输出,除了针对一些大数据超时之外