`

解决问题的思路beta

阅读更多

1. 首先是条件的提取;
2. 然后是寻找解决方案;
3. 做完了一定要验证;
4. 扩展:
a. 列出所有你解决方案中用到的条件;
b. 问自己一些问题,仔细思考你现行的解决方案中有没有做重复或多余或疑似复杂了的步骤;
c. 如果有重复步骤,尝试在原有条件不变的前提下,优化解决方



最近,在波利亚GG的谆谆教诲下,在pongba同学的循循善诱下,在TopLanguage的今天我们思考系列的热情引导下,我终于痛下决心开始琢磨所谓的科学思考问题的方法。对大部分人而言,解题不是终极目的,只是希望在解题中培养的思考问题的方式能够广泛的应用到其他领域。我依然觉得,思维这个抽象的可怕的东西,本质上还是个体化的,要因人而异,很难找到一招鲜吃遍天的套路。但其中,一些共性的东西还是可以抽取出来,一起琢磨一起讨论一起研究,并规范化科学化。大师的书pangbo的文章和大家的讨论提供了足够的参考素材,在这里我只是写我自己这些日子来的一些实践和思考。我混了20多年,还是一个解题白痴,可以想见,这段日子的集训也不会有质变的效果,所以写下一个beta,作为不成熟的挡箭牌。在今后的日子里,我会继续的思考和实践,希望有一天,我可以有勇气在后面加个v1.0。。。

我一直以为,要想解决问题,除了所谓耐心勇气等人性方面的因素外,至少还需要两方面的储备。一面谓之硬件,指的是基本的知识。比如说你要设计一个算法,基本的复杂度分析,分治法,二分法之类的知识应该需要储备起来;如果你要设计个软件架构,基本的模式,操作系统的原理,交互接口的设计也应该了解一些。不要以为自需要很小的一点知识库,就可以像原子弹似的衍生出无限的能量,自己造轮子做汽车的生产方式,已被这个高效的社会淘汰了。牛顿GG很早就教育我们要站在巨人的肩膀上才能站的高看得远,而如《费马大定理》中描绘的一样,怀尔斯如果不是充分了解所有前人的思路深刻掌握许多高精尖的数学方法的话,从最原始的公理开始,我想再送他100年寿命估计也搞不定费马大定理。所以,一个人只有拥有了足够丰富的知识后,才有可能混得和机器猫的口袋似的,要啥有啥。。。

另一面谓之软件,指的就是解决问题的基本思维。在《Why Programs Fail》中我了解到,调试这种貌似依靠经验和灵感的活也是可以有章可循的。与之类似,所有貌似只能靠灵感喷涌来搞定的问题,也不会是只有咣当咣当拍脑袋才能获得解决思路的。我们需要信赖灵感,但很多时候,也可以不依靠灵感。我们有了足够的知识储备和运用知识的经验后,就像骑上了匹宝马良驹,有了一马平川奔向目的地的能力,但是没有良好的驾驭,南辕北辙了,就算是汗血宝马估计也无奈了。。。

从这段时间的实践来看,波利亚GG的解题框架是极具推广性的。不但在解题上会有效果,尝试推广到软件设计甚至其他方面上,也是可以行得通的。主要实践原料的来源,除了TopLanguage里面的一些题目外,还有来自《编程之美》的习题和高大爷仙书上的一些经典算法。在我这里,波利亚GG的框架被我抽取成下述样子,不过强烈建议自己去阅读原著,抽取属于自己的框架:

1. 首先是条件的提取。对于题目而言,就是把题设的条件一部分一部分抽取出来,然后牢牢把握住未知量,从始到终,同时,要对已有已知量是否能导出未知量有个评估。对于其他问题而言,比如软件设计,条件就是做这个项目的一些要求和约束,未知量就是需要做到的效果,评估就是在这样的条件下能不能做到需要的效果。这个问题说起来简单,把握起来却不容易。我在自己做题或者给别人出题的时候,我经常会发现,在做的过程中很多条件被遗忘了,或者没有被充分利用到。 pangba的一个实践方法很好,就是把所有的条件都一条条写下来,并尽可能的扩展开,毕竟,不是每个人都是欧拉。一个例子,可以看这里。。。

2. 然后是寻找解决方案。摊开了讲,就复杂了,想了解,看书算是最好的方式。简单说,先要对题目进行归约,努力回想你做过的类似的题目。有一招就对题目转述,我觉挺好的方式,有的东西说一下就更明晰了。比如,题目是找一组一维点中的最大距离。改下题,说找一组有序点的最大距离,问题就明晰多了。先变有序,再找距离。而从思索的角度上看,从条件开始试错,或从未知量开始倒着做都是可以尝试的方式。有的时候,你需要把特化考虑问题,即考虑特殊的情况类推出普遍情况,这种方式我们常用。还有一种是泛化,把特殊的问题变普遍了,不知不觉中也会常用。比如这里的第二题,把那几个恐怖的数字抹平成了N,问题就清晰了。。。

3. 做完了一定要验证。从小到大数学考试,我从来都是做完了就算,很公平,也被无数次惩罚了。验证可以是不完备的,用基本的逻辑和特例来验证一下,也可以是完备的,用数学方法来证明。说这个就想起测试,写代码的时候,很多自以为完美无缺的逻辑,在测试下都显得脆弱不堪,所以无论如何,验证是必需的,这个懒,在任何时候,还是不偷的好。。。

4. 扩展。前面说的都很简单,客观原因大牛们已经写了很多了,主观原因我很懒。因此,这一点决定多写一点,理由是感触多些。扩展,在我的定义下,有两层意思,一个是对原有方法的改进,另一个是尝试挖掘一下为可能的问题做准备。两者在实践上是一致的,因此归为一点。当你完成并验证了某个题或某件事,你还需要进一步的反思和改进,这就是我所谓的扩展,实践包括以下几个步骤:

4.a. 列出所有你解决方案中用到的条件。这个条件和题设的条件可能是不一致的,因为你在具体解决过程中可能加入了有形或无形的约束。比如,打印一个树的前序遍历。很有可能,你在解决这个问题的时候默认这个树是以最常见的左右链表的形式保存的,这个过程中,你就多添加了一个约束,有没有想过,如果你用穿线树的格式存,解决方案就完全不一样了。这样的条件列举还是蛮不容易的事情,很多隐藏的条件(约束)不容易被挖掘出来,要努力的多想尽量的多列。比如做比较排序的时候,你有没有发现,你可能添加了一个无形的约束,就是每次都是键和键直接比较,想一想,去除掉这个约束视野会更开阔的。

4.b. 问自己一些问题,仔细思考你现行的解决方案中有没有做重复或多余或疑似复杂了的步骤。比如,KMP算法就是在降低一般算法的重复;再比如一题,找一个N位二进制中的1的数目,如果你写了一个O(N)的算法,仔细想想你做了什么多余的工作呢;还比如,冒泡算法作为一种交换的排序算法,有没有做什么疑似复杂的操作呢。

4.c. 如果有重复步骤,尝试在原有条件不变的前提下,优化解决方案。比如,上面所提,找1数目的题目,你可以想到既然是找1的数目,那么应该能做到O(M)的算法,其中M是1的个数。再继续,能不能够用一些其他策略,比如时间换空间,把算法复杂度降到常数级别呢。想知道答案,可以查看一下《编程之美》,呵呵。

4.d. 如果在原有条件上没有继续优化的空间,那么试图改变一些条件试试。这个条件如果是你在设计执行方案的时候添加进去的,那么这就是一个对已有的优化;如果,这个条件是题设的,那么就是对这个未知的扩展尝试。

4.e. 在改进的过程中,你可能引入了新的约束,更新约束列表,继续尝试上述步骤。

最后,写个例子,我尝试按照这个思路把高大爷仙书第三卷的插入排序部分串了一下,本书的其他部分我也实践了一下,但还没有完全一体化,如果有兴趣不妨尝试一下^_^。

a. 根据打扑克牌得来的心得,设计了一个基于顺序数组存放的最最普通的插入排序,经验证正确,至此,上述1~3步的工作完成。
b. 提取了一下约束,包括:存储空间是连续的静态分配的;在排序的过程中,数组的前半部分是有序的;每次插入一个新元素的时候,并不能保证一定把这个元素插入到了最终位置;是基于键值比较来确定大小关系的;没有使用过多的额外空间。

c. 问自己一个问题,能不能提高插入的速度?很自然我们想到了二分,于是有了二分插入。

d. 再继续,发现在目前条件下没有太大改进空间(别说用斐波纳契分...),于是考虑改变一些条件。最简单的,如果我不保证前半部分有序,我能怎么做。于是有了二路插入,将点定在中间,减少移动次数。
e. 如果你和shell一样有更多的硬件储备(前面提到的,有时候是需要硬件储备的...),你会联想到去掉前半部分有序,可以运用局部化和分治的思想,于是就有了Shell排序。
f. 如果,你开始问自己的问题是,能不能减少数据移动提高速度?一个答案是,改变的顺序存储,变链表,于是有了链表插入。
g. 能不能更快?于是,在原有基础上改变,我们添加一个哨兵指针,放在上次插入的位置。

h. 不要忘了,更新一下列表,我们发现添加了一个链式存储的约束,这个约束下,二分不能使用,查找速度降低。

i. 自然而然,我们问自己,能不能查找又快又少移动呢?继续改变条件,还是变数据结构,于是有了树插入(又见硬件储备...)。
j. 想想,还有没有什么条件没有改变过。很显然,基于键的比较没变过,不能保证将元素插入到最终的位置也是一个。联想起来,能不能不基于比较基于计算,能不能尽量一次到位这样可以减少移动次数。如果足够幸运,你可以想出基于地址的排序。

k. 其实,还有很多条件没有变过或可以继续变,有兴趣就继续下去吧:)。。。

分享到:
评论

相关推荐

    五子棋alphabeta--VCF VCT

    5,自己构造大部分的基本数据结构,list stack等等,不做边界判断(省去if else判断),一旦边界异常,正好可以发现错误解决问题. 6,二维数组一维化(加速寻址时间)。 7,提炼精简代码,代码量缩减到2100行。

    五子棋andriod版

    1,具有求解VCF VCT的算杀模块,采用的是与或树的...4,自己构造大部分的基本数据结构,list stack等等,不做边界判断(省去if else判断),一旦边界异常,正好可以发现错误解决问题. 5,二维数组一维化(加速寻址时间)。

    FlashFXP 3.7.9 功能强大的FXP/FTP软件 [附注册机]

    FlashFXP是一个功能强大的FXP/FTP软件,...10、解决了 JSCAPE 服务器 SFTP 兼容性问题 打开注册机,输入你的名字,获得注册码 启动FTP注册向导选择“Enter code”   输入注册机中算出的注册码 注册成功

    [2010.10.14][封装工具][天空作品] Easy Sysprep v3 RC3(+ SkySRS3.00)

    6、Windows Server 2003部署后使用系统命令自动分配盘符,解决个别条件下部署Win2003后无盘符问题 [2010.9.13]Easy Sysprep v3 RC2 1、相对于RC版本,代码几乎无改动 2、增加一个对MS-Office2003的修正功能,对部署...

    ExtAspNet v2.2.1 (2009-4-1) 值得一看

    -修正了使用IFrameUrl的Tab在切换过程中会重复加载的问题,这是一个在v2.1.6引入的问题(feedback:eroach)。 -修正了启用AutoPostBack的Grid,其RowClick会覆盖LinkButtonField, HyperLinkField, CheckBoxField的...

    ExtAspNet_v2.3.2_dll

    -v0.2beta2版本中关于PersistChildren(true)的描述有误,这个是设计时属性,和运行时是否保持状态没有关系。 -修正CheckBox控件的CheckedChanged事件会被触发两次的BUG(Data PostBack->AutoPostBack, Event ...

    asp.net知识库

    帮助解决网页和JS文件中的中文编码问题的小工具 慎用const关键字 装箱,拆箱以及反射 动态调用对象的属性和方法——性能和灵活性兼备的方法 消除由try/catch语句带来的warning 微软的应试题完整版(附答案) 一个...

    x-SCAN -V3.3-CN.

    对于多数已知漏洞,我们给出了相应的漏洞描述、解决方案及详细描述链接,其它漏洞资料正在进一步整理完善中,您也可以通过本站的“安全文摘”和“安全漏洞”栏目查阅相关说明。 3.0及后续版本提供了简单的插件开发...

    强大的扫描工具x-scan

    对于多数已知漏洞,我们给出了相应的漏洞描述、解决方案及详细描述链接,其它漏洞资 料正在进一步整理完善中,您也可以通过本站的“安全文摘”和“安全漏洞”栏目查阅相关说明。 3.0及后续版本提供了简单的插件...

    软件测试经典面试题 (超实用)

    其他问题:(有可能清晰的思路比确切的答案更重要) 27 开发及环境搭建类面试题 28 1、描述软件产生内存泄露的原因以及检查方式。(可以结合一种开发语言进行描述) 28 2、简述什么是值传递,什么是地址传递,两者...

    X-Scan v3.1

    对于多数已知漏洞,我们给出了相应的漏洞描述、解决方案及详细描述链接,其它漏洞资料正在进一步整理完善中,您也可以通过本站的“安全文摘”和“安全漏洞”栏目查阅相关说明。 3.0版本提供了简单的插件开发包,...

    X-Scan

    对于多数已知漏洞,我们给出了相应的漏洞描述、解决方案及详细描述链接,其它漏洞资料正在进一步整理完善中,您也可以通过本站的“安全文摘”和“安全漏洞”栏目查阅相关说明。 3.0版本提供了简单的插件开发包,...

    jquery插件使用方法大全

    jQuery 1.5(2011年1月31日):该版本修复了83个bug,解决了460个问题。重大改进有:重写了Ajax模块;新增延缓对像(Deferred Objects);jQuery替身——jQuery.sub();增强了遍历相邻节点的性能;jQuery开发团队构建...

    JAVA上百实例源码以及开源项目

    百度云盘分享 简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对...

    JAVA上百实例源码以及开源项目源代码

    Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 ...

Global site tag (gtag.js) - Google Analytics