Столкнулся я тут с проблемой. Есть набор значений функции x[n] и y[n];
Функция одной переменной и n коэффициентов. Предположим:
И мне неизвестны коэффициенты a,b,c;
Я заглянул в гугл и подумал, что эту задачу лучше всего решать методом градиентного спуска.
Я понимаю так этот алгоритм:
Есть x[n],y[n] и F(x,args[m]), которая, предположительно, описывает зависимость y[n] от x[n].
Шаг 1. - устанавливаем начальные значения args[m]. Вычисляем новый Y, назовём его Ych
Шаг 2. - вычисляем сумму квадратов невязок (Y[i]-Ych[i])^2. если она не равна 0 то вычисляем градиент и вектор градиента.
Шаг 3.Если модуль градиента не равен нулю. Изменяем каждый аргумент на значение вектора градиента.
Шаг 4. переходим к шагу 2
Наваял я такое изобилие
#include <math.h>
//#include "Solover.h"
#include <iostream>
using namespace std;
struct GARDIENT {
double* vector;//массив вектора гардиента
int len;// длинна вектора
double Norm;//значение гардиента
};
double deltaX = 1e-6; //шаг для дифиринцирования
int len = 10; // длинна массива исходных точек
int lenargs = 3;// длинна массива аргументов
double *x = new double[len]; //ИМХО массив иксов
double *y = new double[len]; //ИМХО массив Y
double *args = new double[lenargs]; // массив аргументов
double *Ych;// массив апроксимированных значений
double Funk(double x,double* args){
// тестовая функция
double a = args[0];
double b = args[1];
double c = args[2];
return a+b*(1-exp(c*x));
}
void countYch(){
// функция считает Y с текущими коеффициентами
Ych = new double[len];
for(int i =0;i<len;i++)
Ych[i]=Funk(x[i],args);
}
double SummAbsValsResiduals(){
// вычисление суммы квадратов невязок
double Summ = 0;
countYch();
for (int i =0;i<len;i++){
Summ+=pow((y[i]-Ych[i]),2);
}
return Summ;
}
double Derivative(int DerivativeVar){
//производная
args[DerivativeVar]-=deltaX;
double Res1 = SummAbsValsResiduals();
args[DerivativeVar]+=2*deltaX;
double Res2 = SummAbsValsResiduals();
double deltaY = Res2-Res1;
args[DerivativeVar]-=deltaX;
return deltaY/(2*deltaX);
}
GARDIENT Gardient(){
//гардиент
GARDIENT result;
result.Norm = 0;
result.len = lenargs;
result.vector = new double[lenargs];
for(int i =0;i<result.len ;i++){
double derivative = Derivative(i);
result.Norm = result.Norm+derivative*derivative;
result.vector[i]= derivative*deltaX;
}
result.Norm = sqrt(result.Norm);
return result;
}
int main(){
double a = 1;
double b = 2;
double c = 2;
// исходные коэффициенты
args[0]=a;
args[1]=b;
args[2]=c;
for(int i = 0;i<len;i++){
x[i]=i;
y[i] = Funk(x[i],args);
}
//коефициенты поменялись
args[0]=1;
args[0]=2;
args[0]=1;
//их надо восстановить
countYch();
GARDIENT gar = Gardient();
int n = 0;
while((gar.Norm>0.1)&&(n<1e6)){
// пока мы не пришли к локальному минимуму функции
n++;
countYch();
gar = Gardient();
for(int i =0;i<gar.len;i++){
args[i]=args[i]-gar.vector[i];
}
cout<<"----"<<gar.Norm<<endl;
for(int i = 0;i<gar.len;i++){
cout<<"---->"<<gar.vector[i]<<endl;//значение вектор гардиента для каждой переменной
}
cout<<"SUMM : "<<SummAbsValsResiduals()<<endl;// сумма на текущем этапе
}
return 1;
}
Однако, изобилие работает паршиво. Можете ли мне подсказать почему? Я подозреваю в неправильной работе функцию вычисления производной или градиента.
Производную я вычисляю вычисляя значения функции на отрезке + dx и -dx , и делю эти (dy1+dy2)/2dx
И порой, даже когда я в локальном минимуме, этот метод всё-равно мне даёт очень не хорошие значения , которые порой, становтся критическими для работы программы
Я подозреваю примерно в таких случаях:
как с этим бороться? Как правильно минимизировать сумму квадратом невязок?