分析和解决问题的心得总结

作为一名软件工程师,大部分的工作时间都在处理各种各样的问题。这些问题发生在不同的技术领域,不同的技术方向,不同的模块,甚至不同的环境下,不一而足,就像数学中的自然数可数无穷一样,我们可以一个个遍历它,但是无法穷尽所有的问题。这多少有点悲观,难道我们就没有办法了么? 不是这样的,问题有无数种,但是解决问题的通用思考方法却是有的,我们从一个数学概念讲起。

必要条件和充分条件

再进一步讨论之前,先来复习一下什么是必要条件和充分条件,这个概念应该是初中数学讲述的,并不陌生,但是真正能够掌握其确切定义的人却很少,如果用机械的方式加以定义,就是:

“当命题 ‘若p则q’ 为真,则p为充分条件,q为必要条件"

如果不结合具体的示例,可能很少有人会理解这句话,我们再分析的详细一点:

必要条件:对某事件的成立来说(至少)必要的条件。

充分条件:对某事件的成立来说(足够)充分的条件。

用这样得到方式解释好像还是不够清楚,举个例子:

居住在广东是居住在珠海的必要条件,居住在珠海市是居住在广东省的充分条件。

一个人如果居住在广东省,就有可能居住在珠海,而一个人如果居住在珠海,就一定居住在广东。根据这样的定义,如果我们把充分条件和必要条件联想成:

必要条件=宽松的条件

充分条件=严格的条件

或许会比较容易理解了。欧拉图一般用来表示两个概念外延之间的一种关系,我们用欧拉图表示如下:

类似的例子可以举很多,比如,考察一场考试年级第一和班级第一的关系。

如果同学A考了班级第一,那么他不一定是全级第一,但是如果这位同学考的是全年纪第一,那么它一定是班级第一,用图形表示就是:

绿色部分就是那些班级考试第一,但是并不是全年级第一的同学,班级第一每个班都有一个,但是全年级第一只有一个。

根据上面两个例子,我们可以把必要条件和充分条件分别视为:

必要条件=大范围,充分条件=小范围

将上述内容整理后,就会是:

必要条件=宽松的条件 = 大范围

充分条件= 严格的条件=小范围

当逐渐缩小必要条件的范围,缩小到和充分条件重合的时候,我们就认为,我们找到了充要条件,从形式逻辑的角度,两个概念互为充要条件,说明了两个概念的外延完全重合,属于同一关系。

前面说了,欧拉图反映的是外延之间的关系,外延等同,并不代表内涵也是一致的,就比如上面的例子,"珠海市"的内涵并不会告诉你区号信息。两个内涵是从不同角度对珠海这个地方的描述,但是描述角度并不相同。我们生活中有时会遇到这样一些高人,对比其他人,他们更容易理解和看透一些事物,然后再次描述出来就会比其它人生动,具体,富有启发性,虽然他们表达的是同样的一件事,但是内涵更丰富。

图灵完备与GPU&CPU

CPU一定是图灵完备的,但是图灵完备的不一定只有CPU,还包括其它一些可编程的加速器,所以图灵完备是CPU的必要非充分条件。CPU是图灵完备的充分非必要条件。、

存在图灵完备的GPU,但是大部分GPU都不要求是图灵完备的,并且图灵完备的设备也并非仅仅有CPU,所以本质上,GPU和图灵完备是没有任何关系的,彼此之间属于既不充分,也不必要。所以如果你要从图灵完备的角度来让CPU和GPU发生点儿啥关系,是达不到目的,这俩没关系。

用欧拉图表示如下:

数学与哲学

与哲学关系最密切的自然科学或许就是数学了,哲学曾经把整个宇宙作为自己的研究对象,那时,它是包罗万象的,数学只不过是算术和几何而已。但是随着数学以及数学带动的自然科学的发展,数学的研究领域正在逐渐扩大,哲学的地盘在缩小。

当数学真真实实地研究哲学家所谈论过的对象时,哲学便开始沉默,倾听数学的发现,准备提出新的问题。

当哲学退出一门学科,代表这门学科的成立,当数学进入一门学科,代表这门学科的成熟。

我们可以说,哲学认知是数学认知的必要条件,数学认知是哲学认知的充分条件。一个人有了哲学认知,但是并不一定有足够的数学认知,而当一个人有了数学认知之后,便自然有了哲学认知。这也是为什么大部分数学家也算是半个哲学家的远古,比如笛卡尔,牛顿,莱布尼兹等,都有著名的哲学论断,数学是由哲学背书的。

很多时候,我们能从解决一个技术问题中获得精神上的愉悦和正向激励,本质上是想通了一个数学问题,而数学方法是抽象的,这种抽象的思路可以应用到各类具体场景中,指导我们的工作。

Linux和RTOS

软件方案开发中,同样一个应用场景,用Linux方案可以做,用RTOS方案也可以做,但是从方案外部,是看不出内核用的是哪种引擎的。所以相同的外延下,内涵却有差异。

Linux 内核导出符号

熟悉Linux内核的同事都了解,内核的符号一般分为静态符号(用static修饰),外部符号( 默认不修饰),和由模块连接符号(由EXPORT_SYMBOL_GPL修饰)。他们满足的约定如下:

  1. 所有的静态连接符号都不是外部符号。

  1. 所有的模块连接符号都是外部符号,但是外部符号不一定是模块连接的符号。

用欧拉图表示如下:

静态符号和外部连接符号是反对关系,具体的说是上反对关系,所有的静态连接符号都不是外部符号,可以推导出所有的外部符号都不是静态连接符号,因为假设存在有的外部符号是静态链接符号,与前提条件矛盾(矛盾律,必有一错,假设错了)从而推导出假设不成立,根据派中律,是和否必须承认一个,所以只能承认所有的外部符号都不是静态连接符号。

逻辑关系矩阵

为了更好的利用逻辑规律,人们总结了一张逻辑关系矩阵图,如下图所示,在这种张图中可以看到,充要条件就是那种正命题和逆命题同时成立的命题。

如何定义API

如果从外延的角度来分析系统API的定义,你会发现和我们通常看到的软件栈层次结构恰恰相反。越高层的靠近应用的API,其包含的场景内容语义内涵越丰富,相应外延越小,只能适用于特定场景。或者说,所有的应用高层API都是基于底层API实现的,但是底层API可以实现多种应用场景的高层API,并不局限于一种,逻辑结构欧拉图如下图所示:

逻辑有些类似于生物学分类按照:界-门-纲-目-科-属-种 将生命类群进行命名和划分。

如何分析软件问题

有一位计算机科学家说过一句话,软件调试要比编写代码困难一倍,如果你发挥了最大才智去编写代码,那么你的智商便不足以调试它。

个人非常赞同这句话,如果认真的观察程序员把最多的事件耗在哪里,就会发现,编写代码其实只占小部分时间,有一些时间用来决定下一步干什么和怎么做,另一些时间花在设计框架上,而最多的时间则是用来调试。

每一位资深的程序员应该都能讲出自己通宵达旦DEBUG只为去解决一个小问题的故事,修复错误是非常快的,但是找出错误简直是一场噩梦,需要极大的脑力和耐心去完成这项任务。

解决问题一般的过程是根据现象找原因,我们要根据必要条件去寻找充分条件。

但是,通常一个问题可能会有多种现象,有些现象是观察到的,有些现象是待发现的。

这个时候,如果我们一味的从现象入手,拘泥于某个个别现象去寻找问题,很可能会陷入死胡同无法自拔,更何况现象空间是未知的,可能随时会有新的现象或者规律被发现,当这些现象和规律全部摆到你的面前的时候,处理的复杂性大大增加。

面对问题,我们总的方向是从各种现象入手,寻找这些现象背后的规律和联系,从中寻找最大公约,并且逐渐缩小这个公约范围直到它既能覆盖所有的现象,也能从逻辑上演绎出最后的问题。

所以,分析过程开始就要搜集各种各样的信息,在这个阶段要把工作做足做细,搜集信息尽量符合MACE原则,个别现象一开始不理解没有关系,先把数据拿到,后面随着分析的深入,不相关的现象之间会逐渐串联起来,建立联系。有些看似无关的现象可能会在分析过程中的某个阶段发挥关键的作用。

从现象归纳到最大公约量的时候,我们用的是归纳的方式,这其中甚至会包含一些大胆的假设和猜测,但是我们基本定位到这个最大公约的范围之后,就可以大胆的使用演绎推理了,直到你发现,你演绎结果恰好是这个问题本身,这个时候,我们就找到了这个问题的rootcause.

软件中解决BUG是最主要的学习手段,它好比解剖手术刀,能够让你摸清脉络,看清本质,弄懂结构。而编码的过程则犹如生物进化的过程,要知道,虽然我们是生物进化的结果,但是我们对人体结构的认知是通过解剖而非进化本身实现的。所以,如果你想了解一个软件模块,那就去解决问题吧。

避免火鸡思维

有一只火鸡,农夫每天来给它喂食。经过长期观察后,火鸡得出结论,“农夫来到鸡舍,我就有吃的”,之后每天的经历都在证实它的这个结论。但是有一天,农夫来到鸡舍,没有带来食物而是把他杀了,因为这天是圣诞节。

火鸡思维指的是,很多时候,人们看到一种个例事实,就习惯用这种个例来解释一系列现象,总结出来一些规律,在这个过程中,很可能会犯错误。

火鸡思维出现的原因是归纳出了问题,分析走入了错误的方向。即便我们掌握了充分的材料,也不能避免思考进入错误的方向,所以在思考问题决定下一步的时候,我们要带有批判思维,怀疑精神。这种批判不止针对外部信息,也要针对自己的思考过程,必要的时候,假设思维过程是错误的,去证明它的正确性。

人类用智的方式主要有纯逻辑,归纳和演绎,归纳只能证伪,不能证明,演绎只能证明,不能证伪,数理逻辑无法解释和世界的关系,所以看到,人类的思维逻辑是有缺陷的,我们应该认识到这种缺陷,避免调入陷阱。

如何打补丁

在芯片领域有一个流行的观点,如果硬件部署后出了BUG,那么问题也就成了feature.,一般需要用软件打补丁的方式去解决。一个好的补丁,一定是满足充分必要条件的补丁。

如果你的补丁出现了错杀或者漏杀的情况,那一定是你对ROOTCAUSE代表的充要条件没有理解透彻,你需要不断回过头来,充分理解根因所代表的条件,去实现它。

分析了这么多,都是在纸上谈兵,针对具体问题该如何找切入点呢?解决BUG的第一步要从复现问题,寻找规律入手,有的时候没有规律也是一种规律,有些奇葩问题的特征就是毫无规律.这里点到为止,日常工作中可以持续问题进行总结分类,无需太多笔墨。

场景因素

大家可能经历过面对不同的应用场景反复解决过同一个问题,每次都感觉是找到了rootcause并加以解决,但是,实际上rootcause不应该是和场景无关的么?为什么会出现这么多解决同一个"rootcause"的补丁呢?这种情况下,很可能是因为你找到的所谓 "rootcause"并非是真正的rootcause, 它可能仅仅是一个充分的,但非必要的条件,不要灰心,这个时候其实你离真正的rootcause已经不远了,如何从:

在场景A下,补丁B是解决问题C的充要条件。

去掉场景约束,变为更弱的:

在所有场景下,补丁M是解决问题C的充要条件。

通用的处理方式是,在所有场景下的充分条件和问题的必要条件之间,一定存在这一个公共的内核,找到这个内核,也就找到了充要条件。

三角形全等

下面用演绎推理的思想,归纳初中几何的三角形全等条件:

三角形是由于三个角和三条边组成,所以一个三角形总共有六组信息,提取三种来作为判断两个三角形全等,根据排列组合知识,共有C(6,3)= 20种方法

一角两个边的选择有:

两角一个边的选择有:

三个角相同 1种

三个边相同1种

其中,两个角1个边的选择中,只有三种情况能够证明三角形全等,也就是两角夹一边的规则

两个边1个角的选择中,也有三种情况能够证明三角形全等,就是两边夹一角

三个角相同只能代表想相似,不能推出全等,而三个边相同则可以。

所以归纳为,三角形的全等条件有三种:

  1. 两角夹一边

  1. 两边夹一角

  1. 三边相同

推理过程为:

经过层层推理,步步抽象,我们将20种情况下的外延归纳为三条全等定理,以3敌20,掌握的信息变得灵活和普适。

扩展,C++基类和子类

熟悉C++的人应该知道,多态性要求所有的子类对象都可以被基类指针引用,但反之则不然,也就是说,所有的子类都可以看成基类,但是基类却并不属于子类,基类的外延包含所有子类的外延,是所有子类外延的超集,例如会飞的动物和麻雀的关系,所有的麻雀都属于会飞的动物,但是会飞的动物不仅限于麻雀。我们用欧拉图表示:

当我们证明的一个结论的时候,一般思维目标是找到强条件,强条件就是更严格的条件,也就是更充分的条件。例如上面的例子,如果我要证明一种动物是鸟,我们只要证明它是麻雀,或者鸽子,或者其它任何属于鸟类的物种,自然而然它就是鸟了,也就是从充分条件找必要条件。如果万一我们找到一只从没见过的鸟,那有可能丰富鸟类的内涵,改变鸟类的外延,从而将新的物种其包括或者剔除出鸟类,再运用前面的推理,进而证明或者证否。

但是当我们做选择的时候,我们一般从必要条件开始选择,先找必要条件的目的是利用必要条件进行筛选,那些不符合必要条件的就没必要浪费时间去考虑了。比如和家人出去吃东西,首先确定这次聚餐是节约一点还是可以稍微奢侈一些,确定了再选择餐厅,到了餐厅再选择食物,逐渐缩小范围,达到满意的目标。也就是从必要条件找充分条件。

在逻辑学里,减少一个词项的内涵可以扩大它的外延,从而过渡到一个外延较广的词项,例如,把人扩大到生物,就要减少关于人的内涵,增加一个词项的内涵以缩小它的外延,从而过度到外延较窄的词项,叫做限制,例如把动物缩小到人,就要在动物内涵中增加人的内涵。内涵越多,条件越强,越严格,外延越多,条件越弱,越不严格。

如果把外延看成集合,则基类相当于超集,而子类则相当于真子集。所以C++基类的外延更大,包含所有的子类,子类的内涵更大,相应的外延就会小,从属于基类。可以按照分形的思路将每个子类作为基类继续向下继承。

从这个角度上讲,充分条件是必要条件增加内涵的结果,子类内涵多适合做充分条件,基类外延广适合做必要条件。

总结:

把解决问题的过程当成一场战役来打,孙子兵法谋攻篇中讲到,上兵伐谋(问题总结,抽象),其次伐交(运用资源,协调整合),其次伐兵(主动学习,锻炼技能,每次的轻而易举,都来自于对原理的深刻理解)其下攻城(无奈之举,被动解决问题)。颇有异曲同工之妙。

依靠正确的方法做引导,可以保证我们持续正确的做事情,但是并不意味着做正确的事情,所以我们做事的同时,不要忘记了目标。

所以,不仅要低头做事,还要抬头看路,保证行进在正确的方向和目标上。不偏离我们的最终目标。就如同欢做一件事,可能并不完全是由兴趣使然,相比于兴趣,更珍贵的是在兴趣和生活中间,还能获得的一种难得的自由。

参考资料

C++多态性分析和与Linux内核中的多态性实现的共性和差异比较_linuxc++ 多态性_papaofdoudou的博客-CSDN博客

结束

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值