浅谈算法学习
浅谈算法学习 - 个人学习方法论
在 浅谈算法能力 中,我尝试回答:算法竞赛能力究竟是什么。
在 浅谈算法知识体系 中,我继续讨论:知识与经验应当怎样被组织,才能形成可调用、可生长的个人工具箱。
最后剩下的问题是:外部的一道题、一篇题解、一个算法,怎样逐渐转化为自己的能力?
这就是本篇要讨论的内容。
一个核心问题
如何把一道题、一篇题解、一个算法等外部资源,逐渐转化为自己的能力,使自己在遇到陌生题目时能够自主调用?
基于一个判断:“学过算法 拥有竞赛能力”。
我个人认为,一个知识节点通常要经过以下六个阶段:认识 理解 推导 实现 迁移 调用。
-
认识:知道算法叫什么,处于什么位置,能解决什么问题,复杂度大致是多少。这一层是地图层面的认识,不代表掌握。
-
理解:能够解释模型为什么成立、状态为什么充分、转移为什么正确、复杂度从哪里得到,以及哪些条件是必要的。
-
推导:在没有给定标签的问题时,能够从 "数据范围",“约束条件”,“决策过程”,“输入输出” 等题面信息推导出相应的模型,而不是只在看到专题后才会复述。
-
实现:能够把确定的思路转化为程序,并处理下标与边界、初始化、更新顺序、溢出、去重、调试、对拍和复杂度等实现问题。
-
迁移:当一道相似题目改变表述、限制、答案要求或数据规模后,仍然能够识别其本质,并在原模型的基础上扩展或修正。
-
调用:在有限时间和不完整信息下,能够识别模型、选择题目、控制投入、组织思路、及时止损,并稳定完成实现。
于是,这套体系的关键不是 “我还要学什么知识”,而是 “关于一个知识节点,我卡在了哪一步”。
不知道网络流:认识问题;
会写 Dijkstra,却解释不清为什么要求非负边权:理解问题;
没有思路,但点开标签后立刻会做:推导问题;
思路正确,但代码反复写挂:实现问题;
学过 LIS,却没有认出二维偏序:迁移问题;
赛后看题解一看就会,比赛时却没有想到:调用问题;
明明能够完成,却在错误题目上投入过多时间:调用问题。
注:这六个阶段描述的是知识内化的不同方面,并不意味着每个知识节点都必须严格按照线性路径推进。实际学习中,它们可能反复往返,也可能通过不同任务分别补足。
基于此,我用三条坐标轴来描述一次完整的学习活动。
三条坐标轴
1. 能力轴
这次训练主要想提升什么能力?
依据 浅谈算法能力,算法竞赛能力可以分为三个互相衔接的部分:
- 解题能力:把题目转化为正确且复杂度可行的算法。
- 实现能力:把理论算法稳定地转化为正确程序。
- 赛场能力:在时间、压力和团队约束下,稳定调度前两种能力。
这条轴决定的是:为什么选择这道题,以及应该用什么方式训练。
同一道题可以承担不同任务:
- 第一次接触时,用于学习模型;
- 已经理解思路后,用于限时实现;
- 隔一段时间无标签重做,用于迁移检验;
- 放入比赛或 VP 中,用于调用检验。
所以,不应该笼统地问:这道题应该自己想多久?
而应该先问:我拿这道题,主要是为了训练什么?
2. 掌握轴
这个知识节点目前内化到了什么程度?
掌握轴就是前文的六个阶段:
- 认识 理解 推导 实现 迁移 调用
这条轴决定的是:当前真正的缺口在哪里。
例如,一个人可能已经理解某个算法,却无法从无标签问题中推导出来;也可能能够独立推导,却总在实现中出错;还可能平时会做,但比赛中无法及时调用。
因此,“会不会”并不是一个简单的是非判断。
同一个知识节点在不同环节上的掌握程度可能完全不同。
3. 工具轴
这次训练产生的经验,应该沉淀到个人知识体系的哪一层?
依据 浅谈算法知识体系,经验可以按照抽象程度分为:
-
事实 问题 方法 机制 思想
-
事实:一道题、一个性质、一个反例、一次失败或一个实现细节;
-
问题:具有共同结构的一类问题与对应模型;
-
方法:跨越多个专题反复出现的关键转化;
-
机制:多个方法背后的共同原理;
-
思想:能够指导更广泛探索的高度压缩认识。
这条轴决定的是:训练产物最终放在哪里。
这里描述的是知识本身的抽象程度,不等同于承载这条知识的 Record 最终应该保留、迁移还是删除。一个具体题目可以先作为事实层证据存在,随后其中的稳定模型进入 Topic,而原 Record 是否继续独立存在,要等知识体系发展到足以判断时再做生命周期审计。
不要求每做一道题都立刻向上抽象。多数题首先只是事实,经过多个案例和迁移检验后,才可能逐步形成问题模型、通用方法,乃至更高层认识。
如何共同使用
第一问:我主要训练哪种能力?
- 解题能力 / 实现能力 / 赛场能力
第二问:这个知识目前掌握到什么程度?
- 认识 / 理解 / 推导 / 实现 / 迁移 / 调用
第三问:这次经验应该沉淀到哪一层?
- 事实 / 问题 / 方法 / 机制 / 思想
做一道“时空可达转二维偏序”的题:
- 能力轴:主要训练解题能力;
- 掌握轴:把 LIS 从理解推进到迁移;
- 工具轴:具体题目属于事实层,二维偏序属于问题层,排序降维属于方法层,改变表示属于机制层。
三条轴分别回答 “训练什么”,“掌握到哪里”,“最后留下什么”。
它们共同描述了一次学习活动,但彼此不能混为一谈。
学习飞轮
个人理解的学习过程是这样的:
地图 任务 尝试 障碍 反馈 重建 沉淀 检验 修补
-
地图:先知道一个专题叫什么、解决什么问题、处于什么位置,以及当前需要掌握到什么程度。
-
任务:选择与已有能力相连,同时包含一两个新障碍的问题。不同任务可以承担不同职责:吸收新知识、训练解题、训练实现,或者检验赛场调用。
-
尝试:在获取完整答案以前,进行足以暴露问题的主动尝试,例如:手算小规模,写暴力,分析数据范围,构造反例,分析复杂度瓶颈。
-
障碍:尝试之后,需要判断自己真正卡在哪里:是知识范围不够,是原理没有理解,还是思路正确但实现失败,又或是平时会做但比赛中无法调用。
-
反馈:理想情况下,只获取足以让思考重新流动的信息,而不是 “不给提示” 或者 “直接看完题解” 两种极端。是否继续独立思考,不应只由时间决定,而应看当前尝试能否继续产生新的信息,例如性质、反例、候选路线或更准确的障碍描述。
-
重建:无论获得了多少帮助,都需要完成与训练目标相对应的个人重建:用自己的语言解释,补全被省略的推导,说明关键步骤解决了什么障碍,并根据训练目标完成正确性验证、复杂度分析、独立实现或测试。
-
沉淀:训练中真正有价值的经验,应当先被如实记录进个人知识体系,但不必在做完一道题时立刻决定它最终应该以什么文档形态存在。多数题只需要留下训练痕迹或轻量记录;带来新模型、新方法、新边界或真实认知变化的内容,则先保存它真正新增的知识。等 Atlas 发展到足以判断归属时,再对 Record 做生命周期审计:
retain、migrate、shelved或delete。其中migrate只有在目标 Topic 真正吸收以后才进入后续 dissolve;shelved记录的是当前知识前置尚未成熟,并不是掌握轴上的新阶段。具体方式参见 浅谈算法知识体系。 -
检验:原题能够完成,并不意味着已经掌握,可以通过多种方式具体检验,比如无标签题,延迟重做,限时实现等。迁移与调用是判断能力是否形成的重要证据。
-
修补:检验失败后,应当回到对应缺口:
1 | |
修补后的新经验再次进入知识体系,并参与下一轮训练。
于是,我们就得到了一个持续运转的学习计划。
四种训练模式
根据训练目标,可以把题目大致分为四种用途。
这四种模式之间没有高低之分,只服务于不同的训练目标。
1. 知识吸收
适合初次接触陌生算法、模型和较高阶内容。这一模式允许较早使用书籍、讲义和题解,但仍然需要完成自己的解释和实现。
主要推动:认识 理解 实现。
2. 解题训练
面对无标签问题,保留充分的主动尝试,并通过分级反馈推进。
主要推动:理解 推导 迁移。
3. 实现训练
算法思路已经确定,只训练代码兑现、测试和调试。
主要推动:实现 稳定实现。
4. 赛场检验
通过正式比赛、VP 或混合题,检验自己能否及时调动已有能力。
主要推动:迁移 调用。
每道题可以偏科,但整个训练周期不能偏科。
总结
算法学习,是通过目标明确的真实问题,主动尝试并暴露自己的具体障碍;再利用适量反馈完成个人重建,通过实现、变式、延迟重做与比赛检验,把外部知识逐渐转化为可迁移、可调用的个人能力。
学习过程可以概括为:
地图 任务 尝试 障碍 反馈 重建 沉淀 检验 修补
而一个知识节点的内化程度,可以通过下面六步判断:
认识 理解 推导 实现 迁移 调用
学习不是不断接触新的信息,而是让外部知识经过尝试、反馈、重建和检验,逐渐进入自己的能力系统。