假设给你一个带有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 它可以回答上述矩阵?
答案 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)
如果您只考虑两个循环巢穴,事情就相当简单:一个遍历上部反三角形,另一个遍历下部。有了这个,您只需要简单的计数器,而不需要max
或min
操作。
对于第一个,向前推进外循环中左列的行,内部向上和向右遍历。
在第二个中,在外环中穿过底行的列,然后再向上和向右穿过内部。这是一个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