Dovetailing矢量

时间:2014-01-05 08:22:04

标签: algorithm matrix

假设给你一个带有d ^ 2个元素的向量v [1..d ^ 2],

  

v [1] v [2] v [3] v [4] v [5] ... v [d ^ 2],

我们希望对角地填充 d x d 矩阵M,而不是逐行或逐列填充。

例如,如果d = 4,则得到的矩阵将是

  

v [01] v [03] v [06] v [10]

     

v [02] v [05] v [09] v [13]

     

v [04] v [08] v [12] v [15]

     

v [07] v [11] v [14] v [16]

此技术记得 dovetailing ,但在这种情况下,我们想要完全填充平方矩阵。

如何设计一个算法,其中给定的向量 v 和维度 d 它可以回答上述矩阵?

3 个答案:

答案 0 :(得分:1)

我看了一下这个问题,我想我找到了一个将矩阵中的坐标映射到向量中的索引的模式。查看4x4示例中用于填充矩阵的索引之间的差异:

 0 (+2)  2 (+3)  5 (+4)  9
(+1)    
 1 (+3)  4 (+4)  8 (+4) 12
(+2)
 3 (+4)  7 (+4) 11 (+3) 14
(+3)
 6 (+4) 10 (+3) 13 (+2) 15

列开头的偏移量以算术级数增加,元素之间的偏移量可视为从列表中获取:offsets = [2, 3, 4, 4, 3, 2]偏移当前行和列。即,从(r,c)转到(r,c+1),您需要添加offset[r + c]

我们可以将此偏移定义为函数:

inline int 
offset(int d, int i)
{
    return i+2 - std::max(0, 2*(i-d+2)-1);
}

它产生如下序列:

d=4: 2, 3, 4, 4, 3, 2
d=5: 2, 3, 4, 5, 5, 4, 3, 2

现在我们可以在循环中使用它来查找索引以填充矩阵:

for( int rOff = 0, r = 0
   ; r < d
   ; rOff += ++r
   )
    for( int i = rOff, c = 0
       ; c < d
       ; i += offset(d, r + c++)
       )
        matrix[r][c] = v[i];

这是一个非常复杂的循环,增量内部有增量,但是如果你稍微过了一点,那就有意义了:

  • rOff是每行开头的偏移量,由外循环更新。 在每一行的开头,我们将新行号添加到rOff,因此我们得到一个 算术级数:0, 1, 3, 6, 10...中的值为rOff

    请注意,我在更新++r时使用rOff,因为我希望在之后添加r 的值,而不是之前。

  • i是特定行和列v的{​​{1}}索引,它由内循环控制。我们使用之前编写的(r,c)方法更新它,使用上面的解释(从offset(d, k)的索引到(r,c)的索引,我们必须添加{{1} }}

    注意,时间,我希望之前的值,所以我使用(r,c+1)来更新offset(d, r + c)。< / EM>

如果这有点令人困惑,那很可能是因为嵌套的递增,所以在这里,所有这些都被解开了:

c++

作为完整性检查,以下是上述循环的输出,但i已替换为int rOff = 0; for(int r = 0; r < d; ++r) { rOff += r; int i = rOff; for(int c = 0; c < d; ++c) { matrix[r][c] = v[i]; i += offset(d, r + c); } } ,并且在每行末尾添加了额外的换行符:

matrix[r][c] = v[i]

std::cout << i << ' ';

d = 4

0 2 5 9 
1 4 8 12 
3 7 11 14 
6 10 13 15

答案 1 :(得分:1)

如果您只考虑两个循环,事情就相当简单:一个遍历上部反三角形,另一个循环代表下部循环。对于第一个,向前推进外循环中的行,内循环向上和向右遍历。在第二个中,在外环中跨越柱子并在内部中再次向上和向右前进。这是一个完整的C程序:

#include <stdio.h>

int main(void) 
{
  int d = 4, v[d * d], m[d][d], start, i, j, k, i0, j0;

  // Fill v with 1,2,...d^2
  for (int i = 0; i < d * d; i++) v[i] = i+1;

  // Start dovetail. 
  k = 0;
  for (i0 = 0; i0 < d; i0++)  // Down left column
    for (i = i0, j = 0; i >= 0; i--, j++)
      m[i][j] = v[k++];

  for (j0 = 1; j0 < d; j0++)  // Across bottom row
    for (i = d - 1, j = j0; j < d; i--, j++)
      m[i][j] = v[k++];
  // End dovetail.

  for (i = 0; i < d; i++) {
    for (j = 0; j < d; j++) printf("%4d", m[i][j]);
    printf("\n");
  }
  return 0;
}

答案 2 :(得分:1)

如果您只考虑两个循环巢穴,事情就相当简单:一个遍历上部反三角形,另一个遍历下部。有了这个,您只需要简单的计数器,而不需要maxmin操作。

对于第一个,向前推进外循环中左列的行,内部向上和向右遍历。

在第二个中,在外环中穿过底行的列,然后再向上和向右穿过内部。这是一个C程序:

#include <stdio.h>

int main(void) 
{
  int d = 4, v[d * d], m[d][d], start, i, j, k, i0, j0;

  // Fill v with 1,2,...d^2
  for (int i = 0; i < d * d; i++) v[i] = i+1;

  // Start dovetail. 
  k = 0;
  for (i0 = 0; i0 < d; i0++)  // Down left column
    for (i = i0, j = 0; i >= 0; i--, j++)
      m[i][j] = v[k++];

  for (j0 = 1; j0 < d; j0++)  // Across bottom row
    for (i = d - 1, j = j0; j < d; i--, j++)
      m[i][j] = v[k++];
  // End dovetail.

  // Print the result.
  for (i = 0; i < d; i++) {
    for (j = 0; j < d; j++) printf("%4d", m[i][j]);
    printf("\n");
  }
  return 0;
}

输出:

1   3   6  10
2   5   9  13
4   8  12  15
7  11  14  16