将IEnumerable中的元素相互比较

时间:2014-10-30 14:57:24

标签: c# linq

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

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

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

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

double GetDistance(Position one, Position two);

我有一些想法,但没有一个想法特别有效。为了提供更多信息,IEnumerable不太可能在任何时候持有超过10,000件物品。

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

OriginalEnumerable.GetMembersCloserThan(kilometers: 100);

任何帮助,非常感谢。

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

2 个答案:

答案 0 :(得分:3)

这样的东西?假设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;
}

答案 1 :(得分:1)

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

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

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