转自:http://www.cnblogs.com/pmer/archive/2012/04/02/2429870.html

糟蹋好题——魔方阵问题

输出"魔方阵"。所谓魔方阵是指这样的方阵,它的每一行、每一列和对角线之和均相等。例如,三阶魔方阵为
      8 1 6
      3 5 7
      4 9 2
要求输出1~n*n的自然数构成的魔方阵。
解:魔方阵中各数的排列规律如下:
(1)将1放在第1行的中间一列。
(2)从2开始直到n×n止各数依次按下列规则存放:每一个数存放的行比前一个数的行数减1,
列数加1(例如上面的三阶魔方阵,5在4的上一行后一列)。
(3)如果上一数的行数为1,则下一个数的行数为n(指最下一行)。例如,1在第一行,则2应放在最下一行,
列数同样加1.
(4)当上一个数的列数为n时,下一个数的列数应为1,行数减1.例如,2在第3行最后一列,
则3应放在第2行第1列。
(5)如果按上面规则确定的位置上已有数,或上一个数是第一行第n列时,则把下一个数放在
上一个数的下面。例如,按上面的规定,4应该放在第1行第2列,但该位置已经被1占据,
所以4就放在3的下面。由于6是第1行第3列(即最后一列),故7放在6下面。
按此方法可以得到任何阶的魔方阵。

    ——谭浩强 ,《C程序设计(第四版)学习辅导》,清华大学出版社,2010年7月,p61

  这个题目可以使用二维数组作为基本的数据结构,题目解法主要是向二维数组中填写数据。作为数组的练习题,本应该是一个很好的题目。然而很可惜,这个好题目却被糟蹋了。
  首先,题目本身有错误:

要求输出1~n*n的自然数构成的魔方阵

  这本身就是一个错误的要求,因为偶数阶的魔方阵阵根本不存在。
  求解这样的问题有害无益而且贻害无穷,因为这使得程序员丧失了必要的职业严谨,对错误的需求变得麻木不仁。
  软件开发过程中最严重的错误,从来不是代码中的错误,也不是设计的错误,最要命的是需求本身是错的。据统计,由需求错误而导致的软件错误占错误总数的一半以上。
  要求模糊不清是这个题目的另一个错误。问题并没有明确n的范围,而对于不同范围的n数据结构和算法完全不同。要求模糊带来的另一个严重问题是根本无法测试。
  所以问题的正确提法应该是:
  输入一个1~15之间的奇数n,输出1~n*n的自然数所构成的魔方阵。
  再来看代码和运行结果:

View Code

#include <stdio.h>
int main()
{ int a[15][15],i,j,k,p,n;p=1;while(p==1){printf("enter n(1--15):");    //要求阶数为1~15之间的奇数
     scanf("%d",&n);if((n!=0)&&(n<=15)&&(n%2!=0))p=0;}
//初始化
  for(i=1;i<=n;i++)for(j=1;j<=n;j++)a[i][j]=0;+++
//建立魔方阵   
  j=n/2+1;a[1][j]=1;for(k=2;k<=n*n;k++){i=i-1;j=j+1;if((i<1)&&(j>n)){i=i+2;j=j-1;}else{if(i<1)i=n;if(j>n)j=1;}if(a[i][j]==0)a[i][j]=k;else{i=i+2;j=j-1;a[i][j]=k;}}//输出魔方阵
  for(i=1;i<=n;i++){for(j=1;j<=n;j++)printf("%5d",a[i][j]);printf("\n");}return 0;
}
复制代码

运行结果:
enter n(1--15):5
   17   24    1    8   15
   23    5    7   14   16
   4     6   13   20   22
   10   12   19   21    3
   11   18   25    2    9

    ——谭浩强 ,《C程序设计(第四版)学习辅导》,清华大学出版社,2010年7月,p61~62
  令人诧异的是,这段代码由于存在错误根本无法通过编译。
  更令人诧异的是书中居然给出了运行结果,这个运行结果究竟是如何得到的呢?不得而知。

  在代码第15行存在一个语法错误

a[i][j]=0;+++

  这种低级幼稚的编译错误只可能出现在初学者的代码之中,任何熟练的C语言编程者的代码,哪怕只要编译过一次,这种错误都不可能存留其中。

  下面分析代码中的其他问题:

int a[15][15],i,j,k,p,n;

  考虑到题目出现时的背景知识(只学过数组,尚未学习函数、指针)

?
int a[15][15],n;

  这种数据结构尚属合理,但是15作为Magic Number有瑕疵。
  至于 “i,j,k,p,”,则属于烂得不能再烂的名字,而且这几个变量根本就毫无必要。

  p=1;while(p==1){printf("enter n(1--15):");    //要求阶数为1~15之间的奇数
     scanf("%d",&n);if((n!=0)&&(n<=15)&&(n%2!=0))p=0;}
复制代码

  这段代码的本意是使程序具有一定的健壮性:在输入不满足要求的情况下实现重新输入。就其本意来说无可指责,但就其实现来说则是像假肢一样笨拙无比,僵硬造作,大概那个p是prosthesis(假肢)的缩写。因为这个p根本毫无必要。即使使用

?
while (1)
{
    if ()
    {
      break ;
    }
}

这种结构也比原来的写法强。
  必须要说的s是,这段代码根本就是错的。输入-3这种错误数据时没有起到任何容错的作用。(n!=0)、(n%2!=0)这两个条件是无意义的重复,n%2!=0时难道还用得着判断n!=0吗?根本没必要,而且(n!=0)这个条件根本就是逻辑错乱。

//初始化
  for(i=1;i<=n;i++)for(j=1;j<=n;j++)a[i][j]=0;
复制代码

  这段代码非常愚蠢,因为其功能只要在定义数组时

?
int a[15][15]={0};

简单地初始化就能实现。

//建立魔方阵   
  j=n/2+1;a[1][j]=1;
复制代码

  这段代码明显是错误的,因为C语言的数组的下标从0开始,而且输入为15时明显会发生数组越界。此外这段代码写在循环内部为好。

  {i=i-1;j=j+1;
复制代码

  这段代码位置不当。这个计算早了,过后可能还得重算,所以算了也是白算。

if((i<1)&&(j>n))

实际上应该写为

?
if ((i==0)&&(j==n+1))

  把条件放宽不但表明缺乏代码自信,而且也为BUG留了一个后门。

   if(a[i][j]==0)a[i][j]=k;else{i=i+2;j=j-1;a[i][j]=k;}
复制代码

  这段代码的可笑之处在于有if和else后面有两个完全一样的“a[i][j]=k; ”
  其实这段代码完全可以简洁地写为:

?
if (a[i][j]!=0)
{
    i=i+2;
    j=j-1;
}
a[i][j]=k;

  另外这条if语句与前面一个if语句并列是一个逻辑错误。
  最后一段代码错误太明显了

      //输出魔方阵
  for(i=1;i<=n;i++){for(j=1;j<=n;j++)printf("%5d",a[i][j]);printf("\n");}
复制代码

  n的值为15时显然存在数组越界的错误。
  一道很好的练习题,被谭先生的教科书给糟蹋成这个样子,实在是令人无语。

  下面是这道题的两种参考写法:

代码1:

View Code ?
/*
作者:hbmhalley 
引自:http://bbs.chinaunix.net/thread-3693406-2-1.html
*/
#include <stdio.h>
  
#define LEN 15
  
int main ( void ) {
         int i , n , x , y ;
         int a[LEN][LEN] = {} ;
  
         do
                 printf ( "enter n (1~%d): " , LEN) ;
         while (! ( scanf ( "%d" , &n) == 1
                                 && n > 0 && n <= LEN && n%2 == 1)) ;
  
         x = 0 ; y = n/2 ;
         for (i = n*n ; i >= 1 ; --i) {
                 a[x][y] = i ;
#define P(k) (((k) + n) % n)
                 (x == 0 && y == n-1 || a[P(x-1)][P(y+1)] != 0)
                         ? (x = P(x+1))
                         : (x = P(x-1) , y = P(y+1)) ;
#undef P
         }
  
         for (x = n-1 ; x >= 0 ; --x , puts ( "" ))
                 for (y = n-1 ; y >= 0 ; --y)
                         printf ( "%3d" , a[x][y]) ;
         puts ( "" ) ;
  
         return 0 ;
}

代码2:

View Code ?
/*
输入一个1~15之间的奇数n,输出1~n*n的自然数所构成的魔方阵。
*/
  
#include <stdio.h>
  
#define MIN   1
#define MAX   15
#define EMPTY 0
  
  
int main( void )
{
   int magic_square[MAX][MAX] = { EMPTY } ;
   int n ;
    
   //输入阶数
   {
       printf ( "输入阶数(1~15之间的奇数):" );
       while ( scanf ( "%d" ,&n) == 0 
           || ( n <  MIN   ) 
           || ( n >  MAX   ) 
           || ( n % 2 == 0 )  )
       {
           while ( getchar () != '\n' )
               ;
           printf ( "输入不正确,请重新输入:" );
       }
   }
    
   //填充
   {
       int num ;
       const int beg = 1 , end = n * n ; // int beg = 1 , end = n * n ;
       int row , col ;
       for ( num = beg ; num <= end ; num ++) {
          if ( num ==  beg ) {
             row = 0 ;
             col = n / 2 ;
          }
          else {
             if ( row == 1 - 1 &&  col == n - 1 ) //右上角 
                row ++ ;
             else {
                int row_n = ( row - 1 + n ) % n ;
                int col_n = ( col + 1 ) % n ;
                if ( magic_square [row_n][col_n] != EMPTY ) //下个位置已填 
                   row ++ ;
                else {
                   row = row_n ;
                   col = col_n ;
                }
             }
          }
          magic_square [row][col] = num ;
       }
   }
    
   //输出
   {
       int row , col ;
       for ( row = 0 ; row < n ; row ++ )
       {
          for ( col = 0 ; col < n ; col ++ )
             printf ( "%5d" , magic_square[row][col]);
          putchar ( '\n' );
       }
   }
   return 0;   
}

绿色通道: 好文要顶 关注我 收藏该文 与我联系
garbageMan
关注 - 7
粉丝 - 50
+加关注

1
0
(请您对文章做出评价)

« 博主前一篇: 亡羊补牢还是越错越远——“C99允许在函数中的复合语句中定义变量”

posted @ 2012-04-02 13:43 garbageMan 阅读(954) 评论(14) 编辑 收藏

发表评论
2346462
回复  引用  查看    

#1楼 2012-04-02 14:45 AD8018

遗憾的是,解毒专家最后改的代码,将好题再次糟蹋了一遍,
有如下问题 ---

1. 宏 MIN, MAX 定义成常数,通常不是一个好主意,多看点代码自然明白。
写程序要讲究入乡随俗。

2. EMPTY,太搞笑了。
int magic_square[MAX][MAX] = { EMPTY } ;

通常我看到以上代码,心里会打个问题,然后还要去找EMPTY的内容。
有意制造麻烦?直接写0不是得了。
为什么数字一定要变成宏?这就是教条主义的毛病。

3. 这个程序某些输入情况下,会死机。判断不完全
$ echo -n a | ./a.out

4. 编译不通过
if( num == begin )
应为
if( num == beg )

回复  引用  查看    

#2楼 2012-04-02 14:47 huangfeidian

看到这个东西 想起了大一时候学c语言 也是这道题 不会做(木有这个的数学背景)结果百度到一个巨牛的答案 好像就一行代码 竟然能够输出正确答案 当时就给跪了
回复  引用  查看    

#3楼 [ 楼主] 2012-04-02 14:58 garbageMan

@huangfeidian
这个解法在数学上没什么难度
用来熟悉数组下标比较好
回复  引用  查看    

#4楼 2012-04-02 15:29 AD8018

哈,楼主又把我找到的bug,一声不吭的修改了。
回复  引用  查看    

#5楼 2012-04-02 17:27 万仓一黍

很遗憾,楼主这个结论是错误的。
偶数阶也是有魔方阵(俗称幻方阵)的,而且是有推导公式的。

回复  引用  查看    

#6楼 [ 楼主] 2012-04-02 18:15 garbageMan

引用 万仓一黍:

很遗憾,楼主这个结论是错误的。
偶数阶也是有魔方阵(俗称幻方阵)的,而且是有推导公式的。
谢谢!
我也有这个印象,
但我并不清楚偶数阶幻方是如何定义的
还望指教
不过那样就成了另一道题目了
是吧

回复  引用  查看    

#7楼 2012-04-02 19:34 万仓一黍

偶数阶幻方定义和奇数阶幻方定义是一样的。
都是行的和、列的和、对角线的和是一样的。
只是奇数阶幻方的对角线是有交叉的,有公共元素
偶数阶的幻方虽然也是交叉的,但没有公共元素
回复  引用  查看    

#8楼 [ 楼主] 2012-04-02 19:36 garbageMan

@万仓一黍
比如说,2阶的应该是什么样的
回复  引用  查看    

#9楼 2012-04-02 21:24 万仓一黍

引用 garbageMan:
@万仓一黍
比如说,2阶的应该是什么样的

这个是抬杠了。2阶是没有幻方的
此外,4阶、6阶、8阶等都有幻方
其中,4阶、8阶、12阶等是一种布局。
剩余的是另一种布局

回复  引用  查看    

#10楼 2012-04-02 21:35 万仓一黍

偶数阶幻方构成方法
http://www.docin.com/p-15666669.html
回复  引用  查看    

#11楼 [ 楼主] 2012-04-02 22:26 garbageMan

引用 万仓一黍:
引用garbageMan:
@万仓一黍
比如说,2阶的应该是什么样的

这个是抬杠了。2阶是没有幻方的
此外,4阶、6阶、8阶等都有幻方
其中,4阶、8阶、12阶等是一种布局。
剩余的是另一种布局
不好意思
我确实不清楚
没有抬杠的意思
误会了

回复  引用  查看    

#12楼 [ 楼主] 2012-04-02 22:26 garbageMan

@万仓一黍
谢谢您提供的资料
回复  引用  查看    

#13楼 2012-04-03 09:39 Ivony...

这个叫幻方吧,,,,魔方阵是啥意思?这东西与魔方啥关系?
回复  引用  查看    

#14楼 [ 楼主] 2012-04-03 10:14 garbageMan

引用 Ivony...:这个叫幻方吧,,,,魔方阵是啥意思?这东西与魔方啥关系?
我也觉得应该是“幻方”
不过老谭书上就这么写的
所以也只好“将错就错”了

糟蹋好题——魔方阵问题相关推荐

  1. 1-3魔方阵 -Java

    题目: 运用Java二维数组打印"魔方阵".所谓魔方阵是指这样的矩阵,它的每一行.每一列和对角线之和均相等,要求打印1~25之间由自然数构成的魔方阵 解答: 这题我能提取到的信息是 ...

  2. Python课后作业 2. 旋转魔方阵(文件) ---- (第七次作业)

    文章目录 我的思路 原题题目 代码实现 提交结果 我的思路 刚刚想着把作业题和考试题赶快赶了 就去忙其他的了 忽然看到这道作业题 我确实觉得很有意思 魔方阵 我思考了两分钟确实没想到用什么公式可以套出 ...

  3. C语言学习之输出“魔方阵”。所谓魔方阵是指这样的方阵,它的每一行、每一列和对角线之和均相等。

    输出"魔方阵".所谓魔方阵是指这样的方阵,它的每一行.每一列和对角线之和均相等. 例如,三阶魔方阵为 8 1 6 3 5 7 4 9 2 要求输出1-n²的自然数构成的魔方阵. # ...

  4. C语言实现魔方阵代码及解析

    问题描述 编写程序,实现如下表所示的5-魔方阵. 17 24 1 8 15 23 5 7 14 16 4 6 13 20 22 10 12 19 21 3 11 18 25 2 9 5-魔方阵 问题分 ...

  5. 魔方阵(奇数,单偶,双偶)

    目录 一.奇数 二.双偶 三.双偶 魔方阵,是指组成元素为自然数1.2.-.n2的平方的n×n的方阵,其中每个元素值都不相等,且每行.每列以及主.副对角线上各n个元素之和都相等. 魔方阵的规律 一.奇 ...

  6. 用c语言输出魔方阵答案,如何用C语言输出一个1—16 组成的4*4的魔方阵

    int b,c,d,e,f; printf("请输入魔方阵的阶数:"); scanf("%d",&b); e=b/2; d=0; for(c=1;c&l ...

  7. 1180魔方阵(每日学习)宁波大学OJ

    题目描述 输出魔方阵,所谓魔方阵就是指这样的方阵,它的每一行每一列和对角线之和都相等,例如,三阶魔方阵为 8 1 6 3 5 7 4 9 2 要求输出由1-n^2之间的自然数构成的魔方阵. 输入要求 ...

  8. c语言魔方阵难不难,C语言魔方阵问题

    /*参考资料: 魔方阵的排列规律如下: ⑴将1放在第一行中间一列: ⑵从2开始直到n×n止各数依次按下列规则存放:每一个数存放的行比前一个数的行数减1,列数加1(例如上面的三阶魔方阵,5在4的上一行后 ...

  9. 5阶魔方阵c语言程序设计,五阶魔方阵源代码c语言课到程设计.docx

    五阶魔方阵源代码c语言课到程设计 主函数 #include #include #include # include "wenjian.c" # include "sanj ...

最新文章

  1. [iOS]调和 pop 手势导致 AVPlayer 播放卡顿
  2. R语言ggplot2可视化:可视化饼图分面图并在图中添加数据标签
  3. 一位人工智能总监对AI行业的【实话实说】
  4. shape file与coverage叠加的问题
  5. Powershell查看AD 组成员的变化
  6. 在本地库不连接远远程库的情况下操作远程库-----sql server
  7. SAP ABAP 平台新的编程模型
  8. JVM内存结构|虚拟机栈
  9. java web Jersey_使用CXF和Jersey框架来进行Java的WebService编程
  10. python语法学习_Python学习1——语法
  11. 如何优化WebAPP性能:从四个层面上彻底优化前端项目性能
  12. stft isar成像 matlab,基于STFT和FRFT的运动目标雷达三维成像方法与流程
  13. 后端如何收取多个文件_前段文件分片后后端怎么接收
  14. python基础1-字符串
  15. C语言实现字符串转数字(包括负数)
  16. 项目管理十大知识领域之间的关系
  17. win10系统ps3手柄蓝牙连接方法_ps3手柄怎么进入配对模式win10
  18. PHP支付宝手机网站支付notify异步通知
  19. 解决关于pycharm启动时持续Updating Indices的问题
  20. 道与术 渠道以及通信方式的架构设计

热门文章

  1. Lab3因而起——对正则表达式的学习(二)
  2. 计算机仿真技术教学大纲,《计算机仿真技术》教学大纲
  3. 联表查询JOIN介绍
  4. 【全过程记录】将windows电脑连接到ipad的rd client远程桌面,通过路由器搭建内网穿透隧道
  5. c语言-餐饮管理与点餐系统
  6. React实现插槽(solt)
  7. 查询数据库中的图片文件
  8. 马斯克看衰经济前景,特斯拉裁员10000人!
  9. 广东省各市之间的距离(矩阵)
  10. 人工智能搜索算法-测试答案(二)