软件工程实践第二次作业——个人项目实战(数独)

作业链接

1)Github项目地址

2)在开始实现程序之前,在下述PSP表格记录下你估计将在程序的各个模块的开发上耗费的时间
见 8)。
3)解题思路描述

拿到题目后,阅读了项目需求,得知这次作业要求我们利用程序随机构造出N个已解答的数独棋盘。项目的关键就是一要构建出9 * 9且符合要求的数独棋盘。由于自己并未写过有关构建数独的算法,于是就去上网搜了相关资料,我选择了比较容易理解的置换法。置换法的思路是:数独棋盘的9宫从左至右,从上至下标记为M1,M2,...,M8,M9(方便理解和阐述),先构建3 * 3的初始棋盘将其放在M5的位置上,将M5进行行变换生成M4,M6,将M5进行列变换生成M2,M8。现在再分别以M2,M8,进行行变换,分别得到M1,M3和M7,M9。构建好之后,将数独棋盘输出至txt,采用了ofstream方法。
885782-20170909085245616-152098109.png

4)设计实现过程

理清了思路,接着就要设计实现了。代码包含了generator类,在generator类中声明了以下函数

  • int get_random_number(); //获取随机数1~9
  • void generate_sudoku(); //构建数独
  • void generate_txt(int num,string str); //将数独棋盘写入文件
    885782-20170912091425438-2117855940.png

5)代码说明

以下是构造数独棋盘的代码:

int begin_board[3][3];  //数独棋盘中心3*3棋盘(基点棋盘)

int sudoku_board[10][10];
//数独棋盘中心9*9棋盘(9*9棋盘分成9个3*3棋盘填充,从左往右,从上到下记为M1,M2, ... ,M8,M9棋盘)

int flag[10]; //判断随机数是否重复


void generator::generate_sudoku() //构建数独
{

memset(begin_board, 0, sizeof(begin_board));
memset(flag, 0, sizeof(flag));
memset(sudoku_board, 0, sizeof(sudoku_board));

/*
 生成基点3*3棋盘
*/

int count = 0;

for (int i = 0; i < 3; i++)
    for (int j = 0; ; j++)
    {
        int temp = get_random_number();
        //cout << temp << endl;
        if (flag[temp] == 0)
        {
            begin_board[i][count] = temp;
            flag[temp] = 1;
            count++;
            if (count == 3)
            {
                count = 0;
                break;
            }
        }
    }



for (int i = 0; i < 3; i++)
    for (int j = 0; j < 3; j++)
    {
        sudoku_board[i + 3][j + 3] = begin_board[i][j];  // 生成9*9棋盘的中心的3*3棋盘(M5)

        /*
         以基点棋盘进行行变换,向左,右扩展,生成M4,M6
        */

        switch (i)
        {
        case 0:
            sudoku_board[i + 4][j] = begin_board[i][j];  // 填充9*9棋盘的[4][0~2]位置
            sudoku_board[i + 5][j + 6] = begin_board[i][j];  // 填充9*9棋盘的[5][6~8]位置
            break;

        case 1:
            sudoku_board[i + 4][j] = begin_board[i][j];  // 填充9*9棋盘的[5][0~2]位置
            sudoku_board[i + 2][j + 6] = begin_board[i][j];  // 填充9*9棋盘的[3][6~8]位置
            break;

        case 2:
            sudoku_board[i + 1][j] = begin_board[i][j];  // 填充9*9棋盘的[3][0~2]位置
            sudoku_board[i + 2][j + 6] = begin_board[i][j];  // 填充9*9棋盘的[4][6~8]位置
            break;
        }

        /*
          以基点棋盘进行列变换,向上,向下扩展,生成M2,M8
        */

        switch (j)
        {
        case 0:
            sudoku_board[i][j + 4] = begin_board[i][j];  // 填充9*9棋盘的[0~2][4]位置
            sudoku_board[i + 6][j + 5] = begin_board[i][j];  // 填充9*9棋盘的[6~8][5]位置
            break;

        case 1:
            sudoku_board[i][j + 4] = begin_board[i][j];  // 填充9*9棋盘的[0~2][5]位置
            sudoku_board[i + 6][j + 2] = begin_board[i][j];  // 填充9*9棋盘的[6~8][3]位置
            break;

        case 2:
            sudoku_board[i][j + 1] = begin_board[i][j];  // 填充9*9棋盘的[0~2][3]位置
            sudoku_board[i + 6][j + 2] = begin_board[i][j];  // 填充9*9棋盘的[6~8][4]位置
            break;
        }
    }

/*
       以M2作为基点棋盘进行行变换,向左,向右扩展,生成M1,M3
*/

for (int i = 0; i <= 2; i++)
    for (int j = 3; j <= 5; j++)
    {
        switch (i)
        {
        case 0:
            sudoku_board[i + 1][j - 3] = sudoku_board[i][j];  // 填充9*9棋盘的[1][0~2]位置
            sudoku_board[i + 2][j + 3] = sudoku_board[i][j];  // 填充9*9棋盘的[2][6~8]位置
            break;

        case 1:
            sudoku_board[i + 1][j - 3] = sudoku_board[i][j];  // 填充9*9棋盘的[2][0~2]位置
            sudoku_board[i - 1][j + 3] = sudoku_board[i][j];  // 填充9*9棋盘的[0][6~8]位置
            break;

        case 2:
            sudoku_board[i - 2][j - 3] = sudoku_board[i][j];  // 填充9*9棋盘的[0][0~2]位置
            sudoku_board[i - 1][j + 3] = sudoku_board[i][j];  // 填充9*9棋盘的[1][6~8]位置
            break;
        }
    }


/*
以M8作为基点棋盘进行行变换,向左,向右扩展,生成M7,M9
*/

for (int i = 6; i <= 8; i++)
    for (int j = 3; j <= 5; j++)
    {
        switch (i)
        {
        case 6:
            sudoku_board[i + 1][j - 3] = sudoku_board[i][j];  // 填充9*9棋盘的[7][0~2]位置
            sudoku_board[i + 2][j + 3] = sudoku_board[i][j];  // 填充9*9棋盘的[9][6~8]位置
            break;

        case 7:
            sudoku_board[i + 1][j - 3] = sudoku_board[i][j];  // 填充9*9棋盘的[8][0~2]位置
            sudoku_board[i - 1][j + 3] = sudoku_board[i][j];  // 填充9*9棋盘的[6][6~8]位置
            break;

        case 8:
            sudoku_board[i - 2][j - 3] = sudoku_board[i][j];  // 填充9*9棋盘的[6][0~2]位置
            sudoku_board[i - 1][j + 3] = sudoku_board[i][j];  // 填充9*9棋盘的[9][6~8]位置
            break;
        }
    }

}

思路的话,前面已经叙述过了,就不再赘述了。

6)测试运行。
输入N=10,在控制台得出以下结果:

885782-20170909165834272-415781711.png885782-20170909165845257-697502356.png885782-20170909165856147-1662252333.png

以下是命令行测试截图:

885782-20170909205100054-204205564.png

885782-20170909173109335-530196206.png885782-20170909173320882-630636458.png885782-20170909173504210-1241637730.png885782-20170909173606054-1499881463.png

7)记录在改进程序性能上所花费的时间,描述你改进的思路,并展示一张性能分析图,并展示你程序中消耗最大的函数

第一次使用性能分析工具,用的不是很得心应手...

以N=1000测试得到以下的性能分析图:
885782-20170910165559007-167486547.png

从性能分析图可得出程序中消耗最大的函数是: void generate_txt(int num,string str); //将数独棋盘写入文件

改进前代码:

void generator::generate_txt(string str) //将数独棋盘写入文件
{
    string str1 = str + "/sudoku.txt";
    //cout << str1 << endl;

    ofstream txt(str1, ios::out);
    //txt.open(str1);

    for (int i = 0; i < 9; i++)
        for (int j = 0; j < 9; j++)
        {
            txt << sudoku_board[i][j] << " ";

            if (j == 8) txt << endl;
        }

    txt << endl;
    txt << endl;
    txt.close();
}

通过在主函数调用写入txt函数,每写入一次就得打开一次文件,比较耗时

改进方法:只打开一次文件,直至写入文件完成后关闭文件

改进后代码:

void generator::generate_txt(int num, string str) //将数独棋盘写入文件
{
generator gene;

string path = str + "/sudoku.txt";

//cout << path << endl;

ofstream  txt(path, ios::out);

for (int k = 0; k < num; k++)
{
    gene.generate_sudoku();

    for (int i = 0; i < 9; i++)
        for (int j = 0; j < 9; j++)
        {
            txt << sudoku_board[i][j] << " ";

            if (j == 8) txt << endl;
        }

    txt << endl;
    txt << endl;
}

txt.close();
}

改进后得到以下的性能分析图:

885782-20170911222754875-762321345.png

好像只快了3秒钟额...虽然提升的不是很多,但自己也挺满足的。

在网上查了一些资料,了解到c++里的文件流fstream输出效率在批量较大的时候远不如c里的freopen,因此再一次改进输出函数

void generator::generate_txt(int num, char * str) //将数独棋盘写入文件
 {
generator gene;

freopen(str, "w", stdout);

for (int k = 0; k < num; k++)
{
    int len = 0;
    gene.generate_sudoku();

    for (int i = 0; i < 9; i++)
        for (int j = 0; j < 9; j++)
        {
            res[len++] = sudoku_board[i][j] + '0' ;
            res[len++]=' ';

            if (j == 8) res[len++] =' \n';
        }

    puts(res);
}

fclose(stdout);
 }

改进后得到以下的性能分析图:

885782-20171004091431255-1486036217.png

由性能分析图可知,输出写入txt的效率大大提高了。

8)PSP 2.1表格

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划6030
· Estimate· 估计这个任务需要多少时间6030
Development开发810995
· Analysis· 需求分析 (包括学习新技术)180200
· Design Spec· 生成设计文档3045
· Design Review· 设计复审 (和同事审核设计文档)3030
· Coding Standard· 代码规范 (为目前的开发制定合适的规范)3030
· Design· 具体设计6090
· Coding· 具体编码300360
· Code Review· 代码复审6090
· Test· 测试(自我测试,修改代码,提交修改)120150
Reporting报告180150
· Test Report· 测试报告6090
· Size Measurement· 计算工作量6030
· Postmortem & Process Improvement Plan· 事后总结, 并提出过程改进计划6030
合计10501175

9)遇到的困难与收获

对于确保生成的棋盘文件 sudoku.txt 与可执行文件在同一目录下,生成文件时请使用相对路径这一要求,自己思考了挺久的。
解决方法:我的想法是去获取.exe的路径,然后在该路径下将数独棋盘写入 sudoku.txt,题目要求我们使用相对路径,然而自己完全没有这个概念,于是就去了解了绝对路径和相对路径的区别;对于获取.exe的路径,运用了GetModuleFileName函数(参考资料1,参考资料2
收获的话,学习了一些新的知识,如获取.exe的路径...也复习了一些知识,如通过命令行调用.exe...除此之外,通过填写PSP表格,让自己估算完成每一步骤的所时间,督促自己提高效率,在实践的过程中更有目标性。因此在之后的项目实践中要好好利用PSP这一利器。
至于附加题,想法是用MFC开发GUI界面,自己尝试着去学习,发现并不能很快的学会,因此就先放着...

积跬步,至千里,未来可期。

转载于:https://www.cnblogs.com/cjqcjq/p/7473816.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值