【问题标题】:Re-sort a vector after a small number of elements have been modified修改少量元素后重新排序向量
【发布时间】:2014-09-15 19:31:04
【问题描述】:

如果我们有一个大小为 N 且先前已排序的向量,并将最多 M 个元素替换为任意值(其中 M 是比 N 小得多),有没有一种简单的方法可以以比完整排序更低的成本(即生成深度降低的排序网络)对它们进行重新排序?

例如,如果 N=10 和 M=2,则输入可能是

10 20 30 40 999 60 70 80 90 -1

注意:被修改元素的索引是未知的(直到我们将它们与周围的元素进行比较。)


这是一个我知道解决方案的示例,因为输入大小很小,我能够通过蛮力搜索找到它:

如果 N = 5 且 M 为 1,则这些将是有效输入:

0 0 0 0 0     0 0 1 0 0     0 1 0 0 0     0 1 1 1 0     1 0 0 1 1     1 1 1 1 0

0 0 0 0 1     0 0 1 0 1     0 1 0 0 1     0 1 1 1 1     1 0 1 1 1     1 1 1 1 1

0 0 0 1 0     0 0 1 1 0     0 1 0 1 1     1 0 0 0 0     1 1 0 1 1

0 0 0 1 1     0 0 1 1 1     0 1 1 0 1     1 0 0 0 1     1 1 1 0 1

例如,如果先前排序的向量是0 1 1 1 1 并且第4 个元素被修改,则输入可能是0 1 1 0 1,但无法将0 1 0 1 0 形成为有效输入,因为它至少有2 个不同来自任何排序向量的元素。

这将是重新排序这些输入的有效排序网络:

>--*---*-----*-------->
   |   |     | 
>--*---|-----|-*---*-->
       |     | |   |
>--*---|-*---*-|---*-->
   |   | |     |
>--*---*-|-----*---*-->
         |         |
>--------*---------*-->

我们不在乎这个网络无法对一些无效输入进行排序(例如0 1 0 1 0。)

而且这个网络的深度为 4,与一般情况相比节省了 1 (a depth of 5 generally necessary to sort a 5-element vector.)

不幸的是,蛮力方法不适用于更大的输入大小。

是否有一种已知的方法可以构建网络以重新排序更大的向量?

我的 N 值大约是几百,M 不会超过 √N

【问题讨论】:

  • 我认为一些排序方法将数据分成几部分,只对这些部分进行测试。也许您可以通过知道每个桶不能有超过一定数量的未排序元素来调整它并降低该方法的成本。对于较小的 M 值,您还可以找到未排序的元素,将它们分开排序,然后再次将它们合并到完整列表中。但这种方法不可并行化。
  • @DarkZeros 我认为您的第一个建议是正确的。我正在尝试使用 Shell 排序。
  • 一些附加信息会很好: - N 真的会在几百的范围内吗? - 您一般期望从排序中获得什么样的性能?如果您的问题规模真的不大,那么复杂的网络可能不值得麻烦。 - 到目前为止,您尝试过什么样的类型? - 对于你的问题,我假设你的意思是:“我只更改了一些元素,如果知道这些信息,有没有办法让排序比常规更快,或者如果大多数元素已经存在,那么它的执行速度会更快排序”。我说得对吗?
  • 另外: - 您在排序和排序网络领域的理解有多深?我问是为了不浪费你的时间,因为我的知识相当有限。
  • 如果您要对几乎排序的数据进行排序,插入排序实际上是一个不错的选择。另一种是自然归并排序。对于几乎排序的数据,我会将 shell 排序排在这两者之后。

标签: sorting sorting-network


【解决方案1】:

好的,我将其发布为答案,因为对长度的评论限制让我发疯:)

你应该试试这个:

  • 在本地内存上实现一个简单的顺序排序(插入排序或类似的)。如果您不知道怎么做,我可以提供帮助。
  • 只有一个工作项对 N 个元素的块执行排序
  • 计算每个工作组的最大本地内存大小(使用CL_DEVICE_LOCAL_MEM_SIZE 调用clGetDeviceInfo)并得出每个工作组的最大工作项数, 因为使用这种方法,您的工作项数量很可能会受到本地内存量的限制。

我怀疑这可能会很好用,因为:

  • 简单的排序可能非常好,特别是因为数组已经在很大程度上排序了
  • 并行处理这么少的项目是不值得的(但是使用本地内存!)
  • 由于您正在处理数十亿个这样的小数组,即使只有单个工作项处理这样的数组,您也将获得很高的占用率

如果您对我的想法有疑问,请告诉我。

编辑 1:

我刚刚意识到我使用了一种可能会让其他人感到困惑的技术: 我对使用本地内存的建议是不用于同步或对单个输入向量/数组使用多个工作项。我只是用它来获得较低的读/写内存延迟。由于我们使用了相当大的内存块,我担心使用私有内存可能会在我们没有意识到的情况下导致交换速度减慢全局内存。这也意味着您必须为每个工作项分配本地内存。每个工作项将访问其自己的本地内存块并将其用于排序(排他性地)。 我不确定这个想法有多好,但我读过使用过多的私有内存可能会导致交换到全局内存,唯一注意的方法是查看性能(不确定我是否正确)。

【讨论】:

  • 这是正确的答案。我希望我先提交它。 :) 插入排序对于几乎排序的小型数组是最佳的。通过同时对多个数组进行排序来利用并发性。赢了。
【解决方案2】:

这是一个应该产生非常好的排序网络的算法。可能不是所有输入大小的绝对最佳网络,但希望足以满足实际用途。

  1. 存储(或有可用的)n
  2. 使用最佳网络对最大的 2^k 个元素进行排序。例如:小于或等于 n 的 2 的最大幂的双调排序。
  3. 对于剩余的元素,重复 #2 直到 m
  4. 使用 #1 中已知的最优网络对任何剩余元素进行排序
  5. 使用归并排序网络对最小和次小的子列表进行归并排序
  6. 重复 #5 直到只剩下一个排序列表

所有这些步骤都可以人工完成,并将比较结果存储到主网络中,而不是作用于数据。

值得指出的是,#2 中的(双音)网络可以并行运行,较小的网络将首先完成。这很好,因为当它们完成时,#5-6 的网络就可以开始执行了。

【讨论】:

  • 请说明“最大 2^k”是什么意思
  • 另外,看起来您所描述的内容是完整的。鉴于您不知道它们在输入中的位置,您在哪里可以利用只有 M 个元素可以更改的事实?
  • 我不认为有一种方法可以优化排序网络,其中包含已知数量的元素,而不是事先知道它们的位置。你甚至不会知道这些 M 元素是否相对于它们自己是有序的。唯一有保证的解决方案是有效地使用列表。至少您知道大多数比较不会导致交换。
  • “最大 2^k”是指“2 小于 n 的最大幂”。我的小写 n 不等于 N 除了第 2 步的第一次迭代。
猜你喜欢
  • 2021-11-16
  • 2021-06-29
  • 2018-05-10
  • 1970-01-01
  • 2011-03-06
  • 2012-01-27
  • 2018-05-15
  • 2017-06-19
  • 1970-01-01
相关资源
最近更新 更多