Заявление об ограничении ответственности: мне не хочется разбираться в том, как это сделать на C ++, поэтому я буду использовать нотацию Python (numpy). Концепции полностью переносимы, поэтому у вас не должно возникнуть проблем с переводом обратно на язык по вашему выбору.
Допустим, у вас есть пара массивов x
и y
, содержащих точки данных, и что x
монотонно увеличивается. Предположим также, что вы всегда будете выбирать точку разделения, которая оставляет не менее двух элементов в каждом разделе, поэтому уравнения разрешимы.
Теперь вы можете вычислить некоторые соответствующие величины:
N = len(x)
sum_x_left = x[0]
sum_x2_left = x[0] * x[0]
sum_y_left = y[0]
sum_y2_left = y[0] * y[0]
sum_xy_left = x[0] * y[0]
sum_x_right = x[1:].sum()
sum_x2_right = (x[1:] * x[1:]).sum()
sum_y_right = y[1:].sum()
sum_y2_right = (y[1:] * y[1:]).sum()
sum_xy_right = (x[1:] * y[1:]).sum()
Причина, по которой нам нужны эти величины (а это O(N)
для инициализации), заключается в том, что вы можете использовать их напрямую для вычисления некоторых хорошо известных формул для параметров линейной регрессии. Например, оптимальные m
и b
для y = m * x + b
задаются как
μ<sub>x</sub> = Σx<sub>i</sub>/N
μ<sub>y</sub> = Σy<sub>i</sub>/N
m = Σ(x<sub>i</sub> - μ<sub>x</sub>)(y<sub>i</sub> - μ<sub>y</sub>) / Σ(x<sub>i</sub> - μ<sub>x</sub>)<sup>2</sup>
b = μ<sub>y</sub> - m * μ<sub>x</sub>
Сумма квадратов ошибок определяется как
e = Σ(y<sub>i</sub> - m * x<sub>i</sub> - b)<sup>2</sup>
Их можно расширить с помощью простую алгебру в следующее:
m = (Σx<sub>i</sub>y<sub>i</sub> - Σx<sub>i</sub>Σy<sub>i</sub>/N) / (Σx<sub>i</sub><sup>2</sup> - (Σx<sub>i</sub>)<sup>2</sup>/N)
b = Σy<sub>i</sub>/N - m * Σx<sub>i</sub>/N
e = Σy<sub>i</sub><sup>2</sup> + m<sup>2</sup> * Σx<sub>i</sub><sup>2</sup> + N * b<sup>2</sup> - m * Σx<sub>i</sub>y<sub>i</sub> - b * Σy<sub>i</sub> + m * b * Σx<sub>i</sub>
Таким образом, вы можете l oop по всем возможностям и записать минимальное e
:
for p in range(1, N - 3):
# shift sums: O(1)
sum_x_left += x[p]
sum_x2_left += x[p] * x[p]
sum_y_left += y[p]
sum_y2_left += y[p] * y[p]
sum_xy_left += x[p] * y[p]
sum_x_right -= x[p]
sum_x2_right -= x[p] * x[p]
sum_y_right -= y[p]
sum_y2_right -= y[p] * y[p]
sum_xy_right -= x[p] * y[p]
# compute err: O(1)
n_left = p + 1
slope_left = (sum_xy_left - sum_x_left * sum_y_left * n_left) / (sum_x2_left - sum_x_left * sum_x_left / n_left)
intercept_left = sum_y_left / n_left - slope_left * sum_x_left / n_left
err_left = sum_y2_left + slope_left * slope_left * sum_x2_left + n_left * intercept_left * intercept_left - slope_left * sum_xy_left - intercept_left * sum_y_left + slope_left * intercept_left * sum_x_left
n_right = N - n_left
slope_right = (sum_xy_right - sum_x_right * sum_y_right * n_right) / (sum_x2_right - sum_x_right * sum_x_right / n_right)
intercept_right = sum_y_right / n_right - slope_right * sum_x_right / n_right
err_right = sum_y2_right + slope_right * slope_right * sum_x2_right + n_right * intercept_right * intercept_right - slope_right * sum_xy_right - intercept_right * sum_y_right + slope_right * intercept_right * sum_x_right
err = err_left + err_right
if p == 1 || err < err_min
err_min = err
n_min_left = n_left
n_min_right = n_right
slope_min_left = slope_left
slope_min_right = slope_right
intercept_min_left = intercept_left
intercept_min_right = intercept_right
Вероятно, есть другие упрощения, которые вы можете make, но этого достаточно, чтобы иметь алгоритм O(n)
.