#include "StdAfx.h" #include "Fuzzy.h" #include #include "Param.h" #include Fuzzy::Fuzzy(string trendType, string seasonType, vector timeSeries, int countPointForecast){ this->countPointForecast = countPointForecast; this->trendType = trendType; this->seasonType = seasonType; this->x = timeSeries; this->partition(); } Fuzzy::~Fuzzy(void) { } void Fuzzy::init() { defineUniversum(); fuzzyTs.clear(); } // определить универсум для временного ряда // ищем максимум, минимум, производим разбиение на заданные интервалы void Fuzzy::defineUniversum() { this->universumMax = x[0]; this->universumMin = x[0]; for (int i = 1; i < x.size(); i++) { if (universumMax < x[i]) { universumMax = x[i]; } if (universumMin > x[i]) { universumMin = x[i]; } } // создаем функции принадлежности // принимаем по умолчанию, что соседние перекрываются на половину между собой // раз есть количество интервалов, то вычисляем размер основания треугольника double baseSize = (universumMax - universumMin) / countFuzzyParts; a.resize(countFuzzyParts + 1); for (int i=0; i < countFuzzyParts + 1;i++){ a[i] = (A(universumMin + (i-1) * baseSize, universumMin + (i+1)*baseSize)); } } // фаззифицировать только одну точку // возвращает индекс функции принадлежности с максимальным значением int Fuzzy::fuzzyfication(double value) { // перебираем все функции принадлежности, // если нашли наибольшую степень принадлежности, то фиксируем индекс функции int indMax = 0; double max = a[0].getValue(value); for (int j =0; j < a.size(); j++) { if (a[j].getValue(value) > max) { indMax = j; max = a[j].getValue(value); } } return indMax; } void Fuzzy::createRules() { rulesIn.clear(); rulesIn.resize(fuzzyTs.size() - countRulesIn); rulesOut.clear(); rulesOut.resize(fuzzyTs.size() - countRulesIn); for (int i=0; i < fuzzyTs.size() - countRulesIn; i ++) { vector v; v.resize(countRulesIn); for (int j=i; j < i + countRulesIn; j++) { v[j-i] = fuzzyTs[j]; } rulesIn[i] = v; rulesOut[i] = fuzzyTs[i+countRulesIn]; } } vector Fuzzy::searchRules(vector inPart) { vector res(rulesOut.size()); int countRes = 0; for (int i = 0; i < rulesIn.size(); i++) { bool isRuleFound = true; for (int j = 0; j < rulesIn[i].size(); j++) { if (rulesIn[i][j] != inPart[j]) { isRuleFound = false; } } if (isRuleFound) { res[countRes++] = rulesOut[i]; } } res.resize(countRes); return res; } vector Fuzzy::searchRulesForSeason(int index) { vector res; int i =index - p; while (i > 0) { res.push_back(fuzzyfication(x[i])); i -= p; } return res; } void Fuzzy::createModelForEstimation() { init(); fuzzyTs.resize(xLearning.size()); for (int t = 0; t < xLearning.size(); t++) { fuzzyTs[t] = fuzzyfication(xLearning[t]); } createRules(); forecast.clear(); forecast.resize(fuzzyTs.size()+countPointForecast); for (int i=0; i < fuzzyTs.size();i++) { forecast[i] = defuzzyfication(fuzzyTs[i]); } int k = fuzzyTs.size(); for (int i=0; i < countPointForecast;i++) { vector lastPoints; lastPoints.resize(countRulesIn); for (int j = countRulesIn; j > 0; j--) { lastPoints[countRulesIn -j] = (fuzzyTs[fuzzyTs.size() - j]); } vector foundFuncs = searchRules(lastPoints); if (foundFuncs.size() == 0) { foundFuncs.push_back(fuzzyTs[fuzzyTs.size()-1]); } double sum = 0; for (int j =0; j < foundFuncs.size(); j++) { sum += a[foundFuncs[j]].getValueAtTop(); } // трендовая составляющая double globInfo = sum / foundFuncs.size(); A at = this->a[fuzzyTs[fuzzyTs.size()-1]]; A atPrev = this->a[fuzzyTs[fuzzyTs.size()-2]]; double localInfo = at.getLeft() + (at.getRight() - at.getLeft())/2 + (at.getValueAtTop() - atPrev.getValueAtTop()) / (at.getValueAtTop() + atPrev.getValueAtTop()); // сезонная компонента vector foundSeasonFuncs = searchRulesForSeason(fuzzyTs.size()); sum = 0; for (int j =0; j < foundSeasonFuncs.size(); j++) { sum += a[foundSeasonFuncs[j]].getValueAtTop(); } double valueAtSeason = sum / foundSeasonFuncs.size(); if (trendType.compare("None") == 0) { if (seasonType.compare("None") == 0) { forecast[i+k] = (localInfo); } else if (seasonType.compare("Add") == 0) { forecast[i+k] = (gamma * localInfo + (1-gamma) * valueAtSeason); } } else if (trendType.compare("Add") == 0) { if (seasonType.compare("None") == 0) { forecast[i+k] = (delta * localInfo + (1-delta) * globInfo); } else if (seasonType.compare("Add") == 0) { forecast[i+k] = (gamma * delta * localInfo + (1-gamma) * valueAtSeason + (1-delta) * globInfo); } } int index = fuzzyfication(forecast[i+k]); // добавить прогнозую точку в конец ряда fuzzyTs.push_back(index); xLearning.push_back(forecast[i+k]); createRules(); } for (int i=0; i < countPointForecast;i++) { fuzzyTs.pop_back(); rulesIn.pop_back(); rulesOut.pop_back(); xLearning.pop_back(); } } // фаззиикация, создать список функций принадлежности по всему ряду void Fuzzy::createModel() { init(); fuzzyTs.resize(x.size()); for (int i = 0; i < x.size(); i++) { fuzzyTs[i] = fuzzyfication(x[i]); } createRules(); forecast.clear(); forecast.resize(fuzzyTs.size()+countPointForecast); for (int i=0; i < fuzzyTs.size();i++) { forecast[i] = defuzzyfication(fuzzyTs[i]); } int k = fuzzyTs.size(); for (int i=0; i < countPointForecast;i++) { vector lastPoints; lastPoints.resize(countRulesIn); for (int j = countRulesIn; j > 0; j--) { lastPoints[countRulesIn -j] = (fuzzyTs[fuzzyTs.size() - j]); } vector foundFuncs = searchRules(lastPoints); if (foundFuncs.size() == 0) { foundFuncs.push_back(fuzzyTs[fuzzyTs.size()-1]); } double sum = 0; for (int j =0; j < foundFuncs.size(); j++) { sum += a[foundFuncs[j]].getValueAtTop(); } // трендовая составляющая double globInfo = sum / foundFuncs.size(); A at = this->a[fuzzyTs[fuzzyTs.size()-1]]; A atPrev = this->a[fuzzyTs[fuzzyTs.size()-2]]; double localInfo = at.getLeft() + (at.getRight() - at.getLeft())/2 + (at.getValueAtTop() - atPrev.getValueAtTop()) / (at.getValueAtTop() + atPrev.getValueAtTop()); // сезонная компонента vector foundSeasonFuncs = searchRulesForSeason(fuzzyTs.size()); sum = 0; for (int j =0; j < foundSeasonFuncs.size(); j++) { sum += a[foundSeasonFuncs[j]].getValueAtTop(); } double valueAtSeason = sum / foundSeasonFuncs.size(); if (trendType.compare("None") == 0) { if (seasonType.compare("None") == 0) { forecast[i+k] = (localInfo); } else if (seasonType.compare("Add") == 0) { forecast[i+k] = (gamma * localInfo + (1-gamma) * valueAtSeason); } } else if (trendType.compare("Add") == 0) { if (seasonType.compare("None") == 0) { forecast[i+k] = (delta * localInfo + (1-delta) * globInfo); } else if (seasonType.compare("Add") == 0) { forecast[i+k] = (gamma * delta * localInfo + (1-gamma) * valueAtSeason + (1-delta) * globInfo); } } int index = fuzzyfication(forecast[i+k]); // добавить прогнозую точку в конец ряда fuzzyTs.push_back(index); x.push_back(forecast[i+k]); createRules(); } for (int i=0; i < countPointForecast;i++) { fuzzyTs.pop_back(); rulesIn.pop_back(); rulesOut.pop_back(); x.pop_back(); } } vector Fuzzy::getForecast() { vector result; for (unsigned int i = forecast.size() - countPointForecast; i < forecast.size(); i++) { result.push_back(forecast[i]); } return forecast; } double Fuzzy::defuzzyfication(int index) { return this->a[index].getValueAtTop(); } void Fuzzy::setParam(string paramName, double value) { if (paramName.compare("countFuzzyParts") == 0) { this->countFuzzyParts = value; } else if (paramName.compare("countRulesIn") == 0) { if (this->xLearning.size() < value) { this->countRulesIn = this->xLearning.size(); } else { this->countRulesIn = value; } } else if (paramName.compare("p") == 0) { this->p = value; } else if (paramName.compare("gamma") == 0) { this->gamma = value; } else if (paramName.compare("delta") == 0) { this->delta = value; } if (paramName.compare("0") == 0) { this->countFuzzyParts = value * 100; } else if (paramName.compare("1") == 0) { if (this->xLearning.size() < value * 5) { this->countRulesIn = this->xLearning.size(); } else { this->countRulesIn = value * 5; } } else if (paramName.compare("2") == 0) { this->gamma = value; } else if (paramName.compare("3") == 0) { this->delta = value; } } // метод получения оценки модели double Fuzzy::calcEstimation(Aic *aic) { return aic->getValue(3, this->xEstimation, this->forecast); } // метод получения оптимизированного значения одного параметра // TODO: реализовать Param* Fuzzy::optimize(Estimation *est) { Param *optimal = new Param(); double minSmape = 99999; for (double gam = 0; gam < 1; gam+= 0.1) { cout << "fuzzy " << gam << " из 1" <<"\n";; for (double del = 0; del < 1;del+= 0.1) { for (double cri = 1; cri < 5;cri+= 1) { for (double cfp = 2; cfp < 50;cfp+= 2) { this->setParam("gamma", gam); this->setParam("delta", del); this->setParam("countRulesIn", cri); this->setParam("countFuzzyParts", cfp); double smapeValue = 0; int maxShift = 5; if (maxShift > this->countPointForecast) { maxShift = this->countPointForecast-1; } this->countPointForecast -= maxShift; for (int shift=0; shift <= maxShift; shift++) { this->partition(); this->createModelForEstimation(); smapeValue += est->getValue(x, getForecast()); this->countPointForecast++; } this->countPointForecast--; smapeValue = smapeValue / maxShift; if (minSmape > smapeValue) { minSmape = smapeValue; optimal->gamma = gam; optimal->delta = del; optimal->estim = smapeValue; optimal->countFuzzyParts = cfp; optimal->countRulesIn = cri; } } } } } return optimal; } int Fuzzy::getNamberParam() { return 4; }