学会玩代码

原文信息

Code Kata--How It Started


http://codekata.pragprog.com/codekata/2007/01/code_katahow_it.html

译文

怎样开始 Code Kata

(这篇文章很长,但很值得一读...)

所有都源于 RubLog 提到的搜索方式(相信我,这不是一篇关于搜索的文章,或者关于位的探讨)。因为我最终想使用基于余弦对比算法来找相似的文章,我建立了一个向量表词汇映射表来记录在博客中文档的词语出现次数。我最终为每一篇文档建立了 1200-1500 位。为了实现一个基本的随机搜索功能,我需要能够按位展示并且在这些向量和一个向量包含的内容之间,然后在最终结果中计算出一位的数量。

我昨晚上有一个非常稀少而珍贵的 45 分钟(我将 Zachary 送到了 karate 课程,那是一个不需要父母的看管的地方),所以我想我可以有充足的时间做这件事情了。首先我尝试用不同的方式存储位向量:结果证明(同时也令我大吃一惊),这种用数组存储位的方式几乎完全可以满足于用做大数的存储位的速度。(另外,更令我高兴的是,在两种呈现方式上的改变,仅仅只需要修改两行代码而已)。因为它控制得非常紧凑,我决定继续使用这个大数的算法。

然后,我在独自一人玩大数的位计算找到了乐趣。这个源代码中使用了非常简单明显的算法:

      max_bit.times { |i| count += word[i] }


让我们来看看这样的速度会有多慢呢?我编写了一个简单的测试实例,用来生成 一百个 1000 位的向量,每一个包含有 20 个左右的随机位数,然后计算循环花费的总时间。对于总共的 100 个向量,花费了将近 4s 的时间。

然后,我尝试使用黑客中 Delight 第五章提到的计数算法“divide-and-conquer”(修改成 Bignum 为 30 位的情况)。

     ((max_bit+29)/30).times do |offset|
       x = (word >> (offset*30)) & 0x3fffffff
       x = x - ((x >> 1) & 0x55555555)
       x = (x & 0x33333333) + ((x >> 2) & 0x33333333)
       x = (x + (x >> 4)) & 0x0f0f0f0f;
       x = x + (x >> 8)
       x = x + (x >> 16)
       count += x & 0x3f
     end


这样运行速度比刚开始的原始算法提升了将近 2.5 倍。

然后,我意识到当我比较这一系列的搜索次数时,仅仅只用到了其中的一小部分字节,向量中的位将变得非常稀少。每一次在循环中每一上面的块将变成很接近 0。所以我添加了这样一个测试用例:

     ((max_bit+29)/30).times do |offset|
       x = (word >> (offset*30)) & 0x3fffffff
       next if x.zero?
       x = x - ((x >> 1) & 0x55555555)
       x = (x & 0x33333333) + ((x >> 2) & 0x33333333)
       x = (x + (x >> 4)) & 0x0f0f0f0f;
       x = x + (x >> 8)
       x = x + (x >> 16)
       count += x & 0x3f
     end


现在,我的代码比最原始的的代码快了将近 7 倍。但是我我的测试正使用的向量包含了 20 位 (超出了1000)。我将它改成定时生成这样的向量:包含 1, 2, 5, 10, 20, 100, 到 900 位。这样将会变得更好:我最终用 1 或 2 个向量中的位等因素来提高了近 15 倍的效率。

但是如果我通过消除零的那块代码,来达到提升速度的,在 bit-twiddling 算法中,我是不是也能够做到同样简单的计算算法呢?我尝试了第三种算法:

     ((max_bit+29)/30).times do |offset|
        x = (word >> (offset*30)) # & 0x3fffffff
        next if x.zero?
        30.times {|i| count += x[i]}
      end


这里内部循环与最原始的计数代码是一样的,但是,我现在的计算位数是 30 作为一块。

这段代码演示了定义这个为 1 位定义摆弄位的代码,并且仅仅比 2 位的代码稍微差劲一点。然后,一旦随着位数的开始增长(过去我给的块大小是 5),效率马上开始变得越来越低了. 在 100 位的时候,它甚至会比原始的计数算法更慢了。

对于我这个特别的应用程序,我能够可能倾向于选择块计算算法。因为这玩弄的 算法在很大的位数目时,将会变得很庞大,并且,我将会需要在以后开始做基于余弦匹配的文档,我抛弃了他。

那么,整个这篇文章的观点是什么呢?

昨天,我发布一篇关于动词重要性博客介绍。它说到:“通常一件事物的真正价值不是事物本身,而是通过这件事物所带来的创造性活动。”Chad Fowler 拣出这一条然后写了这一条非常正确的语句,这句话对演奏家来说是多么准确啊。并且 Brian Marick 在 Chad 的基础上强调了在学习一种创造性活动时“练习”的重要性。

在同一时间,我和 Andy 也讨论过很多他所拥有的的音乐磁带。最原始的设计来帮助演奏家来练习音域和琶音等,这些已经如此流行了以至于他们现在能够压缩一整个曲谱的练习技术。我们正抱怨这个事实:它变得很不像我们之前和其他开发者一样在做的事情:通过参加一些培训课程来帮助他们来练习编程技术。我们仅仅觉得这样课程中的练习并不是程序员实际所做的事情那样。

跳过回到今天的这个早上。在冲凉的时候,我想到了这个,并且意识到我的这小 45 分钟对位计算的探索是一个非常准确的练习过程。我并不是真正担心这篇博客中提到位计算的搜索算法的效率;在实际中,结果反而相当完美。相反,我仅仅就想玩一下代码和体验一种我之前一直没有体验过的一种技术。我用了一种非常简单的方式尝试了一下,一个可控制的环境,并且我尝试多种不同的变化情况(比我在这里列举的情况还多)。同时,我仍然还存在一些可以玩的地方:我想了解块大小的变化影响情况,或者我想看看是不是存在一些其他更加好的位计算算法。

是什么原因造成了这个练习过程呢?好的,我有一些不会被打断的时间。我有一个很想尝试的简单事情,并且我为这件事情尝试很多次。我每次检查结果并进行总结,以便我能够在下一次中得到提高。这样是没有压力的:这个代码非常有效率的工作。这是很有趣的:我每一次改进一小步来前进,这样使得我能保持持续工作的热情。最终,我得到了相对于开始时更多的知识。

更进一步的分析,正是在因为有自由时间才使我能够练习。如果在压力的情况下,如果有一个戒指时间来传递这个博客搜索功能,然后这村子的优化将已经被接受了,然后这个练习将不会再发生。那么这些 45 分钟的无压力时间能够让我玩起来。

那么怎样让我们在实际中这样做呢?我们怎样才能在任何创造性活动中,帮助开发者来做这样的非常需要的练习呢?我也不太清楚,但是我的勇气尝试告诉我需要做两个主要的事情。

首先要做的事情是,每一次都需要在没有压力的情况下。提供一个绿色时间:在一个不需要担心项目的截止日期。它必须是非常接近于放松状态,因为如果你不是放松状态,你不将从实际的练习中学到任何东西。

第二个要做的事情是:帮助你的伙伴学会怎样和代码一起玩:怎么来制作错误,怎么来提高,怎样来反应,并且怎样来评测。这是非常难的:我们总是被训练做正确的事情,为了拿到很高的分数,而不是提高。我大胆的猜测成功都是来自于实际动手行动。

所以,我今天提供挑战是:看你能否在 45 - 60 分钟内,“玩”一小段代码。你不必考虑效率:或许你可以从结构上考虑,或者内存使用情况上,或者接口定义。在最后,它也不会产生任何影响。体验、评测、提高。

练习。

好词好句

gut:n.勇气 内脏 直觉 肠 adj.本能的,直觉的 vt.毁坏(建筑物等)的内部 取出…的内脏

instantaneously:adv.即刻,突如其来地

bemoaning:v.为(某人或某事)抱怨( bemoan的现在分词 ) 悲悼 为…恸哭 哀叹

tail off:变得越来越少[小] 不了了之

spectrum: n.[物理学]谱,光谱:辐射源,能谱 光谱相片 范围 系列,范围,幅度

particular:adj.特别的 详细的 独有的 挑剔的 n.特色,特点 (可分类,列举的)项目 详细情节,细情,细目 某一事项

sparse:adj.稀疏的 稀少的

marshals:n.元帅( marshal的名词复数 ) 典礼官 执法官 消防局长

twiddling:v.(心不在焉地)捻弄( twiddle的现在分词 )

slightly: adv.轻微地,轻轻地 细长地,苗条地

Often the true value of a thing isn’t the thing itself, but instead is the activity that created it.
通常一件事物的真正价值不是事物本身,而是通过这件事物所带来的创造性活动。

译后感

这文章提到的代码完全看不懂,但不影响文章表达的观点。作为一个优秀的开发者,不应该仅仅只考虑完成任务而已。而应该学会和代码一起“玩”,多练习,思考怎么提高效率。

文中的一些句子可能翻译错误,翻译还是挺难的:比如:

I needed to be able to perform a bitwise AND between each of these vectors and a vector containing the search terms, and then count the number of one-bits in the result.




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值