近期在工作中發(fā)現(xiàn),許多同事,尤其是我們的PHP開發(fā)者,基本不會(huì)用Linux/unix下的快捷方式,嚴(yán)重影響工作效率,所以特撰寫此文,每個(gè)用法后我會(huì)詳細(xì)注釋。
下述所有命令在Linux/unix的shell下有效,這里以bash為主。如有出入,以你自己的服務(wù)器為準(zhǔn)。本文所指的Linux主要指RHEL/CentOS,unix指的是FreeBSD,這也是服務(wù)器中用得最多的版本。
Ctrl + a 切換到命令行開始
這個(gè)操作跟Home實(shí)現(xiàn)的結(jié)果一樣的,但Home在某些unix環(huán)境下無法使用,便可以使用這個(gè)組合;在Linux下的vim,這個(gè)也是有效的;另外,在windows的許多文件編輯器里,這個(gè)也是有效的。
Ctrl + e 切換到命令行末尾
這個(gè)操作跟END實(shí)現(xiàn)的結(jié)果一樣的,但End鍵在某些unix環(huán)境下無法使用,便可以使用這個(gè)組合;在Linux下的vim,這個(gè)也是有效的;另外,在windows的許多文件編輯器里,這個(gè)也是有效的。
Ctrl + l 清除屏幕內(nèi)容,效果等同于clear
Ctrl + u 清除剪切光標(biāo)之前的內(nèi)容
這個(gè)命令很有用,在nslookup里也是有效的。我有時(shí)看見同事一個(gè)字一個(gè)字的刪除shell命令,十分崩潰!其實(shí)完全可以用一個(gè)Ctrl + u搞定。
Ctrl + k 剪切清除光標(biāo)之后的內(nèi)容
Ctrl + y 粘貼剛才所刪除的字符
此命令比較強(qiáng)悍,刪除的字符有可能是幾個(gè)字符串,但極有可能是一行命令。
Ctrl + r 在歷史命令中查找 (這個(gè)非常好用,輸入關(guān)鍵字就調(diào)出以前的命令了)
這個(gè)命令我強(qiáng)烈推薦,有時(shí)history比較多時(shí),想找一個(gè)比較復(fù)雜的,直接在這里,shell會(huì)自動(dòng)查找并調(diào)用,方便極了
Ctrl + c 終止命令
Ctrl + d 退出shell,logout
Ctrl + z 轉(zhuǎn)入后臺(tái)運(yùn)行
不過,由Ctrl + z轉(zhuǎn)入后臺(tái)運(yùn)行的進(jìn)程在當(dāng)前用戶退出后就會(huì)終止,所以用這個(gè)不如用nohup命令&,因?yàn)閚ohup命令的作用就是用戶退出之后進(jìn)程仍然繼續(xù)運(yùn)行,而現(xiàn)在許多腳本和命令都要求在root退出時(shí)仍然有效。
下面再被充下大家不是太熟悉,我用得比較多的操作方式:
!! 重復(fù)執(zhí)行最后一條命令
history 顯示你所有執(zhí)行過的編號(hào)+歷史命令。這個(gè)可以配合!編輯來執(zhí)行某某命令
↑(Ctrl+p) 顯示上一條命令
↓(Ctrl+n) 顯示下一條命令
!$ 顯示系統(tǒng)最近的一條參數(shù)
最后這個(gè)比較有用,比如我先用cat /etc/sysconfig/network-scripts/ifconfig-eth0,然后我想用vim編輯。一般的做法是先用↑ 顯示最后一條命令,然后用Home移動(dòng)到命令最前,刪除cat,然后再輸入vim命令。其實(shí)完全可以用vim !$來代替。
開發(fā)和管理員的話,掌握以上用法后,基本上工作就很有效率了;用到最后,你會(huì)不經(jīng)意發(fā)現(xiàn),彈指之間,許多復(fù)雜的指令你會(huì)很輕松的搞定。
附錄:Linux下的桌面環(huán)境的快捷方式
以下指令在Linux/unix的桌面環(huán)境(gnome)下有效,如有出入以你自己的服務(wù)器為準(zhǔn):
Alt + F1 類似Windows下的Win鍵,在GNOME中打開"應(yīng)用程序"菜單(Applications)
Alt + F2 類似Windows下的Win + R組合鍵,在GNOME中運(yùn)行應(yīng)用程序
Ctrl + Alt + D 類似Windows下的Win + D組合鍵,顯示桌面
Ctrl + Alt + L 鎖定桌面并啟動(dòng)屏幕保護(hù)程序
Alt + Tab 同Windows下的Alt + Tab組合鍵,在不同程序窗口間切換
PrintScreen 全屏抓圖
Alt + PrintScreen 當(dāng)前窗口抓圖
Ctrl + Alt + → / ← 在不同工作臺(tái)間切換
Ctrl + Alt + Shift + → / ← 移動(dòng)當(dāng)前窗口到不同工作臺(tái)
Ctrl+Alt+Shift+Fn 終端N或模擬終端N(n和N為數(shù)字1-6)
Ctrl+Alt+Shift+F7 返回桌面
Ctrl+Alt+Shift+F8 未知(終端或模擬終端)
窗口操作快捷鍵
Alt + F4 關(guān)閉窗口
Alt + F5 取消最大化窗口 (恢復(fù)窗口原來的大小)
Alt + F7 移動(dòng)窗口 (注: 在窗口最大化的狀態(tài)下無效)
Alt + F8 改變窗口大小 (注: 在窗口最大化的狀態(tài)下無效)
Alt + F9 最小化窗口
Alt + F10 最大化窗口
Alt + Space 打開窗口的控制菜單 (點(diǎn)擊窗口左上角圖標(biāo)出現(xiàn)的菜單)
應(yīng)用程序中的常用快捷鍵
下面這些并不適用于所有程序。可以和Windows下的快捷鍵類比下:
Ctrl+N 新建窗口
Ctrl+X 剪切
Ctrl+C 復(fù)制
Ctrl+V 粘貼
Ctrl+Z 撤銷上一步操作
Ctrl+Shift+Z 重做剛撤銷的一步操作
Ctrl+S 保存
文件瀏覽器
Ctrl+H 顯示隱藏文件(切換鍵)
Ctrl+T 新建標(biāo)簽
Ctrl+Page Up 上一個(gè)標(biāo)簽
Ctrl+Page Down 下一個(gè)標(biāo)簽
Alt+N 切換到第N個(gè)標(biāo)簽(N為數(shù)字)
打開終端的方式:
1.鼠標(biāo)點(diǎn)右鍵--terminal,即可打開。
2.點(diǎn)任務(wù)欄的“application”里面的“terminal”打開
3.命令方式:Alt+F2后在出現(xiàn)"運(yùn)行應(yīng)用程序"中輸入x-terminal-emulator(一般在你輸入到x-term后系統(tǒng)會(huì)自己顯示全部)或者輸入“gnome-terminal“
本為轉(zhuǎn)自:http://linux.chinaitlab.com/command/815732.html
不管實(shí)在C還是C++代碼中,typedef這個(gè)詞都不少見,當(dāng)然出現(xiàn)頻率較高的還是在C代碼中。typedef與#define有些相似,但更多的是不同,特別是在一些復(fù)雜的用法上,就完全不同了,看了網(wǎng)上一些C/C++的學(xué)習(xí)者的博客,其中有一篇關(guān)于typedef的總結(jié)還是很不錯(cuò),由于總結(jié)的很好,我就不加修改的引用過來了,以下是引用的內(nèi)容(紅色部分是我自己寫的內(nèi)容)。
用途一:
定義一種類型的別名,而不只是簡(jiǎn)單的宏替換。可以用作同時(shí)聲明指針型的多個(gè)對(duì)象。比如:
char* pa, pb; // 這多數(shù)不符合我們的意圖,它只聲明了一個(gè)指向字符變量的指針,
// 和一個(gè)字符變量;
以下則可行:
typedef char* PCHAR;
PCHAR pa, pb;
這種用法很有用,特別是char* pa, pb的定義,初學(xué)者往往認(rèn)為是定義了兩個(gè)字符型指針,其實(shí)不是,而用typedef char* PCHAR就不會(huì)出現(xiàn)這樣的問題,減少了錯(cuò)誤的發(fā)生。
用途二:
用在舊的C代碼中,幫助struct。以前的代碼中,聲明struct新對(duì)象時(shí),必須要帶上struct,即形式為: struct 結(jié)構(gòu)名對(duì)象名,如:
struct tagPOINT1
{
int x;
int y;
};
struct tagPOINT1 p1;
而在C++中,則可以直接寫:結(jié)構(gòu)名對(duì)象名,即:tagPOINT1 p1;
typedef struct tagPOINT
{
int x;
int y;
}POINT;
POINT p1; // 這樣就比原來的方式少寫了一個(gè)struct,比較省事,尤其在大量使用的時(shí)
候,或許,在C++中,typedef的這種用途二不是很大,但是理解了它,對(duì)掌握以前的舊代
碼還是有幫助的,畢竟我們?cè)陧?xiàng)目中有可能會(huì)遇到較早些年代遺留下來的代碼。
用途三:
用typedef來定義與平臺(tái)無關(guān)的類型。
比如定義一個(gè)叫 REAL 的浮點(diǎn)類型,在目標(biāo)平臺(tái)一上,讓它表示最高精度的類型為:
typedef long double REAL;
在不支持 long double 的平臺(tái)二上,改為:
typedef double REAL;
在連 double 都不支持的平臺(tái)三上,改為:
typedef float REAL;
也就是說,當(dāng)跨平臺(tái)時(shí),只要改下 typedef 本身就行,不用對(duì)其他源碼做任何修改。
標(biāo)準(zhǔn)庫(kù)就廣泛使用了這個(gè)技巧,比如size_t。另外,因?yàn)閠ypedef是定義了一種類型的新別名,不是簡(jiǎn)單的字符串替換,所以它比宏來得穩(wěn)健。
這個(gè)優(yōu)點(diǎn)在我們寫代碼的過程中可以減少不少代碼量哦!
用途四:
為復(fù)雜的聲明定義一個(gè)新的簡(jiǎn)單的別名。方法是:在原來的聲明里逐步用別名替換一部
分復(fù)雜聲明,如此循環(huán),把帶變量名的部分留到最后替換,得到的就是原聲明的最簡(jiǎn)化
版。舉例:
原聲明:void (*b[10]) (void (*)());
變量名為b,先替換右邊部分括號(hào)里的,pFunParam為別名一:
typedef void (*pFunParam)();
再替換左邊的變量b,pFunx為別名二:
typedef void (*pFunx)(pFunParam);
原聲明的最簡(jiǎn)化版:
pFunx b[10];
原聲明:doube(*)() (*e)[9];
變量名為e,先替換左邊部分,pFuny為別名一:
typedef double(*pFuny)();
再替換右邊的變量e,pFunParamy為別名二
typedef pFuny (*pFunParamy)[9];
原聲明的最簡(jiǎn)化版:
pFunParamy e;
理解復(fù)雜聲明可用的“右左法則”:從變量名看起,先往右,再往左,碰到一個(gè)圓括號(hào)
就調(diào)轉(zhuǎn)閱讀的方向;括號(hào)內(nèi)分析完就跳出括號(hào),還是按先右后左的順序,如此循環(huán),直
到整個(gè)聲明分析完。舉例:
int (*func)(int *p);
首先找到變量名func,外面有一對(duì)圓括號(hào),而且左邊是一個(gè)*號(hào),這說明func是一個(gè)指針
;然后跳出這個(gè)圓括號(hào),先看右邊,又遇到圓括號(hào),這說明(*func)是一個(gè)函數(shù),所以
func是一個(gè)指向這類函數(shù)的指針,即函數(shù)指針,這類函數(shù)具有int*類型的形參,返回值
類型是int。
int (*func[5])(int *);
func右邊是一個(gè)[]運(yùn)算符,說明func是具有5個(gè)元素的數(shù)組;func的左邊有一個(gè)*,說明
func的元素是指針(注意這里的*不是修飾func,而是修飾func[5]的,原因是[]運(yùn)算符
優(yōu)先級(jí)比*高,func先跟[]結(jié)合)。跳出這個(gè)括號(hào),看右邊,又遇到圓括號(hào),說明func數(shù)
組的元素是函數(shù)類型的指針,它指向的函數(shù)具有int*類型的形參,返回值類型為int。
這種用法是比較復(fù)雜的,出現(xiàn)的頻率也不少,往往在看到這樣的用法卻不能理解,相信以上的解釋能有所幫助。
*****以上為參考部分,以下為本人領(lǐng)悟部分*****
使用示例:
1.比較一:
#include <iostream>
using namespace std;
typedef int (*A) (char, char);
int ss(char a, char b)
{
cout<<"功能1"<<endl;
cout<<a<<endl;
cout<<b<<endl;
return 0;
}
int bb(char a, char b)
{
cout<<"功能2"<<endl;
cout<<b<<endl;
cout<<a<<endl;
return 0;
}
void main()
{
A a;
a = ss;
a('a','b');
a = bb;
a('a', 'b');
}
2.比較二:
typedef int (A) (char, char);
void main()
{
A *a;
a = ss;
a('a','b');
a = bb;
a('a','b');
}
兩個(gè)程序的結(jié)果都一樣:
功能1
a
b
功能2
b
a
*****以下是參考部分*****
參考自:http://blog.hc360.com/portal/personShowArticle.do?articleId=57527
typedef 與 #define的區(qū)別:
案例一:
通常講,typedef要比#define要好,特別是在有指針的場(chǎng)合。請(qǐng)看例子:
typedef char *pStr1;
#define pStr2 char *;
pStr1 s1, s2;
pStr2 s3, s4;
在上述的變量定義中,s1、s2、s3都被定義為char *,而s4則定義成了char,不是我們
所預(yù)期的指針變量,根本原因就在于#define只是簡(jiǎn)單的字符串替換而typedef則是為一
個(gè)類型起新名字。
案例二:
下面的代碼中編譯器會(huì)報(bào)一個(gè)錯(cuò)誤,你知道是哪個(gè)語句錯(cuò)了嗎?
typedef char * pStr;
char string[4] = "abc";
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++;
是p2++出錯(cuò)了。這個(gè)問題再一次提醒我們:typedef和#define不同,它不是簡(jiǎn)單的
文本替換。上述代碼中const pStr p2并不等于const char * p2。const pStr p2和
const long x本質(zhì)上沒有區(qū)別,都是對(duì)變量進(jìn)行只讀限制,只不過此處變量p2的數(shù)據(jù)類
型是我們自己定義的而不是系統(tǒng)固有類型而已。因此,const pStr p2的含義是:限定數(shù)
據(jù)類型為char *的變量p2為只讀,因此p2++錯(cuò)誤。雖然作者在這里已經(jīng)解釋得很清楚了,可我在這個(gè)地方仍然還是糊涂的,真的希望哪位高手能幫忙指點(diǎn)一下,特別是這一句“只不過此處變量p2的數(shù)據(jù)類型是我們自己定義的而不是系統(tǒng)固有類型而已”,難道自己定義的類型前面用const修飾后,就不能執(zhí)行更改運(yùn)算,而系統(tǒng)定義的類型卻可以?
本文轉(zhuǎn)自:http://www.cnblogs.com/csyisong/archive/2009/01/09/1372363.html
1、Replace函數(shù)替換查找
Replace函數(shù)返回值:返回被替換的字符數(shù)。如果這個(gè)字符串沒有改變則返回零。
CString sTest="aabbccaadd";
int nCount=s.Replace("a","a");
nCount就是你的想要的值
CString::Replaceint Replace( TCHAR chOld, TCHAR chNew );int Replace( LPCTSTR lpszOld, LPCTSTR lpszNew );Return ValueThe number of replaced instances of the character. Zero if the string isn't changed.
2、標(biāo)準(zhǔn)函數(shù) count_if
#include <iostream>#include <string>#include <functional>#include <algorithm>using namespace std;int main( void ){ const string a = " 12113"; cout << count_if( a.begin(), a.end(), bind2nd(equal_to<char>(),'1') ) << endl; return 0;}CString也一樣,但它沒有標(biāo)準(zhǔn)的迭代器,因此需要寫成count_if( (LPCTSTR)a, (LPCTSTR)a+a.GetLength(), bind2nd(equal_to<TCHAR>(),_T('某字符')) )
說說異或運(yùn)算^和他的一個(gè)常用作用。
異或的運(yùn)算方法是一個(gè)二進(jìn)制運(yùn)算:
1^1=0
0^0=0
1^0=1
0^1=1
兩者相等為0,不等為1.
這樣我們發(fā)現(xiàn)交換兩個(gè)整數(shù)的值時(shí)可以不用第三個(gè)參數(shù)。
如a=11,b=9.以下是二進(jìn)制
a=a^b=1011^1001=0010;
b=b^a=1001^0010=1011;
a=a^b=0010^1011=1001;
這樣一來a=9,b=13了。
舉一個(gè)運(yùn)用, 按一個(gè)按鈕交換兩個(gè)mc的位置可以這樣。
mybt.onPress=function()
{
mc1._x=mc1._x^mc2._x;
mc2._x=mc2._x^mc1._x;
mc1._x=mc1._x^mc2._x;
//
mc1._y=mc1._y^mc2._y;
mc2._y=mc2._y^mc1._y;
mc1._y=mc1._y^mc2._y;
}
這樣就可以不通過監(jiān)時(shí)變量來傳遞了。
最后要聲明:只能用于整數(shù)。
整數(shù)在計(jì)算機(jī)中用二進(jìn)制的位來表示,C語言提供一些運(yùn)算符可以直接操作整數(shù)中的位,稱為位運(yùn)算,這些運(yùn)算符的操作數(shù)都必須是整型的。在以后的學(xué)習(xí)中你會(huì)發(fā)現(xiàn),有些信息利用整數(shù)中的某幾個(gè)位來存儲(chǔ),要訪問這些位,僅僅有對(duì)整數(shù)的操作是不夠的,必須借助位運(yùn)算,例如第 2 節(jié) “Unicode和UTF-8” 介紹的UTF-8編碼就是如此,學(xué)完本節(jié)之后你應(yīng)該能自己寫出UTF-8的編碼和解碼程序。本節(jié)首先介紹各種位運(yùn)算符,然后介紹與位運(yùn)算有關(guān)的編程技巧。
在第 3 節(jié) “布爾代數(shù)” 講過邏輯與、或、非運(yùn)算,并列出了真值表,對(duì)于整數(shù)中的位也可以做與、或、非運(yùn)算,C語言提供了按位與(Bitwise AND)運(yùn)算符&、按位或(Bitwise OR)運(yùn)算符|和按位取反(Bitwise NOT)運(yùn)算符~,此外還有按位異或(Bitwise XOR)運(yùn)算符^,我們?cè)?a style="color: #336699; text-decoration: initial;">第 1 節(jié) “為什么計(jì)算機(jī)用二進(jìn)制計(jì)數(shù)” 講過異或運(yùn)算。下面用二進(jìn)制的形式舉幾個(gè)例子。
注 意,&、|、^運(yùn)算符都是要做Usual Arithmetic Conversion的(其中有一步是Integer Promotion),~運(yùn)算符也要做Integer Promotion,所以在C語言中其實(shí)并不存在8位整數(shù)的位運(yùn)算,操作數(shù)在做位運(yùn)算之前都至少被提升為int
型了,上面用8位整數(shù)舉例只是為了書寫方便。比如:
unsigned char c = 0xfc;
unsigned int i = ~c;
計(jì)算過程是這樣的:常量0xfc是int
型的,賦給c
要轉(zhuǎn)成unsigned char
,值不變;c
的十六進(jìn)制表示是fc,計(jì)算~c
時(shí)先提升為整型(000000fc)然后取反,最后結(jié)果是ffffff03。注意,如果把~c
看成是8位整數(shù)的取反,最后結(jié)果就得3了,這就錯(cuò)了。為了避免出錯(cuò),一是盡量避免不同類型之間的賦值,二是每一步計(jì)算都要按上一章講的類型轉(zhuǎn)換規(guī)則仔細(xì)檢查。
移位運(yùn)算符(Bitwise Shift)包括左移<<和右移>>。左移將一個(gè)整數(shù)的各二進(jìn)制位全部左移若干位,例如0xcfffffff3<<2得到0x3fffffcc:
最高兩位的11被移出去了,最低兩位又補(bǔ)了兩個(gè)0,其它位依次左移兩位。但要注意,移動(dòng)的位數(shù)必須小于左操作數(shù)的總位數(shù),比如上面的例子,左邊是unsigned int
型,如果左移的位數(shù)大于等于32位,則結(jié)果是Undefined。移位運(yùn)算符不同于+ - * / ==等運(yùn)算符,兩邊操作數(shù)的類型不要求一致,但兩邊操作數(shù)都要做Integer Promotion,整個(gè)表達(dá)式的類型和左操作數(shù)提升后的類型相同。
復(fù)習(xí)一下第 2 節(jié) “不同進(jìn)制之間的換算” 講過的知識(shí)可以得出結(jié)論,在一定的取值范圍內(nèi),將一個(gè)整數(shù)左移1位相當(dāng)于乘以2 。比如二進(jìn)制11(十進(jìn)制3)左移一位變成110,就是6,再左移一位變成1100,就是12。讀者可以自己驗(yàn)證這條規(guī)律對(duì)有符號(hào)數(shù)和無符號(hào)數(shù)都成立,對(duì)負(fù)數(shù)也成立。當(dāng)然,如果左移改變了最高位(符號(hào)位),那么結(jié)果肯定不是乘以2了,所以我加了個(gè)前提“在一定的取值范圍內(nèi) ”。由于計(jì)算機(jī)做移位比做乘法快得多,編譯器可以利用這一點(diǎn)做優(yōu)化,比如看到源代碼中有i * 8
,可以編譯成移位指令而不是乘法指令。
當(dāng)操作數(shù)是無符號(hào)數(shù)時(shí),右移運(yùn)算的規(guī)則和左移類似,例如0xcfffffff3>>2得到0x33fffffc:
最低兩位的11被移出去了,最高兩位又補(bǔ)了兩個(gè)0,其它位依次右移兩位。和左移類似,移動(dòng)的位數(shù)也必須小于左操作數(shù)的總位數(shù),否則結(jié)果是Undefined。在一定的取值范圍內(nèi),將一個(gè)整數(shù)右移1位相當(dāng)于除以2,小數(shù)部分截掉。
當(dāng)操作數(shù)是有符號(hào)數(shù)時(shí),右移運(yùn)算的規(guī)則比較復(fù)雜:
綜上所述,由于類型轉(zhuǎn)換和移位等問題,用有符號(hào)數(shù)做位運(yùn)算是很不方便的,所以,建議只對(duì)無符號(hào)數(shù)做位運(yùn)算,以減少出錯(cuò)的可能 。
1、下面兩行printf
打印的結(jié)果有何不同?請(qǐng)讀者比較分析一下。%x
轉(zhuǎn)換說明的含義詳見第 2.9 節(jié) “格式化I/O函數(shù)” 。
int i = 0xcffffff3;
printf("%x/n", 0xcffffff3>>2);
printf("%x/n", i>>2);
如果要對(duì)一個(gè)整數(shù)中的某些位進(jìn)行操作,怎樣表示這些位在整數(shù)中的位置呢?可以用掩碼(Mask)來表示。比如掩碼0x0000ff00表示對(duì)一個(gè)32位整數(shù)的8~15位進(jìn)行操作,舉例如下。
1、取出8~15位。
unsigned int a, b, mask = 0x0000ff00;
a = 0x12345678;
b = (a & mask) >> 8; /* 0x00000056 */
這樣也可以達(dá)到同樣的效果:
b = (a >> 8) & ~(~0U << 8);
2、將8~15位清0。
unsigned int a, b, mask = 0x0000ff00;
a = 0x12345678;
b = a & ~mask; /* 0x12340078 */
3、將8~15位置1。
unsigned int a, b, mask = 0x0000ff00;
a = 0x12345678;
b = a | mask; /* 0x1234ff78 */
1、統(tǒng)計(jì)一個(gè)無符號(hào)整數(shù)的二進(jìn)制表示中1的個(gè)數(shù),函數(shù)原型是int countbit(unsigned int x);
。
2、用位操作實(shí)現(xiàn)無符號(hào)整數(shù)的乘法運(yùn)算,函數(shù)原型是unsigned int multiply(unsigned int x, unsigned int y);
。例如:(11011)2 ×(10010)2 =((11011)2 <<1)+((11011)2 <<4)。
3、對(duì)一個(gè)32位無符號(hào)整數(shù)做循環(huán)右移,函數(shù)原型是unsigned int rotate_right(unsigned int x, int n);
。所謂循環(huán)右移就是把低位移出去的部分再補(bǔ)到高位上去,例如rotate_right(0xdeadbeef, 8)
的值應(yīng)該是0xefdeadbe。
1、一個(gè)數(shù)和自己做異或的結(jié)果是0。如果需要一個(gè)常數(shù)0,x86平臺(tái)的編譯器可能會(huì)生成這樣的指令:xorl %eax, %eax
。不管eax
寄存器里的值原來是多少,做異或運(yùn)算都能得到0,這條指令比同樣效果的movl $0, %eax
指令快,直接對(duì)寄存器做位運(yùn)算比生成一個(gè)立即數(shù)再傳送到寄存器要快一些。
2、從異或的真值表可以看出,不管是0還是1,和0做異或保持原值不變,和1做異或得到原值的相反值。可以利用這個(gè)特性配合掩碼實(shí)現(xiàn)某些位的翻轉(zhuǎn),例如:
unsigned int a, b, mask = 1U << 6;
a = 0x12345678;
b = a ^ mask; /* flip the 6th bit */
3、如果a1 ^ a2 ^ a3 ^ ... ^ an 的結(jié)果是1,則表示a1 、a2 、a3 ...an 之中1的個(gè)數(shù)為奇數(shù)個(gè),否則為偶數(shù)個(gè)。這條性質(zhì)可用于奇偶校驗(yàn)(Parity Check),比如在串口通信過程中,每個(gè)字節(jié)的數(shù)據(jù)都計(jì)算一個(gè)校驗(yàn)位,數(shù)據(jù)和校驗(yàn)位一起發(fā)送出去,這樣接收方可以根據(jù)校驗(yàn)位粗略地判斷接收到的數(shù)據(jù)是否有誤。
4、x ^ x ^ y == y,因?yàn)閤 ^ x == 0,0 ^ y == y。這個(gè)性質(zhì)有什么用呢?我們來看這樣一個(gè)問題:交換兩個(gè)變量的值,不得借助額外的存儲(chǔ)空間,所以就不能采用temp = a; a = b; b = temp;
的辦法了。利用位運(yùn)算可以這樣做交換:
a = a ^ b;
b = b ^ a;
a = a ^ b;
分析一下這個(gè)過程。為了避免混淆,把a(bǔ)和b的初值分別記為a0 和b0 。第一行,a = a0 ^ b0
;第二行,把a(bǔ)的新值代入,得到b = b0 ^ a0 ^ b0
,等號(hào)右邊的b0 相當(dāng)于上面公式中的x,a0 相當(dāng)于y,所以結(jié)果為a0 ;第三行,把a(bǔ)和b的新值代入,得到a = a0 ^ b0 ^ a0
,結(jié)果為b0 。注意這個(gè)過程不能把同一個(gè)變量自己跟自己交換,而利用中間變量temp
則可以交換。
1、請(qǐng)?jiān)诰W(wǎng)上查找有關(guān)RAID(Redundant Array of Independent Disks,獨(dú)立磁盤冗余陣列)的資料,理解其實(shí)現(xiàn)原理,其實(shí)就是利用了本節(jié)的性質(zhì)3和4。
2、交換兩個(gè)變量的值,不得借助額外的存儲(chǔ)空間,除了本節(jié)講的方法之外你還能想出什么方法?本節(jié)講的方法不能把同一個(gè)變量自己跟自己交換,你的方法有沒有什么局限性?
本文轉(zhuǎn)自:http://blog.csdn.net/yunyuehu/article/details/5408446#t1
示例代碼:DWORD CCommonFun::GetDesignatedDiskFreeSpace(const CString &szPath)
{
DWORD dwTotalDiskSpace,dwFreeDiskSpace,dwUsedDiskSpace;
ULARGE_INTEGER uiFreeBytesAvailableToCaller;
ULARGE_INTEGER uiTotalNumberOfBytes;
ULARGE_INTEGER uiTotalNumberOfFreeBytes;
if(GetDiskFreeSpaceEx(szPath, &uiFreeBytesAvailableToCaller,
&uiTotalNumberOfBytes,
&uiTotalNumberOfFreeBytes))
{
dwTotalDiskSpace = (DWORD)(uiTotalNumberOfBytes.QuadPart / 1024 / 1024);
dwFreeDiskSpace = (DWORD)(uiFreeBytesAvailableToCaller.QuadPart >> 20);
dwUsedDiskSpace = dwTotalDiskSpace - dwFreeDiskSpace;
TRACE("硬盤%s::總空間%dMB, 已用%dMB, 可用%dMB\n", szPath,
dwTotalDiskSpace, dwUsedDiskSpace, dwFreeDiskSpace);
return dwFreeDiskSpace;
}
return -1;
}
變灰代碼:CMenu* menu = this->GetSystemMenu(FALSE);
menu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
恢復(fù)代碼:CMenu* menu = this->GetSystemMenu(FALSE);
menu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_ENABLED);
MFC另存為和保存對(duì)話框:
CString sPath;
TCHAR szFilters[]=_T("All files(*.*)|*.*||");
CFileDialog dlg(nFlag,NULL,_T(m_strTime),OFN_HIDEREADONLY| OFN_OVERWRITEPROMPT,szFilters);
dlg.m_ofn.lpstrInitialDir=_T(
"c:\\"
);
if(IDOK==dlg.DoModal())
{
sPath=dlg.GetPathName();
}
nFlag值為true時(shí),是保存對(duì)話框,為false時(shí)是另存為對(duì)話框,m_strTime為默認(rèn)文件名字。
MFC彈出選擇目錄對(duì)話框:
LPMALLOC lpMalloc;
if(::SHGetMalloc(&lpMalloc)!=NOERROR)
{
AfxMessageBox("選擇下載目錄操作出錯(cuò)");
return;
}
char szDisplayName[_MAX_PATH];
char szBuffer[_MAX_PATH];
BROWSEINFO browseInfo;
browseInfo.hwndOwner=this->m_hWnd;
browseInfo.pidlRoot=NULL;
browseInfo.pszDisplayName=szDisplayName;
browseInfo.lpszTitle="請(qǐng)選擇下載文件的存儲(chǔ)路徑";
browseInfo.ulFlags=BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS;
browseInfo.lpfn=NULL;
browseInfo.lParam=0;
LPITEMIDLIST lpItemIDList;
if((lpItemIDList=::SHBrowseForFolder(&browseInfo))!=NULL)
{
if(::SHGetPathFromIDList(lpItemIDList,szBuffer))
{
if(szBuffer[0]=='\0')
{
AfxMessageBox("Fail to get directory",MB_ICONSTOP|MB_OK);
return;
}
DownFileDirectory=szBuffer;
}
else
{
AfxMessageBox("Fail to get directory!",MB_ICONSTOP|MB_OK);
return;
}
lpMalloc->Free(lpItemIDList);
lpMalloc->Release();
}
CString strMsg;
strMsg.Format("選擇目錄為:%s",DownFileDirectory);
AfxMessageBox(strMsg);
轉(zhuǎn)自:http://www.itivy.com/ivy/archive/2011/11/24/something-that-architecture-must-be-aware-of.html
對(duì)于大多數(shù)架構(gòu)師而言,“可擴(kuò)展性”在軟件架構(gòu)方面是最虛無縹緲的說法。這毫不奇怪,因?yàn)榭蓴U(kuò)展性正是如今軟件設(shè)計(jì)領(lǐng)域最值得優(yōu)先考慮的要素。然 而,計(jì)算機(jī)科學(xué)家們還無法了解一套單獨(dú)的架構(gòu)如何才能擴(kuò)展至各類應(yīng)用環(huán)境當(dāng)中。相反,我們?cè)跀?shù)量繁多的方案中所設(shè)計(jì)出的可擴(kuò)展性架構(gòu),往往以業(yè)界較為通用 的已知可擴(kuò)展模式及個(gè)人偏好為標(biāo)準(zhǔn)。簡(jiǎn)單來講,打造一套具備可擴(kuò)展性的系統(tǒng)已經(jīng)變得更像是一門藝術(shù)而不單單是技術(shù)。
我們常常會(huì)通過觀摩杰作體會(huì)并學(xué)習(xí)藝術(shù)的精髓,而可擴(kuò)展性也應(yīng)該遵循同樣的路線!
在這篇文章中,我將列出數(shù)款為大家所耳熟能詳?shù)目蓴U(kuò)展性架構(gòu)。通常情況下,架構(gòu)師們完全可以借鑒已知的可擴(kuò)展架構(gòu)模式,進(jìn)而創(chuàng)造出新的可擴(kuò)展架構(gòu)。
- LB (負(fù)載平衡器) + 無共享單位 - 該模型中包含一系列單元,各單元彼此間不共享任何內(nèi)容,且一致指向一個(gè)將輸入文訊按一定條件發(fā)往單元處的負(fù)載平衡器(這構(gòu)成一個(gè)循 環(huán),以負(fù)載等情況為基礎(chǔ))。每個(gè)單元可以是一個(gè)單獨(dú)的節(jié)點(diǎn)或是緊密耦合的節(jié)點(diǎn)所構(gòu)成的集群。用戶可以使用DNS循環(huán)、硬件負(fù)載平衡器或者軟件負(fù)載平衡器達(dá) 成負(fù)載平衡效果。創(chuàng)建一套負(fù)載均衡的層次結(jié)構(gòu),并在其中結(jié)合前面提到的各種負(fù)載平衡器也是可行的。在由Michael Stonebraker撰寫的《 無共享體系架構(gòu)實(shí)例 》一文中,專門討論了此類架構(gòu)。
- LB + 無狀態(tài)節(jié)點(diǎn) + 可擴(kuò)展存儲(chǔ) - 傳統(tǒng)的 三層式Web架構(gòu) 使用的就是這種模型。該模型包括數(shù)個(gè)與可擴(kuò)展存儲(chǔ)交互的無狀態(tài)節(jié)點(diǎn)以及一個(gè)分布于節(jié)點(diǎn)間負(fù)載中的負(fù)載平衡器。在這一模型中,存儲(chǔ)通常作為限制因素存在,但NoSQL存儲(chǔ)則可以利用這套模型創(chuàng)建出具備相當(dāng)可擴(kuò)展性的系統(tǒng)。
- 點(diǎn)對(duì)點(diǎn)架構(gòu) (分布式Hash列表 (簡(jiǎn)稱DHT)以及內(nèi)容尋址網(wǎng)絡(luò)(簡(jiǎn)稱CAN)) -這套模型提供了一些傳統(tǒng)的 可擴(kuò)展算法,這些算法的各個(gè)方面幾乎全部按對(duì)數(shù)進(jìn)行了等比例增加。舉例來說,像Chord、Pastry(特指免費(fèi)版)以及CAN都屬于此類。而以 Cassandra為代表的、基于P2P架構(gòu)的幾款NoSQL系統(tǒng)也是其中的成員。《 展望P2P系統(tǒng)中的數(shù)據(jù) 》一文就深入探討了這類模型的各種細(xì)節(jié)。
- 分布式隊(duì)列 – 這種模型以將隊(duì)列實(shí)施(即先進(jìn)先出交付機(jī)制)作為網(wǎng)絡(luò)服務(wù)處理為基礎(chǔ)。該模型通過JMS隊(duì)列而廣泛得到采用。一般會(huì)遵循這種做法的有任務(wù)隊(duì)列以及通過保持隊(duì)列分級(jí)體系實(shí)現(xiàn)擴(kuò)展性的任務(wù)隊(duì)列版本,后者在負(fù)載無法及時(shí)處理時(shí),任務(wù)會(huì)由低級(jí)層面向高級(jí)層面?zhèn)鬟f。
- 發(fā)布/訂閱模式 - 一般用于通過網(wǎng)絡(luò)向彼此發(fā)布訂閱訊息。《 發(fā)布與訂閱的多面性 》這一經(jīng)典論文中詳細(xì)的介紹這一模型,該模型方面最典型的例子即 NaradaBroker與 EventJava 。
- 小道消息與自然靈感式模型 - 這種模型源自日常生活中小道消息的傳播途徑,也就是每個(gè)節(jié)點(diǎn)將隨機(jī)選擇后續(xù)節(jié)點(diǎn)以交 換信息。正如現(xiàn)實(shí)生活中的實(shí)際反饋,這種八卦型算法在信息傳播方面出奇地迅速。該模型的另一大分支則是受到生物學(xué)影響的啟發(fā)式算法。自然世界中存在著大量 協(xié)調(diào)及擴(kuò)展方面極為卓越的固有算法。舉例來說,螞蟻、人類以及蜜蜂等等,都能夠以最簡(jiǎn)潔的交流方式協(xié)調(diào)好擴(kuò)展性方面的需要。模型中的算法正是借鑒了這些實(shí) 際存在的現(xiàn)象。在論文《 從流行病的蔓延到分布式計(jì)算 》中對(duì)這種模型有著詳盡的敘述。
- 地圖縮小/數(shù)據(jù)流 - 這一概念首先由谷歌公司提出,地圖縮小為工作的描述及執(zhí)行提供了一套可擴(kuò)展的模式。雖然內(nèi)容 簡(jiǎn)單,但它仍然成為聯(lián)機(jī)分析處理方面的首要處理模式。數(shù)據(jù)流則是一種更先進(jìn)的方式,用來表達(dá)執(zhí)行信息;而像Dryad及Pig這樣的項(xiàng)目為數(shù)據(jù)流的執(zhí)行提 供了可擴(kuò)展的框架。論文《 地圖縮小:大型集群上的簡(jiǎn)化數(shù)據(jù)處理 》中設(shè)置了專門的主題,詳細(xì)討論這一內(nèi)容。Apache的Hadoop就是這種模型的代表性產(chǎn)品。
- 責(zé)任樹形圖 - 這種模型打破了遞歸問題的束縛,將整個(gè)流程以樹狀形式加以處理;每個(gè)父節(jié)點(diǎn)將工作下放至子節(jié)點(diǎn)。這種模型擴(kuò)展性強(qiáng),并已經(jīng)被應(yīng)用于數(shù)款可擴(kuò)展性架構(gòu)當(dāng)中。
- 流處理 - 這種模型被用于處理源源不斷的數(shù)據(jù)流及數(shù)據(jù)。這種處理方式通過網(wǎng)絡(luò)中的處理節(jié)點(diǎn)獲得支持(例如Aurora、Twitter Strom以及Apache S4等)。
- 可擴(kuò)展存儲(chǔ) – 該模型的應(yīng)用范圍從數(shù)據(jù)庫(kù)、NoSQL存儲(chǔ)、服務(wù)注冊(cè)到文件系統(tǒng)都有體現(xiàn)。 鏈接中的這篇文章 以可擴(kuò)展性為切入點(diǎn)對(duì)其進(jìn)行了深入討論。
綜上所述,可擴(kuò)展性的實(shí)現(xiàn)只有三種方式,即:分布、緩存及異步處理。前文所提到的各種架構(gòu)事實(shí)上都是把這三種方式進(jìn)行不同組合并加以實(shí)施。而另一方 面,不利于可擴(kuò)展性的因素,除了糟糕的編碼本身,全局性協(xié)調(diào)也起到了重要的影響。簡(jiǎn)單來說,任何一種全局性協(xié)調(diào)都會(huì)限制系統(tǒng)的可擴(kuò)展性。本文中所提到的各 種架構(gòu)也只是在做好了本地性協(xié)調(diào),而非全局性協(xié)調(diào)。
然而,將它們有機(jī)地結(jié)合起來以創(chuàng)建一套極具可擴(kuò)展性的架構(gòu)可不像說起來那么容易,除非我們能找到一種全新的擴(kuò)展模式。不過經(jīng)驗(yàn)告訴我們,比起搞一套全新的架構(gòu),采用為我們所熟知且更易駕馭的可擴(kuò)展性解決方案永遠(yuǎn)是更好的選擇。
搜索質(zhì)量評(píng)估是搜索技術(shù)研究的基礎(chǔ)性工作,也是核心工作之一。評(píng)價(jià)(Metrics)在搜索技術(shù)研發(fā)中扮演著重要角色,以至于任何一種新方法與他們的評(píng)價(jià)方式是融為一體的。
搜索引擎結(jié)果的好壞與否,體現(xiàn)在業(yè)界所稱的在相關(guān)性(Relevance)上。相關(guān)性的定義包括狹義和廣義兩方面,狹義的解釋是:檢索結(jié)果和用戶查詢的相關(guān)程度。而從廣義的層面,相關(guān)性可以理解為為用戶查詢的綜合滿意度。直觀的來看,從用戶進(jìn)入搜索框的那一刻起,到需求獲得滿足為止,這之間經(jīng)歷的過程越順暢,越便捷,搜索相關(guān)性就越好。本文總結(jié)業(yè)界常用的相關(guān)性評(píng)價(jià)指標(biāo)和量化評(píng)價(jià)方法。供對(duì)此感興趣的朋友參考。
Cranfield評(píng)價(jià)體系
A Cranfield-like approach這個(gè)名稱來源于英國(guó)Cranfield University,因?yàn)樵诙兰o(jì)五十年代該大學(xué)首先提出了這樣一套評(píng)價(jià)系統(tǒng):由查詢樣例集、正確答案集、評(píng)測(cè)指標(biāo)構(gòu)成的完整評(píng)測(cè)方案,并從此確立了“評(píng)價(jià)”在信息檢索研究中的核心地位。
Cranfield評(píng)價(jià)體系由三個(gè)環(huán)節(jié)組成:
- 抽取代表性的查詢?cè)~,組成一個(gè)規(guī)模適當(dāng)?shù)募?/li>
- 針對(duì)查詢樣例集合,從檢索系統(tǒng)的語料庫(kù)中尋找對(duì)應(yīng)的結(jié)果,進(jìn)行標(biāo)注(通常人工進(jìn)行)
- 將查詢?cè)~和帶有標(biāo)注信息的語料庫(kù)輸入檢索系統(tǒng),對(duì)系統(tǒng)反饋的檢索結(jié)果,使用預(yù)定義好的評(píng)價(jià)計(jì)算公式,用數(shù)值化的方法來評(píng)價(jià)檢索系統(tǒng)結(jié)果和標(biāo)注的理想結(jié)果的接近程度
查詢?cè)~集合的選取
Cranfield評(píng)價(jià)系統(tǒng)在各大搜索引擎公司內(nèi)有廣泛的應(yīng)用。具體應(yīng)用時(shí),首先需要解決的問題是構(gòu)造一個(gè)測(cè)試用查詢?cè)~集合。
按照Andrei Broder(曾在AltaVista/IBM/Yahoo任職)的研究,查詢?cè)~可分為3類:尋址類查詢(Navigational)、信息類查詢(Informational)、事務(wù)類查詢(Transactional)。對(duì)應(yīng)的比例分別為
Navigational : 12.3% Informational : 62.0% Transactional : 25.7%
為了使得評(píng)估符合線上實(shí)際情況,通常查詢?cè)~集合也會(huì)按比例進(jìn)行選取。通常從線上用戶的Query Log文件中自動(dòng)抽取。
另外查詢集合的構(gòu)造時(shí),除了上述查詢類型外,還可以考慮Query的頻次,對(duì)熱門query(高頻查詢)、長(zhǎng)尾query(中低頻)分別占特定的比例。
另外,在抽取Query時(shí),往往Query的長(zhǎng)短也是一個(gè)待考慮的因素。因?yàn)槎蘱uery(單term的查詢)和長(zhǎng)Query(多Term的查詢)排序算法往往會(huì)有一些不同。
構(gòu)成查詢集合后,使用這些查詢?cè)~,在不同系統(tǒng)(例如對(duì)比百度和Google)或不同技術(shù)間(新舊兩套R(shí)anking算法的環(huán)境)進(jìn)行搜索,并對(duì)結(jié)果進(jìn)行評(píng)分,以決定優(yōu)劣。
附圖:對(duì)同一Query:“社會(huì)保險(xiǎn)法”,各大搜索引擎的結(jié)果示意圖。下面具體談?wù)勗u(píng)分的方法。






Precision-recall(準(zhǔn)確率-召回率方法)
計(jì)算方法
信息檢索領(lǐng)域最廣為人知的評(píng)價(jià)指標(biāo)為Precision-Recall(準(zhǔn)確率-召回率)方法。該方法從提出至今已經(jīng)歷半個(gè)世紀(jì),至今在很多搜索引擎公司的效果評(píng)估中使用。
顧名思義,這個(gè)方法由準(zhǔn)確率和召回率這兩個(gè)相互關(guān)聯(lián)的統(tǒng)計(jì)量構(gòu)成:召回率(Recall)衡量一個(gè)查詢搜索到所有相關(guān)文檔的能力,而準(zhǔn)確率(Precision)衡量搜索系統(tǒng)排除不相關(guān)文檔的能力。(通俗的解釋一下:準(zhǔn)確率就是算一算你查詢得到的結(jié)果中有多少是靠譜的;而召回率表示所有靠譜的結(jié)果中,有多少被你給找回來了)。這兩項(xiàng)是評(píng)價(jià)搜索效果的最基礎(chǔ)指標(biāo),其具體的計(jì)算方法如下。
Precision-recall方法假定對(duì)一個(gè)給定的查詢,對(duì)應(yīng)一個(gè)被檢索的文檔集合和一個(gè)不相關(guān)的文檔集合。這里相關(guān)性被假設(shè)為二元的,用數(shù)學(xué)形式化方法來描述,則是:
A表示相關(guān)文檔集合
A表示不相關(guān)集合
B表示被檢索到的文檔集合
B表示未被檢索到的文檔集合
則單次查詢的準(zhǔn)確率和召回率可以用下述公式來表達(dá):

(運(yùn)算符∩ 表示兩個(gè)集合的交集。|x|符號(hào)表示集合x中的元素?cái)?shù)量)
從上面的定義不難看出,召回率和準(zhǔn)確率的取值范圍均在[0,1]之間。那么不難想象,如果這個(gè)系統(tǒng)找回的相關(guān)越多,那么召回率越高,如果相關(guān)結(jié)果全部都給召回了,那么recall此時(shí)就等于1.0。
| 相關(guān)的 | 不相關(guān) |
被檢索到 | A∩ B | A∩ B |
未被檢索到 | A∩B | A∩B |
Precision-Recall曲線
召回率和準(zhǔn)確率分別反映了檢索系統(tǒng)的兩個(gè)最重要的側(cè)面,而這兩個(gè)側(cè)面又相互制約。因?yàn)榇笠?guī)模數(shù)據(jù)集合中,如果期望檢索到更多相關(guān)的文檔,必然需要“放寬”檢索標(biāo)準(zhǔn),因此會(huì)導(dǎo)致一些不相關(guān)結(jié)果混進(jìn)來,從而使準(zhǔn)確率受到影響。類似的,期望提高準(zhǔn)確率,將不相關(guān)文檔盡量去除時(shí),務(wù)必要執(zhí)行更“嚴(yán)格”的檢索策略,這樣也會(huì)使一些相關(guān)的文檔被排除在外,使召回率下降。
所以為了更清晰的描述兩者間的關(guān)系,通常我們將Precison-Recall用曲線的方式繪制出來,可以簡(jiǎn)稱為P-R diagram。常見的形式如下圖所示。(通常曲線是一個(gè)逐步向下的走勢(shì),即隨著Recall的提高,Precision逐步降低)

P-R的其它形態(tài)
一些特定搜索應(yīng)用,會(huì)更關(guān)注搜索結(jié)果中錯(cuò)誤的結(jié)果。例如,搜索引擎的反作弊系統(tǒng)(Anti-Spam System)會(huì)更關(guān)注檢索結(jié)果中混入了多少條作弊結(jié)果。學(xué)術(shù)界把這些錯(cuò)誤結(jié)果稱作假陽性(False Positive)結(jié)果,對(duì)這些應(yīng)用,通常選擇用虛報(bào)率(Fallout)來統(tǒng)計(jì):

Fallout和Presion本質(zhì)是完全相同的。只是分別從正反兩方面來計(jì)算。實(shí)際上是P-R的一個(gè)變種。
再回到上圖,Presion-Recall是一個(gè)曲線,用來比較兩個(gè)方法的效果往往不夠直觀,能不能對(duì)兩者進(jìn)行綜合,直接反映到一個(gè)數(shù)值上呢?為此IR學(xué)術(shù)界提出了F值度量(F -Measure)的方法。F-Measure通過Presion和Recall的調(diào)和平均數(shù)來計(jì)算,公式為:

其中參數(shù)λε(0,1)調(diào)節(jié)系統(tǒng)對(duì)Precision和Recall的平衡程度。(通常取λ=0.5,此時(shí)
)
這里使用調(diào)和平均數(shù)而不是通常的幾何平均或算術(shù)平均,原因是調(diào)和平均數(shù)強(qiáng)調(diào)較小數(shù)值的重要性,能敏感的反映小數(shù)字的變化,因此更適合用來反映檢索效果。
使用F Measure的好處是只需要一個(gè)單一的數(shù)字就可以總結(jié)系統(tǒng)的檢索效果,便于比較不同搜索系統(tǒng)的整體效果。
P@N方法
點(diǎn)擊因素
傳統(tǒng)的Precision-Recall并不完全適用對(duì)搜索引擎的評(píng)估,原因是搜索引擎用戶的點(diǎn)擊方式有其特殊性,包括:
A 60-65%的查詢點(diǎn)擊了名列搜索結(jié)果前10條的網(wǎng)頁; B 20-25%的人會(huì)考慮點(diǎn)擊名列11到20的網(wǎng)頁; C 僅有3-4%的會(huì)點(diǎn)擊名列搜索結(jié)果中列第21到第30名的網(wǎng)頁
也就是說,絕大部分用戶是不愿意翻頁去看搜索引擎給出的后面的結(jié)果。
而即使在搜索結(jié)果的首頁(通常列出的是前10條結(jié)果),用戶的點(diǎn)擊行為也很有意思,我們通過下面的Google點(diǎn)擊熱圖(Heat Map)來觀察(這個(gè)熱圖在二維搜索結(jié)果頁上通過光譜來形象的表達(dá)不同位置用戶的點(diǎn)擊熱度。顏色約靠近紅色表示點(diǎn)擊強(qiáng)度越高):

從圖中可以看出,搜索結(jié)果的前3條吸引了大量的點(diǎn)擊,屬于熱度最高的部分。也就是說,對(duì)搜蘇引擎來說,最前的幾條結(jié)果是最關(guān)鍵的,決定了用戶的滿意程度。

康乃爾大學(xué)的研究人員通過eye tracking實(shí)驗(yàn)獲得了更為精確的Google搜索結(jié)果的用戶行為分析圖。從這張圖中可以看出,第一條結(jié)果獲得了56.38%的搜索流量,第二條和第三條結(jié)果的排名依次降低,但遠(yuǎn)低于排名第一的結(jié)果。前三條結(jié)果的點(diǎn)擊比例大約為11:3:2 。而前三條結(jié)果的總點(diǎn)擊幾乎分流了搜索流量的80%。
另外的一些有趣的結(jié)論是,點(diǎn)擊量并不是按照順序依次遞減的。排名第七位獲得的點(diǎn)擊是最少的,原因可能在于用戶在瀏覽過程中下拉頁面到底部,這時(shí)候就只顯示最后三位排名網(wǎng)站,第七名便容易被忽略。而首屏最后一個(gè)結(jié)果獲得的注意力(2.55)是大于倒數(shù)第二位的(1.45),原因是用戶在翻頁前,對(duì)最后一條結(jié)果印象相對(duì)較深。搜索結(jié)果頁面第二頁排名第一的網(wǎng)頁(即總排名11位的結(jié)果)所獲得的點(diǎn)擊只有首頁排名第十網(wǎng)站的40%,與首頁的第一條結(jié)果相比,更是只有其1/60至1/100的點(diǎn)擊量。
因此在量化評(píng)估搜索引擎的效果時(shí),往往需要根據(jù)以上搜索用戶的行為特點(diǎn),進(jìn)行針對(duì)性的設(shè)計(jì)。
P@N的計(jì)算方法
P@N本身是Precision@N的簡(jiǎn)稱,指的是對(duì)特定的查詢,考慮位置因素,檢測(cè)前N條結(jié)果的準(zhǔn)確率。例如對(duì)單次搜索的結(jié)果中前5篇,如果有4篇為相關(guān)文檔,則P@5 = 4/5 = 0.8 。
測(cè)試通常會(huì)使用一個(gè)查詢集合(按照前文所述方法構(gòu)造),包含若干條不同的查詢?cè)~,在實(shí)際使用P@N進(jìn)行評(píng)估時(shí),通常使用所有查詢的P@N數(shù)據(jù),計(jì)算算術(shù)平均值,用來評(píng)判該系統(tǒng)的整體搜索結(jié)果質(zhì)量。
N的選取
對(duì)用戶來說,通常只關(guān)注搜索結(jié)果最前若干條結(jié)果,因此通常搜索引擎的效果評(píng)估只關(guān)注前5、或者前3結(jié)果,所以我們常用的N取值為P@3或P@5等。
對(duì)一些特定類型的查詢應(yīng)用,如尋址類的查詢(Navigational Search),由于目標(biāo)結(jié)果極為明確,因此在評(píng)估時(shí),會(huì)選擇N=1(即使用P@1)。舉個(gè)例子來說,搜索“新浪網(wǎng)”、或“新浪首頁”,如果首條結(jié)果不是 新浪網(wǎng)(url:www.sina.com.cn),則直接判該次查詢精度不滿足需求,即P@1=0
MRR
上述的P@N方法,易于計(jì)算和理解。但細(xì)心的讀者一定會(huì)發(fā)現(xiàn)問題,就是在前N結(jié)果中,排序第1位和第N位的結(jié)果,對(duì)準(zhǔn)確率的影響是一樣的。但實(shí)際情況是,搜索引擎的評(píng)價(jià)是和排序位置極為相關(guān)的。即排第一的結(jié)果錯(cuò)誤,和第10位的結(jié)果錯(cuò)誤,其嚴(yán)重程度有天壤之別。因此在評(píng)價(jià)系統(tǒng)中,需要引入位置這個(gè)因素。
MRR是平均排序倒數(shù)(Mean Reciprocal Rank)的簡(jiǎn)稱,MRR方法主要用于尋址類檢索(Navigational Search)或問答類檢索(Question Answering),這些檢索方法只需要一個(gè)相關(guān)文檔,對(duì)召回率不敏感,而是更關(guān)注搜索引擎檢索到的相關(guān)文檔是否排在結(jié)果列表的前面。MRR方法首先計(jì)算每一個(gè)查詢的第一個(gè)相關(guān)文檔位置的倒數(shù),然后將所有倒數(shù)值求平均。例如一個(gè)包含三個(gè)查詢?cè)~的測(cè)試集,前5結(jié)果分別為:
查詢一結(jié)果:1.AN 2.AR 3.AN 4.AN 5.AR 查詢二結(jié)果:1.AN 2.AR 3.AR 4.AR 5.AN 查詢?nèi)Y(jié)果:1.AR 2.AN 3.AN 4.AN 5.AR
其中AN表示不相關(guān)結(jié)果,AR表示相關(guān)結(jié)果。那么第一個(gè)查詢的排序倒數(shù)(Reciprocal Rank)RR1 = 1/2=0.5 ;第二個(gè)結(jié)果RR2 = 1/2 = 0.5 ; 注意倒數(shù)的值不變,即使查詢二獲得的相關(guān)結(jié)果更多。同理,RR3= 1/1 = 1。 對(duì)于這個(gè)測(cè)試集合,最終MRR=(RR1+RR2+RR3)/ 3 = 0.67
然而對(duì)大部分檢索應(yīng)用來說,只有一條結(jié)果無法滿足需求,對(duì)這種情況,需要更合適的方法來計(jì)算效果,其中最常用的是下述MAP方法。
MAP
MAP方法是Mean Average Precison,即平均準(zhǔn)確率法的簡(jiǎn)稱。其定義是求每個(gè)相關(guān)文檔檢索出后的準(zhǔn)確率的平均值(即Average Precision)的算術(shù)平均值(Mean)。這里對(duì)準(zhǔn)確率求了兩次平均,因此稱為Mean Average Precision。(注:沒叫Average Average Precision一是因?yàn)殡y聽,二是因?yàn)闊o法區(qū)分兩次平均的意義)
MAP 是反映系統(tǒng)在全部相關(guān)文檔上性能的單值指標(biāo)。系統(tǒng)檢索出來的相關(guān)文檔越靠前(rank 越高),MAP就應(yīng)該越高。如果系統(tǒng)沒有返回相關(guān)文檔,則準(zhǔn)確率默認(rèn)為0。
例如:假設(shè)有兩個(gè)主題:
主題1有4個(gè)相關(guān)網(wǎng)頁,主題2有5個(gè)相關(guān)網(wǎng)頁。
某系統(tǒng)對(duì)于主題1檢索出4個(gè)相關(guān)網(wǎng)頁,其rank分別為1, 2, 4, 7;
對(duì)于主題2檢索出3個(gè)相關(guān)網(wǎng)頁,其rank分別為1,3,5。
對(duì)于主題1,平均準(zhǔn)確率MAP計(jì)算公式為:
(1/1+2/2+3/4+4/7)/4=0.83。
對(duì)于主題2,平均準(zhǔn)確率MAP計(jì)算公式為:
(1/1+2/3+3/5+0+0)/5=0.45。
則MAP= (0.83+0.45)/2=0.64。”
DCG方法
DCG是英文Discounted cumulative gain的簡(jiǎn)稱,中文可翻譯為“折扣增益值”。DCG方法的基本思想是:
- 每條結(jié)果的相關(guān)性分等級(jí)來衡量
- 考慮結(jié)果所在的位置,位置越靠前的則重要程度越高
- 等級(jí)高(即好結(jié)果)的結(jié)果位置越靠前則值應(yīng)該越高,否則給予懲罰
我們首先來看第一條:相關(guān)性分級(jí)。這里比計(jì)算Precision時(shí)簡(jiǎn)單統(tǒng)計(jì)“準(zhǔn)確”或“不準(zhǔn)確”要更為精細(xì)。我們可以將結(jié)果細(xì)分為多個(gè)等級(jí)。比如常用的3級(jí):Good(好)、Fair(一般)、Bad(差)。對(duì)應(yīng)的分值rel為:Good:3 / Fair:2 / Bad:1 。一些更為細(xì)致的評(píng)估使用5級(jí)分類法:Very Good(明顯好)、Good(好)、Fair(一般)、Bad(差)、Very Bad(明顯差),可以將對(duì)應(yīng)分值rel設(shè)置為:Very Good:2 / Good:1 / Fair:0 / Bad:-1 / Very Bad: -2
評(píng)判結(jié)果的標(biāo)準(zhǔn)可以根據(jù)具體的應(yīng)用來確定,Very Good通常是指結(jié)果的主題完全相關(guān),并且網(wǎng)頁內(nèi)容豐富、質(zhì)量很高。而具體到每條

DCG的計(jì)算公式并不唯一,理論上只要求對(duì)數(shù)折扣因子的平滑性。我個(gè)人認(rèn)為下面的DCG公式更合理,強(qiáng)調(diào)了相關(guān)性,第1、2條結(jié)果的折扣系數(shù)也更合理:

此時(shí)DCG前4個(gè)位置上結(jié)果的折扣因子(Discount factor)數(shù)值為:
i | log2 (i+1) | 1/log2 (i+1) |
1 | 1 | 1 |
2 | 1.59 | 0.63 |
3 | 2 | 0.5 |
4 | 2.32 | 0.43 |
取以2為底的log值也來自于經(jīng)驗(yàn)公式,并不存在理論上的依據(jù)。實(shí)際上,Log的基數(shù)可以根據(jù)平滑的需求進(jìn)行修改,當(dāng)加大數(shù)值時(shí)(例如使用log5 代替log2),折扣因子降低更為迅速,此時(shí)強(qiáng)調(diào)了前面結(jié)果的權(quán)重。
為了便于不同類型的query結(jié)果之間橫向比較,以DCG為基礎(chǔ),一些評(píng)價(jià)系統(tǒng)還對(duì)DCG進(jìn)行了歸一,這些方法統(tǒng)稱為nDCG(即 normalize DCG)。最常用的計(jì)算方法是通過除以每一個(gè)查詢的理想值iDCG(ideal DCG)來進(jìn)行歸一,公式為:

求nDCG需要標(biāo)定出理想情況的iDCG,實(shí)際操作的時(shí)候是異常困難的,因?yàn)槊總€(gè)人對(duì)“最好的結(jié)果”理解往往各不相同,從海量數(shù)據(jù)里選出最優(yōu)結(jié)果是很困難的任務(wù),但是比較兩組結(jié)果哪個(gè)更好通常更容易,所以實(shí)踐應(yīng)用中,通常選擇結(jié)果對(duì)比的方法進(jìn)行評(píng)估。
怎樣實(shí)現(xiàn)自動(dòng)化的評(píng)估?
以上所介紹的搜索引擎量化評(píng)估指標(biāo),在Cranfield評(píng)估框架(Cranfield Evaluation Framework)中被廣泛使用。業(yè)界知名的TREC(文本信息檢索會(huì)議)就一直基于此類方法組織信息檢索評(píng)測(cè)和技術(shù)交流。除了TREC外,一些針對(duì)不同應(yīng)用設(shè)計(jì)的Cranfield評(píng)測(cè)論壇也在進(jìn)行進(jìn)行(如 NTCIR、IREX等)。
但Cranfield評(píng)估框架存在的問題是查詢樣例集合的標(biāo)注上。利用手工標(biāo)注答案的方式進(jìn)行網(wǎng)絡(luò)信息檢索的評(píng)價(jià)是一個(gè)既耗費(fèi)人力、又耗費(fèi)時(shí)間的過程,只有少數(shù)大公司能夠使用。并且由于搜索引擎算法改進(jìn)、運(yùn)營(yíng)維護(hù)的需要,檢索效果評(píng)價(jià)反饋的時(shí)間需要盡量縮短,因此自動(dòng)化的評(píng)測(cè)方法對(duì)提高評(píng)估效率十分重要。最常用的自動(dòng)評(píng)估方法是A/B testing系統(tǒng)。
A/B Testing

A/B Testing系統(tǒng)
A/B testing系統(tǒng)在用戶搜索時(shí),由系統(tǒng)來自動(dòng)決定用戶的分組號(hào)(Bucket id),通過自動(dòng)抽取流量導(dǎo)入不同分支,使得相應(yīng)分組的用戶看到的是不同產(chǎn)品版本(或不同搜索引擎)提供的結(jié)果。用戶在不同版本產(chǎn)品下的行為將被記錄下來,這些行為數(shù)據(jù)通過數(shù)據(jù)分析形成一系列指標(biāo),而通過這些指標(biāo)的比較,最后就形成了各版本之間孰優(yōu)孰劣的結(jié)論。
在指標(biāo)計(jì)算時(shí),又可細(xì)分為兩種方法,一種是基于專家評(píng)分的方法;一種是基于點(diǎn)擊統(tǒng)計(jì)的方法。
專家評(píng)分的方法通常由搜索核心技術(shù)研發(fā)和產(chǎn)品人員來進(jìn)行,根據(jù)預(yù)先設(shè)定的標(biāo)準(zhǔn)對(duì)A、B兩套環(huán)境的結(jié)果給予評(píng)分,獲取每個(gè)Query的結(jié)果對(duì)比,并根據(jù)nDCG等方法計(jì)算整體質(zhì)量。
點(diǎn)擊評(píng)分有更高的自動(dòng)化程度,這里使用了一個(gè)假設(shè):同樣的排序位置,點(diǎn)擊數(shù)量多的結(jié)果質(zhì)量?jī)?yōu)于點(diǎn)擊數(shù)量少的結(jié)果。(即A2表示A測(cè)試環(huán)境第2條結(jié)果,如果A2 > B2,則表示A2質(zhì)量更好)。通俗的說,相信群眾(因?yàn)槿罕姷难劬κ茄┝恋模T谶@個(gè)假設(shè)前提下,我們可以將A/B環(huán)境前N條結(jié)果的點(diǎn)擊率自動(dòng)映射為評(píng)分,通過統(tǒng)計(jì)大量的Query點(diǎn)擊結(jié)果,可以獲得可靠的評(píng)分對(duì)比。
Interleaving Testing
另外2003年由Thorsten Joachims 等人提出的Interleaving testing方法也被廣泛使用。該方法設(shè)計(jì)了一個(gè)元搜索引擎,用戶輸入查詢?cè)~后,將查詢?cè)~在幾個(gè)著名搜索引擎中的查詢結(jié)果隨機(jī)混合反饋給用戶,并收集隨后用戶的結(jié)果點(diǎn)擊行為信息.根據(jù)用戶不同的點(diǎn)擊傾向性,就可以判斷搜索引擎返回結(jié)果的優(yōu)劣,
如下圖所示,將算法A和B的結(jié)果交叉放置,并分流量進(jìn)行測(cè)試,記錄用戶點(diǎn)擊信息。根據(jù)點(diǎn)擊分布來判斷A和B環(huán)境的優(yōu)劣。

Interleaving Testing評(píng)估方法
Joachims同時(shí)證明了Interleaving Testing評(píng)價(jià)方法與傳統(tǒng)Cranfield評(píng)價(jià)方法的結(jié)果具有較高的相關(guān)性。由于記錄用戶選擇檢索結(jié)果的行為是一個(gè)不耗費(fèi)人力的過程,因此可以便捷的實(shí)現(xiàn)自動(dòng)化的搜索效果評(píng)估。
總結(jié)
沒有評(píng)估就沒有進(jìn)步——對(duì)搜索效果的量化評(píng)測(cè),目的是準(zhǔn)確的找出現(xiàn)有搜索系統(tǒng)的不足(沒有哪個(gè)搜索系統(tǒng)是完美的),進(jìn)而一步一個(gè)腳印對(duì)算法、系統(tǒng)進(jìn)行改進(jìn)。本文為大家總結(jié)了常用的評(píng)價(jià)框架和評(píng)價(jià)指標(biāo)。這些技術(shù)像一把把尺子,度量著搜索技術(shù)每一次前進(jìn)的距離。
感謝張凱峰對(duì) 本文的審校。
給InfoQ中文站投稿或者參與內(nèi)容翻譯工作,請(qǐng)郵件至editors@cn.infoq.com。也歡迎大家加入到InfoQ中文站用戶討論組中與我們的編輯和其他讀者 朋友交流。
摘要: MySQL索引背后的數(shù)據(jù)結(jié)構(gòu)及算法原理摘要本文以MySQL數(shù)據(jù)庫(kù)為研究對(duì)象,討論與數(shù)據(jù)庫(kù)索引相關(guān)的一些話題。特別需要說明的是,MySQL支持諸多存儲(chǔ)引擎,而各種存儲(chǔ)引擎對(duì)索引的支持也各不相同,因此MySQL數(shù)據(jù)庫(kù)支持多種索引類型,如BTree索引,哈希索引,全文索引等等。為了避免混亂,本文將只關(guān)注于BTree索引,因?yàn)檫@是平常使用MySQL時(shí)主要打交道的索引,至于哈希索引和全文索引本文暫不討論。文...
閱讀全文