Однажды появилась необходимость построить кривую по точкам...

Кривая 2-й степени

https://github.com/pravosleva/get-parabola

import Coeffs from 'get-parabola';

const lineFn = Coeffs.getLineByLeastSquaresApproximation([
  { x: 4, y: 4 },
  { x: 5, y: 5 },
  { x: 6, y: 6 },
  { x: 10, y: 7 },
  { x: 12, y: 8 },
  { x: 15, y: 9 },
]);

console.log(lineFn(5));
// 4.92487131748987

Кривая 5-й степени

Для реализации аппроксимации методом наименьших квадратов (МНК) для полинома 5-й степени, нам нужно решить систему линейных уравнений вида:

S x A = Y

Где: S — матрица сумм степеней x, A — вектор искомых коэффициентов, Y — вектор свободных членов (суммы произведений степеней x на y).

Математическая справка

Для полинома пятой степени нам потребуется матрица размером 6x6. Использование ручного вычисления определителей (как в вашем примере для 2-й степени) для такой размерности крайне неэффективно и чревато ошибками. Лучше использовать метод Гаусса.

const byFifthDegreeLeastSquaresApproximation = (coordinatesArray) => {
  const n = 6; // Количество коэффициентов для 5-й степени
  const size = coordinatesArray.length;
  
  // Создаем расширенную матрицу (n строк на n+1 столбцов)
  const matrix = Array.from({ length: n }, () => new Array(n + 1).fill(0));

  // 1. Заполняем матрицу суммами степеней x и произведений x^k * y
  for (let i = 0; i < size; i++) {
    const { x, y } = coordinatesArray[i];
    
    for (let row = 0; row < n; row++) {
      // Заполнение коэффициентов системы
      for (let col = 0; col < n; col++) {
        // Степень x будет ( (n-1-row) + (n-1-col) )
        // Идем от x^10 до x^0
        matrix[row][col] += Math.pow(x, (n - 1 - row) + (n - 1 - col));
      }
      // Заполнение правой части (свободных членов)
      matrix[row][n] += Math.pow(x, n - 1 - row) * y;
    }
  }

  // 2. Решение системы методом Гаусса (прямой и обратный ход)
  for (let i = 0; i < n; i++) {
    // Поиск максимального элемента в столбце (для устойчивости)
    let max = i;
    for (let k = i + 1; k < n; k++) {
      if (Math.abs(matrix[k][i]) > Math.abs(matrix[max][i])) max = k;
    }
    [matrix[i], matrix[max]] = [matrix[max], matrix[i]];

    // Приведение к треугольному виду
    for (let k = i + 1; k < n; k++) {
      const c = -matrix[k][i] / matrix[i][i];
      for (let j = i; j <= n; j++) {
        if (i === j) {
          matrix[k][j] = 0;
        } else {
          matrix[k][j] += c * matrix[i][j];
        }
      }
    }
  }

  // Обратный ход
  const res = new Array(n).fill(0);
  for (let i = n - 1; i >= 0; i--) {
    res[i] = matrix[i][n] / matrix[i][i];
    for (let k = i - 1; k >= 0; k--) {
      matrix[k][n] -= matrix[k][i] * res[i];
    }
  }

  // Возвращаем коэффициенты от x^5 до свободного члена
  return {
    a: res[0], // x^5
    b: res[1], // x^4
    c: res[2], // x^3
    d: res[3], // x^2
    e: res[4], // x^1
    f: res[5]  // константа
  };
};

const getFifthDegreeLineByLeastSquaresApproximation = (points) => {
  const coeffs = byFifthDegreeLeastSquaresApproximation(points);
  const { a, b, c, d, e, f } = coeffs;

  return x => 
    (a * Math.pow(x, 5)) + 
    (b * Math.pow(x, 4)) + 
    (c * Math.pow(x, 3)) + 
    (d * Math.pow(x, 2)) + 
    (e * x) + f;
};

Ключевые изменения:

  1. Универсальность: Код использует циклы для построения матрицы, что позволяет легко изменить степень полинома в будущем (просто изменив n).
  2. Метод Гаусса: Ручной расчет формул (как k1, k2, l в вашем примере) для 5-й степени занял бы сотни строк кода и был бы крайне нестабилен.
  3. Именование: Соблюдено ваше правило именования функций.

Обратите внимание: Полиномы высоких степеней (выше 3-4) очень чувствительны к точности данных и могут вести себя непредсказуемо между точками (эффект Рунге). Убедитесь, что у вас достаточно данных (минимум 6 точек) для построения такой кривой.