【问题标题】:Reordering algorithm via iterated rotate通过迭代旋转重新排序算法
【发布时间】:2017-02-05 01:20:58
【问题描述】:

考虑一个包含N 样本的数据集,其中每个样本由head 元素和tail 元素组成。我想执行稳定的重新排序,使N tails 在顶部组合在一起,N heads 在底部组合在一起。

例如数据集:

 -1  -2  -3   1   2   3   4   5
 -4  -5  -6   6   7   8   9  10
 -7  -8  -9  11  12  13  14  15
-10 -11 -12  16  17  18  19  20

N = 4 个样本,head = 3(负整数)和tail = 5(正整数)。所需的转换将产生:

  1   2   3   4   5   6   7   8
  9  10  11  12  13  14  15  16
 17  18  19  20  -1  -2  -3  -4
 -5  -6  -7  -8  -9 -10 -11 -12

我实施了一个基于重复应用旋转的解决方案。旋转是由 C++ 算法std::rotate 实现的:

std::rotate(first, n_first, last) 交换范围内的元素 [first, last) 使得元素 n_first 成为 新范围的第一个元素,n_first - 1 成为最后一个元素 元素。

我的实现(如下所示)提供了正确的解决方案,并且可以很好地解决我的问题。但是,它需要执行N 旋转,其中每次旋转的复杂度从O(head + tail) 增加到O(N * tail + head)

您知道复杂度更高的算法吗?

我的代码如下:

#include <algorithm>
#include <vector>
#include <iostream>
#include <iomanip>

template <typename I> // I models Forward Iterator
I reorder(I f, I l, std::size_t head_size, std::size_t tail_size)
{
  std::size_t k = 1;
  auto m = std::next(f, head_size);
  auto t = std::next(m, tail_size);
  while (t != l) {
    f = std::rotate(f, m, std::next(m, tail_size));
    m = std::next(f, ++k * head_size);
    t = std::next(m, tail_size);
  };
  return std::rotate(f, m, t);
}

template <typename C>
void show(const char* message, const C& c)
{
  std::size_t shown { 0 };
  std::cout << message << "\n";
  for (auto && ci : c)
    std::cout << std::setw(3) << ci
              << (++shown % 8 == 0 ? "\n" : " ");
}

int main()
{
  std::vector<int> v {
    -1,  -2,  -3,  1,  2,  3,  4,  5,
    -4,  -5,  -6,  6,  7,  8,  9, 10,
    -7,  -8,  -9, 11, 12, 13, 14, 15,
   -10, -11, -12, 16, 17, 18, 19, 20 };

  std::size_t head_size { 3 };
  std::size_t tail_size { 5 };

  show("before reorder", v);

  reorder(v.begin(), v.end(), head_size, tail_size);

  show("after reorder", v);

  return 0;
}

编译运行:

$ clang++ example.cpp -std=c++14
before reorder
 -1  -2  -3   1   2   3   4   5
 -4  -5  -6   6   7   8   9  10
 -7  -8  -9  11  12  13  14  15
-10 -11 -12  16  17  18  19  20
after reorder
  1   2   3   4   5   6   7   8
  9  10  11  12  13  14  15  16
 17  18  19  20  -1  -2  -3  -4
 -5  -6  -7  -8  -9 -10 -11 -12

有关问题域的详细信息

我正在读取图像,其中每 前面都有像素元数据,后面是原始像素,如下所示:

[metadata] [pixels...]
[metadata] [pixels...]
[ many more of these]
[metadata] [pixels...]

我需要将所有像素打包在一起并将它们传递给 OpenGL,但我还需要维护程序可访问的元数据。所以,我这样做:

 [all the pixels] [all the metadata]

并将[all the pixels] 传递给我的显卡,同时在CPU 中保留[all the metadata] 的句柄。

编辑

感谢您的回复 - 我最终实施了一个不依赖于重新排序的替代解决方案。但是,问题仍然存在:您能否改进“就地”数据集重新排序的算法?类似于原地矩阵转置

【问题讨论】:

  • 元数据和像素块的大小是否提前知道?如果是,不只是 mem 复制那些吗?

标签: c++ algorithm asymptotic-complexity


【解决方案1】:

一个非常快速的解决方案是创建另一个矩阵并将元素直接放置在它们所属的位置。

例如,假设您有 n 行、t 个尾和 h 个头。第一行 (1, 1) 上的第一个尾部是 ((h * n+1)/(h+t), (h * n+1)%(h+t))。我会让你制定一般情况(i,j)去(k,l)。无论如何,这是一个涉及整数除法和模数的计算。

【讨论】:

    【解决方案2】:

    从输入格式开始:

    [metadata] [pixels...]
    [metadata] [pixels...]
    [ many more of these]
    [metadata] [pixels...]
    

    您应该做的是将这些直接读入您需要的结构中,据我所知,这是两个:连续像素和连续元数据。所以:

    std::vector<Pixel> pixels;
    std::vector<Metadata> meta;
    // maybe reserve() a reasonable amount based on input size
    
    // for each record in input:
        pixels.push_back(...);
        meta.push_back(...);
    

    现在您有了一个包含要传递给 GPU 的所有像素数据的向量,以及一个包含您自己的所有元数据的向量。无需复制内存。

    【讨论】:

    • 这就是我最终实现它的方式——但我仍然对一种有效的重新排序算法感到好奇(类似于“内存矩阵转置”)。
    猜你喜欢
    • 2022-06-13
    • 1970-01-01
    • 1970-01-01
    • 2022-11-01
    • 1970-01-01
    • 2018-12-01
    • 1970-01-01
    • 2012-11-13
    • 2021-02-24
    相关资源
    最近更新 更多