编程珠玑读书笔记

   这本书中的程序都很有趣,传授了重要的编程技巧和基本的设计原理。书的结构如下:

 

第1章:

 

问题“怎样给一个磁盘文件排序?”

准确的问题描述

输入:一个最多包含n个正整数的文件,每个数都小于n,其中n=107。如果在输入文件中有任何整数重复出现就是致命错误。没有其他数据与该整数相关联。

输出:按升序排列的输入整数的列表

约束:最多有1MB内存空间可用,有充足的磁盘空间可用。运行时间最多几分钟,运行时间为10s就不需要进一步优化了。

原理;

 

正确的问题。明确问题,这场战役就成功了90%

位图数据结构。该数据结构描述了一个有限定义域的稠密集合,其中每一个元素最多出现一次并且没有其他任何数据与该元素相关联。即使这些条件没有完全满足(例如,存在重复元素或额外的数据),也可以用有限定义域内的键作为一个表项更复杂的表格的索引。

多趟算法。这些算法多趟读入其输入数据,每次完成一步。

时间-空间折中与双赢

简单的设计。更多的程序员都应该使用该标准来检验自己完成的程序。简单的程序通常比具有相同功能的复杂程序更可靠、更安全、更健壮、更高效,而且易于实现和维护。

 

第二章

本章介绍了三个问题,其实看起来很困难的问题也可以有一个简单的、意想不到的答案。

原理:

排序。排序显而易见的用处是产生有序的输出,该输出既可以是系统规范要求的一部分,也可以是另一个程序(也许是一个二分搜索程序)的前期准备工作。

 

二分搜索:该算法在有序表中查找元素时极为高效,并且可用于内存排序和磁盘排序。唯一的缺陷就是整个表必须已知并且事先排好序。基于该简单算法的思想在许多应用程序中都有应用。

标识:当使用等价关系来定义类时,定义一种标识使得类中的每一项都具有相同的标识,而该类以外的其他项则没有该标识,这是很有用的。

问题定义:第1章指出确定用户的真实需求是程序设计的根本。本章的中心思想是问题定义的下一步:使用哪些基本操作来解决问题?

问题解决者得观点:优秀的程序员都有点懒:他们坐下来并等待灵机一动的出现而不基于使用最开始的想法编程。当然这必须通过在适当的时候开始写代码来加以平衡。真正的技能在于对这个适当时候的抱我,这只能来源于解决问题和反思答案所获得的经验。

第3章:数据结构决定程序结构

 

  能用小程序实现的,就不要编写大程序,而且“更一般性的问题也许更容易解决”。对于程序设计来说,这意味着直接编写解决23种情况的问题很困难;而编写一个处理n种情况的通用程序,再令n=23来得到最终结果,却相对要容易一些。

   本章集中讨论了数据结构对软件的一个贡献:将大程序削减为小程序。数据结构还有许多其他正面影响,包括节省时间和空间、提高可移植性和可维护性。常使用的方法:

     程序员在节省空间方面无计可施时,将自己从代码中解脱出来,退回起点并集中心力研究数据,常常能有奇效。(数据的)表示形式是程序设计的根本。

下面是退回起点进行思考的几条原则:

    1 使用数组重写编写重复代码。冗长的相似代码常常可以使用最简单的数据结构---数组来更好地表述。

    2 封装纷杂结构。当需要非常复杂的数据结构时,使用抽象属于进行定义,并将操作表示为类。

    3 尽可能使用高级工具。超文本、名字-值对、电子表格、数据库、编程语言等都是特定问题领域中的强大的工具。

     4 从数据得出程序的结构。本章的主题就是:通过使用恰当的数据结构来替代复杂的代码,从数据可以得出程序的结构。万变不离其宗:在动手写代码之前,优秀的程序员会彻底理解输入、输出和中间数据结构,并围绕这些结构创建程序。

 第4章 编写正确的程序

程序验证很重要,一般的原理如下:

    断言。输入、程序变量和输出之间的关系勾勒出了程序的“状态”,断言使得程序员可以准确阐述这些关系。

    顺序控制结构。控制程序的最简单的结构莫过于采用“执行这条语句然后执行下一条语句”的形式。可以在语句之间增加断言并分别分析程序执行的每一步来理解这样的结构。

  选择控制结构和迭代控制结构都要使用断言。

   函数。验证一个函数,首先需要两个断言来陈述其目的。前置条件是在调用该函数之前就应该成立的状态,后置条件的正确性由函数在终止执行时保证。

 第5章 编程小故事

      到目前为止,你已经做了一切该做的事:通过深入挖掘定义了正确的问题,通过自诩选择算法和数据结构平衡了真正的需求,通过程序验证技术写出了优雅的伪代码,并且对其正确性相当有把握。那么如何将这些成果合并到你的大系统中呢?嗯,万事俱备,只欠不起眼的编程了。

     程序员都是乐观主义者,他们总是试图走捷径:编写函数代码,并将其插入到系统中,然后热切地期望它能运行。有时候这样行的通。但是有千分之九百九十九的概率,这样会导致一场灾难:人们不得不在巨型系统的迷宫中操作这个小小的函数。

      明智的程序员则使用脚手架(scaffolding)来方便地访问函数。编写完代码之后,我们将使用脚手架来探测代码,然后更彻底地测试代码,并通过实验来了解运行时间。对于这样一个小函数来说,这个过程太繁琐了。然而,这样做能够得到一段可以信赖的程序。

 原理

  脚手架。最好的脚手架通常是最容易构建的脚手架。对于某些任务来说,最简单的脚手架由一个使用Visual Basic、Java之类的语言实现的图形用户界面构成。不过,对于许多算法任务而言,我发现更容易的办法是,摒弃这些强大的工具并使用我们在本章中见过的更简单(也更易移植)的命令技术。

编码。对于比较难写的函数,我发现最容易的方法是使用高级伪代码来构建程序框架,然后将伪代码翻译成要实现的语言。

 测试。在脚手架中对组建进行测试要比在大系统中更容易、更彻底。

调试。对于隔离在脚手架中的程序进行调试是很困难的,但是若将其嵌入真实运行环境中调试会更困难。

计时。如果运行时间不重要,线性搜索要比二分搜索简单得多;许多程序员都可以在第一次实现的时候得到正确的代码。正是由于运行时间非常重要,我们才引入了更加复杂的二分搜索,所以,我们应该进行实验以确保程序能够达到我们预期的性能。

 

一个简单而又功能强大的程序,令用户欣喜而又不令开发者烦恼,这正是程序员的终极目标,也是本书前面5章讨论的重点。

 现在我们将注意力转向令人欣喜的程序的一个具体的方面:效率。

第6章 程序性能分析

当一个程序效率很低时,可以考虑从以下层面上来进行改进,来获得巨大的加速。

    算法和数据结构

    算法调优

    数据结构重组

    代码调优

    硬件

第 7章 粗略估算

在进行粗略估算的时候,要切记爱因斯坦的名言:

  任何事都应尽量简单,但不宜过于简单。

第8章 算法设计技术

下面给出了几个重要的算法设计技术

 1保存状态,避免重复计算。通过使用一些空间来保存中间计算结果,我们便面了花时间对其重复计算。

 2将信息预处理至数据结构中。

 3 分治算法。

 4 扫描算法。与数组相关的问题经常可以通过思考“如何将x[0...i-1]的解扩展为x[0...i]的解?”来解决。

  5 累积。经常用一个表中的第i个元素的值为x中前i个值的总和;这一类表常用于处理有范围限制的问题。例如,业务分析师要确定3月份到10月份的销售额,可以从10月份的本年迄今销售额中减去2月份的本年迄今销售额。

  6 下界只有在确定了自己的算法是所有可能的算法中最佳的算法以后,算法设计师才能踏踏实实的睡个好觉。为此,他们必须证明某个相匹配的下界。

第9章 代码调优

前面各个章节已经讨论了提高效率的高层次方法:问题定义、系统结构、算法设计以及数据结构选择。本章讨论一个低层次方法。“代码调优”首先确定程序中开销较大的部分,然后进行少量的修改,以提高其运行速度。

原理

代码调优的最重要的原理就是尽量少用它。这一笼统的叙述可以用以下几点加以解释。

效率的角色。软件的其他许多特性和效率一样重要,甚至更重要。Don Knuth观察发现,不成熟的优化是大量编程灾害的根源,它会危及程序的正确性、功能和可维护性。当可能的危害影响较大时,请考虑适当将效率放一放。

度量工具。当效率很重要时,第一步就是对系统进行性能监视,以确定其运行时间的分布状况。对程序进行性能监视的结果通常类似:多数的时间都消耗在了少量的热点代码上,而余下的代码则很少这姓。(有个例子一个函数就占用了98%的运行时间)。性能监视可以帮助我们找到程序的关键区域;对于其他区域,我们就遵循有名的格言“没有坏的话旧不要修”。

设计层面。在第6章中我们已经看到,效率的问题可以由多种方法来解决。只有在确信没有更好的解决方案的时才考虑进行代码调优。

 

第12章 取样问题

本章示例了编程过程中的几个重要步骤。尽管下面的讨论是按一种比较自然的顺序对哥哥阶段进行介绍的,实际的设计过程应该更加能动一些:可以从一个阶段跳到另一个阶段,在得到一个可以接受的解决方案之前,通常需要对每个步骤迭代好多次。

   正确理解所遇到的问题。与用户讨论问题产生的背景。问题的陈述通常就包含了与解决方案有关的想法;跟早期的想法一样,这些想法也都应当加以考虑,但不应排除其他想法。

   提炼出抽象问题。简洁、明确的问题陈述不仅可以帮助我们解决当前遇到的问题,还有助于我们把解决方案应用到其他问题。

   考虑尽可能多的解法。很多程序员都很快就发现了问题的“解决方案”,他们只愿意花1分钟的时间思考,然后花一天的时间来编写代码,而不是花1小时来思考,再用一个小时来写代码。非正式的高级语言能帮助我们描述设计方案:伪代码表示控制流,抽象数据类型表示关键的数据结构。

   实现一种解决方案。如果运气很好的话,在前一阶段我们就能发现某种解决方案显著优于其他方案;否则我们就得列出几种性能比较好的方案,然后从中选择最佳的。我们应该用简单的代码和最有效的操作来实现最终选择的解决方案。

   回顾。改进的余地总是存在的。经过充分的研究和思考,任何解决方案都有可能被改进;任何情况下,对于解决方案的理解一定能被改进。说明回顾尤其重要。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值