【问题标题】:Maximum and Minimum values in a PointF[] arrayPointF[] 数组中的最大值和最小值
【发布时间】:2014-07-22 11:47:10
【问题描述】:

我有一个 PointF 数组,我想用它来使用 Graphics.DrawCurve 方法绘制曲线。

为此,我现在需要设置 X 和 Y 的最大值和最小值,以便正确缩放位图图像框。

如何在 PointF 的数组中找到 X 和 Y 的最大值和最小值?

我想出了这个想法,但我不确定这是否是最好的方法!

    //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;
        }
    }

【问题讨论】:

  • 如果您使用 TDD,很容易编写测试来探索算法的正确性。作为奖励,您可以获得回归测试,如果您希望更改实现,这将非常有用。
  • @Moo-Juice 我不同意你的最后一票,我已经看到那个帖子了,对于像我这样的初学者来说太复杂了,目前无法帮助我。
  • @Sean87,很公平。 :)

标签: c# arrays floating-point max


【解决方案1】:

您需要遍历数组中的所有元素并针对边界框测试每个元素,当当前项目在边界框之外时增加边界框。像这样:

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 框架 API 可以进行最小/最大测试:Math.Min 和 Math.Max。除非有一些关于点数组的其他信息可用于减少测试次数,否则您将不得不测试数组中的每个点。不幸的是,那里没有捷径。我想知道 JIT 编译器是否足够聪明,可以为此使用 SIMD?

此外,如果数组中的所有点都小于零,则使用值 0 进行初始化可能会导致错误。

【讨论】:

  • 感谢您的提示!在 X 轴上,我处理的是时间,所以它总是一个正数。但是对于 Y 轴,它会更复杂,因为我必须检查最大正 Y 和最大最小 Y(abs 方法可以派上用场)。
【解决方案2】:

如果找到最小值(左上点)和最大值(右下点),则可以计算图形的大小。

首先,您需要一种比较 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;

【讨论】:

  • 使用此解决方案,您必须对数组进行 4 次迭代。根据数组的大小,这可能是个问题。
  • @M4N 你不会,我的例子中的最后 4 行可以很容易地优化为 2 次迭代。对 1 次迭代的进一步优化需要更多的工作,但如果被确定为性能瓶颈(非常不可能)
【解决方案3】:

你的代码不好。如果您有点 (2, 4) 和 (3, 1),则 xMax 将为 3,yMax 将为 4,这不是一个点。

【讨论】:

  • 我猜他的想法是正确的。要绘制图形,最大值。 x/y 值不需要来自同一点。 (所以你的答案不正确)。
【解决方案4】:

使用RyuJIT and 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 个项目的最小值和最大值。理论上,这将根据您的 CPU 提供 4-8 倍的加速。使用支持 AVX2 的 CPU 可提供最快的性能提升。

来源:http://blogs.microsoft.co.il/sasha/2014/04/22/c-vectorization-microsoft-bcl-simd/

【讨论】:

    猜你喜欢
    • 2013-09-02
    • 1970-01-01
    • 2022-12-02
    • 1970-01-01
    • 2017-02-13
    • 2012-02-11
    • 2013-01-11
    • 2011-05-28
    • 2016-09-10
    相关资源
    最近更新 更多