Рассмотрим многочлен вида

Очевидно, что . Сложив подобных многочленов можно получить

где штрих у произведения означает, что произведение берётся по всем . Этот многочлен называется многочленом Лагранжа. Нетрудно видеть, что

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

где коэффициенты

зависят только от разбиения и не зависят от интегрируемой функции. Следовательно, они общие для всех функций и для расчёта интеграла больше не нужно решать систему.

Для большего обобщения можно рассмотреть отображение

Тогда

Точки называются узлами разбиения, а соответствующие им значения – весами.

Теперь обратимся к задаче поиска весов. Подынтегральное произведение является многочленом степени и может быть представлено в виде

Для определения коэффициентов рассмотрим вспомогательную задачу: определим коэффициенты произведения многочлена степени на двучлен . Расписывая, получим:

Этот процесс можно описать в программе, представив многочлен в виде списка его коэффициентов:

def polynomial_multiply(p, a):
    '''
        Вычисляет коэффициенты многочлена, который получается при умножении
        многочлена с коэффициентами p на (x-a)

        p -- список коэффициентов [a0, a1, … , an]
    '''
    n = len(p)
    result = []

    # начинаем формировать результат:
    # 1. добавляем свободный член
    result.append(-a * p[0])

    # 2. добавляем все коэффициенты, кроме самого старшего
    for i in range(1, n):
        result.append(p[i-1] - a * p[i])

    # 3. добавляем старший коэффициент
    result.append(p[-1])
    return result

# пример использования:
polynomial_multiply([1], 2)     # [-2, 1]    : 1 * (x - 2) = x - 2
polynomial_multiply([-2, 1], 2) # [4, -4, 1] : (x - 2) * (x - 2) = x^2 - 4x + 4

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

def polynomial_from_roots(ys):
    '''
        Вычисляет список коэффициентов многочлена, имеющего корни ys
    '''
    result = [1]
    for y in ys:
        result = polynomial_multiply(result, y)
    return result

Зная коэффициенты, можно рассчитать значение многочлена в любой точке. Это нам потребуется для определения знаменателя:

def polynomial_value(p, x):
    '''
        Вычисляет значение многочлена с коэффициентами p в точке x
    '''
    return sum(a * x ** i for i, a in enumerate(p))

Для определения веса нужно проинтегрировать полученный многочлен на промежутке :

Видно, что слагаемые с нечётными обращаются в , что позволяет написать вот такой вот код:

def polynomial_integrate(p):
    '''
        Интегрирует многочлен с коэффициентами p на [-1, 1]
    '''
    return sum(2 * a / (i + 1) for i, a in enumerate(p) if i % 2 == 0)

Ну а теперь, собрав всё вместе, можно определить веса:

def weights(ys):
    '''
        Рассчитывает веса
    '''
    result = []
    for i, y in enumerate(ys):
        # сначала формируем список [yj], j ≠ i
        yj = ys[:i] + ys[i + 1:]

        # определяем коэффициенты числителя
        p = polynomial_from_roots(yj)

        # определяем знаменатель
        value = polynomial_value(p, y)

        # рассчитываем вес и добавляем его в список весов
        result.append(polynomial_integrate(p) / value / 2)
    return result

Внимательный читатель заметит, что произведения отличаются друг от друга только одним множителем, поэтому их можно не пересчитывать несколько раз. Вместо этого можно посчитать произведение всех двучленов, а затем получать каждый многочлен после однократного деления полного произведения на . Для этого можно воспользоваться схемой Горнера.

Ну а теперь можно коротко и ясно описать интегрирование:

def integrate_lagrange(f, a, b, n):
    '''
        Интегрирование полиномом Лагранжа степени n
    '''
    # создаём разбиение отрезка [-1, 1]
    ys = [-1 + 2 * float(i) / n for i in range(n + 1)]

    # считаем веса:
    w = weights(ys)

    # считаем ответ
    x = lambda y: (b + a) / 2 + y * (b - a) / 2 # отображает разбиение ys на
                                                # промежуток интегрирования
    s = sum(w[i] * f(x(y)) for i, y in enumerate(ys))
    return s * (b - a)

Обратите внимание, что пределы интегрирования нужно передавать как числа с плавающей точкой, то есть не 0 или 1, а 0.0 и 1.0. Это особенность python2.