我该如何编写FindClosestMatching函数?

时间:2009-10-23 12:39:37

标签: c#

在符合条件的所有对象中,找到最接近某个位置的对象。据说是一个非常普遍的问题。当前代码如下所示:

protected Foo FindClosestFooMatching (Vec pos, Func<Foo, bool> matches)
{
    float bestDist = float.MaxValue;
    Foo bestFoo = null;

    foreach (Foo foo in AllFoos()) {
        if (matches (foo)) {
            float dist = pos.Dist (foo.Center);
            if (dist <= bestDist) {
                bestDist = dist;
                bestFoo = foo;
            }
        }
    }
    return bestFoo;
}

我正在考虑几种重构代码的方法,但却找不到一个非常好的代码。如果你有一分钟​​,请试一试。 : - )

编辑:关于Eric的问题。它是具有欧几里德度量(=快速)的标准3D空间。点聚类和垃圾查询可能性未知。

3 个答案:

答案 0 :(得分:1)

return (from f in AllFoos()
        where matches(f)
        orderby f.Center.Dist(pos)
        select f).FirstOrDefault();

return AllFoos().Where(matches)
                .OrderBy(f => f.Center.Dist(pos))
                .FirstOrDefault();

答案 1 :(得分:1)

我认为你的解决方案很快(O(n)),简单&amp;愚蠢(在KISS方面)足以让即使是第一年的CS学生也能理解。

我可以看到的一个问题是你应该接受

的声明
float dist;

离开循环。它可能会导致内存碎片。这实际上是一个小问题,因为它只是一个浮动。

另一个会改变

if (dist <= bestDist)

if (dist < bestDist)

保存一些变量赋值的CPU周期。但这有一些副作用,因为它会将返回的对象从最后一个最佳匹配更改为第一个最佳匹配。

答案 2 :(得分:0)

现在无法编译,但我希望这能完成这项工作。

var foos = AllFoos().Where (x => matches (x));
return foos.OrderBy (x => pos.Dist (x.Center)).FirstOrDefault ();

这里需要注意的重点是每个操作(包括OrderBy)都需要延迟执行,因此性能应该没问题。

这将是一个更好看的解决方案:

var res = from foo in AllFoos()
    where matches (foo)
    orderby pos.Dist (foo.Center)
    select foo;
return res.FirstOrDefault ();

编辑:我发现this question是此问题的重要来源。由于OrderBy实际上首先命令整个列表,它当然很慢(正如Eric所说,O(n log n)。更好的解决方案是使用Aggregate或Jon Skeet推荐的MinBy扩展(见上面的链接)例如)。

我真的很喜欢MoreLinq解决方案,因为它将代码缩减为:

return World.AllNodes
    .Where (x => matches (x.T))
    .MinBy (x => pos.DistSq (x.T.Volume.Center));