using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using TSLab.Script; using TSLab.Script.Handlers; using TSLab.Script.Helpers; namespace Trends { [HandlerCategory("Trends")] public class SplineTrend : IDouble2DoubleHandler, IContextUses { [HandlerParameter(true, "21", Min = "3", Max = "100", Step = "1")] public int Period { get; set; } public IContext Context { get; set; } public IList Execute(IList a) { int count = a.Count; var st = spline(a); double[] rez = new double[count]; for (var i = 1; i < count; i++) { rez[i] = st[i]; } return rez; } public IList spline(IList a) { int nm = a.Count; int dn = Period; double[] rez = new double[nm]; List r = lin_intervals(a); int im = r.Count; // цикл по периодам for (int i = 1; i <= im - 1; i++) { int n0 = (i - 1) * (dn - 1) + 1; // x0,y0 и x,y - координаты левой и правой границы текущего интервала (периода) double x00 = n0 - (dn - 1); double x0 = n0; double x = n0 + dn - 1; double x2 = n0 + 2 * (dn - 1); double y0 = r[i - 1]; double y = r[i]; // p0 и p - производные на левой и на правой границе double p0; if (i == 1) p0 = (r[1] - r[0]) / (x - x0); else p0 = 0.5 * (r[i - 1] - r[i - 2]) / (x0 - x00) + 0.5 * (r[i] - r[i - 1]) / (x - x0); double p; if (i == im - 1) p = (r[im - 1] - r[im - 2]) / (x - x0); else p = 0.5 * (r[i] - r[i - 1]) / (x - x0) + 0.5 * (r[i + 1] - r[i]) / (x2 - x); // f = alpha*x*x*x+beta*x*x+gamma*x+delta // f(x0) = y0 // f(x) = y // df/dx(x0) = p0 // df/dx(x) = p // четыре уравнения для четырех неизвестных double alpha = (p + p0 - 2 * (y - y0) / (x - x0)) / ((x - x0) * (x - x0)); double beta = 0.5 * ((p - p0) / (x - x0) - 3 * alpha * (x + x0)); double gamma = p0 - 3 * alpha * x0 * x0 - 2 * beta * x0; double delta = y0 - x0 * p0 + 2 * alpha * x0 * x0 * x0 + beta * x0 * x0; for (int n = n0; n <= n0 + dn - 1; n++) { if (n > nm) break; x = n; rez[n - 1] = alpha * x * x * x + beta * x * x + gamma * x + delta; } } return rez; } // возвращает значения на концах интервалов (периодов) для линейного тренда List lin_intervals(IList a) { int nm = a.Count; int dn = Period; List r = new List(); double sy = 0; for (int n = 1; n <= dn / 2; n++) { if (n > nm) break; sy += a[n - 1]; } r.Add(sy / (dn / 2)); for (int i = 1; i <= nm / (dn - 1) + 1; i++) { double sxx = 0; double sxy = 0; int n0 = (i - 1) * (dn - 1) + 1; if (n0 >= nm) break; double x0 = n0; double y0 = r[i - 1]; double x = 0; for (int n = n0; n <= n0 + dn - 1; n++) { if (n > nm) break; x = n; sxx += (x - x0) * (x - x0); sxy += (x - x0) * (a[n - 1] - y0); } double alpha = sxy / sxx; double beta = y0 - alpha * x0; r.Add(alpha * x + beta); } return r; } } }