Максимални и минимални стойности в масив PointF[].

Имам масив от PointF, който искам да използвам за чертане на крива с метода Graphics.DrawCurve.

За да направя това, сега трябва да задам max и min както на X, така и на Y, за да мога правилно да мащабирам кутията си с растерно изображение.

Как е възможно да се намерят max и min за 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

РЕДАКТИРАНЕ: Имате правилната идея, но има .Net framework API за извършване на теста за мин./макс.: Math.Min и Math.Max. Освен ако няма друга информация за масива от точки, която може да се използва за намаляване на броя на тестовете, ще трябва да тествате всяка точка в масива. За съжаление там няма преки пътища. Чудя се дали JIT компилаторът е достатъчно умен, за да използва SIMD за това?

Освен това инициализирането със стойност 0 може да причини грешка, ако всички точки в масива са по-малки от нула.

person Skizz    schedule 01.11.2011
comment
Благодаря за съветите! По оста X имам работа с времето, така че то винаги е положително число. Но за оста Y ще бъде по-сложно, защото трябва да проверя за максимално положително Y и максимално минимално Y (методът abs може да бъде полезен). - person Saeid Yazdani; 01.11.2011

Ако намерите минимума (горната лява точка) и максимума (долната дясна точка), можете да изчислите размера на графиката.

Първо се нуждаете от начин да сравнявате стойностите на Point - ако класът Point (struct?) внедрява 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 не би трябвало, тези последни 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-8x ускорение в зависимост от вашия процесор. Използването на процесори, които поддържат AVX2, осигурява най-бързото повишаване на производителността.

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

person Tim P.    schedule 22.07.2014