【问题标题】:Fastest way to get all the points within a certain range获得一定范围内所有分数的最快方法
【发布时间】:2019-06-12 03:41:49
【问题描述】:

我正在尝试找出最快的方法来检索二维数组中某个范围内给定坐标周围的所有点/坐标。

我目前正在遍历 X/Y 并将所有点添加到列表中,但是当范围开始增加时,它变得非常慢。 有没有比我目前的做法更有效地实现这一目标的其他方法?

我当前的代码:

public static List<coords> GetCoordinates(coords Position, int nRange)
    {
        List<coords> inRange = new List<coords>();
        for (int i = Position.X - nRange; i <= Position.X + nRange; i++)
            for (int j = Position.Y - nRange; j <= Position.Y + nRange; j++)
                inRange.Add(new coords() { X = i, Y = j });
        return inRange;
    }

【问题讨论】:

  • 最快的方法是不要把所有的点都列出来,如果你需要计算某个东西是否是范围,只需使用一些if语句。取决于您要解决的问题
  • 这是一个O(n^2) 算法,对于大的n,预计会很慢。为什么需要这样做?也许原来的问题可以用不同的方式解决。
  • 是的,没有上下文很难说,但是利用已经有一些优化方法供您使用的库,您可能会得到更好的服务。例如如果您正在使用 GeoJSON,请使用 GeoJSON.Net.Contrib.MsSqlSpatial,它附带查看点/2d 区域是否与其他区域相交等的方法。
  • 这个函数在 3-4 种不同的情况下被调用,但在所有情况下,检索到的点都将受到一些if 语句的影响,以查看在这些检查之后哪些仍然可用。例如,人 A 在坐标 x,y 处,他想跳到 3 范围内的另一个坐标,该坐标中还没有任何人。我得到给定范围内所有坐标的列表,然后删除其中已经包含某人的坐标,最后选择仍然留在列表中的那些坐标的 random 坐标。
  • 我仍然认为您做事倒退且慢,如果您有一个范围内的人员列表,请在需要时枚举他们,并使用您的位置进行范围检查。

标签: c#


【解决方案1】:

第一步: Capitalize your variables correctly。通过对类进行帕斯卡封装和对参数进行骆驼封装,您将获得 50% 的速度提升:

public static List<Coords> GetCoordinates(Coords position, int range)

好吧,我对执行速度的提升撒了谎,但可读性提升是真实的。 ?

第二步:确保Coordsstruct,以消除垃圾收集器的压力:

public struct Coords { public int X; public int Y; }

第三步:预分配List&lt;Coords&gt;所需的空间,避免内部数组多次调整大小。

var inRange = new List<Coords>((range * 2 + 1) ^ 2);

或者不要预先分配任何东西并返回一个迭代器而不是一个列表:

public static IEnumerable<Coords> GetCoordinates(Coords position, int range)
{
    for (int i = position.X - range; i <= position.X + range; i++)
        for (int j = position.Y - range; j <= position.Y + range; j++)
            yield return new Coords() { X = i, Y = j };
}

另一种方法是在范围内返回一个满足条件的随机Coords

public static Random _random = new Random();
public static Coords GetRandomCoordinates(Coords position, int range,
    Func<Coords, bool> condition)
{
    while (true)
    {
        var coords = new Coords()
        {
            X = _random.Next(position.X - range, position.X + range + 1),
            Y = _random.Next(position.Y - range, position.Y + range + 1)
        };
        if (condition(coords)) return coords;
    }
}

...并像这样使用它:

var result = GetRandomCoordinates(position, range,
    (coords) => !players.Any(player => player.X == coords.X && player.Y == coords.Y));

【讨论】:

  • 这是微优化,可能有一种完全不同的方法可以产生更好的结果,例如 K-D 树结构、覆盖树、R 树等,它们可以更好地组织坐标只是一个线性列表。
  • @LasseVågsætherKarlsen 同意。但我猜 OP 的目的是在不牺牲设计简单性的情况下加快程序速度。
【解决方案2】:

如果您已经知道总大小,使用固定数组会快得多

int width = nRange + nRange + 1;
coords[] inRange = new coords[width * width];

并缓存for循环的结束值

int endX = Position.X + nRange;
int endY = Position.Y + nRange;
for (int i = Position.X - nRange; i <= endX; i++)
     for (int j = Position.Y - nRange; j <= endY; j++)

基准测试

坐标是类

范围 | 1000 | 5000 -----------------+--------------+- -------------------------- 方法 |列表数组 |列表数组 -----------------+--------------+- -------------------------- 平均(毫秒)| 312.90858 254.00218 | 8201.48866 7634.8847 最大 | 321.8542 259.0914 | 8498.696 7914.6034 最小 | 300.2323 248.8317 | 7908.7473 7529.3754 标准开发 | 9.564255412 3.654335875 | 220.2477895 159.5085045

坐标是结构

范围 | 1000 | 5000 -----------------+--------------+- ----------------------------------------- 方法 |列表数组 |列表数组 -----------------+--------------+- ----------------------------------------- 平均(毫秒)| 56.68224 14.2345 | 1454.1773 296.05854 最大 | 57.3408 15.4369 | 1472.1977 298.0693 最小 | 56.2184 12.752 | 1444.9573 293.7728 标准开发 | 0.468124121 1.081463106 | 10.57876523 1.925248377

【讨论】:

  • 这并不完全正确,列表在内部使用了一个数组which doubles in size each time it is full,因此附加了can be seen as an O(1) operation。虽然您的方法会更快,但大 n 的差异很小。
  • @tmlye,我做了一些基准测试,结论是如果 coords 是 struct,使用数组的速度会快 70% 以上,我可以说这是一个很大的改进。
  • 对不起,我没有意识到 coords 是一个结构。感谢您进行基准测试!
猜你喜欢
  • 2022-08-10
  • 2016-03-26
  • 1970-01-01
  • 2013-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-08
  • 2021-06-20
相关资源
最近更新 更多