【问题标题】:Select elements from a list according to their properties根据属性从列表中选择元素
【发布时间】:2015-06-19 19:00:50
【问题描述】:

我正在开发一个旨在渲染大量形状的应用程序。每个形状都可以分配给特定的

我将输入数据作为形状列表获取,其中对于每个形状,我都有一个 string 属性,表示该形状所属的图层。

现在,我需要开发一种方法,允许我仅选择(绘制)属于给定选定图层列表的那些形状。 在伪代码中:

void draw_if(sorted_list shapes, list<string> selected_layers)
{
   for each shape in shapes
   {
      if (shape.layer in selected_layers)
        shape.draw();
   }
}

关键是我想尽可能快地执行这个操作;因此我需要选择正确的数据结构和合适的算法。

所选层的列表是一个字符串列表(1÷100 个不同的层),但如果出于性能原因需要,它可以转换为其他数据类型。

形状根据它们的 z 顺序排序。

【问题讨论】:

  • 当你想更新一个特定的层时,你不想遍历所有的形状,所以像 HashMap> 这样的数据结构会很有帮助。但接下来你必须想一想,你需要以多快的速度进行以下操作:插入、删除、搜索、排序?例如,删除通常不会通过这种安排受益,但通过修改它可以。
  • @EdwardDoolittle 您的解决方案似乎是最快的解决方案,但我不确定是否可以应用它。问题是形状列表是一个排序列表(按 z 顺序),因此我不能在位置 1 的形状之前绘制位置 2 的形状,因为如果两个形状占据相同的区域,结果将不同(形状 1 超过形状 2)。鉴于此约束,有没有办法使用您的提示?
  • @gliderkite 这是一个解决方案,您可以按照您想要的顺序形成“选定的形状”,将形状选择的想法与形状本身结合起来。如果您需要这种 Z 排序,那么您可以使用一组来形成您的“选定形状”结构,例如(基本上是二叉搜索树)。在这种情况下,您主要将开销转移到选择的构造上,但您的绘图循环将被加速。
  • 也就是说,如果您想要在算法和内存级别上为循环的绘图端提供最快的解决方案(这并不是真正的微观,因为它可以产生数量级的差异),您希望最终选择的形状在空间局部性的简单排序数组中。基本上,这种“选定形状”解决方案会阻止您检查是否在您绘制的每一帧中都选择了形状。它将这种开销转移到形成形状选择,而不是之后可能多次绘制。
  • 但是,如果您的形状绘图需要 Z 顺序并且 Z 顺序经常变化(例如:对于 3D 元素或不断变化的元素),那么通过消除绘图循环中的选择检查。在这种情况下,您需要一个像我建议的解决方案,您可以检查是否在 O(1) 中选择了形状层并使用简单的数组访问。这里重要的不是 Z 排序,而是什么是静态的(不经常变化)和什么是动态的(经常变化的)。例如,如果您的绘图顺序非常动态,但您的图层选择不是,这就是关键信息。

标签: c++ performance search data-structures


【解决方案1】:

在寻找复杂的数据结构和算法时,这里经常忽略基本的侵入式解决方案,但通常是最快的。

假设您别无选择,只能将选择分开,如果您想要一个非常快速的解决方案,请在每个图层中存储一个布尔选择标志(可能是一个位)。当您形成一个选择时,除了形成一个列表之外,还要设置这些标志。取消选择图层不仅会从您的选择中删除它,还会将该选择标志设置为 false。

接下来,将那些用于指示所选层的字符串转换为随机访问结构的索引(例如:std::vector,如果大小可以在编译时确定,甚至是一个普通的旧数组),如下所示(简化):

struct Layer
{
    string name;

    // Set this to true when the layer is selected, false 
    // when it is deselected. Use atomics if thread safety 
    // is required.
    bool selected;
};

... 并将shape.layer 转换为层的索引(或指针/迭代器)。如果您别无选择,只能从图层字符串开始识别形状所属的图层,因为最初为您提供了字符串输入(例如:从您正在加载的文件中),然后将这些字符串转换为图层索引/指针/迭代器当您从这些字符串输入创建形状时。在这里使用哈希表或至少 std::set/map (初始形状构造的字符串搜索应该是对数或更好)将这些层字符串转换为层索引/指针/迭代器。

如果除了图层选择状态之外还需要图层选择列表,那么可以这样做(伪代码):

void select(Layer layer, LayerList& layer_selection)
{
     if (!layer.selected)
     {
          layer.selected = true;
          layer_selection.insert(&layer);
     }
}

void deselect(Layer layer, LayerList& layer_selection)
{
     if (layer.selected)
     {
          layer.selected = false;
          layer_selection.erase(&layer);
     }
}

...您的图层选择将索引/指针/迭代器存储到图层的位置。如果您喜欢层选择并使用固定的分配器(这是一个复杂的主题,涉及到新的放置、联合和内存池,所以如果需要我会深入研究它,但为简洁起见暂时省略它)。

现在你的主要伪代码变成了这样的:

void draw_if(list shapes, list layers)
{
   for each shape in shapes
   {
      if (layers[shape.layer].selected)
        shape.draw();
   }
}

... 或者如果你使用指针/迭代器:

void draw_if(list shapes, list layers)
{
   for each shape in shapes
   {
      if (shape.layer->selected)
        shape.draw();
   }
}

在性能方面很难超越它,因为即使是最优化的哈希表也无法击败对内存的简单索引数组访问,除了哈希之外您还必须访问。现在如果你能巩固“选择形状”的思想,通过选择/取消选择图层的过程预先形成选择形状,那么你可以这样做:

void draw_selected(list selected_shapes)
{
   for each shape in selected_shapes
       shape.draw();
}

...如果形成选定形状列表的额外成本通过在必须更改之前重复使用它来补偿,这可能会更快。请注意,在这种情况下,您仍然希望将这些字符串转换为索引,因为您不希望“选定形状”列表不仅仅是一个简单的数组。形成选定形状列表:

ShapeList selected_shapes(ShapeList all_shapes, LayerList layers)
{
     // Forming this in advance will help if it is reused for
     // numerous drawing frames before it needs to change (ex:
     // before the Z-order changes, before new elements are inserted
     // or existing ones removed, before the layer selection changes).
     ShapeList results;
     for each shape in all_shapes:
          if layers[shape.layer].selected)
             results.push_back(shape);
     return results;
}

...由于我们现在将选择状态存储在层中,因此形成和访问(由于完美紧凑的形状选择数组的空间局部性)仍然比哈希表更便宜。

这使所有内容保持缓存友好,并避免像哈希表这样昂贵的(相对而言)数据结构,除了在初始字符串->索引/指针转换部分(您只需要在从字符串输入创建形状时才需要这样做)。在这种情况下,唯一需要进行任何类型搜索(对数或恒定时间哈希/trie)的地方是当您将从用户输入获得的那些形状层字符串转换为索引/指针/迭代器时。其他一切都是 O(1)(即使是最坏情况的复杂性),甚至不需要散列。

【讨论】:

    【解决方案2】:

    我建议对所选图层使用集合而不是列表,这样可以执行二进制搜索以确定 shape.layer 是否在所选图层中,并且在保留顺序的同时插入很快。使用列表对于保持二进制搜索所需的顺序效率很低。

    另一种选择是找出一些哈希算法并使用哈希映射。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-08
      • 1970-01-01
      • 2011-10-10
      相关资源
      最近更新 更多