Ответ 1
Вы можете решить эту проблему, рассмотрев два значения:
- Максимальный пик до сих пор, начиная с левого
- Максимальный пик до сих пор, начиная с правого
И не принимайте пик, если он уступает обеим, потому что он будет под водой.
Визуализируйте большой массив чисел, где каждое число представляет собой высоту полосы на гистограмме.
Пример: [5, 4, 3, 7, 2, 3, 1, 12]
█
█
█
█
█
█ █
█ █
█ █ █
██ █ █
████ █ █
██████ █
████████
Это гистограмма предыдущих чисел. Мне нужно найти область, содержащуюся на графике в количестве открытых (или незаполненных) единиц.
Чтобы обойти это, я сделал алгоритм для вычисления всех пиков в массиве.
Это возвращает: [5, 7, 3, 12]
, а также другой список с индексами каждой записи, [0,3,5,7]
Для нас есть только три важных пика. 5
, 7
и 12
.
Затем мы можем сломать это.
Количество открытой площади между 5 и 7 (общее правило):
(([Index Of Larger] - [Index Of Smaller] - [1])*[SmallerValue]) - [Values Of All In B/W]
Таким образом, площадь первого раздела будет (2*5) - (4+3)
или 10-7
или 3
. Это имеет смысл, потому что, если вы посмотрите на график, который вы видите, есть пустая секция L-образной формы, в которую вы могли бы поместить 3 единицы, например, воду без переполнения.
Если вы повторите это со второй секцией, вы также получите ее правильную область.
ALL PEAKS
к IMPORTANT PEAKS
.В этом случае очень легко увидеть, как это можно сделать. Вы просто пишете алгоритм, чтобы узнать, что 3
меньше, чем 7
и 12
, поэтому избавитесь от него и верните уточненную версию пиков.
Однако это не всегда так просто.
У меня есть массив:
[5, 4, 3, 7, 2, 3, 1, 12, 9, 10, 5, 3, 6, 8, 5, 6, 4, 7, 6, 9, 4, 11, 11, 4, 1, 2, 1]
Запуск через базовый алгоритм поиска пиков O(N)
Он возвращает:
[5, 7, 3, 12, 10, 8, 6, 7, 9, 11, 11, 4, 2]
В этом примере мы видим ту же проблему в первой части этого вопроса, однако, после 12
в этом списке пиков, человек может легко увидеть, что следующий наиболее важный пик, на который нужно обратить внимание, - это два 11s
, 4
и 2
. Поэтому мне нужно пройти путь:
[5, 7, 3, 12, 10, 8, 6, 7, 9, 11, 11, 4, 2]
To:
[5, 7, 12, 11, 11, 4, 2]
Вышеупомянутый массив представляет собой список "важных" пиков, необходимых для поиска области, и снова визуализировать открытые блоки, как если бы они содержали воду или что-то такое, что они ограничены самым низким ближайшим пиком до переполнения.
Чтобы лучше визуализировать этот более полный, второй пример, у меня есть изображение графика и всех его пиков и точек данных здесь.
Спасибо.
Вы можете решить эту проблему, рассмотрев два значения:
И не принимайте пик, если он уступает обеим, потому что он будет под водой.
Я думаю, что это обрабатывает все условия, но все максимальные вычисления замедлят его для больших наборов данных. Я использовал IPython Notebook для его построения. Это в основном идея @Rémi:
Для любой точки данных:
Он может быть оптимизирован путем вычисления левого максимума при сканировании вправо и вычисления правильных максимумов для каждой позиции за один раз в один проход справа налево.
Алгоритм, который занимает около 4,1 секунды, чтобы сделать 10 000 точек данных в моей системе.
Незаполненная область (желтый) будет sum(C)
:
%matplotlib inline
import matplotlib.pyplot as plt
import random
def contribution(L,i):
max_left = 0 if i==0 else max(L[:i])
max_right = 0 if i==len(L)-1 else max(L[i+1:])
lower = min(max_left,max_right)
return 0 if lower < L[i] else lower - L[i]
N = [random.randint(0,12) for i in range(50)]
C = [contribution(N,i) for i in range(len(N))]
ind = list(range(len(N))) # the x locations for the groups
width = 1 # the width of the bars: can also be len(x) sequence
p1 = plt.bar(ind, N, width, color='r')
p2 = plt.bar(ind, C, width, color='y',bottom=N)
Здесь приведена более быстрая версия, которая реализует оптимизацию, о которой я упоминал выше. Он вычисляет миллион точек данных за 1,33 секунды, но использует меньшее количество для графического отображения ниже. Я не вижу, как это можно сделать за один проход, учитывая, что ячейка должна знать максимум слева и справа и может быть несколько точек, равных максимуму в любом направлении.
%matplotlib inline
import matplotlib.pyplot as plt
import random
def right_maximums(L):
'''Given list L, compute [max(L[i+1:] for i in range(len(L)-1)]+[0] more efficiently.
This gives the maximum cell to the right of the current cell.
Example: [1,2,3,4,5,4,3,2,1] -> [5,5,5,5,4,3,2,1,0]
'''
N = [0]
for i,v in enumerate(L[:0:-1]):
N.append(max(N[i],v))
return N[::-1]
def contribution(N):
'''In a bar graph of data N, compute how much "water" a data valley, assuming water
spills off the sides of the bar graph.
'''
rmaxs = right_maximums(N) # compute maximums to the right of a data point in advance.
lmax = 0 # compute maximums to the left as we go.
C = []
for i,v in enumerate(N):
# find the lower of the left and right maximum.
lower = min(lmax,rmaxs[i])
# if the data point is higher than the maximums, it won't hold water,
# else it holds the difference between the lower maximum and its value.
C.append(0 if lower < v else lower - v)
lmax = max(lmax,v)
return C
N = [random.randrange(0,50) for i in range(50)]
C = contribution(N)
ind = list(range(len(N))) # the x locations for the groups
width = 1 # the width of the bars: can also be len(x) sequence
p1 = plt.bar(ind, N, width, color='r')
p2 = plt.bar(ind, C, width, color='y',bottom=N)
Это можно сделать за 3 прохода:
public static int areaContained(int[] arr) {
int[] maxL = new int[arr.length];
int[] maxR = new int[arr.length];
int max = 0;
for (int i = 0; i < arr.length; i++) {
max = Math.max(arr[i], max);
maxL[i] = max;
}
max = 0;
for (int i = arr.length - 1; i >= 0; i--) {
max = Math.max(arr[i], max);
maxR[i] = max;
}
int total = 0;
for (int i = 0; i < arr.length; i++) {
int areaI = Math.min(maxL[i], maxR[i]) - arr[i];
if (areaI > 0)
total += areaI;
}
return total;
}
Основная идея заключается в том, что вклад bar i
определяется комбинацией arr[i]
, max значения после i
и максимального значения до i
.