【问题标题】:Is there an algorithm that can be parallelized for the "unique" problem?是否有可以针对“独特”问题并行化的算法?
【发布时间】:2019-12-02 07:50:49
【问题描述】:

我们有一个长的(大约 100,000 个)二维 numpy 数组。 喜欢:

A_in =

[[1, 2, 3, 4, 3, 2, 1, ..., 100000],

[2, 3, 3, 5, 4, 3, 1, ..., 100000]](代码中的edge_index_cpu)

您可以在此处将一列视为一组。每个数字表示一个点,一列表示这两个点之间的线。

我们需要得到输出,比如:

A_out =

(代码中的 new_edge_indices)

这些输出值在原始数组中的索引,如:

Idx_out = [0, 2, 3]

输出组不能与之前的所有组有任何交集。另外,如果前一个组已经被移除(如上面的[[2],[3]]),那么被移除的组将不会用于计算交集(因此,[[3],[3]]被保留)。

它可以通过 for 循环轻松实现。但是因为数据对于‘for循环’来说太大了,所以我们想求一个可以并行化的算法来解决这个问题。

我尝试使用扁平版 A_in 中的 numpy 唯一运算符 ([1, 2, 2, 3, 3, 3, 4, 5, 3, 4, 2, 3, 1, 1, ...])。但它不能满足这个“如果前一个组已经被移除(如上面的[[2],[3]]),那么被移除的组将不会用于计算交集(因此,[[3],[3] ] 保留)”。

我们要处理包含边和点的图。

    edge_index_cpu = edge_index.cpu()
    for edge_idx in edge_argsort.tolist():
        source = edge_index_cpu[0, edge_idx].item()
        if source not in nodes_remaining:
            continue

        target = edge_index_cpu[1, edge_idx].item()
        if target not in nodes_remaining:
            continue

        new_edge_indices.append(edge_idx)

        cluster[source] = i
        nodes_remaining.remove(source)

        if source != target:
            cluster[target] = i
            nodes_remaining.remove(target)

        i += 1

    # The remaining nodes are simply kept.
    for node_idx in nodes_remaining:
        cluster[node_idx] = i
        i += 1
    cluster = cluster.to(x.device)

【问题讨论】:

  • 假设 32 位非负整数索引高达 1000000(可能要少得多),那么为什么不使用直方图标记已使用的点将您的问题转换为单个 O(n) for 循环?您只需要一个直方图,其中包含每个可能的点索引的单个整数(或只是位)所以hist[1000000] ...这应该很快,所以不需要并行化...在标准 PC 上的 C++ 中我希望它应该占用到 1 秒甚至更短……我不会用 Python 编写代码,C++ 示例会有帮助吗?
  • @Spektre 谢谢。我不明白你的想法,你介意给一些更详细的解释吗?有 100 个字符(每个字符 10000 个顶点),一次只处理 12 个字符。你可以考虑 100,000 而不是 100 万。
  • @Spektre CG 电影或游戏中的每个角色都包含大约 10,000 个顶点。我使用这些顶点来构建图形并将这些图形输入到图形神经网络中。期待您的回答

标签: python-3.x graphics


【解决方案1】:

我还不会并行化,因为您的问题可以在 O(n) 中解决,这应该足够快。

  1. 定义

    让我们认为我们得到了这个:

    const int pnts=1000000; // max points
    const int lins=1000000; // number of lines
    int lin[2][lins];       // lines
    bool his[pnts];         // histogram of points (used edge?)
    int out[pnts],outs=0;   // result out[outs]
    

    我是 C++/GL 导向的,所以我使用从零开始的索引!!!我使用静态数组不要与动态分配或列表模板混淆,所以它很容易理解。

  2. 直方图

    为使用的点创建直方图。它只是一个表格,每个可能的点索引都有一个计数器或值。在开始清除它。因为我们不需要知道点被使用了多少次,所以我选择了bool,所以它只是true/false 的值,它告诉我们点是否已经被使用。

    所以请从false开始清除此表:

    for (i=0;i<pnts;i++) his[i]=0;
    
  3. 生产线数据

    只需按顺序处理所有点/线并更新每个点的直方图。因此,从lin[0/1][i] 中获取一个点索引p0/p1 并测试这两个点是否已被使用:

    p0=lin[0][i];
    p1=lin[1][i];
    if ((!his[p0])&&(!his[p1])){ his[p0]=true; his[p1]=true; add i to result }
    

    如果没有将i 添加到结果中,并设置p0,p1 与直方图中使用的一样。如您所见,这是 O(1) 我假设您现在使用 for 循环线性搜索来制作您的版本 O(n^2)

这里是小O(n) C++ 示例(抱歉不是 python 编码器):

void compute()
    {
    const int pnts=1000000;     // max points
    const int lins=1000000;     // number of lines
    int lin[2][lins];           // lines
    bool his[pnts];             // histogram of points (used edge?)
    int out[pnts],outs=0;       // result out[outs]   
    int i,p0,p1;

    // generate data
    Randomize();
    for (i=0;i<lins;i++)
        {
        lin[0][i]=Random(pnts);
        lin[1][i]=Random(pnts);
        }
    // clear histogram
    for (i=0;i<pnts;i++) his[i]=0;
    // compute result O(lins)
    for (i=0;i<lins;i++)    // process all lines
        {
        p0=lin[0][i];       // first point of line
        p1=lin[1][i];       // second point of line
        if ((!his[p0])&&(!his[p1])) // both unused yet?
            {
            his[p0]=true;   // set them as used
            his[p1]=true;
            out[outs]=i;    // add new edge to result list
            outs++;
            }
        }
    // here out[outs] holds the result
    }

运行时间是线性的,在我的机器上大约需要 10 毫秒,所以不需要并行化。

如果bool 不是单个位,您可以使用其位将直方图打包成无符号整数(例如将 32 个点打包到单个 32 位 int 变量中)以节省内存。在这种情况下,1M 点会导致 125000 字节表,这在现在不是问题

当我将您的数据提供给代码时:

int lin[2][lins]=       // lines
    {
    { 1, 2, 3, 4, 3, 2, 1 },
    { 2, 3, 3, 5, 4, 3, 1 },
    };

我得到了这个结果:

{ 0, 2, 3 }

【讨论】:

  • 在处理大的for循环时,python和cpp之间似乎存在巨大的速度差异。也许我应该用 C 重写这部分。谢谢你的回答!
  • @RuiShi Python 是最慢的语言之一...... IIRC 它的解释速度是通过使用从它调用的 C++ 创建的东西(libs 包)来完成的。结果差异哪一个是正确的呢?我的或你的,所以我可以更新代码它只是if 条件的问题
  • 为什么你的结果中有一个 7?并且,请注意,第 (2, 3) 列应该被删除,但第 (3, 3) 列被保留。
  • 在结果中添加边索引 i 时,需要确保这两个点之前都没有使用过。不只是这两者之一。我认为接下来我需要做的是用 cpp 实现这部分代码。 Python在这里太慢了。
  • 我暂时没有 github url。该代码是图池化方法的一部分。 (命名为边缘池)
猜你喜欢
  • 2015-06-27
  • 2022-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多