英文:
Finding the longest interval with a decrease in value faster than quadratic time
问题
我有一组某个度量标准的数值,例如:
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
[50, 52, 58, 54, 57, 51, 55, 60, 62, 65, 68, 72, 62, 61, 59, 63, 72]
我需要找出数值下降最多的最长区间。对于上面的列表,这样的区间从索引 7 到 14(长度为 8)。对此,有一个O(n^2)的解决方案:
def get_longest_len(values: list[int]) -> int:
longest = 0
for i in range(len(values)-1):
for j in range(len(values)-1, i, -1):
if values[i] > values[j] and j - i > longest:
longest = j - i
break
return longest + 1
有没有办法提高它的时间复杂度?
英文:
I have a list of values for some metric, e.g.:
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
[50, 52, 58, 54, 57, 51, 55, 60, 62, 65, 68, 72, 62, 61, 59, 63, 72]
I need to find the longest interval over which the value has decreased. For the above list such interval is from index 7 to 14 (and it's length is 8). An O(n²) solution to this is simple:
def get_longest_len(values: list[int]) -> int:
longest = 0
for i in range(len(values)-1):
for j in range(len(values)-1, i, -1):
if values[i] > values[j] and j - i > longest:
longest = j - i
break
return longest + 1
Is there any way to improve it's time complexity?
答案1
得分: 2
以下是代码的翻译部分:
from itertools import accumulate
from bisect import bisect
def get_longest_len(values: list[int]) -> int:
maxi = list(accumulate(values, max))
return max(
i - bisect(maxi, value) + 1
for i, value in enumerate(values)
)
cases = 100
correct = 0
for _ in range(cases):
values = [i + randint(-10, 10) for i in range(1000)]
for _ in range(5):
i, j = sample(range(1000), 2)
values[i], values[j] = values[j], values[i]
expect = get_longest_len0(values)
result = get_longest_len(values)
correct += result == expect
print(result == expect, expect, result)
print(correct, 'out of', cases, 'correct')
希望这对你有帮助。
英文:
O(n log n):
from itertools import accumulate
from bisect import bisect
def get_longest_len(values: list[int]) -> int:
maxi = list(accumulate(values, max))
return max(
i - bisect(maxi, value) + 1
for i, value in enumerate(values)
)
First I compute the prefix maxima. For your example:
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
values = [50, 52, 58, 54, 57, 51, 55, 60, 62, 65, 68, 72, 62, 61, 59, 63, 72]
maxi = [50, 52, 58, 58, 58, 58, 58, 60, 62, 65, 68, 72, 72, 72, 72, 72, 72]
Then for each value, I can use binary search in these maxima to find the earliest larger value. For example the 59 at index 14 in values
: We find that the earliest number in maxi
larger than 59 is the 60 at index 7.
Correctness testing with 100 lists of 1000 randomized ascending values (the two numbers for each test case are your result and mine, and the boolean says whether they match):
True 461 461
True 360 360
True 909 909
...
True 576 576
True 312 312
True 810 810
100 out of 100 correct
Code:
from itertools import accumulate
from bisect import bisect
from random import randint, sample
def get_longest_len0(values: list[int]) -> int:
longest = 0
for i in range(len(values)-1):
for j in range(len(values)-1, i, -1):
if values[i] > values[j] and j - i > longest:
longest = j - i
break
return longest + 1
def get_longest_len(values: list[int]) -> int:
maxi = list(accumulate(values, max))
return max(
i - bisect(maxi, value) + 1
for i, value in enumerate(values)
)
cases = 100
correct = 0
for _ in range(cases):
values = [i + randint(-10, 10) for i in range(1000)]
for _ in range(5):
i, j = sample(range(1000), 2)
values[i], values[j] = values[j], values[i]
expect = get_longest_len0(values)
result = get_longest_len(values)
correct += result == expect
print(result == expect, expect, result)
print(correct, 'out of', cases, 'correct')
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论