软件工程第二次作业

0.相关链接

题目链接地址
github链接地址

1.解题思路

  • 胡思乱想
    • 大致思路有两条,一个是先生成数独终盘,再矩阵转换,(感觉会比较有限,不考虑),二个是随机填数,可以按照数字填(将1填完,再填2这样),按行/列填,按九宫格填
  • 难度瓶颈
    • 考虑随机填数的思路,难点就是填数要随机,但是同行,同列,同格都有限制,自由又很不自由,一开始打算按照数字填,因为觉得数独,1只跟1有冲突,与其他数字,不在一个位置就好了。但是这样满盘随机放数字,就不知道怎么代码实现。
  • 最终选择
    • 网上看了随机的思路,最后选择按行填数,这里参考silenpy的数独终盘生成算法(java)很暴力,但是很容易实现。相同点:第一行数字无限制,1-9随机放,第二行到第九行,一个一个数字填,每次用SET存1-9,然后将同行,同列,同格数字踢出去,(可能将1-9全部踢出去了)剩余数字随机选择。这样保证当前填数正确。不同点:对无数可选情况的处理,我考虑整行重新填数,但是存在整行数字无论怎么填,都不可能正确的情况。解决方法:设置一个整行重新生成的最大限值,超过此值,整个数独重新生成。
  • 改进版本
    • 最后测试的时候,发现上个思路先判断再放数字太慢,SET会重复插入,删除,而且随机选数必须迭代。改进是先放数字,再判断是否正确,这里参考Swing数独游戏(二):终盘生成之随机法,PS:已经确定一位同学吐槽这个算法,一位同学貌似是吐槽这个算法,但是算法是真的,思路与算法有出入,我也被坑了。

2.设计实现

只是生成数独终盘,不考虑附加作业,就没有考虑类,只是函数。

  • 版本一:
    getShudu首行调用getfirst,剩余72个空格调用getone,最后调用print输出
    885733-20170911100339172-1775563513.png

  • 版本二:
    getshudu首行调用creatArray,首行数字直接等于随机生成的数组数字,第二行到第九行,依旧调用creatArray,但是不可直接填入,checkbtnArray用于判断随机生成一组数字是否可用,for循环从0到8,当前行一个一个填数,checkone判断当前位置能否从数组中取得合法数字,如果有一个位置无法取得合法数字,则此随机数组不可用。需要再次随机生成,最后调用print打印。
    885733-20170911100318297-13456708.png

3.代码说明

  • 版本一:

    • 对首个数字处理,只需要在getfirst先将7从集合移除,放入第一个数字置,剩余数字再随机
    Array[0][0] = 7;
    basic.erase(7);//basic为集合名称
    • getone踢出同行/列/格数字后,如果集合中仍有数字,随机选择一个,如果没有数字,看当前getone调用次数times是否超过最大限制,如果没有,此行重新生成,如果超过,不做处理,getshudu会进行处理
    if (basic.size() == 0) {//无数可选
    
            if(times <= MAX_Time){
                if(col/3==0){
                    for(int k=0;k<3;k++){
                        name[0].insert(Array[col][k]);
                    }
                }
                for (int k = 0; k <= row; k++) {
                    getone(col, k);//此行重新生成
                }
            }
        }else {
            int num = rand() % basic.size();
            set<int>::iterator it;//定义前向迭代器?
            int j;
    
            for (it = basic.begin(), j = 0; it != basic.end(); it++, j++) {
                if (j == num) {
                    Array[col][row] = *it;
                    //basic.erase(Array[0][i]);
                    break;
                }
            }
    
        }
  • 版本二:

    • 随机生成数组,对首个数字的处理,数组btnArray,初始化放入1-9,将1与7交换,然后循环产生随机数字(1-8),将该位置数字与btnArray[1]交换,得到一组随机数字
    void creatArray() {
        times++;
        for (int i = 0; i<9; i++) {
            btnArray[i] = i + 1;
        }
        btnArray[0] = 7;
        btnArray[6] = 1;
        for (int i = 0; i<20; i++) {
            int t = rand() % 8 + 1;
            int temp = btnArray[1];
            btnArray[1] = btnArray[t];
            btnArray[t] = temp;
        }
    }
    • checkbtnArray
     bool checkbtnArray(int row){
        int judge;
        for(int i = 0 ;i < 9;i++){
            judge = 0;
            for(int j = 0;j < 9;j++){
                Array[row][i]= btnArray[j];
                if(checkone(row,i)){//如果数字合适,填入,换下个位置
                    judge = 1;
                    break;
                }
            }
            //数组数字选遍,仍无合适数字,则此数组不可用
            if(judge == 0){
                for(int k = 0;k<=i;k++){
                    Array[row][k]=0;
                }
                return false;
            }
    
        } 
        return true;
    }

4.测试运行

  • 命令行输入:
    885733-20170910213844382-637163792.png

  • 生成文件部分截图
    885733-20170910213958272-2042996125.png

5.效能分析以及改进

仅针对版本二,版本一无力回天了。

  • 第一次改进:100万当时运行了15分钟,发现约50%时间耗费在输出上,(忘记截图,后续补上)原本输出函数
char* path = "./sudoku.txt";    // 创建文件的相对路径
ofstream fout(path);
void print(){
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            fout << Array[i][j] << " ";
        }
        fout << endl;
    }
    fout << endl;
}

看了大佬代码,用的putchar,果断换,也用printf试过,后者慢一些

freopen("./sudoku.txt", "w", stdout); 
void print() {
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            char num = '0'+Array[i][j];
            putchar(num);putchar(' '); 
        }
        puts("");
    }
    puts("");
}

运行截图,100万数据:七分多钟
885733-20170911094738813-1332785324.jpg
printf函数所占比例:
885733-20170910220624538-1588091028.png

  • 第二次改进,一次运行后,print函数消耗时间大大减少,但是checkone,与checkbtnArray函数比重增大。
    查看代码发现:数组里面的数字如果合适,会被填入,但是下次依旧会再次被检验。
for(int j = 0;j < 9;j++){
    Array[col][i]= btnArray[j];
    if(checkone(col,i)){
        judge = 1;
        break;
    }
}

将已经正确填入的数字置0,加以判断

for (int j = 0; j < 9; j++) {
    if(btnArray[j]!=0){
        Array[col][i] = btnArray[j];
        if (checkone(col, i)) {
            judge = 1;
            btnArray[j] = 0;//表示已经使用过 
            break;
        }
    }   
}

这样也保证同行不会有重复,checkone注释掉同行检查

/*
    //同行不可重复 
    for (int i = 0; i < row; i++) {
        if (Array[col][row] == Array[col][i])return false;
    }
    */

    //同格不可重复 
    for (int i = (col / 3) * 3; i < col; i++) {
        for (int j = (row / 3) * 3; j < (row / 3) * 3 + 3; j++) {
            if (Array[col][row] == Array[i][j])return false;
        }
    }

    //同列不可重复 
    for (int i = 0; i < (col / 3) * 3; i++) {
        if (Array[col][row] == Array[i][row])return false;
    }

运行截图,100万数据,跑了六分多,快了一分多,checkone与checkbtnArray占比有所下降,但是依旧很大,其中消耗最大的函数是checkone
885733-20170911094813453-1825040827.png

  • 意外发现,本来构建之法上面写了,使用效能分析工具确保编译的程序是Release版本,但是当时不知道release是什么,后面发现Debug可以切换成Release,又跑了一次100万,三分多,不去想大佬程序耗时,就知足了。

6.PSP

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

转载于:https://www.cnblogs.com/liu424/p/7502217.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值