我编写过的最漂亮的代码

合集下载

10年Labview编程经验

10年Labview编程经验

当我开始在键盘上敲打出这句话的时候,我已经使用LabVIEW 7 年了。

7 年的时间,就算天赋平平也可以积攒下一箩筐可供参考的经验了。

所以我打算利用今后的闲暇时间写一些这方面的东西,既可以同大家交流,也是作为自己这七年工作的总结。

还是在上大学的时候,有一次老师让编写一段软件,用来模拟一个控制系统:给它一个激励信号,然后显示出它的输出信号。

那时我就想过,可以把每一个简单的传递函数都做成一个个小方块,使用的时候可以选择需要的函数模块,用线把它们连起来,这样就可以方便地搭建出各种复杂系统。

后来,我第一次看到别人给我演示的LabVIEW编程,就是把一些小方块用线连起来,完成了一段程序。

我当时就感觉到,这和我曾经有过的想法多么相似啊。

一种亲切感油然而生,从此我对LabVIEW的喜爱就一直胜过其他的编程语言。

LabVIEW 的第一个版本发布于1986年,是在Macintosh 机上实现的,后来才移植到了PC机上,并且LabVIEW 从未放弃过对跨平台的支持。

这也给LabVIEW 带来了一些麻烦。

最明显的就是LabVIEW开发环境的界面风格。

它总是与一般的Windows 应用程序有些格格不入:面板是深灰色的,按键钮是看起来别别扭扭的3D 模样。

还有一些可能不太容易发现:比如对于整数的存储,LabVIEW即便是运行在x86系统上,采用的也是高地址位存高位数据(big-ending)。

这与我们习惯了的x86 CPU使用的格式正相反,这往往给编写存取二进制文件带来了不多不少的麻烦。

我接触过的最早的LabVIEW版本是4.0版,发布包是一个装有十几张三寸软盘的大盒子。

安装的时候要按顺序把软盘一个一个塞到计算机里。

尽管当时LabVIEW的界面不是很好看,但我还是非常喜欢它。

真方便呐!比如说要画一个开关,用LabVIEW 一拖就行了。

如果要自己动手用C 语言设计一个好看的开关,,那得费多少时间啊!我尤其喜欢它通过连线来编程的方式,尽管很多熟悉了文本编程语言的人刚开始时会对这种图形化编程方式非常不适应。

JavaGUI编程创建漂亮的用户界面

JavaGUI编程创建漂亮的用户界面

JavaGUI编程创建漂亮的用户界面JavaGUI编程是一种强大的工具,它可以帮助开发者创建漂亮、直观的用户界面。

在本文中,我将为您介绍如何使用JavaGUI编程来设计和实现一个令人印象深刻的用户界面。

一、选择合适的图形库在开始编写JavaGUI程序之前,我们需要选择一个合适的图形库。

Java提供了多种图形库选项,包括Swing、JavaFX等。

在这里,我将以Swing为例进行演示。

二、设计用户界面设计用户界面是实现漂亮用户界面的第一步。

在设计过程中,我们应该考虑到用户的需求和使用习惯,使界面具有良好的可用性和可访问性。

以下是一些设计原则可以帮助您创建漂亮的用户界面:1. 简洁明了:界面应该简洁明了,避免过多的干扰元素和复杂的图形。

2. 色彩搭配:选择适合的颜色搭配,使界面看起来舒适和谐。

同时要注意颜色的对比度,以确保文字和界面元素的可读性。

3. 布局:合理的布局可以使界面更加整洁美观。

可以使用不同的布局管理器(如FlowLayout、GridBagLayout等)来帮助您实现布局。

4. 图标和图片:适当使用图标和图片可以增加界面的吸引力。

选择高质量的图标和图片,确保其分辨率和大小适合当前界面的需求。

5. 文字字体:选择易读的字体和合适的字号。

正文通常使用宋体、微软雅黑等,标题可以选择一些特殊的字体以增加视觉效果。

三、编写代码一旦您完成了用户界面的设计,接下来就是编写代码来实现这个界面。

下面是一个简单的示例,展示如何使用Swing来创建一个基本的用户界面。

```javaimport javax.swing.*;import java.awt.*;public class GUIExample {public static void main(String[] args) {JFrame frame = new JFrame("漂亮的用户界面");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(400, 300);JPanel panel = new JPanel();panel.setLayout(new FlowLayout());JLabel label = new JLabel("欢迎使用漂亮的用户界面!");label.setFont(new Font("宋体", Font.BOLD, 18));JButton button = new JButton("点击我");button.setPreferredSize(new Dimension(100, 50));panel.add(label);panel.add(button);frame.getContentPane().add(panel);frame.setVisible(true);}}```上述代码演示了如何创建一个简单的用户界面,其中包括一个标签和一个按钮。

菱形的python代码

菱形的python代码

菱形的python代码菱形的Python代码Python是一种广泛使用的高级编程语言,它具有简洁易读的语法和强大的功能。

在Python中,我们可以使用一些简单的代码来绘制菱形图案。

下面我将介绍如何使用Python代码来绘制一个漂亮的菱形。

我们需要导入一个名为turtle的模块,它可以帮助我们在屏幕上绘制图形。

我们可以使用以下代码来导入turtle模块:import turtle接下来,我们需要创建一个turtle对象,它将帮助我们在屏幕上移动并绘制。

我们可以使用以下代码来创建一个turtle对象:t = turtle.Turtle()现在,我们可以开始绘制菱形了。

首先,让我们定义一个变量size 来表示菱形的大小。

然后,我们可以使用以下代码来绘制菱形:size = 100t.forward(size)t.right(45)t.forward(size)t.right(135)t.forward(size)t.right(45)t.forward(size)在上面的代码中,我们使用turtle对象t的forward()和right()方法来移动和旋转。

通过多次调用这些方法,我们可以绘制出一个菱形。

在每次绘制完成后,我们需要将turtle对象t的方向调整一下,以便绘制下一个边。

现在,我们已经成功绘制出一个菱形了。

如果我们想要绘制更大或更小的菱形,只需要修改变量size的值即可。

此外,我们还可以使用turtle模块中的其他方法来添加颜色、填充等效果,使菱形更加丰富多彩。

绘制菱形只是Python语言中的一小部分应用。

Python还可以用于数据分析、人工智能、网络开发等众多领域。

它的简洁易读的语法和丰富的库使得Python成为了许多程序员喜爱的编程语言。

通过学习Python,我们可以更好地理解编程的基本概念和思维方式。

无论是初学者还是有经验的开发者,都可以通过编写Python代码来加深对编程的理解。

同时,Python社区庞大而活跃,我们可以通过阅读他人的代码和参与开源项目来不断提升自己的编程能力。

很漂亮的蓝色经典CSS导航菜单代码

很漂亮的蓝色经典CSS导航菜单代码

很漂亮的蓝⾊经典CSS导航菜单代码代码简介:很不错的⼀款蓝⾊风格经典的CSS导航菜单,只不过⽤到了⼏张背景图⽚,好像这些图⽚可以合并起来⽤,有兴趣⽤的朋友可以适当优化⼀下,从外观上来看,这款菜单还是⾮常好看的。

代码内容:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="/1999/xhtml"><head><title>很漂亮的蓝⾊经典CSS导航菜单代码_⽹页代码站()</title><meta http-equiv="content-Type" content="text/html;charset=gb2312"><style type="text/css">body{margin:20px 0 0 0; padding:0px; font-size:12px; font-family:""; line-height:20px; background:#fff;}div{margin:0 auto; padding:0px;}h1,h2,h3,h4,h5,h6,ul,li,dt,dl,dd,form,p{margin:0px; padding:0px; list-style-type:none;}img{border:none;}.f_l{float:left;}.f_r{float:right;}.tl{text-align:left;}.tr{text-align:right;}.tc{text-align:center;}.clearing{clear:both; height:0px; line-height:0px; overflow:hidden;}.pos{position:relative;}.dis{display:block;}.marg{margin-top:-1px;}.block{width:933px; height:auto;}.border_1{border:1px solid #999;}.blank9{height:9px; line-height:9px; clear:both; overflow:hidden;}.f1{color:#0066cc; text-decoration:underline;}.f2{color:#ff0000;}.f3{color:#ff0000; font-weight:bold;}.f4{color:#0066cc;}.f5{color:#cc6633;}.f_weight{ font-weight:bold; color:#333333; font-size:14px;}.fLink{color:#dc1a00; text-decoration:none;}#top{height:69px;}#top img{margin-left:20px;}#top .text{color:#b1b1b1; position:absolute; right:0px; bottom:12px;}#top .text a{color:#b1b1b1; text-decoration:none;}#nav{width:933px; height:30px; background:url(/images/20090920/0_navBg.gif) repeat-x lefttop;}#nav span{width:4px; height:30px; display:block; background:url(/images/20090920/0_bg.gif)no-repeat 0 0;position:absolute; left:0px; top:0px;}#nav span.right{background:url(/images/20090920/0_bg.gif) no-repeat -5px 0;position:absolute; left:929px; top:0px;}#nav .navBox{background:url(/images/20090920/0_dhling.gif) no-repeat left top; padding-left:1px;}#nav a{display:block; width:99px; height:30px; line-height:30px; font-size:13px; color:#fff; font-weight:bold;background:url(/images/20090920/0_dhbg.gif) no-repeat left top; float:left; text-align:center; text-decoration:none;}#nav a:hover,#nav .cur{display:block; width:99px; height:30px; line-height:30px; font-size:13px; color:#fff;font-weight:bold;background:url(/images/20090920/0_dhbg1.gif) no-repeat left top; float:left; text-align:center; text-decoration:none;}</style></head><body><div id="nav" class="pos"><span class="right"></span><div class="navBox f_r"><a href="/" class="cur">⽹站⾸页</a><a href="/">Ajax</a><a href="/">脚本资源</a><a href="/">电⼦书籍</a><a href="/">⽅案及⽂档</a><a href="/">⼯具软件</a></div></div></body></html><br><p><a href="">⽹页代码站</a> - 最专业的代码下载⽹站 - 致⼒为中国站长提供有质量的代码!</p>。

上古卷轴5控制台代码

上古卷轴5控制台代码

player.additem 000000f 99999 (增加金币99999)常用命令TGM - 无敌Tcl- 穿墙tcai - 开关NPC攻击反应tfc - 拍照用!自由视角!resurrect - 复活你鼠标所选择的人或怪lock * - 再控制台下将鼠标所选择的门或者箱子用等级为*的锁锁住。

#必须介于1到100之间,unlock - 再控制台下用鼠标选择门或箱子,输入指令即开。

kill –再控制台下用鼠标选择人或其它生物,输入指令秒杀。

set timescale to # - 时间流失速度!数字越大!时间流失越快!默认是7caqs - 完成所有任务(不要轻易尝试!一旦使用!恭喜你通关了~)ShowSubtitle - 开关NPC对话字幕SexChange - 改变性别advlevel - 立刻升级psb - 获得所有魔法!包括一些没有测试完成的BUG魔法!其它命令TM - 开关所有菜单~包括控制台TDETECT -开关人工智能探测TLL - 开关远景TS - 开关天空TLV - 开关树叶TWF -开关框架模式TAI - 开关人工智能(所有npc无法正常对话!)TG - 开关草丛TT - 开关树木tws - 开关水(有问题)player.AddItem FormID # - 得到代号FormID的物品#个000000f为金币player.AddSpell FormID - 得到代号FormID的法术PlaceAtMe formid, FormID, x, y - 在地图上生成代号为formid的物品或者生物#个player.removeitem FormID # - 从你身上移除代号为FormID的物品#个player.removespell FormID - 移除代号为FormID的法术modpcs 技能名称# - 技能名称对应的技能,比如sneak,增加#个技能点modpca 属性名称# - 属性名称对应的属性,比如luck,增加#点player.setlevel # - 改变角色的等级到#你想要的等级player.setAV - 技能或属性名称# - 将名称为技能或属性名称的技能或者属性的值设为# advskill 技能名称# - 技能名称对应的技能上升#个级别,比如advskill blade 5 ShowRaceMenu - 调出改变种族窗口TFOW - 开关战争迷雾FOV # - 改变视野为#度,缺省设定为75度setcamerafov # - 设置相机视野为#度。

编写单片机C语言代码的技巧和经验

编写单片机C语言代码的技巧和经验

编写单片机C语言代码的技巧和经验编写单片机C语言代码的技巧和经验C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

那么编写单片机C语言代码的技巧和经验都有哪些呢。

以下仅供参考!具体如下:1、如果可以的话少用库函数,便于不同的mcu和编译器间的移植2、选择合适的算法和数据结构应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料,有很多计算机书籍上都有介绍。

将比较慢的顺序查找法用较快的二分查找或乱序查找法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大提高程序执行的效率。

选择一种合适的数据结构也很重要,比如你在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。

数组与指针语句具有十分密码的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。

对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。

但是在Keil中则相反,使用数组比使用的指针生成的代码更短。

3、使用尽量小的数据类型能够使用字符型(char)定义的变量,就不要使用整型(int)变量来定义;能够使用整型变量定义的变量就不要用长整型(long int),能不使用浮点型(float)变量就不要使用浮点型变量。

当然,在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C编译器并不报错,但程序运行结果却错了,而且这样的错误很难发现。

在ICCAVR中,可以在Options中设定使用printf参数,尽量使用基本型参数(%c、%d、%x、%X、%u和%s格式说明符),少用长整型参数(%ld、%lu、%lx和%lX格式说明符),至于浮点型的参数(%f)则尽量不要使用,其它C编译器也一样。

在其它条件不变的情况下,使用%f 参数,会使生成的代码的数量增加很多,执行速度降低。

4、使用自加、自减指令通常使用自加、自减指令和复合赋值表达式(如a-=1及a+=1等)都能够生成高质量的程序代码,编译器通常都能够生成inc和dec之类的指令,而使用a=a+1或a=a-1之类的指令,有很多C编译器都会生成二到三个字节的指令。

css按钮样式

css按钮样式

css按钮样式创建漂亮的 CSS 按钮的 10 个代码⽚段如果你正在寻找⼀些⾼质量的 CSS 按钮的⽰例,那么这篇⽂章⼀定是你的“菜”。

在本⽂中,我们从 CodePen 上收集了 10 个独特的 CSS 按钮合集,并附有它们的代码⽚段,⽅便你将它们应⽤在你的 Web 项⽬上。

⽹页设计师已经不必再依赖 Photoshop 制作酷炫的按钮了。

通过使⽤ CSS3,你可以实现背景的渐变、阴影以及光泽/闪亮的效果。

1. Plastic Buttons相当的简洁、⼲净。

由于它们拥有不同的颜⾊、尺⼨以及风格,并提供了⼩、中、⼤号按钮供你任意挑选。

所以,你可以轻松地重新调整或更换它们。

⽽利⽤纯 CSS 的实现⽅式,或许它也是⽹上最简洁、⼲净的按钮样式之⼀。

代码地址:【】2. Cool Buttons这是⼀组由 Felipe Marcos 制作的。

与上⾯的塑料按钮略有不同,但它们也易于使⽤。

虽然没有闪亮的塑料设计,但当你点击后,依然会感受到有种“推”的效果。

你可以从 6 款默认设计的颜⾊中随意挑选,或者你也可以定制⾃⼰喜欢的颜⾊、尺⼨与样式。

由于根据 CSS 类名进⾏分类,所以你可以在⼀个类上设置默认的按钮样式以及颜⾊。

代码地址:【】3. Google ButtonsGoogle 的在线⼯具(如 Blogger,云盘,Gmail 及其搜索功能)都有不同的按钮样式,⽽开发者 Tim Wagner 在中克隆了这些风格。

作者受 Google 设计的启发,利⽤纯 CSS3 实现了这些看上去很酷的按钮。

这还有个与此,它是由 Monkey Raptor 制作的扩展按钮,他为这些按钮进⾏了⼀些其他的混合。

代码地址:【】4. Bunch-o-Buttons这是由 Alan Collins 在 CodePen 上制作的基于塑料风格的按钮合集。

它拥有多种颜⾊以及不同的款式。

这个按钮集合设计的独特之处在于,它仅通过⼀个单独的 CSS 类就可以在光滑的样式与间任意切换。

ass字幕特效代码大全

ass字幕特效代码大全

ass字幕特效代码大全ASS字幕特效代码学好SSA主要从两个方面着手:一个是SSA文件的组成结构;一个是SSA代码。

一.SSA文件的组成结构SSA文件主要由三大部分组成:[Script Info]部分,[v4 Styles]部分和[Events]部分。

1.[Script Info]部分PlayResX: 384PlayResY: 288384×288是标准的4:3画面分辨率之一。

ssa字幕里的坐标(字幕的位置)即根据这2个数值的范围来定义。

16:9宽银幕的片子略做调整即可。

这里主要介绍文件本身和制的信息,这里面最重要的两项是PlayResX和PlayResY,它实际上是按给定的PlayResX和PlayResY数值将屏幕按比例化分,屏幕的左上角坐标为(0,0),右下角坐标为(PlayResX数值,PlayResY数值).这两个值最好是和视频文件的长宽相同,这样在下面[Events]部分文本位置坐标的设定很方便而且准确. 当然这两个值也可能和原视频文件的长宽不同,比如视频文件尺寸为640x360,而PlayResX:384,PlayResY: 288,如同前面所说的,它是将屏幕长宽按384:288来化分的.一旦这样划定,下面的各种坐标设定都要以384和288为根据.特别要注意的是:对于一个已经存在并且文本坐标都设置好的SSA文件,如果它的PlayResX和PlayResY数值和视频文件的长宽尺寸不符(包括成比例的情况,比如视频文件尺寸为640x360,而 PlayResX:320,PlayResY: 180),我们不要简单的把PlayResX和PlayResY数值改成屏幕尺寸,这样将导致字幕失真,是因为文本部分的尺寸并没有改,仍按照原来的PlayResX和PlayResY数值设定.如果有较多的坐标设定,就不要改原来的PlayResX和PlayResY数值,在原基础上编辑.2.[V4 Styles]部分这是个存放[Events]部分里所用到的各种字体信息的地方.可以存在多种字体,每种字体有多种特征.当[Events]部分的Name项引用到这里的某种字体时,则意味这该字体的各种特征也被引用.字体的特征还可以在文本行中修改,这将在[Events]部分讨论.常见的字体属性有Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour,TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding我们可以再定义一个Style,并命名为mine,如Style: mine,华文新魏,18,&HFF0000,&HFF80FF0,&Hffffff,&Hffffff,0,0,1,2,1,2,20,20,20,0,134即可以这样:[V4 Styles]Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle,Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, EncodingStyle: Default,华文彩云,18,&Hff0000,&Hff8080,&Hffffff,&Hffffff,0,0,1,2,1,2,20,20,20,0,134Style: mine,华文新魏,18,&HFF0000,&HFF80FF0,&Hffffff,&Hffffff,0,0,1,2,1,2,20,20,20,0,134有了具体经验后,一般可以不作调整,需要改动的可以是“Fontname”,如改成黑体,“Fontsize”,做字幕字号24比较合适。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

第3章我编写过的最漂亮的代码Jon Bentley我曾经听一位大师级的程序员这样称赞到,“我通过删除代码来实现功能的提升。

”而法国著名作家兼飞行家Antoine de Saint-Exupéry的说法则更具代表性,“只有在不仅没有任何功能可以添加,而且也没有任何功能可以删除的情况下,设计师才能够认为自己的工作已臻完美。

”某些时候,在软件中根本就不存在最漂亮的代码,最漂亮的函数,或者最漂亮的程序。

当然,我们很难对不存在的事物进行讨论。

本章将对经典Quicksort(快速排序)算法的运行时间进行全面的分析,并试图通过这个分析来说明上述观点。

在第一节中,我将首先根据我自己的观点来回顾一下Quicksort,并为后面的内容打下基础。

第二节的内容将是本章的重点部分。

我们将首先在程序中增加一个计数器,然后通过不断地修改,从而使程序的代码变得越来越短,但程序的功能却会变得越来越强,最终的结果是只需要几行代码就可以使算法的运行时间达到平均水平。

在第三节将对前面的技术进行小结,并对二分搜索树的运行开销进行简单的分析。

最后的两节将给出学完本章得到的一些启示,这将有助于你在今后写出更为优雅的程序。

3.1 我编写过的最漂亮代码当Greg Wilson最初告诉我本书的编写计划时,我曾自问编写过的最漂亮的代码是什么。

这个有趣的问题在我脑海里盘旋了大半天,然后我发现答案其实很简单:Quicksort算法。

但遗憾的是,根据不同的表达方式,这个问题有着三种不同的答案。

当我撰写关于分治(divide-and-conquer)算法的论文时,我发现 C.A.R. Hoare的Quicksort算法(“Quicksort”,Computer Journal 5)无疑是各种Quicksort算法的鼻祖。

这是一种解决基本问题的漂亮算法,可以用优雅的代码实现。

我很喜欢这个算法,但我总是无法弄明白算法中最内层的循环。

我曾经花两天的时间来调试一个使用了这个循环的复杂程序,并且几年以来,当我需要完成类似的任务时,我会很小心地复制这段代码。

虽然这段代码能够解决我所遇到的问题,但我却并没有真正地理解它。

我后来从Nico Lomuto那里学到了一种优雅的划分(partitioning)模式,并且最终编写出了我能够理解,甚至能够证明的Quicksort算法。

William Strunk Jr.针对英语所提出的“良好的写作风格即为简练”这条经验同样适用于代码的编写,因此我遵循了他的建议,“省略不必要的字词”(来自《The Elements of Style》一书)。

我最终将大约40行左右的代码缩减为十几行的代码。

因此,如果要回答“你曾编写过的最漂亮代码是什么?”这个问题,那么我的答案就是:在我编写的《Programming Pearls, Second Edition》(Addison-Wesley)一书中给出的Quichsort算法。

在示例3-1中给出了用C语言编写的Quicksort函数。

我们在接下来的章节中将进一步地研究和改善这个函数。

【示例】 3-1 Quicksort函数void quicksort(int l, int u){ int i, m;if (l >= u) return;swap(l, randint(l, u));m = l;for (i = l+1; i <= u; i++)if (x[i] < x[l])swap(++m, i);swap(l, m);quicksort(l, m-1);quicksort(m+1, u);}如果函数的调用形式是quicksort(0, n-1),那么这段代码将对一个全局数组x[n]进行排序。

函数的两个参数分别是将要进行排序的子数组的下标:l是较低的下标,而u是较高的下标。

函数调用swap(i,j)将会交换x[i]与x[j]这两个元素。

第一次交换操作将会按照均匀分布的方式在l和u之间随机地选择一个划分元素。

在《Programming Pearls》一书中包含了对Quicksort算法的详细推导以及正确性证明。

在本章的剩余内容中,我将假设读者熟悉在《Programming Pearls》中所给出的Quicksort 算法以及在大多数初级算法教科书中所给出的Quicksort算法。

如果你把问题改为“在你编写那些广为应用的代码中,哪一段代码是最漂亮的?”我的答案还是Quicksort算法。

在我和M. D. McIlroy一起编写的一篇文章("Engineering a sort function," Software-Practice and Experience, Vol. 23, No. 11)中指出了在原来Unix qsort函数中的一个严重的性能问题。

随后,我们开始用C语言编写一个新排序函数库,并且考虑了许多不同的算法,包括合并排序(Merge Sort)和堆排序(Heap Sort)等算法。

在比较了Quicksort的几种实现方案后,我们着手创建自己的Quicksort算法。

在这篇文章中描述了我们如何设计出一个比这个算法的其他实现要更为清晰,速度更快以及更为健壮的新函数——部分原因是由于这个函数的代码更为短小。

Gordon Bell的名言被证明是正确的:“在计算机系统中,那些最廉价,速度最快以及最为可靠的组件是不存在的。

”现在,这个函数已经被使用了10多年的时间,并且没有出现任何故障。

考虑到通过缩减代码量所得到的好处,我最后以第三种方式来问自己在本章之初提出的问题。

“你没有编写过的最漂亮代码是什么?”。

我如何使用非常少的代码来实现大量的功能?答案还是和Quicksort有关,特别是对这个算法的性能分析。

我将在下一节给出详细介绍。

3.2 事倍功半Quicksort是一种优雅的算法,这一点有助于对这个算法进行细致的分析。

大约在1980年左右,我与Tony Hoare曾经讨论过Quicksort算法的历史。

他告诉我,当他最初开发出Quicksort时,他认为这种算法太简单了,不值得发表,而且直到能够分析出这种算法的预期运行时间之后,他才写出了经典的“Quicksoft”论文。

我们很容易看出,在最坏的情况下,Quicksort可能需要n2的时间来对数组元素进行排序。

而在最优的情况下,它将选择中值作为划分元素,因此只需nlgn次的比较就可以完成对数组的排序。

那么,对于n个不同值的随机数组来说,这个算法平均将进行多少次比较?Hoare对于这个问题的分析非常漂亮,但不幸的是,其中所使用的数学知识超出了大多数程序员的理解范围。

当我为本科生讲授Quicksort算法时,许多学生即使在费了很大的努力之后,还是无法理解其中的证明过程,这令我非常沮丧。

下面,我们将从Hoare的程序开始讨论,并且最后将给出一个与他的证明很接近的分析。

我们的任务是对示例3-1中的Quicksort代码进行修改,以分析在对元素值均不相同的数组进行排序时平均需要进行多少次比较。

我们还将努力通过最短的代码、最短运行时间以及最小存储空间来得到最深的理解。

为了确定平均比较的次数,我们首先对程序进行修改以统计次数。

因此,在内部循环进行比较之前,我们将增加变量comps的值(参见示例3-2)。

【示例3-2】修改Quicksort的内部循环以统计比较次数。

for (i = l+1; i <= u; i++) {comps++;if (x[i] < x[l])swap(++m, i);}如果用一个值n来运行程序,我们将会看到在程序的运行过程中总共进行了多少次比较。

如果重复用n来运行程序,并且用统计的方法来分析结果,我们将得到Quicksort在对n个元素进行排序时平均使用了1.4 nlgn次的比较。

在理解程序的行为上,这是一种不错的方法。

通过十三行的代码和一些实验可以反应出许多问题。

这里,我们引用作家Blaise Pascal和T. S. Eliot的话,“如果我有更多的时间,那么我给你写的信就会更短。

”现在,我们有充足的时间,因此就让我们来对代码进行修改,并且努力编写出更短(同时更好)的程序。

我们要做的事情就是提高这个算法的速度,并且尽量增加统计的精确度以及对程序的理解。

由于内部循环总是会执行u-l次比较,因此我们可以通过在循环外部增加一个简单的操作来统计比较次数,这就可以使程序运行得更快一些。

在示例3-3的Quicksort算法中给出了这个修改。

【示例3-3】 Quicksort的内部循环,将递增操作移到循环的外部comps += u-l;for (i = l+1; i <= u; i++)if (x[i] < x[l])swap(++m, i);这个程序会对一个数组进行排序,同时统计比较的次数。

不过,如果我们的目标只是统计比较的次数,那么就不需要对数组进行实际地排序。

在示例3-4中去掉了对元素进行排序的“实际操作”,而只是保留了程序中各种函数调用的“框架”。

【示例3-4】将Quicksort算法的框架缩减为只进行统计void quickcount(int l, int u){ int m;if (l >= u) return;m = randint(l, u);comps += u-l;quickcount(l, m-1);quickcount(m+1, u);}这个程序能够实现我们的需求,因为Quichsort在选择划分元素时采用的是“随机”方式,并且我们假设所有的元素都是不相等的。

现在,这个新程序的运行时间与n成正比,并且相对于示例3-3需要的存储空间与n成正比来说,现在所需的存储空间缩减为递归堆栈的大小,即存储空间的平均大小与lgn成正比。

虽然在实际的程序中,数组的下标(l和u)是非常重要的,但在这个框架版本中并不重要。

因此,我们可以用一个表示子数组大小的整数(n)来替代这两个下标(参见示例3-5)【示例3-5】在Quicksort代码框架中使用一个表示子数组大小的参数void qc(int n){ int m;if (n <= 1) return;m = randint(1, n);comps += n-1;qc(m-1);qc(n-m);}现在,我们可以很自然地把这个过程整理为一个统计比较次数的函数,这个函数将返回在随机Quicksort算法中的比较次数。

在示例3-6中给出了这个函数。

【示例3-6】将Quicksort框架实现为一个函数int cc(int n){ int m;if (n <= 1) return 0;m = randint(1, n);return n-1 + cc(m-1) + cc(n-m);}在示例3-4、示例3-5和示例3-6中解决的都是相同的基本问题,并且所需的都是相同的运行时间和存储空间。

相关文档
最新文档