Как рассчитать коэффициенты многочлена с помощью интерполяции Лагранжа

Мне нужно вычислить коэффициенты полинома, используя полином интерполяции Лагранжа, в качестве домашнего задания я решил сделать это в Javascript.

вот определение многочлена Лагранжа (L(x))

введите здесь описание изображения

Базисные полиномы Лагранжа определяются следующим образом

введите здесь описание изображения

Вычислить значение y для конкретного X (функция W(x)) просто, но мне нужно вычислить коэффициенты многочлена (массив [a0, a1, ..., an]). Мне нужно сделать это до n‹=10, но это было бы неплохо иметь произвольное n, тогда я могу поместить эту функцию в функцию Хорнера и нарисовать этот многочлен.

введите здесь описание изображения

У меня есть функция, которая вычисляет знаменатель в первом уравнении

function denominator(i, points) {
   var result = 1;
   var x_i = points[i].x;
   for (var j=points.length; j--;) {
      if (i != j) {
        result *= x_i - points[j].x;
      }
   }
   return result;
}

и функция, которая возвращает y с помощью метода Хорнера (у меня также есть функция рисования с использованием холста)

function horner(array, x_scale, y_scale) {
   function recur(x, i, array) {
      if (i == 0) {
         return x*array[0];
      } else {
         return array[i] + x*recur(x, --i, array);
      }
   }
   return function(x) {
      return recur(x*x_scale, array.length-1, array)*y_scale;
   };
}

кто-нибудь знает алгоритм для этого или идею, как рассчитать эти коэффициенты


person jcubic    schedule 25.03.2012    source источник
comment
Не используйте Википедию — planetmath.org/encyclopedia/LagrangePolynomial.html   -  person James Sumners    schedule 25.03.2012
comment
@JamesSumners — у Википедии есть несколько существенных преимуществ, например, страницы с гораздо меньшей вероятностью исчезнут и закончатся кодом 404, как ваша ссылка.   -  person reducing activity    schedule 06.06.2015
comment
Ну что 404 удивительно. Это довольно важная алгебраическая концепция. PlanetMath почти в каждом случае имеет более подробное объяснение, и оно было сделано, когда я добавил ссылку.   -  person James Sumners    schedule 07.06.2015
comment
https://web.archive.org/web/20180110121005/http://planetmath.org:80/lagrangeinterpolationformula ‹== ссылка на архив, чтобы обойти 404 в моем предыдущем комментарии   -  person James Sumners    schedule 05.05.2019


Ответы (3)


Ну, вы можете сделать это наивным способом. Представьте многочлен массивом его коэффициентов, массивом

[a_0,a_1,...,a_n]

соответствующий a_0 + a_1*X + ... + a_n*X^n. Я плохо разбираюсь в JavaScript, поэтому псевдокод должен будет сделать:

interpolation_polynomial(i,points)
    coefficients = [1/denominator(i,points)]
    for k = 0 to points.length-1
        if k == i
            next k
        new_coefficients = [0,0,...,0] // length k+2 if k < i, k+1 if k > i
        if k < i
            m = k
        else
            m = k-1
        for j = m downto 0
            new_coefficients[j+1] += coefficients[j]
            new_coefficients[j] -= points[k]*coefficients[j]
        coefficients = new_coefficients
    return coefficients

Начните с постоянного многочлена 1/((x_1-x_0)* ... *(x_i-x_{i-1})*(x_i-x_{i+1})*...*(x_i-x_n)) и умножьте на X - x_k все k != i. Итак, это дает коэффициенты для Li, затем вы просто умножаете их на yi (вы можете сделать это, инициализируя coefficients в y_i/denominator(i,points), если вы передаете значения y в качестве параметров ) и, наконец, сложите все коэффициенты вместе.

polynomial = [0,0,...,0] // points.length entries
for i = 0 to points.length-1
    coefficients = interpolation_polynomial(i,points)
    for k = 0 to points.length-1
        polynomial[k] += y[i]*coefficients[k]

Вычисление каждого Li — это O(n²), поэтому общее вычисление равно O(n³).

Обновление: В вашем jsFiddle у вас была ошибка в цикле полиномиального умножения в дополнение к (теперь исправленной) ошибке с начальным индексом, которую я сделал, она должна быть

for (var j= (k < i) ? (k+1) : k; j--;) {
     new_coefficients[j+1] += coefficients[j];
     new_coefficients[j] -= points[k].x*coefficients[j];
}

Поскольку при тестировании вы уменьшаете j, его нужно начинать на единицу выше.

Это еще не дает правильной интерполяции, но, по крайней мере, более разумно, чем раньше.

Кроме того, в вашей функции horner

function horner(array, x_scale, y_scale) {
   function recur(x, i, array) {
      if (i == 0) {
         return x*array[0];
      } else {
         return array[i] + x*recur(x, --i, array);
      }
   }
   return function(x) {
      return recur(x*x_scale, array.length-1, array)*y_scale;
   };
}

вы дважды умножаете старший коэффициент на x, должно получиться

if (i == 0) {
    return array[0];
}

вместо. Но все равно нет хорошего результата.

Update2: Окончательные исправления опечаток, следующие работы:

function horner(array, x_scale, y_scale) {
   function recur(x, i, array) {
      if (i == 0) {
         return array[0];
      } else {
         return array[i] + x*recur(x, --i, array);
      }
   }
   return function(x) {
      return recur(x*x_scale, array.length-1, array)*y_scale;
   };
}

// initialize array
function zeros(n) {
   var array = new Array(n);
   for (var i=n; i--;) {
     array[i] = 0;
   }
   return array;
}

function denominator(i, points) {
   var result = 1;
   var x_i = points[i].x;
   for (var j=points.length; j--;) {
      if (i != j) {
        result *= x_i - points[j].x;
      }
   }
    console.log(result);
   return result;
}

// calculate coefficients for Li polynomial
function interpolation_polynomial(i, points) {
   var coefficients = zeros(points.length);
    // alert("Denominator " + i + ": " + denominator(i,points));
   coefficients[0] = 1/denominator(i,points);
    console.log(coefficients[0]);
    //new Array(points.length);
   /*for (var s=points.length; s--;) {
      coefficients[s] = 1/denominator(i,points);
   }*/
   var new_coefficients;

   for (var k = 0; k<points.length; k++) {
      if (k == i) {
        continue;
      }
      new_coefficients = zeros(points.length);
       for (var j= (k < i) ? k+1 : k; j--;) {
         new_coefficients[j+1] += coefficients[j];
         new_coefficients[j] -= points[k].x*coefficients[j];
      }   
      coefficients = new_coefficients;
   }
   console.log(coefficients);
   return coefficients;
}

// calculate coefficients of polynomial
function Lagrange(points) {
   var polynomial = zeros(points.length);
   var coefficients;
   for (var i=0; i<points.length; ++i) {
     coefficients = interpolation_polynomial(i, points);
     //console.log(coefficients);
     for (var k=0; k<points.length; ++k) {
       // console.log(points[k].y*coefficients[k]);
        polynomial[k] += points[i].y*coefficients[k];
     }
   }
   return polynomial;
}
person Daniel Fischer    schedule 25.03.2012
comment
Я не могу заставить его работать в JS, что означает эта строка coefficients = [1/denominator(i,points)] создать массив с одним элементом или создать каждый элемент одинаковым или каждый элемент должен быть другим? - person jcubic; 26.03.2012
comment
Первый, массив с одним элементом. Вы также можете создать более длинный массив и установить для всех остальных записей значение 0. Глядя на вашу функцию horner, я просто замечаю, что вы используете массивы в качестве коэффициентов с a[0], соответствующим коэффициенту наивысшей степени, в то время как я сделал его постоянным членом. Если вы этого не заметили, это привело бы к совершенно неправильным результатам. Вы должны обратить некоторые массивы. - person Daniel Fischer; 26.03.2012
comment
Я создал jsfiddle jsfiddle.net/JdwNw, и только 1 и последний полином в функции interpolation_polynomial возвращают некоторые значения . Я думаю, что приму ответ и создам для этого еще один вопрос. - person jcubic; 26.03.2012
comment
Немного повозившись со скрипкой, denominator(i,points) имеет тенденцию возвращать NaN. Я в недоумении, почему, хотя. - person Daniel Fischer; 26.03.2012
comment
@daniel-fisher Я поставил console.log(result) в функцию знаменателя и console.log(coefficients[0]); в функцию interpolation_polynomial, и обе они возвращают числа. - person jcubic; 26.03.2012
comment
Ой, я сменил поколение points и все испортил. - person Daniel Fischer; 26.03.2012
comment
давайте продолжим это обсуждение в чате - person Daniel Fischer; 26.03.2012

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

import numpy as np
# input
x = [0, 2, 4, 5]  # <- x's
y = [2, 5, 7, 7]  # <- y's
# calculating coefficients
M = [[_x**i*(-1)**(i*len(x)) for _x in x] for i in range(len(x))]
C = [np.linalg.det((M+[y]+M)[d:d+len(x)]) for d in range(len(x)+1)]
C = (C / C[0] * (-1)**(len(x)+1) )[1:]
# polynomial lambda-function
poly = lambda _x: sum([C[i] * _x**i for i in range(len(x))])
# output and tests
print("Coefficients:\n", C)
print("TESTING:")
for _x, _y in zip(x, y):
    result = "[OK]" if np.allclose(_y, poly(_x)) else "[ERROR]"
    print(_x, " mapped to: ", poly(_x), " ; expected: ", _y, result)

Этот код вычисляет коэффициенты интерполяционного полинома Лагранжа, печатает их и проверяет, преобразуются ли заданные значения x в ожидаемые значения y. Вы можете протестировать этот код с помощью Google colab, поэтому вам не нужно установить что-нибудь. Возможно, вы сможете перевести его на JS.

person guest    schedule 31.05.2019
comment
Я не думаю, что это будет полезно, потому что для этого требуется использование numpy, который скрывает все алгоритмы за API. Так что это не говорит вам, как это на самом деле работает. Этот код слишком высокоуровневый, чтобы его можно было использовать на другом языке, кроме Python. - person jcubic; 31.05.2019
comment
Мне нужно время, чтобы подумать... Возможно, я смогу найти способ свести к минимуму использование numpy. - person guest; 31.05.2019
comment
Кажется, единственная функция numpy, которая жизненно важна для расчета коэффициентов, - это определитель (numpy.linalg.det) - я смог удалить все остальные (кроме numpy.allclose, который используется только для тестирования). - person guest; 01.06.2019

Этот код будет определять коэффициенты, начиная с постоянного члена.

var xPoints=[2,4,3,6,7,10]; //example coordinates
var yPoints=[2,5,-2,0,2,8];
var coefficients=[];
for (var m=0; m<xPoints.length; m++) coefficients[m]=0;
    for (var m=0; m<xPoints.length; m++) {
        var newCoefficients=[];
        for (var nc=0; nc<xPoints.length; nc++) newCoefficients[nc]=0;
        if (m>0) {
            newCoefficients[0]=-xPoints[0]/(xPoints[m]-xPoints[0]);
            newCoefficients[1]=1/(xPoints[m]-xPoints[0]);
    } else {
        newCoefficients[0]=-xPoints[1]/(xPoints[m]-xPoints[1]);
        newCoefficients[1]=1/(xPoints[m]-xPoints[1]);
    }
    var startIndex=1; 
    if (m==0) startIndex=2; 
    for (var n=startIndex; n<xPoints.length; n++) {
        if (m==n) continue;
        for (var nc=xPoints.length-1; nc>=1; nc--) {
            newCoefficients[nc]=newCoefficients[nc]*(-xPoints[n]/(xPoints[m]-xPoints[n]))+newCoefficients[nc-1]/(xPoints[m]-xPoints[n]);
        }
        newCoefficients[0]=newCoefficients[0]*(-xPoints[n]/(xPoints[m]-xPoints[n]));
    }    
    for (var nc=0; nc<xPoints.length; nc++) coefficients[nc]+=yPoints[m]*newCoefficients[nc];
}

person Kevin Bertman    schedule 17.04.2020