[英]O(n) solution for finding maximum sum of differences python 3.x?
我想知道,給定一個整數列表,比如l
,如果我們被允許從這個列表中選擇3個整數,說left
, middle
, right
, middle > left, right
和left, middle, right
出現在列表(即index(left)<index(middle)<index(right)
),是否存在O(n)
解決方案,用於查找middle - left + middle - right
的最大值? 您可能會認為不符合這些條件的列表不會出現(例如[5,0,5],如Eric Duminil所指出的那樣)
目前,我能夠提出我認為(大致) O(n^2)
解決方案(如果我錯了,請糾正我)。
從本質上講,我目前的想法是:
maximum = 0
for idx in range(1, N - 1):
left = min(l[0: idx])
right = min(l[idx + 1:])
middle = l[idx]
if left < middle and right < middle:
new_max = middle - left + middle - right
maximum = max(new_max, maximum)
幫助/提示將不勝感激。
謝謝!
您可以運行一次數字,保持運行的最小值 ,並將其存儲在每一步,以便最后知道每個索引左側的最小值。 那是O(n)。
同樣,您可以從右到左遍歷所有數字,並計算出每個索引右側的最小值。 那是O(n)。
然后你就可以通過每個可能的運行middle
值,並采取left
和right
從早期的計算值。 那是O(n)。
O(n)+ O(n)+ O(n)= O(n)。
訣竅是列表的最小值始終是解決方案的一部分( 左或右 )。
這是一種計算每個索引的最小值,左和右的方法,在O(n)中:
import random
N = 10
l = [random.randrange(N) for _ in range(N)]
print(l)
# => [9, 9, 3, 4, 6, 7, 0, 0, 7, 6]
min_lefts = []
min_left = float("inf")
min_rights = [None for _ in range(N)]
min_right = float("inf")
for i in range(N):
e = l[i]
if e < min_left:
min_left = e
min_lefts.append(min_left)
print(min_lefts)
# => [9, 9, 3, 3, 3, 3, 0, 0, 0, 0]
for i in range(N-1,-1,-1):
e = l[i]
if e < min_right:
min_right = e
min_rights[i] = min_right
print(min_rights)
# => [0, 0, 0, 0, 0, 0, 0, 0, 6, 6]
您現在可以迭代l
每個中間元素( idx
在1
和N-2
),並找到最小值2 * l[idx] - min_rights[idx] - min_lefts[idx]
。 這個操作也是O(n):
print(max(2 * l[i] - min_rights[i] - min_lefts[i] for i in range(1, N-2)))
它輸出:
11
這是2 * 7 - 0 - 3
。
這是一些時間! 隨意編輯執行計時和\\添加新條目的代碼。
from timeit import timeit
setup10 = '''
import numpy.random as nprnd
lst = list(nprnd.randint(1000, size=10))
'''
setup100 = '''
import numpy.random as nprnd
lst = list(nprnd.randint(1000, size=100))
'''
setup1000 = '''
import numpy.random as nprnd
lst = list(nprnd.randint(1000, size=1000))
'''
fsetup = '''
import sys
def f2(lst):
N = len(lst)
maximum = 0
for idx in range(1, N - 1):
left = min(lst[0: idx])
right = min(lst[idx + 1:])
middle = lst[idx]
if left < middle and right < middle:
new_max = middle - left + middle - right
maximum = max(new_max, maximum)
return maximum
def eric(lst):
N = len(lst)
min_lefts = []
min_left = float("inf")
min_rights = [None for _ in range(N)]
min_right = float("inf")
for i in range(N):
e = lst[i]
if e < min_left:
min_left = e
min_lefts.append(min_left)
for i in range(N-1,-1,-1):
e = lst[i]
if e < min_right:
min_right = e
min_rights[i] = min_right
return max(2 * lst[i] - min_rights[i] - min_lefts[i] for i in range(1, N-2))
def bpl(lst):
res = -sys.maxsize
a = sys.maxsize
b = -sys.maxsize
c = sys.maxsize
for i, v in enumerate(lst[1:-1]):
a = min(lst[i], a)
c = min(lst[i + 2], c)
b = max(lst[i], b)
res = max(2 * b - a - c, res)
return res
def meow(l):
N = len(l)
right_min = (N - 2) * [sys.maxsize]
right_min[0] = l[N - 1]
for i in range(3, N):
right_min[i - 2] = min(right_min[i - 2], l[N - i + 1])
left = l[2]
maximum = 2*l[1] - left - right_min[N - 3]
for idx in range(2, N - 1):
left = min(left, l[idx-1])
right = right_min[N - idx - 2]
middle = l[idx]
if left < middle and right < middle:
new_max = middle - left + middle - right
maximum = max(new_max, maximum)
return maximum
'''
print('OP with 10\t:{}'.format(timeit(stmt="f2(lst)", setup=setup10 + fsetup, number=100)))
print('eric with 10\t:{}'.format(timeit(stmt="eric(lst)", setup=setup10 + fsetup, number=100)))
print('bpl with 10\t:{}'.format(timeit(stmt="bpl(lst)", setup=setup10 + fsetup, number=100)))
print('meow with 10\t:{}'.format(timeit(stmt="meow(lst)", setup=setup10 + fsetup, number=100)))
print()
print('OP with 100\t:{}'.format(timeit(stmt="f2(lst)", setup=setup100 + fsetup, number=100)))
print('eric with 100\t:{}'.format(timeit(stmt="eric(lst)", setup=setup100 + fsetup, number=100)))
print('bpl with 100\t:{}'.format(timeit(stmt="bpl(lst)", setup=setup100 + fsetup, number=100)))
print('meow with 10\t:{}'.format(timeit(stmt="meow(lst)", setup=setup100 + fsetup, number=100)))
print()
print('OP with 1000\t:{}'.format(timeit(stmt="f2(lst)", setup=setup1000 + fsetup, number=100)))
print('eric with 1000\t:{}'.format(timeit(stmt="eric(lst)", setup=setup1000 + fsetup, number=100)))
print('bpl with 1000\t:{}'.format(timeit(stmt="bpl(lst)", setup=setup1000 + fsetup, number=100)))
print('meow with 10\t:{}'.format(timeit(stmt="meow(lst)", setup=setup1000 + fsetup, number=100)))
10 elements on the list, 100 repetitions
OP :0.00102
eric :0.00117
bpl :0.00141
meow :0.00159
100 elements on the list, 100 repetitions
OP :0.03200
eric :0.00654
bpl :0.01023
meow :0.02011
1000 elements on the list, 100 repetitions
OP :2.34821
eric :0.06086
bpl :0.10305
meow :0.21190
作為一個低效率的單線程獎勵:
maximum = max(2*z -sum(x) for x, z in zip([[min(lst[:i+1]), min(lst[i+2:])] for i, _ in enumerate(lst[:-2])], lst[1:-1]))
解決方案:
import sys
import random
random.seed(1)
l = [random.randint(0, 100) for i in range(10)]
print(l)
res = -sys.maxsize
a = sys.maxsize
b = -sys.maxsize
c = sys.maxsize
for i, v in enumerate(l[1:-1]):
a = min(l[i], a)
c = min(l[i + 2], c)
b = max(l[i], b)
res = max(2 * b - a - c, res)
print(res)
輸出:
[13, 85, 77, 25, 50, 45, 65, 79, 9, 2]
155
你肯定是在正確的軌道上,你只需要擺脫那些最小的操作。 所以我的暗示是你可以預先計算它們(在線性時間內),然后在循環中查找min,就像你已經在做的那樣。
澄清一下:你必須在你已經擁有的部分之前為所有i
預先計算min(list[0:i])
和min(list[i:n])
。 我們的想法是將它們存儲在兩個數組中,比如m1
和m2
,這樣m1[i] = min(list[0:i])
和m2[i] = min(list[i:n])
。 然后,在循環中使用m1
和m2
現在的挑戰是在線性時間內計算m1
和m2
,這意味着不允許使用min
函數來計算它們。 如果你有m1[i]
,怎么用list[i+1]
計算m1[i+1]
list[i+1]
?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.