幻方阵是一个矩阵,即将n*n(n>=3)个数字放入n*n的方格内,使方格的各行、各列及对角线上各数字之各相等。
Merzirac 算法。
在第最上面一行居中的方格内放1,依次向右上方填入2、3、4…,如果右上方已有数字,则向下移一格继续填写。如果坐标值超出上限,则归0,如果坐标值超出下限,则变为最大坐标值。
实现:
public static int[,] Generat(int n)
{
int[,] matrix = new int[n, n];
int x = n / 2;
int y = n-1;
int maxNumber = n*n;
for (int i = 1; i <= maxNumber; i++)
{
//修正坐标
x = GetRealPositon(x, n);
y = GetRealPositon(y, n);
if (matrix[x, y] != 0)
{
//到原始方格的正下方方格
x--;
y = y-2;
//修正坐标
x = GetRealPositon(x, n);
y = GetRealPositon(y, n);
//寻找原始方格的正下方中未被附值的方格
while (matrix[x, y] != 0)
{
y--;
y = GetRealPositon(y, n);
}
}
matrix[x, y] = i;
//到右上角
x++;
y++;
}
return matrix;
}
private static int GetRealPositon(int x, int n)
{
if (x < 0)
return n - 1;
else if (x >= n)
return 0;
else
return x;
}
直接的数学公式。
N(I,J)=((I+J+(n-3)/2) MOD n)*n+(I-J+(3*n-1)/2) MOD n +1;
问题分析:
假定N = 1, 则可能的整数集这能为{1}, 所以个数为1。
假定M = 1,N > 1, 则不可能有合适的整数集,所以个数为0。
假定M > N, 则结果集的个数和M = N的一样多, 因为不可能出现比N大的数。
假定M <= N, M > 1, N > 1, 此时我们有两种情况,结果集中包括M, 或者不包括。最终的数量为这两种情况的数量之和。
假定我们用F(N,M)来表示结果集的数量。则有如下表达式:
F(1,M) = 1; M为任意值.
F(N,1) = 0; N > 1.
F(N,M) = F(N,N); M > N.
F(N,M) = F(N - M,M - 1) + F(N,M - 1);M <= N, M > 1, N > 1.
一般有两种方式来解决这种问题,递归和动态规划。
递归方式:
public static int GetPartitionCount2(int number, int maxElement)
{
if (number == 1)
return 1;
if (maxElement == 1)
return 0;
if (maxElement < number)
return GetPartitionCount2(number - maxElement, maxElement - 1) + GetPartitionCount2(number, maxElement - 1);
else
return 1 + GetPartitionCount2(number, number - 1);
}
动态规划:
public static int GetPartitionCount1(int number, int maxElement)
{
int[,] calculateTable = new int[number + 1, maxElement + 1];
calculateTable[1, 1] = 1;
for (int i = 2; i < number + 1; i++)
{
calculateTable[i, 1] = 0;
}
for (int i = 1; i < maxElement + 1; i++)
{
calculateTable[1, i] = 1;
}
for (int i = 2; i < maxElement + 1; i++)
{
for (int j = 2; j < number + 1; j++)
{
if(i<j)
calculateTable[j, i] = calculateTable[j - i, i - 1] + calculateTable[j, i - 1];
else if(i>=j)
calculateTable[j, i] = 1 + calculateTable[j, j - 1];
}
}
return calculateTable[number, maxElement];
}
就递归方式而言,最大的不好之处就是递归次数太多,做了太多的冗余计算。
(N,M) 所需时间(ms)
100,100 250
120,120 1281.3
140,140 6297
160,160 27484.7
180,180 110844.5
200,200 398417.4
相对而言,动态规划效率要高很多,不是一个数量级的。
(N,M) 所需时间(ms)
1000,1000 46
2000,2000 187.5
3000,3000 593.8
4000,4000 1719.0
5000,5000 3172.3
6000,6000 5657.0
7000,7000 9345.1
8000,8000 13220.6
9000,9000 17736.9
10000,10000 22329.8
但是,在上面的动态规划算法中,有一个致命的地方就是数组分配,
int[,] calculateTable, 如果m,n较大,马上就内存溢出。
1)用缩写。比如c:\Program Files缩写为c:\Progra~1
再来刨根问底查查这个命名是否有规则,于是找到:
采用8个字符缩写,即写头六个字母(略去空白),另加波浪号和1,首字母不足六个字母,略去空白,用了第二个词的字母,凑成六个。例如:
"Documents and Settings“ -- DOCUME~1
"Local Settings" -- LOCALS~1 (注意略去空白,用了第二个词的字母,凑成六个,再加波浪号和1)。
如果多个文件前6字符一样怎么办?为什么最后是1而不是0或者其他数字呢?看看这个例子
假设下面是你的C盘根目录中的文件夹:
Program Files
Progra file
Progra zhang
则三个目录分别表示为:C:\Progra~1; C:\Progra~2; C:\Progra~3;
2)绕过去,创建一个镜像。例如在cmd中输入 subst w: "C:\Documents and Settings\hopeshared"。然后就可以直接用w:\替代C:\Documents and Settings\hopeshared了
以下内容以2007为准,不排除2003上有不同
每个Cell都可以设置Format,设置不同,处理的结果也会不一样。
情况1,没有设置Format。如果你输入的内容是日期,他会自动把这个Cell的Format变为日期型的,日期格式以当前的机器设置为准。这个时候,你直接以读数据库的方式去读的话,那个
你得到的是一个DateTime类型的值。
情况2,设置Format为日期类型,并指定固定的日期格式。你将得到的是一个DateTime类型的值。
情况3,设置Format为字符型,你将得到的是一个string类型的值。
举个实际的例子:
比如,你输入一个值:02\04\2008,本机的日期格式设置为英语(月-日-年)
情况1,你将得到一个DateTime:2008年02月04日
情况2。假设你Format为法语(日-月-年),你将得到一个DateTime:2008年04月02日
情况3,你将得到一个string:02\04\2008
所以,在处理的时候,要十分的小心,以避免出现日期不对的情况。为避免Excel做一些你不希望的操作,最好事先就给一个固定的Format,