1.问题描述
幸运方阵问题:任意指定一个阶数,例如3,在任意选定一个“幸运数”,例如100,要求生成一个3阶方阵。从方阵中任意划去一行与一列,记下交叉点的数值;再从方阵剩余部分划去一行与一列,在记下交叉点数值;继续这一过程,当方阵已不剩下任何元素时,所有记下的值之和恰好为100。
12 | 13 | 68 |
14 | 15 | 70 |
17 | 18 | 73 |
如左图所示,任意不同行不同列的3个数之和均为100。目标就是为了生成指定阶数n和幸运数num的方阵。
2.算法分析
如果采用先生成随机数然后用递归的计算和是否为幸运数的方法来处理这个问题,过程会非常繁琐而且算法效率低下。那么我们可以考虑,方阵是n*n型的,我们可以用两个数组行数组row和列数组col各存放n个数,行数组和列数组中的数两两相加,一共可以得到n*n个数,分别是我们需要生成的方阵的每个元素的值。
由于要求方阵n个不同行不同列的元素值的和为幸运数num,方阵n个不同行不同列的元素值之和恰好是行数组和列数组所有元素之和,由上述思路,我们只需让行数组row和列数组col中所有数的和为幸运数num即可。具体实现可以这样做,让列数组最后一个数为num减去其余n+(n-1)个数之和。
3.问题解决
由上述算法分析可以编写代码如下:
#include "stdafx.h"
#include "stdlib.h"
#include "time.h"
#define SIZE 10
int _tmain(int argc, _TCHAR* argv[])
{
int square[SIZE][SIZE],row[SIZE],col[SIZE],n,num,k,sum,i,j;
printf("\n请输入幸运方阵的阶数: \n");
scanf("%d",&n);
printf("请输入一个幸运数字: \n");
scanf("%d",&num);
//srand(0);
srand(time(0)); //srand放在循环外可以保证每次运行都可以得到不同的结果,增强随机性
k=num/(2*n); //当取k=num/n时方阵元素值有可能会出现负数值
if(k==0)
{
printf("\n你输入的幸运数字太小了,请重新输入:\n");
scanf("%d",&num);
}
sum=0;
for(i=0;i<n;i++)
{
row[i]=rand()%k;
col[i]=rand()%k;
sum=sum+row[i]+col[i];
}
col[n-1]=col[n-1]-(sum-num); //列数组最后一个值等于num减去行数组n个数和列数组前n-1个数之和
printf("\n");
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
square[i][j]=row[i]+col[j];
printf("%5d",square[i][j]);
}
printf("\n");
}
return 0;
}
运行结果如下:
由结果可知生成方阵任意3个不同行不同列的数之和确实为100。当我们改变阶数和幸运数的时候结果也符合要求。如下:
我们可以发现生成方阵最后一列的数普遍偏大,这是因为算法中行数组n个数和列数组中前n-1个数的值都太小,这是由k=num/(2*n)和rand()%k决定的,而列数组最后一个数等于num减去n+(n-1)个数之和,因此偏大。
4.优化思考
为了方阵中每个元素值更均匀,那能不能把k=num/(2*n)改成k=num/n呢?
经分析,确实这样生成方阵中每个数的值大小较为均匀,但是带来的问题是这样有可能生成矩阵中会产生负数。下面将n设置为固定数3,幸运数固定为100,循环产生合乎要求的方阵,从而找到包含负数的方阵的情况。代码如下:
#include "stdafx.h"
#include "stdlib.h"
#include "time.h"
#define SIZE 10
int _tmain(int argc, _TCHAR* argv[])
{
int t=1;
do
{
int square[SIZE][SIZE],row[SIZE],col[SIZE],n,num,k,sum,i,j;
/*
printf("\n请输入幸运方阵的阶数: \n");
scanf("%d",&n);
printf("请输入一个幸运数字: \n");
scanf("%d",&num);
*/
n=3;
num=100;
//srand(0);
srand(time(0));
k=num/n;
if(k==0)
{
printf("\n你输入的幸运数字太小了,请重新输入:\n");
scanf("%d",&num);
}
sum=0;
for(i=0;i<n;i++)
{
row[i]=rand()%k;
col[i]=rand()%k;
sum=sum+row[i]+col[i];
}
col[n-1]=col[n-1]-(sum-num);
printf("\n");
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
square[i][j]=row[i]+col[j];
printf("%5d",square[i][j]);
if(square[i][j]<0)
t=0;
else t=1;
}
printf("\n");
}
}while(t);
return 0;
}
运行结果如下:
由此可见确实产生了负数。