Максимальное и минимальное значения в массиве PointF[]

У меня есть массив PointF, который я хочу использовать для рисования кривой с помощью метода Graphics.DrawCurve.

Для этого мне нужно теперь максимальное и минимальное значение как X, так и Y, чтобы я мог правильно масштабировать свое растровое изображение.

Как можно найти максимум и минимум для X и Y в массиве PointF?

Я придумал эту идею, но я не уверен, что это лучший способ!

    //Find the max value on X axis (Time) and Y axis (Current)
    float xMax = 0;
    float yMax = 0;

    foreach (PointF point in points)
    {
        if (point.X > xMax)
        {
            xMax = point.X;
        }

        if (point.Y > yMax)
        {
            yMax = point.Y;
        }
    }

person Saeid Yazdani    schedule 01.11.2011    source источник
comment
Если вы используете TDD, легко написать тесты для проверки правильности вашего алгоритма. В качестве бонуса вы получаете регрессионные тесты, которые очень полезны, если вы хотите изменить реализацию.   -  person Tim Lloyd    schedule 01.11.2011
comment
@ Moo-Juice Я не согласен с вашим заключительным голосованием, я уже видел этот пост, он слишком сложен для такого новичка, как я, и не могу мне помочь в данный момент.   -  person Saeid Yazdani    schedule 01.11.2011
comment
@Sean87, достаточно честно. :)   -  person Moo-Juice    schedule 01.11.2011


Ответы (4)


Вам нужно перебрать все элементы в массиве и проверить каждый из них на ограничивающую рамку, увеличивая ограничивающую рамку, когда текущий элемент находится за ее пределами. Как это:

Point 
  min = first item in array, 
  max = first item in array;

foreach (item in array of points)
{
  min.x = Math.Min (min.x, item.x)
  min.y = Math.Min (min.y, item.y)
  max.x = Math.Max (max.x, item.x)
  max.y = Math.Max (max.y, item.y)
}


(min,max) are now the opposite corners of an axis aligned bounding box

РЕДАКТИРОВАТЬ: у вас есть правильная идея, но есть API-интерфейс .Net framework для выполнения теста min/max: Math.Min и Math.Max. Если нет какой-либо другой информации о массиве точек, которую можно использовать для уменьшения количества тестов, вам придется проверять каждую точку в массиве. К сожалению, там нет коротких путей. Интересно, достаточно ли умен JIT-компилятор, чтобы использовать для этого SIMD?

Кроме того, инициализация со значением 0 может вызвать ошибку, если все точки в массиве меньше нуля.

person Skizz    schedule 01.11.2011
comment
Спасибо за советы! По оси X я имею дело со временем, поэтому оно всегда положительное число. Но для оси Y это будет сложнее, потому что мне нужно проверить максимальное положительное значение Y и максимальное минимальное значение Y (метод абс может пригодиться). - person Saeid Yazdani; 01.11.2011

Если вы найдете минимум (верхняя левая точка) и максимум (нижняя правая точка), вы можете рассчитать размер графика.

Во-первых, вам нужен способ сравнения значений Point — если класс Point (структура?) реализует IComparable, вы уже готовы к работе, в противном случае вам может потребоваться написать собственный класс IComparer.

Затем вы можете написать простой метод расширения для IEnumerable, чтобы получить минимальное или максимальное значение из коллекции:

static class ExtensionsClass
{
    /// <summary>
    /// Returns the mimimum value within the collection.
    /// </summary>
    static public T Min(this IEnumerable<T> values) where T : IComparable<T>
    {
        T min = values.First();

        foreach(T item in values)
        {
            if (item.CompareTo(min) < 0)
                min = item;
        }

        return min;
    }

    /// <summary>
    /// Returns the maximum value within the collection.
    /// </summary>
    static public T Max(this IEnumerable<T> values) where T : IComparable<T>
    {
        T max= values.First();

        foreach(T item in values)
        {
            if (item.CompareTo(min) > 0)
                max= item;
        }

        return max;
    }
}

Используя эти методы расширения, было бы намного проще найти точки минимума/максимума и, следовательно, размер графика.

var minX = points.Min().x;
var minY = points.Min().y;
var maxX = points.Max().x;
var maxY = points.Max().y;
person MattDavey    schedule 01.11.2011
comment
С этим решением вам придется перебирать массив 4 раза. В зависимости от размера массива это может быть проблемой. - person M4N; 01.11.2011
comment
@M4N M4N вам не должно быть, эти последние 4 строки в моем примере можно легко оптимизировать до 2 итераций. Дальнейшая оптимизация до 1 итерации потребует немного больше работы, но все же тривиальна, если будет определена как узкое место в производительности (очень маловероятно). - person MattDavey; 01.11.2011

Ваш код не годится. Если у вас есть точки (2, 4) и (3, 1), то xMax будет равно 3, а yMax будет равно 4, что не является одной точкой.

person petko_stankoski    schedule 01.11.2011
comment
Думаю, его мысль верна. Для построения графика макс. значения x/y не обязательно должны быть из одной и той же точки. (поэтому ваш ответ неверный). - person M4N; 01.11.2011

Используя RyuJIT и SIMD эти операции можно значительно ускорить.

  void MinMax(int[] a, out int minimum, out int maximum) {
  int simdLength = Vector<int>.Length;
  Vector<int> vmin = new Vector<int>(int.MaxValue);
  Vector<int> vmax = new Vector<int>(int.MinValue);
  for (int i = 0; i < a.Length; i += simdLength) {
      Vector<int> va = new Vector<int>(a, i);
      Vector<int> vLessThan = Vector.LessThan(va, vmin);
      vmin = Vector.ConditionalSelect(vLessThan, va, vmin);
      Vector<int> vGreaterThan = Vector.GreaterThan(va, vmax);
      vmax = Vector.ConditionalSelect(vGreaterThan, va, vmax);
  }
  int min = int.MaxValue, max = int.MinValue;
  for (int i = 0; i < simdLength; ++i) {
      min = Math.Min(min, vmin[i]);
      max = Math.Max(max, vmax[i]);
  }
  minimum = min;
  maximum = max;
}

Очевидно, замените массив int массивом PointF. По сути, здесь происходит то, что SIMD может обрабатывать минимальные и максимальные значения 4-8 элементов за итерацию цикла. Теоретически это обеспечит ускорение в 4-8 раз в зависимости от вашего процессора. Использование процессоров, поддерживающих AVX2, обеспечивает самый быстрый прирост производительности.

Источник: http://blogs.microsoft.co.il/sasha/2014/04/22/c-vectorization-microsoft-bcl-simd/

person Tim P.    schedule 22.07.2014