作者归档:admin

2012年度总结

在2011年的年度总结有说过:“以前没有写总结的习惯,那么从2011开始吧。”,今天算是第二年了。时间真的是把杀刀,咔咔的就过去了,带过了即将结束的青春的尾巴,也许明年就不能过青年节了。

一、2012年总结

1、2012计划实现情况

<< 2011年度总结 >> 总结中,有量化2012的计划:

2012,给个量化的值吧。
1、读书:10+技术书,30+非技术且不含小说
2、工作:把没有实现的那2个想法实现,参与开发,每周至少一个工作日在编码。
3、生活:把1件大事给办了。
4、TIPI;发布3章+

第1项完成,还有多的,虽然看的书比较扯淡。技术没有明显进步,还是局限在一个小地方打转。没有深度和广度的探究,轻慢了,唉。第2项只能算是基本完成,想法实现了大半,又有了新的想法,只是不晓得有没有机会去实现了。坚持在工作中参与编码,有时却有些力不从心,打断太多,事儿太杂,也该理理了。第3项圆满完成,虽然过程中有些小波澜,但是结果是好的。第4项貌似没有完成……

2、关于生活

对于我来说,2012是一个很忙的年份,在深圳买了房子,算是深圳人了,在深圳结婚了,后代也算是深圳人了,也许这根就留在深圳了。

深圳的房子贵,关内 2万+ 一平的房子,2个月看了不下30间,有那么一两间合适的,还巨贵。算了,和媳妇商量着是不是去关外买一个。在关外看了两间,都是小复式,都挺喜欢的,于是在5月1号劳动节之前的那个晚上就定了。到年底,到手的小窝总价涨了10多万,直叹媳妇决策英明。给自己住的,也没有太多的讲究,到时候简单买些家具,布置下,这事就算是完了。

结婚。嗯,现在是已婚人士了。
求婚策划了好久,不过因为没有经验,也没有给媳妇买过戒指,在晚上量媳妇手指大小时把媳妇给弄醒了,于是这事就给让她知道了,但是打死也不透露具体时间。终于在其好友的帮忙下,把她约出去后,在家里客厅详细布置了一番,才求婚成功,算是有点惊喜。比较纠结的是在求婚的时候嘴巴还是歪着的。

说完喜事,得说点纠结的事,在<< 那段不堪回首的日子 >> 记录中,已经说过了,这事影响了求婚,影响了结婚,也影响了工作,因为每天都需要请假做冶疗。

这一年基本就三件大事了。

3、关于工作

在5月份因为某些事情写了篇 << 转岗一年总结 >>,这里算是其延续。虽然拉长整个时间轴会发现遇到的困难都不算困难,因为一切都会过去。老大换了一个部门,我也换了一个老大,不晓得将来会是怎样,只是还在那里坚持,不知道是为了什么而坚持。忙,盲,茫?

这一年以来,明显感觉到自己成长了,不再是那个只会编码的程序员了,一些新的认识如下:
(1)对技术管理有了一点新的认识。
技术管理管的技术,管的是技术能力,管的是项目开发。技术是指你所领导的团队的技术规划,正在使用的技术的优化,正在运行的架构调优等。技术能力是指团队成员的技术能力,技术管理更多的是关注成员的技术成长,为其规划成长路线。项目开发是指实际项目过程中对项目采用的技术进行审核,跟进开发过程。
(2)对编程和技术管理的关系的重新认识。
做技术管理需要编程,但不能全部投入到编程中,只能有大概10%到20%的时间在编程上。如果投入时间过多,其结果是你所参与的项目延期,部门工作混乱。并且参与的编程工作尽量不是那种与项目相关,不能让其它人的任务依赖于你的任务。可以是预研性质的代码,或改BUG性质的。
(3)开始学会从更高的层面或不同的角度去看问题。
看问题不能局限于自己的一亩三分地,需要从小部门或大部分,甚至整个公司去考虑某个项目的必要性。老大一直强调要有产品经理的思维,有些许改进,却不甚理想。

还存在问题的地方:
(1)识人的能力
这一年招聘了一些人,有些人还在试用期就走了,什么原因呢?没有想清楚需要的是什么样的人。黄叔说:“交友须胜己,似我不如无”,是要一个技术厉害的,还是一个合适的,技术一般的。能力,态度,孰重?孰轻?可能有人能说会道,实际能力并没有嘴巴厉害;可能有人在面试时态度极好,实际工作态度却大相径庭;如何在面试时把人的能力和态度在一定程度上认清,是一个大的课题。
(2)说话沟通的能力
说话存在两个问题,一个是有时说话声音小,让人听不清,这点我们家领导多次批评了我,却没有明显好转;另一个是在激动时会出现说话中断情况。沟通存在表达的问题,即在表达一个内容时干巴巴的,不够丰满和形象,用一句话来说就是说话太程序员了。
(3)写文档的能力
不管是写WORD,还是写PPT,形式只是外在表现形式,关键是思路。有些东西存在,却无法形成合适的文档表现出来。
(4)技术广度和深度
现在技术到了一个瓶颈点,需要量的积累,这个量不仅仅是知识的量,还有实践的量,另外还需要加强对新知识和新技术的了解和实践。

以上的这些问题都需要时间的积累,在流水的日子里练习,只有不断的练习才会有提高,面试更多的人,多表达,多沟通,多写文档,在这些练习过程中和练习之后,思考你的问题,不断提高。

4、关于学习

读了蛮多书(列表见附录A:2012读书列表),大多不求甚解。读书的目的,一是让自己了解一些经典的东西和一些新的东西,二是让自己养成读书的习惯,三是让自己的思维更系统化。可能读书的ROI不高,却是一个细水长流的活,在将来的某一天你会发现自己的巨大的成长。

TIPI项目在上半还有一些时间兼顾,写了一些东西,却没有太大的改版,也算是去年的遗憾。

二、2013年计划

2013年已经开始了,这三天也狂看书中渡过,算是一个好的开始吧。计划,简单点,量化:

1、过PMP
2、看5+管理 10+技术 10+其它书籍
3、熟悉YII框架的源代码,对其各个模块有清晰的认识(貌似这里没量化了)
4、加强对Linux的认识(量化一些,虐APUE两遍,并结合PHP内核)
5、完成媳妇的两个梦想
6、加强在工作上的掌控力,项目的按时完成率在80%以上。
7、Blog – 平均每月两篇+,这个一直在减少,一部分是觉得写的东西不是自己想要的,有时候文章写了一半,
发现没啥意义,就不写了;一部分是时间更少了,有新的东西需要自己去做。

三、感恩

到感恩环节了。

感谢两边的爸妈,累了半辈子了,养育之恩。感谢一直陪着的兄弟,感谢求婚时帮忙的朋友们,感谢到结婚现场祝福的兄弟姐妹,感谢这一年在我生命中出现的人们,心怀感恩。2013我们还活着。

四、结束语

到这里,应该要写白驹过隙、日月穿梭了。一年就真的过去了,回首望去,荆棘的路上布满了努力 的脚印,新的一年,会有新的开始。

五、附录A 2012读书列表

感谢刀马douban读书列表生成工具

PHP的相似度计算函数:levenshtein

在之前的文章 << PHP中计算字符串相似度的函数 >>中我们介绍了similar_text函数的使用及实现过程。similar_text() 函数主要是用来计算两个字符串的匹配字符的数目,也可以计算两个字符串的相似度(以百分比计)。与 similar_text() 函数相比,我们今天要介绍的 levenshtein() 函数更快。不过,similar_text() 函数能通过更少的必需修改次数提供更精确的结果。在追求速度而少精确度,并且字符串长度有限时可以考虑使用 levenshtein() 函数。

使用说明

先看手册上 levenshtein() 函数的说明:

levenshtein() 函数返回两个字符串之间的 Levenshtein 距离。

Levenshtein 距离,又称编辑距离,指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。

例如把 kitten 转换为 sitting:

sitten (k→s)
sittin (e→i)
sitting (→g)

levenshtein() 函数给每个操作(替换、插入和删除)相同的权重。不过,您可以通过设置可选的 insert、replace、delete 参数,来定义每个操作的代价。

语法:

levenshtein(string1,string2,insert,replace,delete)

参数 描述

  • string1 必需。要对比的第一个字符串。
  • string2 必需。要对比的第二个字符串。
  • insert 可选。插入一个字符的代价。默认是 1。
  • replace 可选。替换一个字符的代价。默认是 1。
  • delete 可选。删除一个字符的代价。默认是 1。

提示和注释

  • 如果其中一个字符串超过 255 个字符,levenshtein() 函数返回 -1。
  • levenshtein() 函数对大小写不敏感。
  • levenshtein() 函数比 similar_text() 函数更快。不过,similar_text() 函数提供需要更少修改的更精确的结果。

例子

<?php
    echo levenshtein("Hello World","ello World");
    echo "<br />";
    echo levenshtein("Hello World","ello World",10,20,30);
    ?>

输出: 1 30

源码分析

levenshtein() 属于标准函数,在/ext/standard/目录下有专门针对此函数实现的文件:levenshtein.c。

levenshtein()会根据参数个数选择实现方式,针对参数为2和参数为5的情况,都会调用 reference_levdist() 函数计算距离。其不同在于对后三个参数,参数为2时,使用默认值1。

并且在实现源码中我们发现了一个在文档中没有说明的情况: levenshtein() 函数还可以传递三个参数,其最终会调用 custom_levdist() 函数。它将第三个参数作为自定义函数的实现,其调用示例如下:

 echo levenshtein("Hello World","ello World", 'strsub');

执行会报Warning: The general Levenshtein support is not there yet。这是因为现在这个方法还没有实现,仅仅是放了一个坑在那。

reference_levdist() 函数的实现算法是一个经典的DP问题。

给定两个字符串x和y,求最少的修改次数将x变成y。修改的规则只能是如下三种之一:删除、插入、改变。
用a[i][j]表示把x的前i个字符变成y的前j个字符所需的最少操作次数,则状态转移方程为:

当x[i]==y[j]时:a[i][j]  = min(a[i-1][j-1], a[i-1][j]+1, a[i][j-1]+1);
当x[i]!=y[j]时:a[i][j] =  min(a[i-1][j-1], a[i-1][j], a[i][j-1])+1;

在用状态转移方程前,我们需要初始化(n+1)(m+1)的矩阵d,并让第一行和列的值从0开始增长。 扫描两字符串(nm级的),对比字符,使用状态转移方程,最终$a[$l1][$l2]为其结果。

简单实现过程如下:

<?PHP
    $s1 = "abcdd";
    $l1 = strlen($s1);
    $s2 = "aabbd";
    $l2 = strlen($s2);
 
 
    for ($i = 0; $i < $l1; $i++) {
        $a[0][$i + 1] = $i + 1;
    }
    for ($i = 0; $i < $l2; $i++) {
        $a[$i + 1][0] = $i + 1;
    }
 
    for ($i = 0; $i < $l2; $i++) {
        for ($j = 0; $j < $l1; $j++) {
            if ($s2[$i] == $s1[$j]) {
                $a[$i + 1][$j + 1] = min($a[$i][$j], $a[$i][$j + 1] + 1, $a[$i + 1][$j] + 1);
            }else{
                $a[$i + 1][$j + 1] = min($a[$i][$j], $a[$i][$j + 1], $a[$i + 1][$j]) + 1;
            }
        }
    }
 
    echo $a[$l1][$l2];
    echo "\n";
    echo levenshtein($s1, $s2);

在PHP的实现中,实现者在注释中很清楚的标明:此函数仅优化了内存使用,而没有考虑速度,从其实现算法看,时间复杂度为O(m×n)。其优化点在于将上面的状态转移方程中的二维数组变成了两个一组数组。简单实现如下:

<?PHP
    $s1 = "abcjfdkslfdd";
    $l1 = strlen($s1);
    $s2 = "aab84093840932bd";
    $l2 = strlen($s2);
 
    $dis = 0;
    for ($i = 0; $i <= $l2; $i++){
        $p1[$i] = $i;
    }
 
    for ($i = 0; $i < $l1; $i++){
        $p2[0] = $p1[0] + 1;
 
        for ($j = 0; $j < $l2; $j++){
            if ($s1[$i] == $s2[$j]){
                $dis = min($p1[$j], $p1[$j + 1] + 1, $p2[$j] + 1);
            }else{
                $dis = min($p1[$j] + 1, $p1[$j + 1] + 1, $p2[$j] + 1);  // 注意这里最后一个参数为$p2  
            }
            $p2[$j + 1] = $dis;
        }
        $tmp = $p1;
        $p1 = $p2;
        $p2 = $tmp;  
    }
 
    echo "\n";
    echo $p1[$l2];
    echo "\n";
    echo levenshtein($s1, $s2);

如上为PHP内核开发者对前面经典DP的优化,其优化点在于不停的复用两个一维数组,一个记录上次的结果,一个记录这一次的结果。如果按照PHP的参数,分别给三个操作赋值不同的值,在上面的算法中将对应的1变成操作对应的值就可以了。 min函数的第一个参数对应的是修改,第二个参数对应的是删除,第三个参数对应的是添加。

Levenshtein distance说明

Levenshtein distance最先是由俄国科学家Vladimir Levenshtein在1965年发明,用他的名字命名。不会拼读,可以叫它edit distance(编辑距离)。Levenshtein distance可以用来:

  • Spell checking(拼写检查)
  • Speech recognition(语句识别)
  • DNA analysis(DNA分析)
  • Plagiarism detection(抄袭检测) LD用mn的矩阵存储距离值。

HTTP缓存算法

HTTP协议缓存的目标是去除许多情况下对于发送请求的需求和去除许多情况下发送完整请求的需求。以不发送请求或减少请求传输的数据量来优化整个HTTP架构,此目标的实现可以产生如下好处:

  • 减少网络传输的冗余信息量
  • 缓解网络瓶颈的问题
  • 降低对原始服务器的请求量
  • 减少了传送距离,降低了因为距离而产生的时延

缓存基本处理过程包括七个步骤。

  1. 接收 – 缓存从网络中读取抵达的请求报文
  2. 解析 – 缓存对报文进行解析,提取出URL和各种首部
  3. 查询 – 缓存查看是否有本地副本可用,如果没有,就获取一份副本,并保存在本地
  4. 新鲜度检测 – 缓存查看已缓存副本是否足够新鲜,如果不是,就询问服务器是否有任何更新
  5. 创建响应 – 缓存会用新的首部和已缓存主体来构建一条响应报文
  6. 发送 – 缓存通过网络将响应发回给客户端
  7. 日志 – 缓存可选地创建一个日志文件条目来描述这个事务

这里的缓存可以是本地客户端缓存,也可以是代理缓存之类的公共缓存。

HTTP缓存模型

HTTP缓存可以在不依赖服务器记住有哪些缓存拥有文档副本,而实现文档的一致。这些机制称为文档过期(document expiration)和服务器再验证(server revalidation),也可以称它们为截止模型和证实模型。

截止模型是HTTP请求中带上标记文档的过期时间,HTTP协议中使用如下两个字段标记过期时间:

  • Expires字段 – 指定一个绝对的过期日期。
  • Cache-control:max-age – 定义文档的最大使用期,从第一次生成文档到文档不再新鲜,无法使用为止,最大的合法生存时间(单位为s)

仅仅使用截止模型还不够,即使文档过期了,也并不意味着当前文档和原始服务器的文档不一致了。此时就到证实模型大显身手的时候了。证实模型需要询问原始服务器文档是否发生了变化。其依赖于HTTP协议的如下字段:

  • If-Modified-Since字段 – 如果从指定日期之后文档被修改了,就执行请求的方法。可以与Last-modified服务器响应首部配合使用。它告诉服务器只有在客户端缓存了对象的副本后,又服务器对其进行了修改的情况下,才在回复中发送此对象。如果服务器对象没有修改,返回304 Not Modified。如果服务器修改了此对象,发送此对象,返回200 OK。如果服务器删除了些对象,返回404 Not Found。
  • If-None-Match字段 – 服务器可以为文档提供特殊的标签(ETag),如果此标签与服务器的标签不一样,就会执行请求的方法。

如果服务器应答中包括一个ETag,又包括一个Last-Mofidied值,则客户端在发送请求时使用两种证实机制,并且只有当两种证实机制都满足时才会返回304 Not Modified。

缓存在新鲜度检测时,只需要计算两个值:已缓存副本的使用期和已缓存副本的新鲜生存期。

HTTP缓存使用期算法

响应的使用期是服务器发布响应(或通过证实模型再验证)之后经过的总时间。使用期包括了因特网中传输的时间,在中间节点缓存的时间,以及在本地缓存中的停留时间。

       /*
       * age_value 当代理服务器用自己的头部去响应请求时,Age标明实体产生到现在多长时间了。
       * date_value HTTP 服务器应答中的Date字段 原始服务器
       * request_time 缓存的请求时间
       * response_time 缓存获取应答的时间
       * now 当前时间
       */
 
      apparent_age = max0, response_time - date_value); //缓存收到响应时响应的年龄 处理时钟偏差存在时,可能为负的情况
 
      corrected_received_age = max(apparent_age, age_value);  //  容忍Age首部的错误
 
      response_delay = response_time - request_time; // 处理网络时延,导致结果保守
 
      corrected_initial_age = corrected_received_age + response_delay;
 
      resident_time = now - response_time; // 本地的停留时间,即收到响应到现在的时间间隔
 
      current_age   = corrected_initial_age + resident_time;

因此,完整的使用期计算算法是通过查看Date首部和Age首部来判断响应已使用的时间,再记录其在本地缓存中的停留时间就是总的使用期。除此之外,HTTP协议对时钟偏差和网络时延进行了一补偿,特别是其对网络时延的补偿,可能会重复计算已使用的时间,从而使整个算法产生保守的结果。这种保守的效果时,如果出错了,算法只会使文档看起来比实际使用期要老,并引发再验证。

HTTP缓存新鲜度算法

通过已缓存文档的使用期,根据服务器和客户端限制来计算新鲜生存期,就可以确定已缓存的文档是否新鲜。已缓存文档的使用期在前面已经介绍过了,这小节我们来看看新鲜生存期的计算。

为了确定一条响应是保鲜的(fresh)还是陈旧的(stale),我们需要将其保鲜寿命(freshness lifetime)和年龄(age)进行比较。年龄的计算见13.2.3节,本节讲解怎样计算保鲜寿命,以及判定一个响应是否已经过期。在下面的讨论中,数值可以用任何适于算术操作的形式表示。

与此相关的首部字段包括(按优先级从高到低): Cache-Control字段中“max-age”控制指令的值、Expires、Last-Modified、默认最小的生存期。用PHP代码体现如下:

    /**
     * $heuristic 启发式过期值应不大于从那个时间开始到现在这段时间间隔的某个分数
     * $Max_Age_value_set  是否存在Max_Age值  Cache-Control字段中“max-age”控制指令的值
     * $Max_Age_value  Max_Age值
     * $Expires_value_set 是否存在Expires值
     * $Expires_value Expires值
     * $Date_value Date头部
     * $default_cache_min_lifetime 
     * $default_cache_max_lifetime
     */
    function server_freshness_limit() {
        global $Max_Age_value_set, $Max_Age_value;
        global $Expires_value_set, $Expires_value;
        global $Date_value, $default_cache_min_lifetime, $default_cache_max_lifetime;
 
        $factor = 0.1; //典型设置为10%
 
        $heuristic = 0; //  启发式 默认为0
 
        if ($Max_Age_value_set) {   // 优先级一为 Max_Age
            $freshness_lifetime = $Max_Age_value;
        }elseif($Expires_value_set) {  //   优先级二为Expires
            $freshness_lifetime = $Expires_value - $Date_value;
        }elseif($Last_Modified_value_set) { //  优先级三为Last_Modified
            $freshness_lifetime = (int)($factor * max(0, $Last_Modified_value - $Date_value));
            $heuristic = 1; //  启发式
        }else{  
            $freshness_lifetime = $default_cache_min_lifetime;
            $heuristic = 1; //  启发式
        }
 
        if ($heuristic) {
            $freshness_lifetime = $freshness_lifetime > $default_cache_max_lifetime ? $default_cache_max_lifetime : $freshness_lifetime;
            $freshness_lifetime = $freshness_lifetime < $default_cache_min_lifetime ? $default_cache_min_lifetime : $freshness_lifetime;
        }
 
        return $freshness_lifetime;
 
    }

计算响应是否过期非常简单: response_is_fresh = (server_freshness_limit() > current_age)

以此为《HTTP权威指南》第七章读书笔记。