#Введение
Я налётывал дома на дроне очень много кругов, и автоматически замерял их время, были круги по 8.5с, много кругов по 10с, и какое-то количество по 15с, и очень мало по 30с, 60с. Конечно, можно построить гистограммку, чтобы визуализировать эти результаты, но я хотел какую-то гладкую кривую, чтобы мне не приходилось подбирать длину ведра в гистограмме, и чтобы эта гладкая кривая учитывала насколько близко находятся результаты рядом друг с другом.
Пытался нагуглить - не нашёл. Или находилось что-то, но я ничего не понял (как, например, эта статья - начали за здравие хорошо, а потом ушли куда-то и я не понял). Поэтому пришлось изобретать всё самому. Посидел немного над тетрадкой, подумал, затем покодил и получил результат (не сразу, конечно же). В этой статье расскажу как аппроксимировать одномерное распределение полиномом N-ой степени. Итоговый алгоритм получился по сложности , где - количество точек.
#Вывод
У нас есть много данных: , всего их . Мы хотим аппроксимировать их распределение полиномом .
Что значит аппроксимировать распределение? Это значит что на произвольном отрезке интеграл данной функции стремится к плотности данных: ( - количество точек , попавших в отрезок )
И если взять все отрезки, количество которых K:
Здесь я взял разницу в квадрат, чтобы она всегда была больше 0, и чтобы в сумме на разных отрезках получалось положительное число. И чтобы итоговая сумма по всем отрезкам не равнялась нулю, а стремилась к некоторому минимуму. В этом уравнении мы хотим найти неизвестные .
Если раскрыть скобки, и сложить слагаемые, то получится уравнение следующего вида:
Видно что это уравнение второй степени с множеством неизвестных. А найти минимум такой функции можно напрямую: взяв производную и найдя её 0:
Итого, надо просто посчитать какие-то коэффициенты и затем решить СЛАУ, и у нас получятся коэффициенты полинома, который честным образом аппроксимирует данное распределение.
#Алгоритм
Входные данные:
- - массив значений размера M, распределение которых надо аппроксимировать.
- - степень итогового полинома.
- - количество отрезков.
Степень полинома, очевидно, надо брать небольшую (5-10), чтобы не "переобучаться" на данных, и не делать график, который равен гистограмме или ещё чему-то такому.
Количество отрезков нужно брать тоже не очень большое. Я прикинул, что вполне достаточно. В итоге на практике оказалось что оптимальным получается .
Значит весь алгоритм состоит из двух этапов:
- Перебор отрезков и получение матрицы и вектора.
- Решение СЛАУ размером , то есть не очень большой.
Перебирать отрезки можно двумя способами:
- Рандомно
- Организованно
В рандомном случае самой ресурсоёмкой операцией получается подсчёт числа точек, попадающих на этот отрезок, что занимает , если данные отсортированы.
Если же пытаться перебирать отрезки организованно, от минимума к максимуму, то можно использовать метод двух указателей, и за знать количество точек, находящихся на данном отрезке. А так как количество отрезков равно , то это - оптимальный вариант.
Итого весь алгоритм выглядит так в псевдокоде:
vec = /* одномерный вектор размера n+1 */
mat = /* двумерная матрица размера n+1 */
minv = D[0];
maxv = D[-1];
seg_count = sqrt(K);
step = (maxv - minv) / seg_count;
apos = 0;
for ai in 0..seg_count+1 {
a = minv + ai * step;
while apos < M && a > D[apos] { apos += 1; }
bpos = apos;
for bi in ai+1..seg_count+1 {
b = minv + bi * step;
while bpos < M && b > D[bpos] { bpos += 1; }
count = (bpos - apos) / M;
for i in 0..n+1 {
vec[i] += 2 * count * (b^(i+1) - a^(i+1))/(i+1);
}
for i in 0..n+1 {
for j in 0..n+1 {
to_add = (b^(i+1) - a^(i+1))/(i+1)
* (b^(j+1) - a^(j+1))/(j+1);
mat[i, j] += to_add;
mat[j, i] += to_add;
}
}
}
}
x = solve_SLE(vec, mat)
#Веб-интерфейс
Ссылки:
Я постарался и накодил веб-интерфейс где с этим алгоритмом можно поиграться. И вообще какой смысл выпускать говорить о том что ты придумал какой-то алгоритм, без кода? Вот если бы в той статье был хотя бы код, я не говорю про веб-интерфейс, может я бы сам ничего не придумывал бы...
Там уже вставлены данные моих полётов на дроне. Да, я отлетал 300 кругов за день, и что вы мне сделаете 😁. Видно как сильно прокачался навык на данной трассе на следующий день, что более быстрые времена стали намного чаще.