Все темы
21

Переиспользуем работу прошлого окна

Tier 1Бесплатно

Где применяется

1. Задача

Дашборд мониторинга показывает среднее время ответа на запрос по последним 10 замерам. С каждым новым замером среднее нужно обновить. Приходит новое число, самое старое выпадает, и нужно свежее среднее.

Простой способ при каждом новом замере заново складывает все 10 чисел. Для десяти это пустяк. Теперь возьми окно из 10 000 замеров и историю из миллиона замеров. Это миллион раз по десять тысяч сложений, и почти все они повторяют работу, сделанную на предыдущем замере.

Почти все числа нового окна были и в старом. Их сумма уже посчитана. Если считать её с нуля, ты выбрасываешь ответ, который у тебя уже был.

2. Простыми словами

Тебе нужна величина (например, среднее) по нескольким последним показаниям, и она обновляется с каждым новым показанием. Если каждый раз складывать всё заново, работа пропадает зря: почти все числа остались теми же.

Возьми показания 4, 2, 1, 7 и окно из двух чисел. Сумма первого окна 4 + 2 = 6. Окно сдвигается на шаг: 1 входит, 4 выходит. Новая сумма 6 + 1 - 4 = 3, и складывать заново ничего не нужно. На каждом шаге работы ровно две: прибавить вошедшее число и вычесть вышедшее.

3. Идея

Храни сумму текущего окна. Когда окно сдвигается на шаг, ровно один элемент входит справа и ровно один уходит слева. Поэтому сумму обновляют двумя действиями: прибавь новый элемент, вычти ушедший.

Уходит всегда элемент, который стоит на k позиций раньше входящего. Если ширина окна k, а входящий элемент стоит на позиции i, то уходящий стоит на позиции i - k. Все элементы между ними остаются в окне, и они уже учтены в сумме.

4 2 1 7 8 уходит входит sum += вошедшее; sum -= ушедшее

Проследим на [4, 2, 1, 7, 8] с окном из 2 чисел.

  • Первое окно [4, 2], сумма 6.
  • Сдвиг: прибавляем 1, вычитаем 4, сумма 3. Это окно [2, 1].
  • Сдвиг: прибавляем 7, вычитаем 2, сумма 8. Это окно [1, 7].
  • Сдвиг: прибавляем 8, вычитаем 1, сумма 15. Это окно [7, 8].

Самая большая сумма окна равна 15.

4. Во что это обходится

Время O(n). Каждый элемент один раз входит в окно и один раз уходит, то есть с ним делают два действия, какой бы ширины ни было окно. Память O(1): одна текущая сумма. Если складывать каждое окно заново, выйдет O(n·k). У простого способа цена растёт вместе с шириной окна, у скользящего окна не растёт.

5. Где это встречается

Так дашборды считают скользящее среднее (среднее по последним k значениям). В языке запросов к метрикам для этого есть функция avg_over_time. Торговые системы считают простое скользящее среднее цены. Системы, которые читают датчики, сглаживают шумные показания по окну фиксированного размера. Счётчики частоты считают события за последние k отрезков времени. Любая сводка по «последним k замерам», которая обновляется постоянно, использует прошлое окно и не пересматривает все числа заново.

Самая большая сумма 3 чисел подряд
windowStat([2, 1, 5, 1, 3, 2, 4], 3)
arr
2
1
5
1
3
2
4
k = 3windowSum = 0best = -

Ищем самую большую сумму 3 чисел подряд. В окне всегда ровно 3 числа.

Гонка скорости
Наибольшая сумма 10 подряд идущих значений из одних и тех же 40

Столбики: 40 значений. Окно длиной 10, это четверть массива, и таблица ниже сохраняет эту долю.

Одна операция: одно чтение значения. За каждый кадр все дорожки делают одинаковое число операций, поэтому медленная отстаёт.

Пересчёт каждого окна O(n·k)
операций: 0
Скользящее окно O(n)
операций: 0

 

А если чисел больше

Операции для n чисел и примерное время при 100 миллионах простых операций в секунду. Это оценка по тому же счёту, а не замер.

n = 1 000
Пересчёт каждого окна187,8 тыс. · 1,9 мс
Скользящее окно1,8 тыс. · меньше 1 мс
n = 100 000
Пересчёт каждого окна1,9 млрд · 19 с
Скользящее окно175 тыс. · 1,8 мс
n = 10 000 000
Пересчёт каждого окна18,8 трлн · 2,2 дн.
Скользящее окно17,5 млн · 175 мс

Как узнать

Скользящее окно (фиксированный размер) (Sliding window (fixed size))

Эти слова в условии значат этот паттерн

  • непрерывный подмассив/подстрока заданной длины k
  • каждое окно размера k
  • скользящее среднее или скользящая сумма
  • мин/макс/количество/сумма по каждому фиксированному отрезку

Похоже, но не то

n до 1e6 при окне размера k даёт O(n). Каждый элемент один раз входит в окно и один раз выходит.

На LeetCode

Задачи про каждый отрезок ровно из k элементов подряд, например лучшую сумму, среднее или количество в окне размера k. Прибавляй элемент, который входит, и вычитай тот, что выходит.

Твоя задача (Tier 1)

Напиши функцию maxWindowSum(nums, k).

Найди наибольшую сумму k чисел, стоящих подряд. Двигай окно размера k по списку и обновляй сумму на ходу, а не складывай её заново на каждом шаге.

  • Вход: nums, список чисел, и k, размер окна. Пример: [4, 2, 1, 7, 8], 2.
  • Возвращает: наибольшую сумму любых k чисел подряд. Если окно размера k не помещается в список, возвращает null (в Python это None).

Шаги:

  1. Если k равно 0, или k больше длины списка, или список пустой, верни null.
  2. Сложи первые k чисел. Это сумма первого окна. Запомни её как лучшую.
  3. Сдвигай окно по одному шагу до конца списка. На каждом шаге прибавляй новое число справа и вычитай число, которое ушло слева. Запоминай самую большую сумму из увиденных.
  4. Верни лучшую сумму.

Готово, когда проходят все тесты:

  • [4, 2, 1, 7, 8], 2 -> 15 (7 + 8)
  • [1, 1, 1, 1], 2 -> 2
  • [5, 6], 2 -> 11 (окно занимает весь список)
  • [-1, -5, -2], 2 -> -6 (отрицательные числа допустимы)
  • [-1, -5, -2], 1 -> -1 (самое большое отдельное число)
  • [1, 2], 5 -> null (k больше длины списка)
  • [], 3 -> null (пустой список)
  • [3, 4], 0 -> null (k равно 0)

Практика

maxWindowSum

maxWindowSum(nums, k) -> number | null

export function maxWindowSum(nums, k) {
  // Your code here. The task is described in the box above the editor.
}

Ты здесь как гость

Тесты запускаются прямо здесь, без аккаунта. Код сохраняется только в этом браузере. Бесплатный аккаунт сохранит его на любом устройстве, отметит шаг пройденным и покажет, где ты в курсе.

Лестница подсказок

Застрял? Открывай по одной, но сначала попробуй сам.

    Мы используем необходимые куки для работы сайта (вход и язык). Если ты согласишься, мы также загрузим Google Analytics, чтобы видеть, какие страницы читают, и Google reCAPTCHA для защиты форм обратной связи и сообщений об ошибке от спама. Политика конфиденциальности