LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
楼主: lordbyorn

求能计算所有方阵的程序

[复制链接]
发表于 2003-7-10 00:57:09 | 显示全部楼层
改进。
因为N行和N列可以用SUM减前N-1项求得,所以我们只要穷举(N - 1) * (N - 1)的方阵就可以了,
代码增加了一个gen函数,改变了func函数,
因为行和列的和已经保证等于SUM,所以testsum函数中去掉了行和列的检查。
另:因为我的程序是用define定义的N,所以改方阵阶数的时候要同时改
#define N 3
#define N2 9
#define SUM 15
三行。

  1. #include <stdio.h>

  2. #define N 3
  3. #define N2 9
  4. #define SUM 15
  5. #define FALSE 0
  6. #define TRUE 1
  7. #define WIDTH 2

  8. int testsum(void);
  9. void print_a(void);
  10. void gen(void);
  11. void func(int);

  12. int a[N2];
  13. int num[N2];
  14. char line[(WIDTH + 1) * N + 1];
  15. int count;

  16. int main(void)
  17. {
  18.   int i;

  19.   memset(line, '-', (WIDTH + 1) * N);
  20.   line[(WIDTH + 1) * N] = '\0';
  21.   for(i = 0; i < N2; ++i)
  22.     num[i] = 0;

  23.   func(0);
  24.   printf("count: %d\n", count);

  25.   exit(0);
  26. }

  27. int
  28. testsum(void)
  29. {
  30.   int x, y, sum;

  31.   sum = 0;
  32.   for(x = 0, y = 0; x < N; ++x, ++y)
  33.     sum += *(a + y * N + x);
  34.   if(sum != SUM)
  35.     return(FALSE);
  36.   sum = 0;
  37.   for(x = N - 1, y = 0; y < N; --x, ++y)
  38.     sum += *(a + y * N + x);
  39.   if(sum != SUM)
  40.     return(FALSE);

  41.   ++count;
  42.   return(TRUE);
  43. }

  44. void
  45. print_a(void)
  46. {
  47.   int x, y, i;

  48.   for(y = 0; y < N; ++y){
  49.     for(x = 0; x < N; ++x)
  50.       printf("%*d ", WIDTH, *(a + y * N + x));
  51.     printf("\n");
  52.   }
  53.   printf("%s\n", line);
  54. }

  55. void
  56. gen(void)
  57. {
  58.   int al[N2];
  59.   int i, j, sum, val;

  60.   memcpy(al, num, N2 * sizeof(num[0]));
  61.   for(i = N2 - N; i < N2 - 1; ++i){
  62.     sum = 0;
  63.     for(j = i % N; j < i; j += N)
  64.       sum += a[j];
  65.     val = SUM - sum;
  66.     if(val < 1 || val > N2 || al[val - 1] == 1)
  67.       return;
  68.     else{
  69.       al[val - 1] = 1;
  70.       a[i] = val;
  71.     }
  72.   }
  73.   for(i = N - 1; i < N2; i += N){
  74.     sum = 0;
  75.     for(j = i - N + 1; j < i; ++j)
  76.       sum += a[j];
  77.     val = SUM - sum;
  78.     if(val < 1 || val > N2 || al[val - 1] == 1)
  79.       return;
  80.     else{
  81.       al[val - 1] = 1;
  82.       a[i] = val;
  83.     }
  84.   }
  85.   if(testsum())
  86.     print_a();
  87. }

  88. void
  89. func(int n)
  90. {
  91.   int i;

  92.   for(i = 0; i < N2; ++i){
  93.     if(num[i] == 0){
  94.       num[i] = 1;
  95.       a[n] = i + 1;
  96.       if(n == N2 - N - 2)
  97.         gen();
  98.       else if(n % N == N - 2)
  99.         func(n + 2);
  100.       else
  101.         func(n + 1);
  102.       num[i] = 0;
  103.     }
  104.   }
  105. }
复制代码

time:
N=3:0.010 (比完全穷举快了26倍)
N=4:39m25s 7040个结果

to:lordbyorn
我运行过你的程序,N=4的时候好象没有30s就出来吧,是不是30m?
我运行程序的时候是在X下,开了十几个窗口,还有人在我的ftp上传一个电影,所以有点慢,呵呵

再改进,想法是:
对N-1行和N-1列的取值是有范围的,最小值是SUM - N2 - (前N-2项的和)
因为SUM - 第N项的值 == 前N-1项的和
又因为第N项的值最大是N2,所以SUM - N2 == 前N-2项的和 + 第N-1项的和
回头再改改。
发表于 2003-7-10 01:33:41 | 显示全部楼层
按上面的想法改了func函数,其他代码没变:

  1. void
  2. func(int n)
  3. {
  4.   int i;
  5.   int min = 0, max = N2, sum = 0;

  6.   if(n % N == N - 2){
  7.     for(i = n - N + 2; i < n; ++i)
  8.       sum += a[i];
  9.     min = SUM - N2 - sum - 1 > 0 ? SUM - N2 - sum - 1 : 0;
  10.     max = SUM - sum - 1 < N2 ? SUM - sum - 1 : N2;
  11.   }else if(n / N == N - 2){
  12.     for(i = n % N; i < n; i += N)
  13.       sum += a[i];
  14.     min = SUM - N2 - sum - 1 > 0 ? SUM - N2 - sum - 1 : 0;
  15.     max = SUM - sum - 1 < N2 ? SUM - sum - 1 : N2;
  16.   }

  17.   for(i = min; i < max; ++i){
  18.     if(num[i] == 0){
  19.       num[i] = 1;
  20.       a[n] = i + 1;
  21.       if(n == N2 - N - 2)
  22.         gen();
  23.       else if(n % N == N - 2)
  24.         func(n + 2);
  25.       else
  26.         func(n + 1);
  27.       num[i] = 0;
  28.     }
  29.   }
  30. }
复制代码

time:
N=3:0.006
N=4:7m39s (呵呵,快了5倍) 7040个结果
 楼主| 发表于 2003-7-10 12:34:00 | 显示全部楼层
我的想法是这样的。对每一个数都要检查。我的range函数就是这样的作用。
为什么要这样做呢??在(N×N)!中,每减小一个乘数,程序的速度就会快很多。
我的是这样的:
用sum减去同列(行,对角线)中已经填了的数,若再减去可能填的最大数的和,可以求出可能填的最小数(若再减去可能填的最小数的和,可以求出可能填的最大数)

1 2 - - -
要填第三个数,
sum=65
minnum=65-1-2-24-25=13
maxnum=65-1-2-3-4=55
因为55>25;所以maxnum=25
所以第三个数的范围是 13<=num<=25
很好吧???

我现在想的是怎样优化range函数。
libinary,你从这个起点出发吧,除非你有更好的办法。
我想到一点办法了。开工!!!!
 楼主| 发表于 2003-7-10 12:42:48 | 显示全部楼层
最初由 libinary 发表

我运行过你的程序,N=4的时候好象没有30s就出来吧,是不是30m?

什么意思??30s出不来??什么30M?
我的最高记录是23s(N=4).
用O2,在console下,没有开其它的程序。
发表于 2003-7-10 21:36:40 | 显示全部楼层
考虑到方阵的对称性,可以用旋转和对称操作来减少运算量。一个满足条件的幻方分别旋转90度、180度和270度就变成了另外一个满足条件的幻方。再看看对称,軕对称方式有两种,分别是上下对称和左右对称。由于上下对称和旋转180度等价。实际上只有左右对称。这样组合起来,一个满足条件的幻方就可以通过旋转和对称操作来变成8个满足条件的幻方。事实上算出八分之一,然后通过旋转和对称操作就可以得到全部的幻方。以N=4为例,只要算出880个,然后通过旋转和对称操作就可以得到880X4=7040个幻方了。
 楼主| 发表于 2003-7-10 23:42:12 | 显示全部楼层
问题是怎样判断递归结束?
发表于 2003-7-11 04:25:26 | 显示全部楼层
程序正在继续改进,今天看了一下你的程序,我的程序简直快变成和你的一模一样了,呼呼,晕。

第一次看你的程序的时候感觉看不懂,就没好好看,后来写的求等式数目的程序还附了一堆说明,原来这个不加注释你也懂呀,又晕。
 楼主| 发表于 2003-7-11 11:15:47 | 显示全部楼层
呵呵,我还没看就懂了。

第一次看你的程序的时候感觉看不懂

我第二次看我的程序时都没用看懂(range函数不懂)
 楼主| 发表于 2003-7-11 12:41:07 | 显示全部楼层
我准备求完美魔方.如果一个n阶幻方任一行任一列及任一泛对角线上各数的和相等, 则称此幻方为n阶完美幻方。
 楼主| 发表于 2003-7-12 03:59:01 | 显示全部楼层
exit 函数在哪里??
我的n阶完美幻方程序写好了。


  1. #include<time.h>
  2. #include<stdio.h>

  3. #define Max 20
  4. #define MAX(x,y) (x)>(y)?(x) : (y)

  5. int count=0;
  6. int END;
  7. int M,MM;
  8. int sum;
  9. int maxnum,minnum;
  10. int data[Max*Max];
  11. int flags[Max*Max];

  12. FILE *outfile;
  13. time_t start,end;
  14. int min_not_used[Max+1]={0};
  15. int max_not_used[Max+1]={0};

  16. class STACKDATA{
  17. private:
  18.         int buff[Max+1];
  19.         int cursor;
  20. public:
  21.         STACKDATA(){
  22.                 cursor=-1;
  23.                 sum=0;
  24.         }
  25.         ~STACKDATA(){
  26.         }
  27.         int push(int newdata){
  28.                 buff[++cursor]=newdata;
  29.                 sum+=newdata;
  30.                 return 1;
  31.         }
  32.         int pop(){
  33.                 if(cursor<0)return -1;
  34.                 sum-=buff[cursor];
  35.                 return buff[cursor--];
  36.         }
  37.         int sum;
  38. };

  39. void init();
  40. void bye();
  41. void range(int,int,int,int );
  42. void magic(int);
  43. void output(void);


  44. STACKDATA rowbuff[Max+1];
  45. STACKDATA colbuff[Max+1];
  46. STACKDATA ldbuff[Max+1];
  47. STACKDATA rdbuff[Max+1];

  48. /***********  bigin main   *************/

  49. int main(int argc,char *argv[]) {
  50. if(argc!=2){
  51.          fprintf(stderr,"usage: %s num \n",argv[0]);
  52.          return 1;
  53. }
  54. sscanf(argv[1],"%d",&M);
  55. init();
  56. magic(0);
  57. bye();
  58. return 0;
  59. }

  60. /******    end of main  *****************/

  61. void init()
  62. {

  63. int i;
  64. outfile=fopen("result.txt","w");
  65. for(i=0;i<Max*Max;i++)flags[i]=0;
  66. if(M>Max){puts("Max M exceed!!\n");}
  67. MM=M*M;
  68. sum=(MM+1)*M/2;
  69. END=MM-1;
  70. start=time(&start);
  71. fprintf(outfile,"start at %s\n//////////////////////////////////\n",ctime(&start));

  72. }

  73. /************************************************/
  74. void bye()
  75. {
  76. end=time(&end);
  77. fprintf(outfile,"\n//////////////////////////////////\nend at %s\n",ctime(&end));
  78. fprintf(outfile,"%d matchs found!",count);
  79. fclose(outfile);
  80. }
  81. /***************************************************/
  82. void magic(int n)
  83. {
  84. int i,ii;
  85. int max;
  86. int col,row,diag;

  87. row=n/M;
  88. col=n%M;
  89. diag=(col-row+M)%M;
  90. range(n,row,col,diag);

  91.         for(i=minnum,max=maxnum;i<=max;i++) {
  92.                 if(flags[i]==0) {
  93.                         ii=i+1;
  94.                         flags[i]=1;
  95.                         data[n]=ii;
  96.                         rowbuff[row].push(ii);
  97.                         colbuff[col].push(ii);
  98.                         ldbuff[diag].push(ii);
  99.                         rdbuff[diag].push(ii);
  100.                         if(n==(END)){
  101.                                 output();
  102.                         }
  103.                         else magic(n+1);
  104.                         flags[i]=0;
  105.                         rowbuff[row].pop();
  106.                         colbuff[col].pop();
  107.                         ldbuff[diag].pop();
  108.                         rdbuff[diag].pop();
  109.                 }
  110.         }
  111.         return;
  112. }

  113. /**********************************************************/
  114. void output()
  115. {
  116.         int i,j;

  117.         count++;
  118.         fprintf(outfile,"%d:\n",count);
  119.         for(i=0;i<M;i++){
  120.                 for(j=0;j<M;j++){
  121.                         fprintf(outfile,"%5d",data[i*M+j]);
  122.                 }
  123.                 fputc('\n',outfile);
  124.         }
  125.         fflush(outfile);
  126. }



  127. /*******************  This function to large!!!        ************/
  128. void range(int n,int row,int col,int diag) {
  129. static int used;
  130. static int bottom,top,left;
  131. static int c;
  132. static int tmp;
  133. static int i,j,k;

  134. k=MAX(M-col,M-row);

  135. if(k>M)k=M;
  136. for(i=0,c=1,tmp=0;c<=k;i++){
  137.          if(flags[i]==0){
  138.                 tmp+=i+1;
  139.                 c++;
  140.                 min_not_used[c]=tmp;
  141.          }
  142. }
  143. for(i=END,c=1,tmp=0;c<=k;i--){
  144.          if(flags[i]==0){
  145.                 tmp+=i+1;
  146.                 c++;
  147.                 max_not_used[c]=tmp;
  148.          }
  149. }

  150. used=rowbuff[row].sum;
  151. left=sum-used-1;
  152. bottom=max_not_used[M-col];
  153. top   =min_not_used[M-col];
  154.         minnum=left-bottom;
  155.         maxnum=left-top;
  156.          
  157. used=colbuff[col].sum;
  158. left=sum-used-1;
  159. bottom=max_not_used[M-row];
  160. top   =min_not_used[M-row];
  161. minnum=minnum<left-bottom?left-bottom:minnum;
  162. maxnum=maxnum>left-top?left-top:maxnum;

  163. used=ldbuff[diag].sum;
  164. left=sum-used-1;
  165. bottom=max_not_used[M-row];
  166. top   =min_not_used[M-row];
  167. minnum=minnum<left-bottom?left-bottom:minnum;
  168. maxnum=maxnum>left-top?left-top:maxnum;

  169. used=rdbuff[diag].sum;
  170. left=sum-used-1;
  171. bottom=max_not_used[M-row];
  172. top   =min_not_used[M-row];
  173. minnum=minnum<left-bottom?left-bottom:minnum;
  174. maxnum=maxnum>left-top?left-top:maxnum;

  175. if(minnum<0)minnum=0;
  176. if(maxnum>=MM)maxnum=MM-1;
  177. }

  178. /*****************  end of program   *****************************************/
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表