Интерполяция по Лагранжу

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

Если у вас возникла необходимость воспользоваться точечной интерполяцией, то в этом случае вам поможет небольшой класс, который реализует интерполяцию посредством полиномов Лагранжа:

class LagrangeInterpolator
{
	private:
		float[] xi;
		float[] yi;

	float basePolynom(float x, size_t n)
	{
		float product = 1;

		for (size_t i = 0; i < xi.length; i++)
		{
			if (i != n)
			{
				product *= (x - xi[i]) / (xi[n] - xi[i]);
			}
		}
		return product;
	}

	public:
		this()
		{

		}

		void setX(float[] xi)
		{
			this.xi = xi;
		}

		void setY(float[] yi)
		{
			this.yi = yi;
		}


		float interpolate(float x)
		{
			float sum = 0.0f;

			for (size_t i = 0; i < yi.length; i++)
			{
				sum += yi[i] * basePolynom(x, i);
			}
			return sum;
		}
}

unittest
{
	LagrangeInterpolator interpolator = new LagrangeInterpolator;
	interpolator.setX([0.0, 4.0, 6.0, 7.0, 8.0]);
	interpolator.setY([0.0, 2.0, 4.0, 6.0, 8.0]);

	
	assert(interpolator.interpolate(5.0) == 2.58929);
	assert(interpolator.interpolate(0.0) == 0.0);
	assert(interpolator.interpolate(6.0) == 4.0);
}

Сам класс очень просто устроен, а единственным используемым алгоритмом класса является метод interpolate, который просто вычисляет промежуточное значение Y для некоторой точки с уже известной координатой X. Таким образом, LagrangeInterpolator лучше всего использовать с некоторыми массивами известных значений функции, которые предварительно загружаются в объект класса с помощью тривиальных методов setX и setY, после применения которых и следует вызвать метод interpolate.

Загрузить значения для X и Y функции в интерполятор можно не только из заранее подготовленных массивов, но и к примеру из обычного CSV-файла, для чего можно воспользоваться готовым загрузчиком —  функцией readDataFromCSV, которая возвратит уже подготовленный к работе экземпляр LagrangeInterpolator:

auto readDataFromCSV(string filename, LagrangeInterpolator interpolator)
{
	import std.algorithm;
	import std.array;
	import std.csv;
	import std.stdio;
	import std.typecons;

	float[] xi, yi;

	auto file = File(filename, "r");

	void addPoint(float x, float y)
	{
		xi ~= x;
		yi ~= y;
	}
    
    file
		.byLine
		.joiner("\n")
		.csvReader!(Tuple!(float, float))
		.each!(a => addPoint(a[0], a[1]));

	interpolator.setX(xi);
	interpolator.setY(yi);

	return interpolator;
}

Естественно, помимо CSV-файлов возможно использование других форматов файла, но иногда бывает и так, что чего-то более хорошего, чем CSV-файла под рукой нет.

P.S: Более подробно про методы интерполяции вы можете узнать из курса математического анализа или же просто можете посмотреть сведения по ссылке, которая была указана выше.

Добавить комментарий