【问题标题】:De-interleave an array in place?就地去交错阵列?
【发布时间】:2011-10-15 19:55:13
【问题描述】:

假设我有一个交错数据数组,例如 1a2b3c4d5e,我想将它去交错成一个看起来像 12345abcde 的数组,就位(没有临时缓冲区)。最快的方法是什么?

我目前拥有的是这个

template<typename T>
void deinterlace(T* arr, int length){
  if(length<=1) return;

  int i = 1;
  for(i = 1; i*2<length; i++){
    //swap i with i*2
    T temp = arr[i];
    arr[i] = arr[i*2];
    arr[i*2] = temp;
  }
  deinterlace(arr+i, length-i);
}

不幸的是,它不适用于大小不是 2 次方的数组

编辑:无论如何,这个算法在 2 的更大幂时都失败了,所以我想我又在 0 方格了

edit 2:我找到了一个 nlogn 算法,给定一个 O(n) 数组旋转函数,或者一个初始大小是 2 的幂

像这样工作:

1a2b3c4d5e6f7g,“块大小”= 1 个初始值,

分成几组块大小 *4 1a2b 3c4d 5e6f 7g

交换每个组的内部 2 个块 12ab 34cd 56ef 7g

重复块大小 = 块大小 *2

12ab34cd 56ef7g(阅读:56 ef 7 g)-> 1234abcd 567efg

1234abcd567efg -> 1234567abcdefg

template<typename T>
void deinterlace(T* arr, int length, int group_ct = 1){
  if(group_ct*2 >= length) return;

  for(int i = 0; i<length; i+=group_ct*4){
    int rot_count = group_ct;

    int i1 = i + group_ct;
    int i2 = i+group_ct*4 - group_ct;

    if(i2+group_ct > length){
      i2 = i1 + (length-i1)/2+group_ct/2;
    }

    rotate(arr, i1, i2, group_ct);

  }

  deinterlace(arr, length, group_ct * 2);
}

edit 3我猜正确的术语是去交错,而不是去交错

【问题讨论】:

  • 一般来说,这不是一个简单的任务。这在 DSP 算法中很常见,并且有很多关于如何有效地做到这一点的研究。也许这种情况有一个简单有效的解决方案。我会等待有人证明我错了。
  • 是的,它用于音频引擎。我想我可以将初始数组填充到 2 的幂,但是这样我就浪费了空间,还不如使用一个临时数组。
  • @GlaielGamer 填充到最近的幂可能比将数组加倍小很多,具体取决于块的大小(取 60 或 4000)。
  • 你提前知道数组的大小吗?它是常数,还是少数常数之一?
  • 是的,但是填充的版本将永远保留在内存中,而临时文件可以在使用完成后被释放。

标签: c++ arrays


【解决方案1】:

这本质上是一个矩阵转置问题。你的数组

[1 a]
[2 b]
[3 c]
[4 d]

等效于 1, a, 2, b, 3, c, 4, d 如果表示为向量(通过首先读取行)。这个矩阵的转置是:

[1 2 3 4]
[a b c d]

相当于1, 2, 3, 4, a, b, c, d

有一个wikipedia page 处理一般情况下的就地矩阵转置。我想,非方阵算法可以直接应用。


您可以使用一种慢速(不确定是否为 O(n^2) 或更糟,并且为时已晚)的算法。想法是将子阵列从位置i 旋转到位置2*i。例如:

START: 1a2b3c4d5e6f
1(a2)...         -> 1(2a)...
12(ab3)...       -> 12(3ab)...
123(abc4)...     -> 123(4abc)...
1234(abcd5)...   -> 1234(5abcd)...
12345(abcde6)... -> 12345(6abcde)..
123456(abcdef)   -> DONE

数组的第一个成员是索引 0。在步骤 1,您选择子数组 a[1:2],并将其向右旋转(所有成员都转到下一个位置,最后一个开始)。下一步,选择a[2:4],然后旋转它等等。确保不要旋转最后一个子数组a[n/2:n]


如果您不需要为了性能而进行批量操作(例如memcpy),最后一个选项是提供一个访问器函数,并转换索引而不是移动任何字节。这样的函数写起来几乎是微不足道的:如果索引小于max/2,则返回2*index处的条目,否则,返回2*(index-max/2)+1处的条目。

【讨论】:

  • 好的,谢谢,这正是我需要的。好吧,这不是我真正需要的,但是知道没有临时存储就无法真正做到这一点也是一个足够令人满意的答案。
  • 关于您的编辑:是的,对于大小为兆字节的数组来说,n^2 太慢了
  • @GlaielGamer 这是迄今为止我能想到的最好的。如果我能想到更好的,我会更新答案。
  • 是的,它仍然是一个有趣的问题,但我找到了我需要的解决方法
【解决方案2】:

您最初的想法几乎适用于就地去交错。您只需要考虑这样一个事实,即当您将项目交换到位时,您会替换公式期望在那里找到的项目。

首先,定义source_index 函数:给定一个长度为N 和索引i 的完美交错数组,返回应该在i 中的项目。前半部分来自其他偶数项,后半部分来自其他奇数项。

int source_index(int i, int length) {
  int mid = length-length/2;

  if (i<mid) {
    return i*2;
  }
  return (i-mid)*2+1;
}

现在您可以遍历数组,将项目交换到位。但如果发现源索引小于当前目标索引,则需要重新计算,找出它被交换到的位置。

template<typename T>
void deinterlace(T* arr, int length){
  if(length<=1) return;

  int i = 1;
  for(i = 1; i<length; i++){
    int j = source_index(i, length);
    while (j<i) { //walk the chain of swaps
      j = source_index(j, length);
    }
    T temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }
}

这恰好进行了 N 次交换。对source_index 的调用次数有些混乱,但似乎表现出 NlgN 增长。

【讨论】:

    【解决方案3】:

    如果您不关心结果数组的顺序,我能想到的最快方法是使用“头”和“尾”索引进行连续交换。

    int head = 1;
    int tail = length - 2;
    while (head < tail)
    {
        T temp = arr[head];
        temp = arr[head];
        arr[head] = arr[tail];
        arr[tail] = temp;
        head += 2;
        tail -= 2;
    }
    

    对于您的示例情况,2 次迭代后的结果将是 15243cbdae。

    【讨论】:

    • 然后将两半排序为第二步
    • 它们不应该被排序,只是按照它们在原始数组中的顺序,即如果数组是 KZUHYDIA,结果应该是 KUYIZHDA
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-14
    • 2013-04-06
    • 2012-03-02
    • 2017-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多