【问题标题】:Comparing elements in an IEnumerable to eachother比较 IEnumerable 中的元素
【发布时间】:2014-10-30 23:31:04
【问题描述】:

希望是一个快速的问题。我有一个 Position 类型的 IEnumerable,其中 Position 定义如下:

public class Position {
 public double Latitude { get; set; }
 public double Longitude { get; set; }
}

我需要做的是快速排序 IEnumerable 以找到落在彼此一定距离内的元素。 IEnumerable 中的元素不会以任何特定顺序填充,但在任何时候我都需要能够计算 IEnumerable 的哪些成员彼此相距 x 公里。

现在,我已经有了一个 Haversine 实现,为了论证,我们可以说它称为 GetDistance 并具有以下签名:

double GetDistance(Position one, Position two);

我有一些想法,但我觉得没有一个特别有效。提供更多信息,IEnumerable 在任何时候都不太可能容纳超过 10,000 个项目。

我想要达到的是一些东西,也许是一个扩展方法,它让我调用它来返回一个 IEnumerable,它只包含原始集合中符合条件的子集,例如:

OriginalEnumerable.GetMembersCloserThan(kilometers: 100);

任何帮助,非常感谢。

编辑:为清楚起见,考虑我要搜索的 IEnumerable 描述了一个半径为 r 的圆。它的成员是圈内的坐标。我正在尝试确定哪些成员(点)在给定的距离范围内。

【问题讨论】:

  • 那么OriginalEnumerable.GetMembersCloserThan(kilometers: 100); 会得到距离至少 1 个其他 Position 对象 100 公里以内的 Position 对象吗?
  • 如果您有一个比较两个位置的GetDistance 方法,为什么不简单地自己创建所需的扩展方法呢?有什么问题?
  • @SamIam 是的,其想法是 GetMembersCloserThan()... 方法的返回值将返回彼此相距 x 公里范围内的成员。
  • @BenRobinson 尝试了很多,但我想出的最好的方法是嵌套 for-each 循环,效率很低。
  • @Richard within x kilometers of eachother 模棱两可。以您有 4 个元素的情况为例,每个元素都分成对。每对距离它们中的另一个元素只有 1 公里,但它们之间的距离大约为 200 公里。在这种情况下,您要选择所有元素吗?还是一个都没有?

标签: c# linq


【解决方案1】:

这样的?假设GetDistance 可用。

public static IEnumerable<Position> GetMembersCloserThan(this IEnumerable<Position> positions, double maxDistance)
{
    return positions.Where(a => positions.Any(b => a != b && GetDistance(a, b) < maxDistance));
}

编辑我现在看到您也对性能感兴趣。以上并不是特别快,尽管它也不是非常慢,因为比较距离的数学非常简单。让我知道它是否满足您的要求。

编辑 2 这个速度要快得多——它不会针对过去的失败进行测试,并且会自动将匹配项添加到成功列表中

public static IEnumerable<Position> GetMembersCloserThan(this IEnumerable<Position> positions, double maxDistance)
{
    List<Position> closePositions = new List<Position>();
    List<Position> testablePositions = positions.ToList();

    foreach (Position position in positions)
    {
        // Skip this one, it has already been matched.
        if (closePositions.Contains(position))
            continue;

        bool isClose = false;
        foreach (Position testAgainstPosition in testablePositions)
        {
            if (position == testAgainstPosition)
                continue;

            if (GetDistance(position, testAgainstPosition) < maxDistance)
            {
                // Both the position and the tested position pass.
                closePositions.Add(position);
                closePositions.Add(testAgainstPosition);
                isClose = true;
                break;
            }
        }

        // Don't test against this position in the future, it was already checked.
        if (!isClose)
        {
            testablePositions.Remove(position);
        }
    }

    return closePositions;
}

【讨论】:

  • 谢谢@smdrager,让我快速看看这是怎么回事。
  • 完美,smdrager! EDIT 2 是我希望得到的那种东西。加油,感谢您的帮助!
【解决方案2】:

如果您需要更高的性能:将项目放入按纬度排序的列表中。

要计算您想要的一组位置,请迭代其中一个。但是对于您的距离计算,您只需要考虑纬度差异最多 100 公里的项目。这意味着,您可以逐项返回,直到差异大于 100 公里。但是,您需要环绕列表的末尾。 标记所有距离小于 100 公里的项目(或 yyield return)并继续前进。

虽然我无法量化费用,但排序应该为大集合摊销。此外,如果大多数点位于相似的纬度,它可能会表现不佳。如果这是一个问题,请使用带有圆角坐标的二维字典作为键。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-24
    相关资源
    最近更新 更多