• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            面對現實,超越自己
            逆水行舟,不進則退
            posts - 269,comments - 32,trackbacks - 0

             近期在工作中發現,許多同事,尤其是我們的PHP開發者,基本不會用Linux/unix下的快捷方式,嚴重影響工作效率,所以特撰寫此文,每個用法后我會詳細注釋。

              下述所有命令在Linux/unix的shell下有效,這里以bash為主。如有出入,以你自己的服務器為準。本文所指的Linux主要指RHEL/CentOS,unix指的是FreeBSD,這也是服務器中用得最多的版本。

              Ctrl + a 切換到命令行開始

              這個操作跟Home實現的結果一樣的,但Home在某些unix環境下無法使用,便可以使用這個組合;在Linux下的vim,這個也是有效的;另外,在windows的許多文件編輯器里,這個也是有效的。

              Ctrl + e 切換到命令行末尾

              這個操作跟END實現的結果一樣的,但End鍵在某些unix環境下無法使用,便可以使用這個組合;在Linux下的vim,這個也是有效的;另外,在windows的許多文件編輯器里,這個也是有效的。

              Ctrl + l 清除屏幕內容,效果等同于clear

              Ctrl + u 清除剪切光標之前的內容

              這個命令很有用,在nslookup里也是有效的。我有時看見同事一個字一個字的刪除shell命令,十分崩潰!其實完全可以用一個Ctrl + u搞定。

              Ctrl + k 剪切清除光標之后的內容

              Ctrl + y 粘貼剛才所刪除的字符

              此命令比較強悍,刪除的字符有可能是幾個字符串,但極有可能是一行命令。

              Ctrl + r 在歷史命令中查找 (這個非常好用,輸入關鍵字就調出以前的命令了)

              這個命令我強烈推薦,有時history比較多時,想找一個比較復雜的,直接在這里,shell會自動查找并調用,方便極了

              Ctrl + c 終止命令

              Ctrl + d 退出shell,logout

              Ctrl + z 轉入后臺運行

              不過,由Ctrl + z轉入后臺運行的進程在當前用戶退出后就會終止,所以用這個不如用nohup命令&,因為nohup命令的作用就是用戶退出之后進程仍然繼續運行,而現在許多腳本和命令都要求在root退出時仍然有效。

              下面再被充下大家不是太熟悉,我用得比較多的操作方式:

              !!  重復執行最后一條命令

              history 顯示你所有執行過的編號+歷史命令。這個可以配合!編輯來執行某某命令

              ↑(Ctrl+p) 顯示上一條命令

              ↓(Ctrl+n) 顯示下一條命令

              !$ 顯示系統最近的一條參數

              最后這個比較有用,比如我先用cat /etc/sysconfig/network-scripts/ifconfig-eth0,然后我想用vim編輯。一般的做法是先用↑ 顯示最后一條命令,然后用Home移動到命令最前,刪除cat,然后再輸入vim命令。其實完全可以用vim !$來代替。

              開發和管理員的話,掌握以上用法后,基本上工作就很有效率了;用到最后,你會不經意發現,彈指之間,許多復雜的指令你會很輕松的搞定。

              附錄:Linux下的桌面環境的快捷方式

              以下指令在Linux/unix的桌面環境(gnome)下有效,如有出入以你自己的服務器為準:

              Alt + F1 類似Windows下的Win鍵,在GNOME中打開"應用程序"菜單(Applications)

              Alt + F2 類似Windows下的Win + R組合鍵,在GNOME中運行應用程序

              Ctrl + Alt + D 類似Windows下的Win + D組合鍵,顯示桌面

              Ctrl + Alt + L 鎖定桌面并啟動屏幕保護程序

              Alt + Tab 同Windows下的Alt + Tab組合鍵,在不同程序窗口間切換

              PrintScreen 全屏抓圖

              Alt + PrintScreen 當前窗口抓圖

              Ctrl + Alt + → / ← 在不同工作臺間切換

              Ctrl + Alt + Shift + → / ← 移動當前窗口到不同工作臺

              Ctrl+Alt+Shift+Fn 終端N或模擬終端N(n和N為數字1-6)

              Ctrl+Alt+Shift+F7 返回桌面

              Ctrl+Alt+Shift+F8 未知(終端或模擬終端)

              窗口操作快捷鍵

              Alt + F4 關閉窗口

              Alt + F5 取消最大化窗口 (恢復窗口原來的大小)

              Alt + F7 移動窗口 (注: 在窗口最大化的狀態下無效)

              Alt + F8 改變窗口大小 (注: 在窗口最大化的狀態下無效)

              Alt + F9 最小化窗口

              Alt + F10 最大化窗口

              Alt + Space 打開窗口的控制菜單 (點擊窗口左上角圖標出現的菜單)

              應用程序中的常用快捷鍵

              下面這些并不適用于所有程序。可以和Windows下的快捷鍵類比下:

              Ctrl+N 新建窗口

              Ctrl+X 剪切

              Ctrl+C 復制

              Ctrl+V 粘貼

              Ctrl+Z 撤銷上一步操作

              Ctrl+Shift+Z 重做剛撤銷的一步操作

              Ctrl+S 保存

              文件瀏覽器

              Ctrl+H 顯示隱藏文件(切換鍵)

              Ctrl+T 新建標簽

              Ctrl+Page Up 上一個標簽

              Ctrl+Page Down 下一個標簽

              Alt+N 切換到第N個標簽(N為數字)

                  打開終端的方式:

                  1.鼠標點右鍵--terminal,即可打開。

                  2.點任務欄的“application”里面的“terminal”打開

                  3.命令方式:Alt+F2后在出現"運行應用程序"中輸入x-terminal-emulator(一般在你輸入到x-term后系統會自己顯示全部)或者輸入“gnome-terminal“


            本為轉自:http://linux.chinaitlab.com/command/815732.html

            posted @ 2013-02-25 09:57 王海光 閱讀(1303) | 評論 (0)編輯 收藏

               不管實在C還是C++代碼中,typedef這個詞都不少見,當然出現頻率較高的還是在C代碼中。typedef與#define有些相似,但更多的是不同,特別是在一些復雜的用法上,就完全不同了,看了網上一些C/C++的學習者的博客,其中有一篇關于typedef的總結還是很不錯,由于總結的很好,我就不加修改的引用過來了,以下是引用的內容(紅色部分是我自己寫的內容)。

            用途一:

            定義一種類型的別名,而不只是簡單的宏替換。可以用作同時聲明指針型的多個對象。比如:

            char* pa, pb; // 這多數不符合我們的意圖,它只聲明了一個指向字符變量的指針,

            // 和一個字符變量;

            以下則可行:

            typedef char* PCHAR;

            PCHAR pa, pb;  

            這種用法很有用,特別是char* pa, pb的定義,初學者往往認為是定義了兩個字符型指針,其實不是,而用typedef char* PCHAR就不會出現這樣的問題,減少了錯誤的發生。

            用途二:
            用在舊的C代碼中,幫助struct。以前的代碼中,聲明struct新對象時,必須要帶上struct,即形式為: struct 結構名對象名,如:

            struct tagPOINT1

             {
                int x;

                int y; 
            };

            struct tagPOINT1 p1;

            而在C++中,則可以直接寫:結構名對象名,即:tagPOINT1 p1;

            typedef struct tagPOINT
            {
                int x;

                int y;
            }POINT;

            POINT p1; // 這樣就比原來的方式少寫了一個struct,比較省事,尤其在大量使用的時

            候,或許,在C++中,typedef的這種用途二不是很大,但是理解了它,對掌握以前的舊代

            碼還是有幫助的,畢竟我們在項目中有可能會遇到較早些年代遺留下來的代碼。

            用途三:

            用typedef來定義與平臺無關的類型。

            比如定義一個叫 REAL 的浮點類型,在目標平臺一上,讓它表示最高精度的類型為:

            typedef long double REAL;

            在不支持 long double 的平臺二上,改為:

            typedef double REAL;

            在連 double 都不支持的平臺三上,改為:

            typedef float REAL;

            也就是說,當跨平臺時,只要改下 typedef 本身就行,不用對其他源碼做任何修改。

            標準庫就廣泛使用了這個技巧,比如size_t。另外,因為typedef是定義了一種類型的新別名,不是簡單的字符串替換,所以它比宏來得穩健。
                 這個優點在我們寫代碼的過程中可以減少不少代碼量哦!

            用途四:

            為復雜的聲明定義一個新的簡單的別名。方法是:在原來的聲明里逐步用別名替換一部

            分復雜聲明,如此循環,把帶變量名的部分留到最后替換,得到的就是原聲明的最簡化

            版。舉例: 

             原聲明:void (*b[10]) (void (*)());

            變量名為b,先替換右邊部分括號里的,pFunParam為別名一:

            typedef void (*pFunParam)();

            再替換左邊的變量b,pFunx為別名二:

            typedef void (*pFunx)(pFunParam);

            原聲明的最簡化版:

            pFunx b[10];
             
            原聲明:doube(*)() (*e)[9];

            變量名為e,先替換左邊部分,pFuny為別名一:

            typedef double(*pFuny)();

            再替換右邊的變量e,pFunParamy為別名二

            typedef pFuny (*pFunParamy)[9];

            原聲明的最簡化版:

            pFunParamy e;

            理解復雜聲明可用的“右左法則”:從變量名看起,先往右,再往左,碰到一個圓括號

            就調轉閱讀的方向;括號內分析完就跳出括號,還是按先右后左的順序,如此循環,直

            到整個聲明分析完。舉例:

            int (*func)(int *p);

            首先找到變量名func,外面有一對圓括號,而且左邊是一個*號,這說明func是一個指針

            ;然后跳出這個圓括號,先看右邊,又遇到圓括號,這說明(*func)是一個函數,所以

            func是一個指向這類函數的指針,即函數指針,這類函數具有int*類型的形參,返回值

            類型是int。

            int (*func[5])(int *);

            func右邊是一個[]運算符,說明func是具有5個元素的數組;func的左邊有一個*,說明

            func的元素是指針(注意這里的*不是修飾func,而是修飾func[5]的,原因是[]運算符

            優先級比*高,func先跟[]結合)。跳出這個括號,看右邊,又遇到圓括號,說明func數

            組的元素是函數類型的指針,它指向的函數具有int*類型的形參,返回值類型為int。

            這種用法是比較復雜的,出現的頻率也不少,往往在看到這樣的用法卻不能理解,相信以上的解釋能有所幫助。

            *****以上為參考部分,以下為本人領悟部分*****

            使用示例:

            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');
            }
             

            兩個程序的結果都一樣:

            功能1

            a

            b

            功能2

            b

            a

             

            *****以下是參考部分*****

            參考自:http://blog.hc360.com/portal/personShowArticle.do?articleId=57527

            typedef 與 #define的區別:

            案例一:

            通常講,typedef要比#define要好,特別是在有指針的場合。請看例子:

            typedef char *pStr1;

            #define pStr2 char *;

            pStr1 s1, s2;

            pStr2 s3, s4;
            在上述的變量定義中,s1、s2、s3都被定義為char *,而s4則定義成了char,不是我們

            所預期的指針變量,根本原因就在于#define只是簡單的字符串替換而typedef則是為一

            個類型起新名字。

            案例二:

            下面的代碼中編譯器會報一個錯誤,你知道是哪個語句錯了嗎?

            typedef char * pStr;

            char string[4] = "abc";

            const char *p1 = string;

            const pStr p2 = string;

            p1++;

            p2++;

              是p2++出錯了。這個問題再一次提醒我們:typedef和#define不同,它不是簡單的

            文本替換。上述代碼中const pStr p2并不等于const char * p2。const pStr p2和

            const long x本質上沒有區別,都是對變量進行只讀限制,只不過此處變量p2的數據類

            型是我們自己定義的而不是系統固有類型而已。因此,const pStr p2的含義是:限定數

            據類型為char *的變量p2為只讀,因此p2++錯誤。雖然作者在這里已經解釋得很清楚了,可我在這個地方仍然還是糊涂的,真的希望哪位高手能幫忙指點一下,特別是這一句“只不過此處變量p2的數據類型是我們自己定義的而不是系統固有類型而已”,難道自己定義的類型前面用const修飾后,就不能執行更改運算,而系統定義的類型卻可以?

            本文轉自:
            http://www.cnblogs.com/csyisong/archive/2009/01/09/1372363.html

            posted @ 2013-02-20 17:19 王海光 閱讀(481) | 評論 (0)編輯 收藏
            1、Replace函數替換查找

            Replace函數返回值:返回被替換的字符數。如果這個字符串沒有改變則返回零。

            CString sTest="aabbccaadd";
            int nCount=s.Replace("a","a");

            nCount
            就是你的想要的值

            CString::Replace
            int Replace( TCHAR chOld, TCHAR chNew );
            int Replace( LPCTSTR lpszOld, LPCTSTR lpszNew );

            Return Value
            The number of replaced instances of the character. Zero if the string isn't changed.


            2、標準函數 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也一樣,但它沒有標準的迭代器,因此需要寫成
            count_if( (LPCTSTR)a, (LPCTSTR)a+a.GetLength(), bind2nd(equal_to<TCHAR>(),_T('某字符')) )
            posted @ 2013-02-19 14:34 王海光 閱讀(11930) | 評論 (2)編輯 收藏

            說說異或運算^和他的一個常用作用。
            異或的運算方法是一個二進制運算:
            1^1=0
            0^0=0
            1^0=1
            0^1=1

            兩者相等為0,不等為1.

            這樣我們發現交換兩個整數的值時可以不用第三個參數。
            如a=11,b=9.以下是二進制
            a=a^b=1011^1001=0010;
            b=b^a=1001^0010=1011;
            a=a^b=0010^1011=1001;
            這樣一來a=9,b=13了。


            舉一個運用, 按一個按鈕交換兩個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;
            }

            這樣就可以不通過監時變量來傳遞了。

            最后要聲明:只能用于整數。

             

            1. 位運算 請點評

            整數在計算機中用二進制的位來表示,C語言提供一些運算符可以直接操作整數中的位,稱為位運算,這些運算符的操作數都必須是整型的。在以后的學習中你會發現,有些信息利用整數中的某幾個位來存儲,要訪問這些位,僅僅有對整數的操作是不夠的,必須借助位運算,例如第 2 節 “Unicode和UTF-8” 介紹的UTF-8編碼就是如此,學完本節之后你應該能自己寫出UTF-8的編碼和解碼程序。本節首先介紹各種位運算符,然后介紹與位運算有關的編程技巧。

            1.1. 按位與、或、異或、取反運算 請點評

            第 3 節 “布爾代數” 講過邏輯與、或、非運算,并列出了真值表,對于整數中的位也可以做與、或、非運算,C語言提供了按位與(Bitwise AND)運算符&、按位或(Bitwise OR)運算符|和按位取反(Bitwise NOT)運算符~,此外還有按位異或(Bitwise XOR)運算符^,我們在第 1 節 “為什么計算機用二進制計數” 講過異或運算。下面用二進制的形式舉幾個例子。

            圖 16.1. 位運算

            位運算

            注 意,&、|、^運算符都是要做Usual Arithmetic Conversion的(其中有一步是Integer Promotion),~運算符也要做Integer Promotion,所以在C語言中其實并不存在8位整數的位運算,操作數在做位運算之前都至少被提升為int 型了,上面用8位整數舉例只是為了書寫方便。比如:

            unsigned char c = 0xfc;
            unsigned int i = ~c;

            計算過程是這樣的:常量0xfc是int 型的,賦給c 要轉成unsigned char ,值不變;c 的十六進制表示是fc,計算~c 時先提升為整型(000000fc)然后取反,最后結果是ffffff03。注意,如果把~c 看成是8位整數的取反,最后結果就得3了,這就錯了。為了避免出錯,一是盡量避免不同類型之間的賦值,二是每一步計算都要按上一章講的類型轉換規則仔細檢查。

            1.2. 移位運算 請點評

            移位運算符(Bitwise Shift)包括左移<<和右移>>。左移將一個整數的各二進制位全部左移若干位,例如0xcfffffff3<<2得到0x3fffffcc:

            圖 16.2. 左移運算

            左移運算

            最高兩位的11被移出去了,最低兩位又補了兩個0,其它位依次左移兩位。但要注意,移動的位數必須小于左操作數的總位數,比如上面的例子,左邊是unsigned int 型,如果左移的位數大于等于32位,則結果是Undefined。移位運算符不同于+ - * / ==等運算符,兩邊操作數的類型不要求一致,但兩邊操作數都要做Integer Promotion,整個表達式的類型和左操作數提升后的類型相同。

            復習一下第 2 節 “不同進制之間的換算” 講過的知識可以得出結論,在一定的取值范圍內,將一個整數左移1位相當于乘以2 。比如二進制11(十進制3)左移一位變成110,就是6,再左移一位變成1100,就是12。讀者可以自己驗證這條規律對有符號數和無符號數都成立,對負數也成立。當然,如果左移改變了最高位(符號位),那么結果肯定不是乘以2了,所以我加了個前提“在一定的取值范圍內 ”。由于計算機做移位比做乘法快得多,編譯器可以利用這一點做優化,比如看到源代碼中有i * 8 ,可以編譯成移位指令而不是乘法指令。

            當操作數是無符號數時,右移運算的規則和左移類似,例如0xcfffffff3>>2得到0x33fffffc:

            圖 16.3. 右移運算

            右移運算

            最低兩位的11被移出去了,最高兩位又補了兩個0,其它位依次右移兩位。和左移類似,移動的位數也必須小于左操作數的總位數,否則結果是Undefined。在一定的取值范圍內,將一個整數右移1位相當于除以2,小數部分截掉。

            當操作數是有符號數時,右移運算的規則比較復雜:

            • 如果是正數,那么高位移入0

            • 如果是負數,那么高位移入1還是0不一定,這是Implementation-defined的。對于x86平臺的gcc 編譯器,最高位移入1,也就是仍保持負數的符號位,這種處理方式對負數仍然保持了“右移1位相當于除以2 ”的性質。

            綜上所述,由于類型轉換和移位等問題,用有符號數做位運算是很不方便的,所以,建議只對無符號數做位運算,以減少出錯的可能 。

            習題 請點評

            1、下面兩行printf 打印的結果有何不同?請讀者比較分析一下。%x 轉換說明的含義詳見第 2.9 節 “格式化I/O函數” 。

            int i = 0xcffffff3;
            printf("%x/n", 0xcffffff3>>2);
            printf("%x/n", i>>2);

            1.3. 掩碼 請點評

            如果要對一個整數中的某些位進行操作,怎樣表示這些位在整數中的位置呢?可以用掩碼(Mask)來表示。比如掩碼0x0000ff00表示對一個32位整數的8~15位進行操作,舉例如下。

            1、取出8~15位。

            unsigned int a, b, mask = 0x0000ff00;
            a = 0x12345678;
            b = (a & mask) >> 8; /* 0x00000056 */

            這樣也可以達到同樣的效果:

            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、統計一個無符號整數的二進制表示中1的個數,函數原型是int countbit(unsigned int x); 。

            2、用位操作實現無符號整數的乘法運算,函數原型是unsigned int multiply(unsigned int x, unsigned int y); 。例如:(11011)2 ×(10010)2 =((11011)2 <<1)+((11011)2 <<4)。

            3、對一個32位無符號整數做循環右移,函數原型是unsigned int rotate_right(unsigned int x, int n); 。所謂循環右移就是把低位移出去的部分再補到高位上去,例如rotate_right(0xdeadbeef, 8) 的值應該是0xefdeadbe。

            1.4. 異或運算的一些特性 請點評

            1、一個數和自己做異或的結果是0。如果需要一個常數0,x86平臺的編譯器可能會生成這樣的指令:xorl %eax, %eax 。不管eax 寄存器里的值原來是多少,做異或運算都能得到0,這條指令比同樣效果的movl $0, %eax 指令快,直接對寄存器做位運算比生成一個立即數再傳送到寄存器要快一些。

            2、從異或的真值表可以看出,不管是0還是1,和0做異或保持原值不變,和1做異或得到原值的相反值。可以利用這個特性配合掩碼實現某些位的翻轉,例如:

            unsigned int a, b, mask = 1U << 6;
            a = 0x12345678;
            b = a ^ mask; /* flip the 6th bit */

            3、如果a1 ^ a2 ^ a3 ^ ... ^ an 的結果是1,則表示a1 、a2 、a3 ...an 之中1的個數為奇數個,否則為偶數個。這條性質可用于奇偶校驗(Parity Check),比如在串口通信過程中,每個字節的數據都計算一個校驗位,數據和校驗位一起發送出去,這樣接收方可以根據校驗位粗略地判斷接收到的數據是否有誤。

            4、x ^ x ^ y == y,因為x ^ x == 0,0 ^ y == y。這個性質有什么用呢?我們來看這樣一個問題:交換兩個變量的值,不得借助額外的存儲空間,所以就不能采用temp = a; a = b; b = temp; 的辦法了。利用位運算可以這樣做交換:

            a = a ^ b;
            b = b ^ a;
            a = a ^ b;

            分析一下這個過程。為了避免混淆,把a和b的初值分別記為a0 和b0 。第一行,a = a0 ^ b0 ;第二行,把a的新值代入,得到b = b0 ^ a0 ^ b0 ,等號右邊的b0 相當于上面公式中的x,a0 相當于y,所以結果為a0 ;第三行,把a和b的新值代入,得到a = a0 ^ b0 ^ a0 ,結果為b0 。注意這個過程不能把同一個變量自己跟自己交換,而利用中間變量temp 則可以交換。

            習題 請點評

            1、請在網上查找有關RAID(Redundant Array of Independent Disks,獨立磁盤冗余陣列)的資料,理解其實現原理,其實就是利用了本節的性質3和4。

            2、交換兩個變量的值,不得借助額外的存儲空間,除了本節講的方法之外你還能想出什么方法?本節講的方法不能把同一個變量自己跟自己交換,你的方法有沒有什么局限性?

            本文轉自:http://blog.csdn.net/yunyuehu/article/details/5408446#t1

            posted @ 2013-01-18 11:07 王海光 閱讀(906) | 評論 (0)編輯 收藏
            示例代碼:
            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;
            }
            posted @ 2013-01-16 17:00 王海光 閱讀(2929) | 評論 (0)編輯 收藏
            變灰代碼:
            CMenu*   menu   =   this->GetSystemMenu(FALSE);   
            menu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);

            恢復代碼:
            CMenu*   menu   =   this->GetSystemMenu(FALSE);   
            menu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_ENABLED);
            posted @ 2013-01-16 16:57 王海光 閱讀(919) | 評論 (0)編輯 收藏
            MFC另存為和保存對話框:

            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時,是保存對話框,為false時是另存為對話框,m_strTime為默認文件名字。


            MFC彈出選擇目錄對話框:

            LPMALLOC lpMalloc;
            if(::SHGetMalloc(&lpMalloc)!=NOERROR)
            {
            AfxMessageBox("選擇下載目錄操作出錯");
            return;
            }
            char szDisplayName[_MAX_PATH];
            char szBuffer[_MAX_PATH];
            BROWSEINFO browseInfo;
            browseInfo.hwndOwner=this->m_hWnd;
            browseInfo.pidlRoot=NULL;
            browseInfo.pszDisplayName=szDisplayName;
            browseInfo.lpszTitle="請選擇下載文件的存儲路徑";
            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);
            posted @ 2013-01-10 16:25 王海光 閱讀(1469) | 評論 (0)編輯 收藏

            轉自:http://www.itivy.com/ivy/archive/2011/11/24/something-that-architecture-must-be-aware-of.html

            對于大多數架構師而言,“可擴展性”在軟件架構方面是最虛無縹緲的說法。這毫不奇怪,因為可擴展性正是如今軟件設計領域最值得優先考慮的要素。然 而,計算機科學家們還無法了解一套單獨的架構如何才能擴展至各類應用環境當中。相反,我們在數量繁多的方案中所設計出的可擴展性架構,往往以業界較為通用 的已知可擴展模式及個人偏好為標準。簡單來講,打造一套具備可擴展性的系統已經變得更像是一門藝術而不單單是技術。

            我們常常會通過觀摩杰作體會并學習藝術的精髓,而可擴展性也應該遵循同樣的路線!

            在這篇文章中,我將列出數款為大家所耳熟能詳的可擴展性架構。通常情況下,架構師們完全可以借鑒已知的可擴展架構模式,進而創造出新的可擴展架構。

            1. LB (負載平衡器) + 無共享單位 - 該模型中包含一系列單元,各單元彼此間不共享任何內容,且一致指向一個將輸入文訊按一定條件發往單元處的負載平衡器(這構成一個循 環,以負載等情況為基礎)。每個單元可以是一個單獨的節點或是緊密耦合的節點所構成的集群。用戶可以使用DNS循環、硬件負載平衡器或者軟件負載平衡器達 成負載平衡效果。創建一套負載均衡的層次結構,并在其中結合前面提到的各種負載平衡器也是可行的。在由Michael Stonebraker撰寫的《 無共享體系架構實例 》一文中,專門討論了此類架構。
               
            2. LB + 無狀態節點 + 可擴展存儲 - 傳統的 三層式Web架構 使用的就是這種模型。該模型包括數個與可擴展存儲交互的無狀態節點以及一個分布于節點間負載中的負載平衡器。在這一模型中,存儲通常作為限制因素存在,但NoSQL存儲則可以利用這套模型創建出具備相當可擴展性的系統。
               
            3. 點對點架構 (分布式Hash列表 (簡稱DHT)以及內容尋址網絡(簡稱CAN)) -這套模型提供了一些傳統的 可擴展算法,這些算法的各個方面幾乎全部按對數進行了等比例增加。舉例來說,像Chord、Pastry(特指免費版)以及CAN都屬于此類。而以 Cassandra為代表的、基于P2P架構的幾款NoSQL系統也是其中的成員。《 展望P2P系統中的數據 》一文就深入探討了這類模型的各種細節。
               
            4. 分布式隊列 – 這種模型以將隊列實施(即先進先出交付機制)作為網絡服務處理為基礎。該模型通過JMS隊列而廣泛得到采用。一般會遵循這種做法的有任務隊列以及通過保持隊列分級體系實現擴展性的任務隊列版本,后者在負載無法及時處理時,任務會由低級層面向高級層面傳遞。
               
            5. 發布/訂閱模式 - 一般用于通過網絡向彼此發布訂閱訊息。《 發布與訂閱的多面性 》這一經典論文中詳細的介紹這一模型,該模型方面最典型的例子即 NaradaBroker與 EventJava 
               
            6. 小道消息與自然靈感式模型 - 這種模型源自日常生活中小道消息的傳播途徑,也就是每個節點將隨機選擇后續節點以交 換信息。正如現實生活中的實際反饋,這種八卦型算法在信息傳播方面出奇地迅速。該模型的另一大分支則是受到生物學影響的啟發式算法。自然世界中存在著大量 協調及擴展方面極為卓越的固有算法。舉例來說,螞蟻、人類以及蜜蜂等等,都能夠以最簡潔的交流方式協調好擴展性方面的需要。模型中的算法正是借鑒了這些實 際存在的現象。在論文《 從流行病的蔓延到分布式計算 》中對這種模型有著詳盡的敘述。
               
            7. 地圖縮小/數據流 - 這一概念首先由谷歌公司提出,地圖縮小為工作的描述及執行提供了一套可擴展的模式。雖然內容 簡單,但它仍然成為聯機分析處理方面的首要處理模式。數據流則是一種更先進的方式,用來表達執行信息;而像Dryad及Pig這樣的項目為數據流的執行提 供了可擴展的框架。論文《 地圖縮小:大型集群上的簡化數據處理 》中設置了專門的主題,詳細討論這一內容。Apache的Hadoop就是這種模型的代表性產品。
               
            8. 責任樹形圖 - 這種模型打破了遞歸問題的束縛,將整個流程以樹狀形式加以處理;每個父節點將工作下放至子節點。這種模型擴展性強,并已經被應用于數款可擴展性架構當中。
               
            9. 流處理 - 這種模型被用于處理源源不斷的數據流及數據。這種處理方式通過網絡中的處理節點獲得支持(例如Aurora、Twitter Strom以及Apache S4等)。
               
            10. 可擴展存儲 – 該模型的應用范圍從數據庫、NoSQL存儲、服務注冊到文件系統都有體現。 鏈接中的這篇文章 以可擴展性為切入點對其進行了深入討論。

            綜上所述,可擴展性的實現只有三種方式,即:分布、緩存及異步處理。前文所提到的各種架構事實上都是把這三種方式進行不同組合并加以實施。而另一方 面,不利于可擴展性的因素,除了糟糕的編碼本身,全局性協調也起到了重要的影響。簡單來說,任何一種全局性協調都會限制系統的可擴展性。本文中所提到的各 種架構也只是在做好了本地性協調,而非全局性協調。

            然而,將它們有機地結合起來以創建一套極具可擴展性的架構可不像說起來那么容易,除非我們能找到一種全新的擴展模式。不過經驗告訴我們,比起搞一套全新的架構,采用為我們所熟知且更易駕馭的可擴展性解決方案永遠是更好的選擇。

            posted @ 2013-01-07 16:49 王海光 閱讀(517) | 評論 (0)編輯 收藏

            轉自:http://www.infoq.com/cn/articles/cyw-evaluate-seachengine-result-quality

            前言

            搜索質量評估是搜索技術研究的基礎性工作,也是核心工作之一。評價(Metrics)在搜索技術研發中扮演著重要角色,以至于任何一種新方法與他們的評價方式是融為一體的。


            搜索引擎結果的好壞與否,體現在業界所稱的在相關性(Relevance)上。相關性的定義包括狹義和廣義兩方面,狹義的解釋是:檢索結果和用戶查詢的相關程度。而從廣義的層面,相關性可以理解為為用戶查詢的綜合滿意度。直觀的來看,從用戶進入搜索框的那一刻起,到需求獲得滿足為止,這之間經歷的過程越順暢,越便捷,搜索相關性就越好。本文總結業界常用的相關性評價指標和量化評價方法。供對此感興趣的朋友參考。

            Cranfield評價體系

            A Cranfield-like approach這個名稱來源于英國Cranfield University,因為在二十世紀五十年代該大學首先提出了這樣一套評價系統:由查詢樣例集、正確答案集、評測指標構成的完整評測方案,并從此確立了“評價”在信息檢索研究中的核心地位。

            Cranfield評價體系由三個環節組成:

            1. 抽取代表性的查詢詞,組成一個規模適當的集合
            2. 針對查詢樣例集合,從檢索系統的語料庫中尋找對應的結果,進行標注(通常人工進行)
            3. 將查詢詞和帶有標注信息的語料庫輸入檢索系統,對系統反饋的檢索結果,使用預定義好的評價計算公式,用數值化的方法來評價檢索系統結果和標注的理想結果的接近程度

            查詢詞集合的選取

            Cranfield評價系統在各大搜索引擎公司內有廣泛的應用。具體應用時,首先需要解決的問題是構造一個測試用查詢詞集合。

            按照Andrei Broder(曾在AltaVista/IBM/Yahoo任職)的研究,查詢詞可分為3類:尋址類查詢(Navigational)、信息類查詢(Informational)、事務類查詢(Transactional)。對應的比例分別為

            Navigational : 12.3%  Informational : 62.0%  Transactional : 25.7% 

            為了使得評估符合線上實際情況,通常查詢詞集合也會按比例進行選取。通常從線上用戶的Query Log文件中自動抽取。

            另外查詢集合的構造時,除了上述查詢類型外,還可以考慮Query的頻次,對熱門query(高頻查詢)、長尾query(中低頻)分別占特定的比例。

            另外,在抽取Query時,往往Query的長短也是一個待考慮的因素。因為短query(單term的查詢)和長Query(多Term的查詢)排序算法往往會有一些不同。

            構成查詢集合后,使用這些查詢詞,在不同系統(例如對比百度和Google)或不同技術間(新舊兩套Ranking算法的環境)進行搜索,并對結果進行評分,以決定優劣。

            附圖:對同一Query:“社會保險法”,各大搜索引擎的結果示意圖。下面具體談談評分的方法。

            Precision-recall(準確率-召回率方法)

            計算方法

            信息檢索領域最廣為人知的評價指標為Precision-Recall(準確率-召回率)方法。該方法從提出至今已經歷半個世紀,至今在很多搜索引擎公司的效果評估中使用。

            顧名思義,這個方法由準確率和召回率這兩個相互關聯的統計量構成:召回率(Recall)衡量一個查詢搜索到所有相關文檔的能力,而準確率(Precision)衡量搜索系統排除不相關文檔的能力。(通俗的解釋一下:準確率就是算一算你查詢得到的結果中有多少是靠譜的;而召回率表示所有靠譜的結果中,有多少被你給找回來了)。這兩項是評價搜索效果的最基礎指標,其具體的計算方法如下。

            Precision-recall方法假定對一個給定的查詢,對應一個被檢索的文檔集合和一個不相關的文檔集合。這里相關性被假設為二元的,用數學形式化方法來描述,則是:

            A表示相關文檔集合

            A表示不相關集合

            B表示被檢索到的文檔集合

            B表示未被檢索到的文檔集合

            則單次查詢的準確率和召回率可以用下述公式來表達:

            (運算符∩ 表示兩個集合的交集。|x|符號表示集合x中的元素數量)

            從上面的定義不難看出,召回率和準確率的取值范圍均在[0,1]之間。那么不難想象,如果這個系統找回的相關越多,那么召回率越高,如果相關結果全部都給召回了,那么recall此時就等于1.0。

             

            相關的

            不相關

            被檢索到

            A∩ B

            A∩ B

            未被檢索到

            A∩B

            AB

            Precision-Recall曲線

            召回率和準確率分別反映了檢索系統的兩個最重要的側面,而這兩個側面又相互制約。因為大規模數據集合中,如果期望檢索到更多相關的文檔,必然需要“放寬”檢索標準,因此會導致一些不相關結果混進來,從而使準確率受到影響。類似的,期望提高準確率,將不相關文檔盡量去除時,務必要執行更“嚴格”的檢索策略,這樣也會使一些相關的文檔被排除在外,使召回率下降。

            所以為了更清晰的描述兩者間的關系,通常我們將Precison-Recall用曲線的方式繪制出來,可以簡稱為P-R diagram。常見的形式如下圖所示。(通常曲線是一個逐步向下的走勢,即隨著Recall的提高,Precision逐步降低)

            P-R的其它形態

            一些特定搜索應用,會更關注搜索結果中錯誤的結果。例如,搜索引擎的反作弊系統(Anti-Spam System)會更關注檢索結果中混入了多少條作弊結果。學術界把這些錯誤結果稱作假陽性(False Positive)結果,對這些應用,通常選擇用虛報率(Fallout)來統計:

            Fallout和Presion本質是完全相同的。只是分別從正反兩方面來計算。實際上是P-R的一個變種。

            再回到上圖,Presion-Recall是一個曲線,用來比較兩個方法的效果往往不夠直觀,能不能對兩者進行綜合,直接反映到一個數值上呢?為此IR學術界提出了F值度量(F -Measure)的方法。F-Measure通過Presion和Recall的調和平均數來計算,公式為:

            其中參數λε(0,1)調節系統對Precision和Recall的平衡程度。(通常取λ=0.5,此時 

            這里使用調和平均數而不是通常的幾何平均或算術平均,原因是調和平均數強調較小數值的重要性,能敏感的反映小數字的變化,因此更適合用來反映檢索效果。

            使用F Measure的好處是只需要一個單一的數字就可以總結系統的檢索效果,便于比較不同搜索系統的整體效果。

            P@N方法

            點擊因素

            傳統的Precision-Recall并不完全適用對搜索引擎的評估,原因是搜索引擎用戶的點擊方式有其特殊性,包括:

            A 60-65%的查詢點擊了名列搜索結果前10條的網頁;  B 20-25%的人會考慮點擊名列11到20的網頁;  C 僅有3-4%的會點擊名列搜索結果中列第21到第30名的網頁 

            也就是說,絕大部分用戶是不愿意翻頁去看搜索引擎給出的后面的結果。

            而即使在搜索結果的首頁(通常列出的是前10條結果),用戶的點擊行為也很有意思,我們通過下面的Google點擊熱圖(Heat Map)來觀察(這個熱圖在二維搜索結果頁上通過光譜來形象的表達不同位置用戶的點擊熱度。顏色約靠近紅色表示點擊強度越高):

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

            康乃爾大學的研究人員通過eye tracking實驗獲得了更為精確的Google搜索結果的用戶行為分析圖。從這張圖中可以看出,第一條結果獲得了56.38%的搜索流量,第二條和第三條結果的排名依次降低,但遠低于排名第一的結果。前三條結果的點擊比例大約為11:3:2 。而前三條結果的總點擊幾乎分流了搜索流量的80%。

            另外的一些有趣的結論是,點擊量并不是按照順序依次遞減的。排名第七位獲得的點擊是最少的,原因可能在于用戶在瀏覽過程中下拉頁面到底部,這時候就只顯示最后三位排名網站,第七名便容易被忽略。而首屏最后一個結果獲得的注意力(2.55)是大于倒數第二位的(1.45),原因是用戶在翻頁前,對最后一條結果印象相對較深。搜索結果頁面第二頁排名第一的網頁(即總排名11位的結果)所獲得的點擊只有首頁排名第十網站的40%,與首頁的第一條結果相比,更是只有其1/60至1/100的點擊量。

            因此在量化評估搜索引擎的效果時,往往需要根據以上搜索用戶的行為特點,進行針對性的設計。

            P@N的計算方法

            P@N本身是Precision@N的簡稱,指的是對特定的查詢,考慮位置因素,檢測前N條結果的準確率。例如對單次搜索的結果中前5篇,如果有4篇為相關文檔,則P@5 = 4/5 = 0.8 。

            測試通常會使用一個查詢集合(按照前文所述方法構造),包含若干條不同的查詢詞,在實際使用P@N進行評估時,通常使用所有查詢的P@N數據,計算算術平均值,用來評判該系統的整體搜索結果質量。

            N的選取

            對用戶來說,通常只關注搜索結果最前若干條結果,因此通常搜索引擎的效果評估只關注前5、或者前3結果,所以我們常用的N取值為P@3或P@5等。

            對一些特定類型的查詢應用,如尋址類的查詢(Navigational Search),由于目標結果極為明確,因此在評估時,會選擇N=1(即使用P@1)。舉個例子來說,搜索“新浪網”、或“新浪首頁”,如果首條結果不是 新浪網(url:www.sina.com.cn),則直接判該次查詢精度不滿足需求,即P@1=0

            MRR

            上述的P@N方法,易于計算和理解。但細心的讀者一定會發現問題,就是在前N結果中,排序第1位和第N位的結果,對準確率的影響是一樣的。但實際情況是,搜索引擎的評價是和排序位置極為相關的。即排第一的結果錯誤,和第10位的結果錯誤,其嚴重程度有天壤之別。因此在評價系統中,需要引入位置這個因素。

            MRR是平均排序倒數(Mean Reciprocal Rank)的簡稱,MRR方法主要用于尋址類檢索(Navigational Search)或問答類檢索(Question Answering),這些檢索方法只需要一個相關文檔,對召回率不敏感,而是更關注搜索引擎檢索到的相關文檔是否排在結果列表的前面。MRR方法首先計算每一個查詢的第一個相關文檔位置的倒數,然后將所有倒數值求平均。例如一個包含三個查詢詞的測試集,前5結果分別為:

            查詢一結果:1.AN 2.AR 3.AN 4.AN 5.AR  查詢二結果:1.AN 2.AR 3.AR 4.AR 5.AN  查詢三結果:1.AR 2.AN 3.AN 4.AN 5.AR  

            其中AN表示不相關結果,AR表示相關結果。那么第一個查詢的排序倒數(Reciprocal Rank)RR1 = 1/2=0.5 ;第二個結果RR2 = 1/2 = 0.5 ; 注意倒數的值不變,即使查詢二獲得的相關結果更多。同理,RR3= 1/1 = 1。 對于這個測試集合,最終MRR=(RR1+RR2+RR3)/ 3 = 0.67

            然而對大部分檢索應用來說,只有一條結果無法滿足需求,對這種情況,需要更合適的方法來計算效果,其中最常用的是下述MAP方法。

            MAP

            MAP方法是Mean Average Precison,即平均準確率法的簡稱。其定義是求每個相關文檔檢索出后的準確率的平均值(即Average Precision)的算術平均值(Mean)。這里對準確率求了兩次平均,因此稱為Mean Average Precision。(注:沒叫Average Average Precision一是因為難聽,二是因為無法區分兩次平均的意義)

            MAP 是反映系統在全部相關文檔上性能的單值指標。系統檢索出來的相關文檔越靠前(rank 越高),MAP就應該越高。如果系統沒有返回相關文檔,則準確率默認為0。

            例如:假設有兩個主題:

            主題1有4個相關網頁,主題2有5個相關網頁。

            某系統對于主題1檢索出4個相關網頁,其rank分別為1, 2, 4, 7;

            對于主題2檢索出3個相關網頁,其rank分別為1,3,5。

            對于主題1,平均準確率MAP計算公式為:

            (1/1+2/2+3/4+4/7)/4=0.83。 

            對于主題2,平均準確率MAP計算公式為:

            (1/1+2/3+3/5+0+0)/5=0.45。 

            則MAP= (0.83+0.45)/2=0.64。”

            DCG方法

            DCG是英文Discounted cumulative gain的簡稱,中文可翻譯為“折扣增益值”。DCG方法的基本思想是:

            1. 每條結果的相關性分等級來衡量
            2. 考慮結果所在的位置,位置越靠前的則重要程度越高
            3. 等級高(即好結果)的結果位置越靠前則值應該越高,否則給予懲罰

            我們首先來看第一條:相關性分級。這里比計算Precision時簡單統計“準確”或“不準確”要更為精細。我們可以將結果細分為多個等級。比如常用的3級:Good(好)、Fair(一般)、Bad(差)。對應的分值rel為:Good:3 / Fair:2 / Bad:1 。一些更為細致的評估使用5級分類法:Very Good(明顯好)、Good(好)、Fair(一般)、Bad(差)、Very Bad(明顯差),可以將對應分值rel設置為:Very Good:2 / Good:1 / Fair:0 / Bad:-1 / Very Bad: -2

            評判結果的標準可以根據具體的應用來確定,Very Good通常是指結果的主題完全相關,并且網頁內容豐富、質量很高。而具體到每條

            DCG的計算公式并不唯一,理論上只要求對數折扣因子的平滑性。我個人認為下面的DCG公式更合理,強調了相關性,第1、2條結果的折扣系數也更合理:

            此時DCG前4個位置上結果的折扣因子(Discount factor)數值為:

            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值也來自于經驗公式,并不存在理論上的依據。實際上,Log的基數可以根據平滑的需求進行修改,當加大數值時(例如使用log5 代替log2),折扣因子降低更為迅速,此時強調了前面結果的權重。

            為了便于不同類型的query結果之間橫向比較,以DCG為基礎,一些評價系統還對DCG進行了歸一,這些方法統稱為nDCG(即 normalize DCG)。最常用的計算方法是通過除以每一個查詢的理想值iDCG(ideal DCG)來進行歸一,公式為:

            求nDCG需要標定出理想情況的iDCG,實際操作的時候是異常困難的,因為每個人對“最好的結果”理解往往各不相同,從海量數據里選出最優結果是很困難的任務,但是比較兩組結果哪個更好通常更容易,所以實踐應用中,通常選擇結果對比的方法進行評估。

            怎樣實現自動化的評估?

            以上所介紹的搜索引擎量化評估指標,在Cranfield評估框架(Cranfield Evaluation Framework)中被廣泛使用。業界知名的TREC(文本信息檢索會議)就一直基于此類方法組織信息檢索評測和技術交流。除了TREC外,一些針對不同應用設計的Cranfield評測論壇也在進行進行(如 NTCIR、IREX等)。

            但Cranfield評估框架存在的問題是查詢樣例集合的標注上。利用手工標注答案的方式進行網絡信息檢索的評價是一個既耗費人力、又耗費時間的過程,只有少數大公司能夠使用。并且由于搜索引擎算法改進、運營維護的需要,檢索效果評價反饋的時間需要盡量縮短,因此自動化的評測方法對提高評估效率十分重要。最常用的自動評估方法是A/B testing系統。

            A/B Testing

            A/B Testing系統

            A/B testing系統在用戶搜索時,由系統來自動決定用戶的分組號(Bucket id),通過自動抽取流量導入不同分支,使得相應分組的用戶看到的是不同產品版本(或不同搜索引擎)提供的結果。用戶在不同版本產品下的行為將被記錄下來,這些行為數據通過數據分析形成一系列指標,而通過這些指標的比較,最后就形成了各版本之間孰優孰劣的結論。

            在指標計算時,又可細分為兩種方法,一種是基于專家評分的方法;一種是基于點擊統計的方法。

            專家評分的方法通常由搜索核心技術研發和產品人員來進行,根據預先設定的標準對A、B兩套環境的結果給予評分,獲取每個Query的結果對比,并根據nDCG等方法計算整體質量。

            點擊評分有更高的自動化程度,這里使用了一個假設:同樣的排序位置,點擊數量多的結果質量優于點擊數量少的結果。(即A2表示A測試環境第2條結果,如果A2 > B2,則表示A2質量更好)。通俗的說,相信群眾(因為群眾的眼睛是雪亮的)。在這個假設前提下,我們可以將A/B環境前N條結果的點擊率自動映射為評分,通過統計大量的Query點擊結果,可以獲得可靠的評分對比。

            Interleaving Testing

            另外2003年由Thorsten Joachims 等人提出的Interleaving testing方法也被廣泛使用。該方法設計了一個元搜索引擎,用戶輸入查詢詞后,將查詢詞在幾個著名搜索引擎中的查詢結果隨機混合反饋給用戶,并收集隨后用戶的結果點擊行為信息.根據用戶不同的點擊傾向性,就可以判斷搜索引擎返回結果的優劣,

            如下圖所示,將算法A和B的結果交叉放置,并分流量進行測試,記錄用戶點擊信息。根據點擊分布來判斷A和B環境的優劣。

            Interleaving Testing評估方法

            Joachims同時證明了Interleaving Testing評價方法與傳統Cranfield評價方法的結果具有較高的相關性。由于記錄用戶選擇檢索結果的行為是一個不耗費人力的過程,因此可以便捷的實現自動化的搜索效果評估。

            總結

            沒有評估就沒有進步——對搜索效果的量化評測,目的是準確的找出現有搜索系統的不足(沒有哪個搜索系統是完美的),進而一步一個腳印對算法、系統進行改進。本文為大家總結了常用的評價框架和評價指標。這些技術像一把把尺子,度量著搜索技術每一次前進的距離。


            感謝張凱峰對 本文的審校。

            給InfoQ中文站投稿或者參與內容翻譯工作,請郵件至editors@cn.infoq.com。也歡迎大家加入到InfoQ中文站用戶討論組中與我們的編輯和其他讀者 朋友交流。

            posted @ 2013-01-07 16:46 王海光 閱讀(426) | 評論 (0)編輯 收藏
                 摘要: MySQL索引背后的數據結構及算法原理摘要本文以MySQL數據庫為研究對象,討論與數據庫索引相關的一些話題。特別需要說明的是,MySQL支持諸多存儲引擎,而各種存儲引擎對索引的支持也各不相同,因此MySQL數據庫支持多種索引類型,如BTree索引,哈希索引,全文索引等等。為了避免混亂,本文將只關注于BTree索引,因為這是平常使用MySQL時主要打交道的索引,至于哈希索引和全文索引本文暫不討論。文...  閱讀全文
            posted @ 2013-01-07 16:43 王海光 閱讀(305) | 評論 (0)編輯 收藏
            僅列出標題
            共27頁: First 7 8 9 10 11 12 13 14 15 Last 
            久久无码中文字幕东京热| 亚洲av伊人久久综合密臀性色 | 久久精品久久久久观看99水蜜桃| 四虎国产精品成人免费久久| 日韩人妻无码精品久久免费一 | 777米奇久久最新地址| 久久99精品久久久久久野外| 久久久久亚洲AV成人网人人网站| 国产精品久久久久久吹潮| 色播久久人人爽人人爽人人片aV | 精品久久久久久久无码| 青青草原综合久久大伊人导航| 精品国产一区二区三区久久久狼| 久久国产精品一区| 97久久国产亚洲精品超碰热| 国产99久久久国产精品小说| 国产精品gz久久久| 久久99精品久久久久子伦| 精品久久久久成人码免费动漫| 999久久久免费国产精品播放| 久久久无码一区二区三区| 国内精品伊人久久久影院| 91精品国产色综久久| 久久久久高潮毛片免费全部播放| 香蕉久久影院| 久久久久无码专区亚洲av| 久久亚洲国产中v天仙www| 精品久久无码中文字幕| 中文国产成人精品久久不卡| 亚洲欧美精品一区久久中文字幕| 91久久精品国产免费直播| 国产精品女同久久久久电影院 | 久久99国产精品久久99| 久久精品亚洲中文字幕无码麻豆 | 狠狠综合久久综合中文88| 久久国产乱子精品免费女| 久久丫精品国产亚洲av| 亚洲精品国产字幕久久不卡| 99久久国产综合精品女同图片| 午夜精品久久久久成人| 2019久久久高清456|