You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何减少双层循环的迭代次数——圆集合圆心包含关系处理的性能优化问询

哥们,你这个问题太典型了——n=1e4时O(n²)的复杂度直接干到1亿次循环,再加上每次都算sqrt和pow这种耗时的浮点操作,慢是必然的。我之前做过类似的空间查询优化,给你几个实用的方向,从易到难,总能解决你的问题:

先搞最见效的小优化:砍掉冗余计算

这部分不用改整体逻辑,就能立竿见影提升速度:

  • 去掉sqrt和pow:比较c[i].r > 两点距离等价于c[i].r² > 两点距离平方,完全可以把pow换成乘法,同时省去sqrt——这俩都是浮点运算里的大户,省下来的时间非常可观。
  • 预处理半径平方:提前把每个圆的半径平方算好存在结构体里,不用每次循环都重复计算。

改完之后内层循环的判断逻辑变成这样:

double dx = c[i].x - c[j].x;
double dy = c[i].y - c[j].y;
if (c[i].r_sq > dx*dx + dy*dy) {
    // 处理操作
}

光这一步就能把单次循环的耗时砍一半以上。

核心优化:用空间索引把O(n²)降到接近O(n)

上面的小优化只是减少单次循环的时间,但总迭代次数还是1亿次,要本质提升得从减少遍历次数入手。这里推荐两个性价比最高的方案:

网格划分(Grid Partitioning)

思路非常简单:把整个坐标空间切成一个个大小合适的网格,每个网格里存落在这个区域的圆的索引。然后对每个圆i,只需要检查它所在网格和周围相邻的几个网格里的圆——毕竟只有这些网格里的圆心才有可能在i的内部。

网格的大小建议设为所有圆里的最大半径,这样能保证任何可能被i包含的圆心,一定在i所在网格或相邻的8个网格里。

我给你写了个简化的实现示例(已经包含预处理和网格逻辑):

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>

typedef struct Points {
    double x;
    double y;
    double r;
    double r_sq; // 预处理半径平方
} Points;

// 网格节点,存储该网格内的圆索引列表
typedef struct GridNode {
    int* indices;
    int count;
    int capacity;
} GridNode;

int main() {
    int num = 10000;
    Points* c = malloc(num * sizeof(Points));
    if (!c) { perror("malloc failed"); return 1; }

    // 假设这里已经完成所有圆的x/y/r初始化
    // 预处理:计算半径平方,同时找坐标范围和最大半径
    double max_r = 0.0;
    double min_x = DBL_MAX, max_x = -DBL_MAX;
    double min_y = DBL_MAX, max_y = -DBL_MAX;
    for (int i = 0; i < num; i++) {
        c[i].r_sq = c[i].r * c[i].r;
        if (c[i].r > max_r) max_r = c[i].r;
        if (c[i].x < min_x) min_x = c[i].x;
        if (c[i].x > max_x) max_x = c[i].x;
        if (c[i].y < min_y) min_y = c[i].y;
        if (c[i].y > max_y) max_y = c[i].y;
    }

    // 网格大小设为最大半径,确保覆盖所有可能的候选圆
    double grid_size = max_r;
    int grid_cols = ceil((max_x - min_x) / grid_size) + 1;
    int grid_rows = ceil((max_y - min_y) / grid_size) + 1;

    // 初始化网格
    GridNode* grid = malloc(grid_rows * grid_cols * sizeof(GridNode));
    if (!grid) { perror("malloc grid failed"); free(c); return 1; }
    for (int i = 0; i < grid_rows * grid_cols; i++) {
        grid[i].indices = NULL;
        grid[i].count = 0;
        grid[i].capacity = 0;
    }

    // 将圆分配到对应网格
    for (int i = 0; i < num; i++) {
        int col = floor((c[i].x - min_x) / grid_size);
        int row = floor((c[i].y - min_y) / grid_size);
        GridNode* node = &grid[row * grid_cols + col];
        // 动态扩容列表
        if (node->count >= node->capacity) {
            node->capacity = node->capacity == 0 ? 8 : node->capacity * 2;
            node->indices = realloc(node->indices, node->capacity * sizeof(int));
            if (!node->indices) {
                perror("realloc indices failed");
                // 简化内存清理,实际项目要处理更严谨
                free(c);
                for (int j = 0; j < grid_rows * grid_cols; j++) free(grid[j].indices);
                free(grid);
                return 1;
            }
        }
        node->indices[node->count++] = i;
    }

    // 处理每个圆,只遍历相邻网格的圆
    for (int i = 0; i < num; i++) {
        int col = floor((c[i].x - min_x) / grid_size);
        int row = floor((c[i].y - min_y) / grid_size);

        // 遍历当前网格+周围8个网格(边界判断越界)
        for (int dr = -1; dr <= 1; dr++) {
            for (int dc = -1; dc <= 1; dc++) {
                int curr_row = row + dr;
                int curr_col = col + dc;
                if (curr_row < 0 || curr_row >= grid_rows || curr_col <0 || curr_col >= grid_cols) continue;
                
                GridNode* node = &grid[curr_row * grid_cols + curr_col];
                for (int k = 0; k < node->count; k++) {
                    int j = node->indices[k];
                    double dx = c[i].x - c[j].x;
                    double dy = c[i].y - c[j].y;
                    double dist_sq = dx*dx + dy*dy;
                    if (c[i].r_sq > dist_sq) {
                        // 执行你的处理操作
                    }
                }
            }
        }
    }

    // 清理内存
    free(c);
    for (int i = 0; i < grid_rows * grid_cols; i++) free(grid[i].indices);
    free(grid);
    return 0;
}

这个实现里,每个圆只需要遍历周围最多9个网格里的圆,假设圆分布均匀的话,每个网格里的圆数量大概是num/(grid_rows*grid_cols),总迭代次数会从1亿降到几万甚至几十万,速度提升几十到上百倍。

四叉树(Quadtree)

如果你的圆分布非常不均匀(比如大部分圆集中在某一小块区域,其他地方很稀疏),网格划分的效率会打折扣,这时候可以用四叉树。它会递归地把空间分成四个象限,只在有圆的区域创建节点,查询的时候只遍历和当前圆范围重叠的节点。实现起来比网格复杂一点,但对稀疏分布的场景优化效果更好。

锦上添花:并行化利用多核

如果你的机器是多核CPU,还可以把外层循环并行化。比如用OpenMP,只需要在外层循环前加一行:

#pragma omp parallel for
for (int i = 0; i < num; i++) {
    // 原来的处理逻辑
}

前提是每个i的处理操作是独立的,不会有共享资源的竞争问题。这样可以直接把运行时间降到原来的1/4、1/8甚至更低,取决于你的CPU核心数。

最后说一句:如果遇到极端情况(比如所有圆的圆心都挤在同一个点),那任何空间索引的优化效果都有限,但这种场景在实际业务里非常少见,大部分情况下上面的优化方案都能解决你的性能问题。

内容的提问来源于stack exchange,提问作者Googlebot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 23:27:32