矩阵常见的转换方式有旋转和倒置两种(本人还没学到线性代数~~doge).
1.矩阵旋转
矩阵的创建需要对二维数组的行列,元素进行赋初始值(这里假定 i 为行,j 为列).
建立后的矩阵旋转的方式有顺时针和逆时针两种,无论哪种,都需要对行,列进行数量关系转化.
1.1顺时针旋转
比如输入
i = 3 j = 5
c[3][5] = { { 4,7,9,1,21 } , { 11,14,2,3,5 } , { 15,29,11,45,14 } }
就形成了这样一个矩阵
1列 (0) | 2列 ( 1 ) | 3列 ( j - 3 ) | 4列 ( j - 2 ) | 5列 ( j - 1 ) | |
1行(0) | 4 | 7 | 9 | 1 | 21 |
2行( i - 2 ) | 11 | 14 | 2 | 3 | 5 |
3行( i - 1 ) | 15 | 29 | 11 | 45 | 14 |
若要完成顺时针旋转,形成以下矩阵
0 | i - 2 | i - 1 | |
0 | 15 | 11 | 4 |
1 | 29 | 14 | 7 |
j - 3 | 11 | 2 | 9 |
j - 2 | 45 | 3 | 1 |
j - 1 | 14 | 5 | 21 |
不妨对 a - 1 行,b - 1 列的元素坐标记为( a , b )( 起始行列均为0 ).
不难发现,对于矩阵中的任意一个元素,无论是代数特征还是几何特点(实际上是脑补~).
其坐标( a , b )都转化为( b ,( i - 1 ) - a ),
如原( i - 1 , j - 2 )的45坐标转化为( j - 2 ,i - 1 - ( i - 1 ) ) 即( j - 2 , 0 ).
于是你十分自信地写下了如下代码
for (a = 0; a <= i - 1; a++)
{
for (b = 0; b <= j - 1; b++)
{
c[b][i - 1 - a] = c[a][b];
}
}
结果......咦
为什么我的部分数据混乱了?
原因:若只对原数组元素间进行赋值,会使部分元素的值丢失!
图例如下
因此,我们需要一个新的二维数组来接收原数组的值的转换
for (a = 0; a <= i - 1; a++)
{
for (b = 0; b <= j - 1; b++)
{
d[b][i - 1 - a] = c[a][b]; //新数组d[][]
}
}
以下就是该顺时针旋转的函数
void clockwise(int i, int j)
{
int a, b;
int c[N][N], d[N][N]; //使用新数组d是为了防止原数组在赋值过程中丢失数据
printf("请输入矩阵的行 列:");
scanf("%d%d", &i, &j);
for (a = 0; a <= i - 1; a++)
{
printf("请输入第%d行的数:", a+1);
for (b = 0; b <= j - 1; b++)
{
scanf("%d", &c[a][b]);
}
}
for (a = 0; a <= i - 1; a++) //旋转后新矩阵的列为原来矩阵的纵长-行
{ //新矩阵的行为原来矩阵的列
for (b = 0; b <= j - 1; b++)
{
d[b][i - 1 - a] = c[a][b];
}
}
printf("矩阵顺时针旋转后:\n");
for (a = 0; a <= j - 1; a++)
{
for (b = 0; b <= i - 1; b++)
{
printf("%d ", d[a][b]);
}
printf("\n");
}
}
1.2逆时针旋转
逆时针旋转和顺时针旋转大同小异,此处略述
这次使用占用内存较少的输入(其实是本人懒了~)
i = 3 j = 2
e[3][2] = { { 4 , 21 } , { 14 , 5 } , { 29 , 45 } }
矩阵如下
0 | j - 1 (1) | |
0 | 4 | 21 |
1 | 14 | 5 |
i - 1 (2) | 29 | 45 |
逆时针旋转后
0 | 1 | i - 1 (2) | |
0 | 21 | 5 | 45 |
j - 1 (1) | 4 | 14 | 29 |
依上顺时针方法,不难想到其中一元素( c , d )坐标变换:
( c , d ) ————>( j - 1 - d , c )
考虑到数据丢失,同理也需要一个新的二维数组接收.
该部分代码如下
void anticlockwise(int i, int j)
{
int c, d;
int e[N][N], f[N][N]; //使用新数组f是为了防止原数组在赋值过程中丢失数据
printf("请输入矩阵的行 列:");
scanf("%d%d", &i, &j);
for (c = 0; c <= i - 1; c++)
{
printf("请输入第%d行的数:", c+1);
for (d = 0; d <= j - 1; d++)
{
scanf("%d", &e[c][d]);
}
}
for (c = 0; c <= i - 1; c++) //旋转后新矩阵的列为原来矩阵的行
{ //新矩阵的行为原来矩阵的横长-列
for (d = 0; d <= j - 1; d++)
{
f[j - 1 - d][c] = e[c][d];
}
}
printf("矩阵逆时针旋转后:\n");
for (c = 0; c <= j - 1; c++)
{
for (d = 0; d <= i - 1; d++)
{
printf("%d ", f[c][d]);
}
printf("\n");
}
}
2.矩阵倒置
矩阵的倒置是将矩阵每个元素的行 列交换
如输入
i = 3 j = 5
c[3][5] = { { 4,7,9,1,21 } , { 11,14,2,3,5 } , { 15,29,11,45,14 } }
倒置效果如下
按照常规思路,
倒置,将每一个元素的行 列进行交换,
但是这样必然会出现重复交换,导致交换失败的情况,
如若 g < h ,在嵌套的两个for循环中,
( g , h ) 和( h , g )交换后( h , g )又会和 ( g , h )交换,产生重复。
那这一步该如何实现呢?
这里我使用了对称性,如下
中轴蓝线所覆的元素在倒置后不改变,其两侧的元素进行交换 ,该方法只需将蓝线一侧的元素与另一侧交换一次(不能忽略空的橙色区域),不会产生重复和错误.
但这里需要分类讨论
2.1
1.行 < 列 时(就是上述情况)
有四种倒置方法
2.1.1 由蓝线右上方与左下方倒置
该类又分为横向和纵向两种
2.1.1.1横向
从第1行第2列开始,到第1行第 j 列,
再从第2行第3列开始,到第2行第 j 列,
......
......
最后从第 i 行第 i + 1列开始,到第 i 行第 j 列,依次对称转换
代码如下(g是行的参数,h是列的参数)
for (g = 1; g <= i; g++)
{
for (h = g + 1; h <= j; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
2.1.1.2纵向
在该方法中,每一列的倒置过程会在 该列数-1 列 和 i 列的较小者处结束,取 s = min{ g - 1 ,i }(以下均用s表示)
从第2列第1行直接结束 ,再
从第3列第1行开始,到第3列第 s 列,
......
......
最后从第 j 列第1行开始,到第 j 列第 s 列,依次对称转换(参考上图的几何意义)
for (h = 1; h <= j; h++)
{
if (h - 1 < i)
s = h - 1;
else
s = i;
for (g = 1; g <= s; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
2.1.2 由蓝线左下方与右上方倒置
2.1.2.1纵向
上图橙色空区域不能忽略,仍可进行交换
从第1列第2行开始,到第1列第 j 行结束,
从第2列第3行开始,到第2列第 j 行结束,
......
......
最后从第 i 列第3行开始,到第 i 列第 j 行结束,依次对称转换.
for (h = 1; h <= i; h++)
{
for (g = h + 1; g <= j; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
2.1.2.2横向
参考第一类的纵向方法,在该方法中,每一行的倒置过程会在 该行数-1 列 和 i 列的较小者处结束,取 s = min{ g - 1 ,i }(以下均用s表示)
从第2行第1列开始,到第2行第 s 列,
从第3行第1列开始,到第3行第 s 列,
......
......
最后从第 j 行第1列开始,到第 j 行第 s 列,依次对称转换(参考上图的几何意义)
代码如下
for (g = 2; g <= j; g++)
{
if (g - 1 < i)
s = g - 1;
else
s = i;
for (h = 1; h <= g; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
2.2
1.行 > 列 时(就是上述情况)
同样有两种倒置方法
2.2.1由红线左下方与右上方倒置
2.2.1.1纵向
从第1列第2行开始,到第1列第 i 行,
从第2列第3行开始,到第2列第 i 行,
......
......
最后从第 j 列第 j + 1 行,到第 j 列第 i 行结束.
for (h = 1; h <= j; h++)
{
for (g = h + 1; g <= i; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
2.2.1.2横向
(s = min{ g - 1 , j ))
从第2行第1列开始,到第2行第 s 列,
从第3行第1列开始,到第3行第 s 列,
......
......
最后从第 i 行第1列开始,到第 i 行第 s 列,依次对称转换(参考上图的几何意义)
代码如下
for (g = 2; g <= i; g++)
{
if (g - 1 < j)
s = g - 1;
else
s = j;
for (h = 1; h <= s; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
2.2.2.1由红线右上方与左下方倒置
2.2.2.1横向
从第1行第2列开始,到第1行第 i 列,
从第2行第3列开始,到第2行第 i 列,
......
......
最后从第 j 行第 j + 1 列开始,到第 j 行第 i 列,依次对称转换.
for(g = 1; g <= j; g++)
{
for(h = g+1; h <= i; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
2.2.2.2纵向
s = min{ g - 1 , j }
从第2列第1行开始直接结束,
从第3列第1行开始,到第3列第 s 行结束,
......
......
最后从第 i 列第1行开始,到第 3 列第 s 行结束,依次对称转换.
for(h = 2; h <= i; h++)
{
if(h - 1 < j)
s = h - 1;
else
s = j;
for(g = 1; g <= s; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
2.3总结
通过总结,我们不难发现,无论 i , j 谁更大,无论是中轴左下方还是右上方,无论是横向还是纵向,其核心思想都是将轴线一侧的元素不多不少,正确地进行对称交换,只不过方法选取有所不同
而且我们发现2.1.1.1与2.2.2.1 2.1.1.2与2.2.2.2 2.1.2.1与2.2.1.1 2.1.2.2与2.2.1.2所使用的方式和代码结构是相似的,在每组中通过几何特点和归纳——
得到以下总结后代码
void inversion(int i, int j)
{
int g, h, s;
int l[10][10] = { 0 }, n;
int v;
printf("请输入要选择的方法(1,2,3,4)\n");
scanf("%d", &v);
printf("请输入矩阵的行 列:");
scanf("%d%d", &i, &j);
for (g = 1; g <= i; g++)
{
printf("请输入第%d行的数:", g);
for (h = 1; h <= j; h++)
scanf("%d", &l[g][h]);
}
switch (v)
{
case 1:
if (i < j)
{
for (g = 1; g <= i; g++)//i < j的方法一
{
for (h = g + 1; h <= j; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
else
{
for (g = 1; g <= j; g++)//i > j方法三
{
for (h = g + 1; h <= i; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
case 2:
if (i < j)
{
for (g = 2; g <= j; g++)//i < j的方法二
{
if (g - 1 < i)
s = g - 1;
else
s = i;
for (h = 1; h <= g; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
else
{
for (h = 2; h <= i; h++)//i > j方法四
{
if (h - 1 < j)
s = h - 1;
else
s = j;
for (g = 1; g <= s; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
case 3:
if (i < j)
{
for (h = 1; h <= j; h++)//i < j的方法三
{
if (h - 1 < i)
s = h - 1;
else
s = i;
for (g = 1; g <= s; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
else
{
for (h = 1; h <= j; h++)//i > j的方法一
{
for (g = h + 1; g <= i; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
case 4:
if (i < j)
{
for (h = 1; h <= i; h++)//i < j的方法四
{
for (g = h + 1; g <= j; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
else
{
for (g = 2; g <= i; g++)//i > j方法二
{
if (g - 1 < j)
s = g - 1;
else
s = j;
for (h = 1; h <= s; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
default:
printf("请好好输入选择的方法:");
scanf("%d", &v);
break;
}
printf("矩阵倒置后:\n");
for (g = 1; g <= j; g++)
{
for (h = 1; h <= i; h++)
printf("%d ", l[g][h]);
printf("\n");
}
}
矩阵转化的所有代码如下,希望对你有帮助~
#define _CRT_SECURE_NO_WARNINGS
#define N 32
#include<stdio.h>
//____________________________________________________________
void clockwise(int i, int j)
{
int a, b;
int c[N][N], d[N][N]; //使用新数组d是为了防止原数组在赋值过程中丢失数据
printf("请输入矩阵的行 列:");
scanf("%d%d", &i, &j);
for (a = 0; a <= i - 1; a++)
{
printf("请输入第%d行的数:", a + 1);
for (b = 0; b <= j - 1; b++)
{
scanf("%d", &c[a][b]);
}
}
for (a = 0; a <= i - 1; a++) //旋转后新矩阵的列为原来矩阵的纵长-行
{ //新矩阵的行为原来矩阵的列
for (b = 0; b <= j - 1; b++)
{
d[b][i - 1 - a] = c[a][b];
}
}
printf("矩阵顺时针旋转后:\n");
for (a = 0; a <= j - 1; a++)
{
for (b = 0; b <= i - 1; b++)
{
printf("%d ", d[a][b]);
}
printf("\n");
}
}
//____________________________________________________________
void anticlockwise(int i, int j)
{
int c, d;
int e[N][N], f[N][N]; //使用新数组f是为了防止原数组在赋值过程中丢失数据
printf("请输入矩阵的行 列:");
scanf("%d%d", &i, &j);
for (c = 0; c <= i - 1; c++)
{
printf("请输入第%d行的数:", c + 1);
for (d = 0; d <= j - 1; d++)
{
scanf("%d", &e[c][d]);
}
}
for (c = 0; c <= i - 1; c++) //旋转后新矩阵的列为原来矩阵的行
{ //新矩阵的行为原来矩阵的横长-列
for (d = 0; d <= j - 1; d++)
{
f[j - 1 - d][c] = e[c][d];
}
}
printf("矩阵逆时针旋转后:\n");
for (c = 0; c <= j - 1; c++)
{
for (d = 0; d <= i - 1; d++)
{
printf("%d ", f[c][d]);
}
printf("\n");
}
}
//____________________________________________________________
void inversion(int i, int j)
{
int g, h, s;
int l[10][10] = { 0 }, n;
int v;
printf("请输入要选择的方法(1,2,3,4)\n");
scanf("%d", &v);
printf("请输入矩阵的行 列:");
scanf("%d%d", &i, &j);
for (g = 1; g <= i; g++)
{
printf("请输入第%d行的数:", g);
for (h = 1; h <= j; h++)
scanf("%d", &l[g][h]);
}
switch (v)
{
case 1:
if (i < j)
{
for (g = 1; g <= i; g++)//i < j的方法一
{
for (h = g + 1; h <= j; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
else
{
for (g = 1; g <= j; g++)//i > j方法三
{
for (h = g + 1; h <= i; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
case 2:
if (i < j)
{
for (g = 2; g <= j; g++)//i < j的方法二
{
if (g - 1 < i)
s = g - 1;
else
s = i;
for (h = 1; h <= g; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
else
{
for (h = 2; h <= i; h++)//i > j方法四
{
if (h - 1 < j)
s = h - 1;
else
s = j;
for (g = 1; g <= s; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
case 3:
if (i < j)
{
for (h = 1; h <= j; h++)//i < j的方法三
{
if (h - 1 < i)
s = h - 1;
else
s = i;
for (g = 1; g <= s; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
else
{
for (h = 1; h <= j; h++)//i > j的方法一
{
for (g = h + 1; g <= i; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
case 4:
if (i < j)
{
for (h = 1; h <= i; h++)//i < j的方法四
{
for (g = h + 1; g <= j; g++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
else
{
for (g = 2; g <= i; g++)//i > j方法二
{
if (g - 1 < j)
s = g - 1;
else
s = j;
for (h = 1; h <= s; h++)
{
n = l[g][h];
l[g][h] = l[h][g];
l[h][g] = n;
}
}
}
default:
printf("请好好输入选择的方法:");
scanf("%d", &v);
break;
}
printf("矩阵倒置后:\n");
for (g = 1; g <= j; g++)
{
for (h = 1; h <= i; h++)
printf("%d ", l[g][h]);
printf("\n");
}
}
//____________________________________________________________
int main()
{
int i = 0, j = 0, a, b, m;
printf("请选择矩阵的变换方式\n");
printf("1.顺时针旋转 2.逆时针旋转 3.矩阵倒置 0.退出\n");
printf("请选择:");
while (EOF != scanf("%d", &m))
{
switch (m)
{
case 0:
printf("已退出\n");
return 0;
case 1:
clockwise(i, j);
break;
case 2:
anticlockwise(i, j);
break;
case 3:
inversion(i, j);
break;
default:
printf("重新输入\n");
break;
}
printf("请选择:");
}
return 0;
}
本人是大一的学生,第一次写博客,很多知识掌握得还不太好,如果上文有错误或需要改进的地方,欢迎大家指正,我也会慢慢进步哒~~
本人gitee账号:小奶狼 大柔情 (runrun-brother) - Gitee.com
原创不易,请勿未经允许转载~