Leonid Eremin (vamireh) wrote,
Leonid Eremin
vamireh

Интерполяция NaN'ов

Однажды мне понадобилось интерполировать массив чисел, в которым переодические были double.NaN. Достаточно было интерполяции линейно между двумя ближайшими числами. Разумеется, нашел несколько реализаций, но они не работали при варианте, когда double.NaN находилсись в начале или конце массива, а также работали оченб долго. Например один из найденных мной вариантов работал на порядок дольше моего варианта. Правда, после моих патчей к нему он резко ускорился, но все равно работал почти вдвое медленнее моего варианта.
Однако мой вариант тоже не лишен недостатков, а именно: если double.NaN находятся в начале или в конце массива, то вместо линейной интерполяции они фактически заменяются крайними числами.
Собственно, вот код:

using System.Collections.Generic;
using System.Linq;

namespace Algebra
{
    public static class Interpolation
    {
        public static void InterpolateNan(ref List<double> list)
        {
            if (double.IsNaN(list[0]))
            {
                list[0] = list.First(t => !double.IsNaN(t));
            }

            if (double.IsNaN(list[list.Count - 1]))
            {
                list[list.Count - 1] = list.Last(t => !double.IsNaN(t));
            }

            for (int i = 0; i < list.Count; i++)
            {
                if (!double.IsNaN(list[i]))
                {
                    continue;
                }

                int begin = 0;
                int end = 0;

                for (int j = i; j >= 0; j--)
                {
                    begin = j;

                    if (!double.IsNaN(list[j]))
                    {
                        break;
                    }
                }

                for (int j = i; j < list.Count; j++)
                {
                    end = j;

                    if (!double.IsNaN(list[j]))
                    {
                        i = j;
                        break;
                    }
                }

                double delta = (list[end] - list[begin]) / (end - begin);
                double newVal = list[begin];

                for (int j = begin + 1; j < end; j++)
                {
                    newVal += delta;
                    list[j] = newVal;
                }
            }
        }
    }
}


Поясню один момент: первый цикл (там, где вычисляется begin) можно упразднить, т.к. и так ясно, чему будет равен begin. Однако, внезапно, из-за лишнего цикла функция работает несколько быстрее. Я не знаю почему, надо смотреть во что это компилируется.
Tags: .net, coding, dotnet, interpolation, nan, интерполяция, программирование
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 1 comment