K-дисперсія#11317

K-дисперсія (Розбір)

Розглянемо суму:

\sum_{i=1}^n (a_i - \bar{a})^2 = \sum_{i=1}^n \left(a_i - \frac{\sum_{i=1}^n a_i}{n} \right)^2 = \sum_{i=1}^n \left(a^2_i - \frac{2}{n} a_i \sum_{i=1}^n a_i + \frac{\left(\sum_{i=1}^n a_i \right)^2}{n^2} \right) = \\ \sum_{i=1}^n a^2_i - \frac{2}{n} \sum_{i=1}^n a_i \sum_{i=1}^n a_i + \frac{\left(\sum_{i=1}^n a_i \right)^2}{n} = \\ \sum_{i=1}^n a^2_i - \frac{2}{n} \left(\sum_{i=1}^n a_i \right)^2 + \frac{1}{n} \left(\sum_{i=1}^n a_i \right)^2 = \sum_{i=1}^n a^2_i - \frac{1}{n} \left(\sum_{i=1}^n a_i \right)^2

Тепер формулу дисперсії можна переписати в такому вигляді:

\sqrt{\sum_{i=1}^n (a_i - \bar{a})^2 \over n - 1} = \sqrt{\frac{\sum_{i=1}^n a^2_i - \frac{1}{n} \left(\sum_{i=1}^n a_i \right)^2}{n - 1}}

Якщо для послідовності \{a_i, a_{i+1}, ..., a_{i+k-1}\} підтримувати суму її елементів (a_i + a_{i+1} + ... + a_{i+k-1}) та суму її квадратів (a^2_i + a^2_{i+1} + ... + а^2_{i+k-1}), то за константний час можна обчислити шукану дисперсію на відрізку.

Приклад

Розпишемо чисельник дробу в дисперсії для n = 3:

\left(a_1 - \frac{a_1 + a_2 + a_3}{3}\right)^2 + \left(a_2 - \frac{a_1 + a_2 + a_3}{3}\right)^2 + \left(a_3 - \frac{a_1 + a_2 + a_3}{3}\right)^2 = \\ a_1^2 + a_2^2 + a_3^2 - 2a_1 \cdot \frac{a_1 + a_2 + a_3}{3} - 2a_2 \cdot \frac{a_1 + a_2 + a_3}{3} - 2a_3 \cdot \frac{a_1 + a_2 + a_3}{3} + \\ \left(\frac{a_1 + a_2 + a_3}{3}\right)^2 + \left(\frac{a_1 + a_2 + a_3}{3}\right)^2 + \left(\frac{a_1 + a_2 + a_3}{3}\right)^2 = \\ a_1^2 + a_2^2 + a_3^2 - \frac{2}{3}(a_1 + a_2 + a_3)(a_1 + a_2 + a_3) + 3 \cdot \left(\frac{a_1 + a_2 + a_3}{3}\right)^2 = \\ a_1^2 + a_2^2 + a_3^2 - \frac{2}{3}(a_1 + a_2 + a_3)^2 + \frac{1}{3}(a_1 + a_2 + a_3)^2 = \\ a_1^2 + a_2^2 + a_3^2 - \frac{1}{3}(a_1 + a_2 + a_3)^2

Реалізація алгоритму

Читаємо вхідні дані.

scanf("%d %d", &n, &k);
a.resize(n);
for (i = 0; i < n; i++)
  scanf("%d", &a[i]);

На відрізку [i - k + 1; i] підтримуємо дві змінні:

sum = sum2 = 0;

Перебираємо елементи послідовності.

for (i = 0; i < n; i++)
{

Оновлюємо значення та .

  sum += a[i];
  sum2 += a[i] * a[i];

Якщо є вікно [i - k + 1; i] довжини , то виводимо відповідь для нього.

  if (i >= k - 1)
  {
    printf("%lf\n", sqrt((sum2 - (sum * sum) / k) / (k - 1)));

Видаляємо з розглянутої послідовності елемент з індексом i - k + 1.

    sum -= a[i - k + 1];
    sum2 -= a[i - k + 1] * a[i - k + 1];
  }
}