繁体   English   中英

O(n)求解最大差值和python 3.x的解决方案?

[英]O(n) solution for finding maximum sum of differences python 3.x?

我想知道,给定一个整数列表,比如l ,如果我们被允许从这个列表中选择3个整数,说leftmiddlerightmiddle > left, rightleft, 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值,并采取leftright从早期的计算值。 那是O(n)。

O(n)+ O(n)+ O(n)= O(n)。

诀窍是列表的最小值始终是解决方案的一部分( )。

  1. 找到列表的最小值,即O(n)。 现在这个最小元素将是左或右。
  2. 找到最大值(2x-y),其中idx(x)> idx(y)和idx(x)<idx(min),即检查列表的左侧部分
  3. 找到max(2x-y),其中idx(x)<idx(y)和idx(x)> idx(min),即检查列表的右侧部分
  4. 现在最多采取步骤2和3,即左/中(或右/中)。

这是一种计算每个索引的最小值,左和右的方法,在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每个中间元素( idx1N-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]) 我们的想法是将它们存储在两个数组中,比如m1m2 ,这样m1[i] = min(list[0:i])m2[i] = min(list[i:n]) 然后,在循环中使用m1m2

现在的挑战是在线性时间内计算m1m2 ,这意味着不允许使用min函数来计算它们。 如果你有m1[i] ,怎么用list[i+1]计算m1[i+1] list[i+1]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM