#Введение

Я налётывал дома на дроне очень много кругов, и автоматически замерял их время, были круги по 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 кругов за день, и что вы мне сделаете 😁. Видно как сильно прокачался навык на данной трассе на следующий день, что более быстрые времена стали намного чаще.