【问题标题】:How to find point closest to 0,0 point with LINQ and C#如何使用 LINQ 和 C# 找到最接近 0,0 点的点
【发布时间】:2013-02-26 15:03:11
【问题描述】:

我有一个点列表(List)

  • 7,43
  • 7,42
  • 6,42
  • 5,42
  • 6,43
  • 5,43

我想使用 linq 表达式来获取最接近 0,0 的点。例如 - 对于这个列表,我期望 5,42 值。

如何使用 LINQ 找到最接近 0,0 点的点?

【问题讨论】:

  • 尝试订购您的清单
  • 是使用欧几里得距离定义的“最近”吗?即是从x, y0,0 sqrt(x^2 + y^2) 的距离?
  • 最接近的定义为....?与 (0,0) 的文字线性距离?
  • points = points.OrderBy(p => p.X).ThenBy(p => p.Y).ToList()?
  • @mattytommo 所以你认为 [0,2000] 比 [1,0] 更接近 [0,0]?

标签: c# linq point


【解决方案1】:

在不执行整个列表的昂贵排序的情况下,以下查找具有最低 L^2 范数(二维中“距离”的最常见定义)的点:

var closestToOrigin = points
    .Select(p => new { Point = p, Distance2 = p.X * p.X + p.Y * p.Y })
    .Aggregate((p1, p2) => p1.Distance2 < p2.Distance2 ? p1 : p2)
    .Point;

【讨论】:

  • 如果我们关心性能,那么您的解决方案很好,但您可以在我更新的帖子中看到更好的解决方案。
  • @Hamlet 是什么让你说你的更好?我看到了一些权衡(匿名对象创建与距离的第二次计算),但它的实质基本上是这个答案的克隆。
【解决方案2】:

试试这个:

List<Point> points = new List<Point>();
// populate list
var p = points.OrderBy(p => p.X * p.X + p.Y * p.Y).First();

或更快速的解决方案:

var p = points.Aggregate(
            (minPoint, next) =>
                 (minPoint.X * minPoint.X + minPoint.Y * minPoint.Y)
                 < (next.X * next.X + next.Y * next.Y) ? minPoint : next);

【讨论】:

  • 当您只想要最低限度时,进行整个排序会很昂贵。 (如果只有 Microsoft 为 First 提供了一个重载 IOrderedEnumerable,那就没关系了!)
  • 实际上 - 按平方距离排序就可以了 :)。不需要 sqrt。此外,正如@Rawling 指出的那样 - Enumerable.Min 扩展方法 (msdn.microsoft.com/en-us/library/bb359972.aspx) 将比排序更有效。
【解决方案3】:

作为一种替代方法,您可以考虑在标准库中添加 IEnumerable.MinBy() 和 IEnumerable.MaxBy() 的实现。

如果你有可用的,代码就变得简单了:

var result = points.MinBy( p => p.X*p.X + p.Y*p.Y );

Jon Skeet 提供了 MinBy 和 MaxBy 的良好实现。

他在这里谈论它:How to use LINQ to select object with minimum or maximum property value

但是那里的链接已经过时了;最新版本在这里:

http://code.google.com/p/morelinq/source/browse/MoreLinq/MinBy.cs

http://code.google.com/p/morelinq/source/browse/MoreLinq/MaxBy.cs

这是一个完整的示例。显然,这是一把大锤,但我认为这些方法足够有用,可以包含在您的标准库中:

using System;
using System.Collections.Generic;
using System.Drawing;

namespace Demo
{
    public static class EnumerableExt
    {
        public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IComparer<TKey> comparer)
        {
            using (IEnumerator<TSource> sourceIterator = source.GetEnumerator())
            {
                if (!sourceIterator.MoveNext())
                {
                    throw new InvalidOperationException("Sequence was empty");
                }

                TSource min = sourceIterator.Current;
                TKey minKey = selector(min);

                while (sourceIterator.MoveNext())
                {
                    TSource candidate = sourceIterator.Current;
                    TKey candidateProjected = selector(candidate);

                    if (comparer.Compare(candidateProjected, minKey) < 0)
                    {
                        min    = candidate;
                        minKey = candidateProjected;
                    }
                }

                return min;
            }
        }

        public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
        {
            return source.MinBy(selector, Comparer<TKey>.Default);
        }
    }

    public static class Program
    {
        static void Main(string[] args)
        {
            List<Point> points = new List<Point>
            {
                new Point(7, 43),
                new Point(7, 42),
                new Point(6, 42),
                new Point(5, 42),
                new Point(6, 43),
                new Point(5, 43)
            };

            var result = points.MinBy( p => p.X*p.X + p.Y*p.Y );

            Console.WriteLine(result);
        }
    }
}

【讨论】:

    【解决方案4】:

    Rawling 的解决方案肯定更短,但这里有一个替代方案

    // project every element to get a map between it and the square of the distance
    var map = pointsList                                            
        .Select(p => new { Point = p, Distance = p.x * p.x + p.y * p.y });
    
    var closestPoint = map // get the list of points with the min distance
        .Where(m => m.Distance == map.Min(t => t.Distance)) 
        .First() // get the first item in that list (guaranteed to exist)
        .Point; // take the point
    

    如果您需要找到所有0,0 距离最短的元素,只需删除First 并执行Select(p =&gt; p.Point) 即可获得积分(而不是映射)。

    【讨论】:

    • @Rawling 额外的log(n) 是一个杀手。我更新了我的答案。
    • @Rawling 已修复,但我更喜欢您的解决方案。
    • 如果你愿意,这个也可以让你获得最接近的点,而我的很难/不可能这样做:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    • 2019-11-04
    • 2021-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多