• <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>

            不會飛的鳥

            2010年12月10日 ... 不鳥他們?。?! 我要用自己開發(fā)的分布式文件系統(tǒng)、分布式調(diào)度系統(tǒng)、分布式檢索系統(tǒng), 做自己的搜索引擎?。?!大魚有大志?。。?---楊書童

            2015年2月26日 #

            [轉(zhuǎn)]Win7下使用VS2010編譯的Win32控制臺應(yīng)用程序在XP下運(yùn)行報(bào)錯(cuò):找不到msvcp100d.dll

            //targetver.h

            #pragma once

            // 包括 SDKDDKVer.h 將定義可用的最高版本的 Windows 平臺。

            // 如果要為以前的 Windows 平臺生成應(yīng)用程序,請包括 WinSDKVer.h,并將
            // WIN32_WINNT 宏設(shè)置為要支持的平臺,然后再包括 SDKDDKVer.h。

            #include <WinSDKVer.h>

            #define _WIN32_WINNT _WIN32_WINNT_WINXP

            #include <SDKDDKVer.h>

            1、如上:在targetver.h中添加代碼

            2、如下:修改運(yùn)行庫



            posted @ 2015-02-26 18:53 不會飛的鳥 閱讀(733) | 評論 (0)編輯 收藏

            2014年12月18日 #

            [轉(zhuǎn)]Linux下g++編譯C++連接oracle(OCCI)出現(xiàn)的問題及解決方式

            由于項(xiàng)目原因,開始學(xué)習(xí)C++,剛接觸半個(gè)多月,今天參考網(wǎng)上例子,寫了個(gè)簡單的C++連接ORACLE的DEMO,可是使用g++編譯時(shí)不順利,不是報(bào)這個(gè)錯(cuò)就是那個(gè),最后參考網(wǎng)上的解決方式和個(gè)人理解,終于調(diào)試好了,現(xiàn)把編譯中出現(xiàn)的問題和解決方法總結(jié)出來。 

              源代碼 
             
            C++代碼
            1. #include <iostream>  
            2. #include <string>  
            3. #include "occi.h"  
            4. using namespace oracle::occi;  
            5. using namespace std;  
            6.   
            7. int main()  
            8. {  
            9.         string usr="sys";  
            10.         string pwd="orcl";  
            11.         string SID="ORCL";  
            12.         string date;  
            13.   
            14.         Environment *env=Environment::createEnvironment(Environment::OBJECT);  
            15.         Connection *conn= env->createConnection(usr,pwd,SID);//all strings  
            16.         if(conn)  
            17.                 cout<<"success createConnection!"<<endl;  
            18.         else  
            19.                 cout<<"failure createConnection!"<<endl;  
            20.   
            21.         Statement *stmt = conn->createStatement();  
            22.         string sSQL = "select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual";  
            23.         stmt->setSQL(sSQL);  
            24.   
            25.   
            26.         ResultSet *rs = stmt->executeQuery();  
            27.         if(rs->next())  
            28.         {  
            29.                 date = rs->getString(1);  
            30.         }  
            31.   
            32.         cout<<"now time :"<<date<<endl;  
            33.   
            34.         env->terminateConnection(conn);  
            35.         Environment::terminateEnvironment(env);  
            36.   
            37.         return 0;  
            38. }  
            39.     
            1. #include <iostream>  
            2. #include <string>  
            3. #include "occi.h"  
            4. using namespace oracle::occi;  
            5. using namespace std;  
            6.   
            7. int main()  
            8. {  
            9.         string usr="sys";  
            10.         string pwd="orcl";  
            11.         string SID="ORCL";  
            12.         string date;  
            13.   
            14.         Environment *env=Environment::createEnvironment(Environment::OBJECT);  
            15.         Connection *conn= env->createConnection(usr,pwd,SID);//all strings  
            16.         if(conn)  
            17.                 cout<<"success createConnection!"<<endl;  
            18.         else  
            19.                 cout<<"failure createConnection!"<<endl;  
            20.   
            21.         Statement *stmt = conn->createStatement();  
            22.         string sSQL = "select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual";  
            23.         stmt->setSQL(sSQL);  
            24.   
            25.   
            26.         ResultSet *rs = stmt->executeQuery();  
            27.         if(rs->next())  
            28.         {  
            29.                 date = rs->getString(1);  
            30.         }  
            31.   
            32.         cout<<"now time :"<<date<<endl;  
            33.   
            34.         env->terminateConnection(conn);  
            35.         Environment::terminateEnvironment(env);  
            36.   
            37.         return 0;  
            38. }  
            39.     


              本人linux上安裝oracle路徑:/opt/app/oracle/product/10.2.0/db_1 

              編譯命令:g++ -o conn -L/opt/app/oracle/product/10.2.0/db_1/lib -L/opt/oracle/product/10.2.0/db_1/rdbms/lib conn_db.cpp -g 

            問題一:編譯時(shí)報(bào)如下錯(cuò)誤: 
                
            Shell代碼
            1.       [oracle@localhost demo]$ g++ g++ -o conn -L/opt/app/oracle/product/10.2.0/db_1/lib -L/opt/oracle/product/10.2.0/db_1/rdbms/lib -lclntsh -locci /usr/lib/libstdc++.so.5 conn_db.cpp -g  
            2. g++: g++: No such file or directory  
            3. conn_db.cpp:3:18: error: occi.h: No such file or directory  
            4. conn_db.cpp:4: error: 'oracle' has not been declared  
            5. conn_db.cpp:4: error: 'occi' is not a namespace-name  
            6. conn_db.cpp:4: error: expected namespace-name before ';' token  
            7. conn_db.cpp: In function 'int main()':  
            8. conn_db.cpp:14: error: 'Environment' was not declared in this scope  
            9. conn_db.cpp:14: error: 'env' was not declared in this scope  
            10. conn_db.cpp:14: error: 'Environment' is not a class or namespace  
            11. conn_db.cpp:14: error: 'Environment' is not a class or namespace  
            12. conn_db.cpp:15: error: 'Connection' was not declared in this scope  
            13. conn_db.cpp:15: error: 'conn' was not declared in this scope  
            14. conn_db.cpp:21: error: 'Statement' was not declared in this scope  
            15. conn_db.cpp:21: error: 'stmt' was not declared in this scope  
            16. conn_db.cpp:26: error: 'ResultSet' was not declared in this scope  
            17. conn_db.cpp:26: error: 'rs' was not declared in this scope  
            18. conn_db.cpp:35: error: 'Environment' is not a class or namespace  
            19.        

               
                解決:編譯時(shí)沒有引入OCCI頭文件,如果沒有,先下載對應(yīng)的 ORACLE client安裝,比如我的是oracle10g,下載了oracle-instantclient-basic- 10.2.0.4-1.i386.zip,解壓到一個(gè)目錄下(/home/oracle/oracle/include),然后在編譯文件的時(shí)候引進(jìn)這個(gè)解壓目錄 

               編譯命令增加OCCI目錄:g++ -o conn -I/home/oracle/oracle/include -L/opt/app/oracle/product/10.2.0/db_1/lib -L/opt/oracle/product/10.2.0/db_1/rdbms/lib conn_db.cpp -g 


            問題2:找不到對應(yīng)函數(shù) 
             
            Shell代碼
            1.    [oracle@localhost demo]$ g++ -o conn -I/home/oracle/oracle/include -L/opt/app/oracle/product/10.2.0/db_1/lib -L/opt/oracle/product/10.2.0/db_1/rdbms/lib conn_db.cpp -Wall -O -g  
            2. /tmp/cclFs9xq.o: In function `main':  
            3. /home/oracle/oracle/demo/conn_db.cpp:14: undefined reference to `oracle::occi::Environment::createEnvironment(oracle::occi::Environment::Mode, void*, void* (*)(void*, unsigned int), void* (*)(void*, void*, unsigned int), void (*)(void*, void*))'  
            4. /home/oracle/oracle/demo/conn_db.cpp:35: undefined reference to `oracle::occi::Environment::terminateEnvironment(oracle::occi::Environment*)'  
            5. collect2: ld returned 1 exit status   
            6.    

            解決:增加libocci.so和libclntsh.so指定編譯 
              
              修改后的編譯命令: g++ -o conn -I/home/oracle/oracle/include -L/opt/app/oracle/product/10.2.0/db_1/lib -L/opt/oracle/product/10.2.0/db_1/rdbms/lib conn_db.cpp -lclntsh -locci -Wall -O -g 

              另外可能在引入-lclntsh -locci編譯時(shí)可能會報(bào)找不到以下錯(cuò)誤: 
                
            Shell代碼
            1.      [oracle@localhost demo]$ g++ -o conn -I/home/oracle/oracle/include -L/opt/app/oracle/product/10.2.0/db_1/lib -L/opt/oracle/product/10.2.0/db_1/rdbms/lib conn_db.cpp -lclntsh -locci /usr/lib/libstdc++.so.5 -Wall -O -g  
            2. /usr/bin/ld: cannot find -lclntsh  
            3. collect2: ld returned 1 exit status  
            4. [oracle@localhost demo]$   
            5.        

                 解決:這是因?yàn)闆]有找到libclntsh.so和libocci.so鏈接庫,你在可以把oracle client安裝目錄下把這兩個(gè)文件拷貝到$ORACLE_HOME/lib目錄下,或加到/usr/lib目錄下就可以了 

            問題三:occi在linux編譯運(yùn)行時(shí)報(bào)libstdc++.so.6沖突的問題 
               
            Java代碼
            1. [oracle@localhost demo]$ g++ -o conn -I/home/oracle/oracle/include -L/opt/app/oracle/product/10.2.0/db_1/lib -L/opt/oracle/product/10.2.0/db_1/rdbms/lib conn_db.cpp -lclntsh -locci -Wall -O -g  
            2. /usr/bin/ld: warning: libstdc++.so.5, needed by /opt/app/oracle/product/10.2.0/db_1/lib/libocci.so, may conflict with libstdc++.so.6  
            1. [oracle@localhost demo]$ g++ -o conn -I/home/oracle/oracle/include -L/opt/app/oracle/product/10.2.0/db_1/lib -L/opt/oracle/product/10.2.0/db_1/rdbms/lib conn_db.cpp -lclntsh -locci -Wall -O -g  
            2. /usr/bin/ld: warning: libstdc++.so.5, needed by /opt/app/oracle/product/10.2.0/db_1/lib/libocci.so, may conflict with libstdc++.so.6  

              解決:OCCI庫在linux編譯的時(shí)候,由于linux版本太高,會提示以上情況,實(shí)際上,在大多數(shù)linux系統(tǒng)上,還保留有l(wèi)ibstdc++5的庫,自己手工在編譯的時(shí)候加上去就好了 

              修改后的編譯命令:g++ -o conn -I/home/oracle/oracle/include -L/opt/app/oracle/product/10.2.0/db_1/lib -L/opt/oracle/product/10.2.0/db_1/rdbms/lib -lclntsh -locci /usr/lib/libstdc++.so.5 conn_db.cpp -g 

              編譯通過后執(zhí)行結(jié)果輸出: 
              
            Shell代碼
            1. [oracle@localhost demo]$ g++ -o conn -I/home/oracle/oracle/include -L/opt/app/oracle/product/10.2.0/db_1/lib -L/opt/oracle/product/10.2.0/db_1/rdbms/lib conn_db.cpp -lclntsh -locci /usr/lib/libstdc++.so.5 -Wall -O -g  
            2. [oracle@localhost demo]$ ./conn  
            3. success createConnection!  
            4. now time :2010-11-14 22:49:24  
            5. [oracle@localhost demo]$  


            posted @ 2014-12-18 13:04 不會飛的鳥 閱讀(854) | 評論 (0)編輯 收藏

            2014年11月10日 #

            各種字符串Hash函數(shù)比較

            常用的字符串Hash函數(shù)還有ELFHash,APHash等等,都是十分簡單有效的方法。這些函數(shù)使用位運(yùn)算使得每一個(gè)字符都對最后的函數(shù)值產(chǎn)生影響。另外還有以MD5和SHA1為代表的雜湊函數(shù),這些函數(shù)幾乎不可能找到碰撞。

            常用字符串哈希函數(shù)有BKDRHash,APHash,DJBHash,JSHash,RSHash,SDBMHash,PJWHash,ELFHash等等。對于以上幾種哈希函數(shù),我對其進(jìn)行了一個(gè)小小的評測。

            Hash函數(shù)數(shù)據(jù)1數(shù)據(jù)2數(shù)據(jù)3數(shù)據(jù)4數(shù)據(jù)1得分數(shù)據(jù)2得分數(shù)據(jù)3得分數(shù)據(jù)4得分平均分
            BKDRHash20477448196.5510090.9582.0592.64
            APHash23475449396.5588.4610051.2886.28
            DJBHash22497547496.5592.31010083.43
            JSHash14476150610084.6296.8317.9581.94
            RSHash10486150510010051.5820.5175.96
            SDBMHash32484950493.192.3157.0123.0872.41
            PJWHash302648785130043.89021.95
            ELFHash302648785130043.89021.95

            其中數(shù)據(jù)1為100000個(gè)字母和數(shù)字組成的隨機(jī)串哈希沖突個(gè)數(shù)。數(shù)據(jù)2為100000個(gè)有意義的英文句子哈希沖突個(gè)數(shù)。數(shù)據(jù)3為數(shù)據(jù)1的哈希值與1000003(大素?cái)?shù))求模后存儲到線性表中沖突的個(gè)數(shù)。數(shù)據(jù)4為數(shù)據(jù)1的哈希值與10000019(更大素?cái)?shù))求模后存儲到線性表中沖突的個(gè)數(shù)。

            經(jīng)過比較,得出以上平均得分。平均數(shù)為平方平均數(shù)??梢园l(fā)現(xiàn),BKDRHash無論是在實(shí)際效果還是編碼實(shí)現(xiàn)中,效果都是最突出的。APHash也是較為優(yōu)秀的算法。DJBHash,JSHash,RSHash與SDBMHash各有千秋。PJWHash與ELFHash效果最差,但得分相似,其算法本質(zhì)是相似的。

            在信息修競賽中,要本著易于編碼調(diào)試的原則,個(gè)人認(rèn)為BKDRHash是最適合記憶和使用的。

            BYVoid原創(chuàng),歡迎建議、交流、批評和指正。

            附:各種哈希函數(shù)的C語言程序代碼

            unsigned int SDBMHash(char *str)
            {
                unsigned 
            int hash = 0;

                
            while (*str)
                {
                    
            // equivalent to: hash = 65599*hash + (*str++);
                    hash = (*str+++ (hash << 6+ (hash << 16- hash;
                }

                
            return (hash & 0x7FFFFFFF);
            }

            // RS Hash Function
            unsigned int RSHash(char *str)
            {
                unsigned 
            int b = 378551;
                unsigned 
            int a = 63689;
                unsigned 
            int hash = 0;

                
            while (*str)
                {
                    hash 
            = hash * a + (*str++);
                    a 
            *= b;
                }

                
            return (hash & 0x7FFFFFFF);
            }

            // JS Hash Function
            unsigned int JSHash(char *str)
            {
                unsigned 
            int hash = 1315423911;

                
            while (*str)
                {
                    hash 
            ^= ((hash << 5+ (*str+++ (hash >> 2));
                }

                
            return (hash & 0x7FFFFFFF);
            }

            // P. J. Weinberger Hash Function
            unsigned int PJWHash(char *str)
            {
                unsigned 
            int BitsInUnignedInt = (unsigned int)(sizeof(unsigned int* 8);
                unsigned 
            int ThreeQuarters    = (unsigned int)((BitsInUnignedInt  * 3/ 4);
                unsigned 
            int OneEighth        = (unsigned int)(BitsInUnignedInt / 8);
                unsigned 
            int HighBits         = (unsigned int)(0xFFFFFFFF<< (BitsInUnignedInt - OneEighth);
                unsigned 
            int hash             = 0;
                unsigned 
            int test             = 0;

                
            while (*str)
                {
                    hash 
            = (hash << OneEighth) + (*str++);
                    
            if ((test = hash & HighBits) != 0)
                    {
                        hash 
            = ((hash ^ (test >> ThreeQuarters)) & (~HighBits));
                    }
                }

                
            return (hash & 0x7FFFFFFF);
            }

            // ELF Hash Function
            unsigned int ELFHash(char *str)
            {
                unsigned 
            int hash = 0;
                unsigned 
            int x    = 0;

                
            while (*str)
                {
                    hash 
            = (hash << 4+ (*str++);
                    
            if ((x = hash & 0xF0000000L!= 0)
                    {
                        hash 
            ^= (x >> 24);
                        hash 
            &= ~x;
                    }
                }

                
            return (hash & 0x7FFFFFFF);
            }

            // BKDR Hash Function
            unsigned int BKDRHash(char *str)
            {
                unsigned 
            int seed = 131// 31 131 1313 13131 131313 etc..
                unsigned int hash = 0;

                
            while (*str)
                {
                    hash 
            = hash * seed + (*str++);
                }

                
            return (hash & 0x7FFFFFFF);
            }

            // DJB Hash Function
            unsigned int DJBHash(char *str)
            {
                unsigned 
            int hash = 5381;

                
            while (*str)
                {
                    hash 
            += (hash << 5+ (*str++);
                }

                
            return (hash & 0x7FFFFFFF);
            }

            // AP Hash Function
            unsigned int APHash(char *str)
            {
                unsigned 
            int hash = 0;
                
            int i;

                
            for (i=0*str; i++)
                {
                    
            if ((i & 1== 0)
                    {
                        hash 
            ^= ((hash << 7^ (*str++^ (hash >> 3));
                    }
                    
            else
                    {
                        hash 
            ^= (~((hash << 11^ (*str++^ (hash >> 5)));
                    }
                }

                
            return (hash & 0x7FFFFFFF);
            }

            posted @ 2014-11-10 19:53 不會飛的鳥 閱讀(831) | 評論 (0)編輯 收藏

            2014年5月26日 #

            [轉(zhuǎn)]C++的Json解析庫:jsoncpp和boost

            JSON(JavaScript Object Notation)跟xml一樣也是一種數(shù)據(jù)交換格式,了解json請參考其官網(wǎng)http://json.org/,本文不再對json做介紹,將重點(diǎn)介紹c++的json解析庫的使用方法。json官網(wǎng)上列出了各種語言對應(yīng)的json解析庫,作者僅介紹自己使用過的兩種C++的json解析庫:jsoncpp(v0.5.0)和Boost(v1.34.0)。

            一. 使用jsoncpp解析json

            Jsoncpp是個(gè)跨平臺的開源庫,首先從http://jsoncpp.sourceforge.net/上下載jsoncpp庫源碼,我下載的是v0.5.0,壓縮包大約107K,解壓,在jsoncpp-src-0.5.0/makefiles/vs71目錄里找到j(luò)soncpp.sln,用VS2003及以上版本編譯,默認(rèn)生成靜態(tài)鏈接庫。 在工程中引用,只需要include/json及.lib文件即可。

            使用JsonCpp前先來熟悉幾個(gè)主要的類:

            Json::Value 可以表示里所有的類型,比如int,string,object,array等,具體應(yīng)用將會在后邊示例中介紹。

            Json::Reader 將json文件流或字符串解析到Json::Value, 主要函數(shù)有Parse。

            Json::Writer 與Json::Reader相反,將Json::Value轉(zhuǎn)化成字符串流,注意它的兩個(gè)子類:Json::FastWriter和Json::StyleWriter,分別輸出不帶格式的json和帶格式的json。

            1. 從字符串解析json

            int ParseJsonFromString()
            {
              const char* str = "{\"uploadid\": \"UP000000\",\"code\": 100,\"msg\": \"\",\"files\": \"\"}";

              Json::Reader reader;
              Json::Value root;
              if (reader.parse(str, root))  // reader將Json字符串解析到root,root將包含Json里所有子元素
              {
                std::string upload_id = root["uploadid"].asString();  // 訪問節(jié)點(diǎn),upload_id = "UP000000"
                int code = root["code"].asInt();    // 訪問節(jié)點(diǎn),code = 100
              }
              return 0;
            }

            2. 從文件解析json

            {
                "uploadid": "UP000000",
                "code": "0",
                "msg": "",
                "files":
                [
                    {
                        "code": "0",
                        "msg": "",
                        "filename": "1D_16-35_1.jpg",
                        "filesize": "196690",
                        "width": "1024",
                        "height": "682",
                        "images":
                        [
                            {
                                "url": "fmn061/20111118",
                                "type": "large",
                                "width": "720",
                                "height": "479"
                            },
                            {
                                "url": "fmn061/20111118",
                                "type": "main",
                                "width": "200",
                                "height": "133"
                            }
                        ]
                    }
                ]
            }
            解析代碼:
            int ParseJsonFromFile(const char* filename)
            {
              // 解析json用Json::Reader
              Json::Reader reader;
              // Json::Value是一種很重要的類型,可以代表任意類型。如int, string, object, array
              Json::Value root;       

              std::ifstream is;
              is.open (filename, std::ios::binary );  
              if (reader.parse(is, root))
              {
                std::string code;
                if (!root["files"].isNull())  // 訪問節(jié)點(diǎn),Access an object value by name, create a null member if it does not exist.
                  code = root["uploadid"].asString();
                
                // 訪問節(jié)點(diǎn),Return the member named key if it exist, defaultValue otherwise.
                code = root.get("uploadid", "null").asString();

                // 得到"files"的數(shù)組個(gè)數(shù)
                int file_size = root["files"].size();

                // 遍歷數(shù)組
                for(int i = 0; i < file_size; ++i)
                {
                  Json::Value val_image = root["files"][i]["images"];
                  int image_size = val_image.size();
                  for(int j = 0; j < image_size; ++j)
                  {
                    std::string type = val_image[j]["type"].asString();
                    std::string url = val_image[j]["url"].asString();
                  }
                }
              }
              is.close();
              return 0;
            }

            3. 在json結(jié)構(gòu)中插入json

                Json::Value arrayObj;   // 構(gòu)建對象
                Json::Value new_item, new_item1;
                new_item["date"] = "2011-12-28";
                new_item1["time"] = "22:30:36";
                arrayObj.append(new_item);  // 插入數(shù)組成員
                arrayObj.append(new_item1); // 插入數(shù)組成員
                int file_size = root["files"].size();
                for(int i = 0; i < file_size; ++i)
                  root["files"][i]["exifs"] = arrayObj;   // 插入原json中

            4. 輸出json

            // 轉(zhuǎn)換為字符串(帶格式)
            std::string out = root.toStyledString();
            // 輸出無格式j(luò)son字符串
            Json::FastWriter writer;
            std::string out2 = writer.write(root);

            二. 使用Boost property_tree解析json

            property_tree可以解析xml,json,ini,info等格式的數(shù)據(jù),用property_tree解析這幾種格式使用方法很相似。

            解析json很簡單,命名空間為boost::property_tree,reson_json函數(shù)將文件流、字符串解析到ptree,write_json將ptree輸出為字符串或文件流。其余的都是對ptree的操作。

            解析json需要加頭文件:

            #include <boost/property_tree/ptree.hpp>
            #include <boost/property_tree/json_parser.hpp>

            1. 解析json

            解析一段下面的數(shù)據(jù):

            {
              "code": 0,
              "images":
              [
                {
                  "url": "fmn057/20111221/1130/head_kJoO_05d9000251de125c.jpg"
                },
                {
                  "url": "fmn057/20111221/1130/original_kJoO_05d9000251de125c.jpg"
                }
              ]
            }
            int ParseJson()
            {
              std::string str = "{\"code\":0,\"images\":[{\"url\":\"fmn057/20111221/1130/head_kJoO_05d9000251de125c.jpg\"},{\"url\":\"fmn057/20111221/1130/original_kJoO_05d9000251de125c.jpg\"}]}";
              using namespace boost::property_tree;

              std::stringstream ss(str);
              ptree pt;
              try{    
                read_json(ss, pt);
              }
              catch(ptree_error & e) {
                return 1; 
              }

              try{
                int code = pt.get<int>("code");   // 得到"code"的value
                ptree image_array = pt.get_child("images");  // get_child得到數(shù)組對象
                
                
            // 遍歷數(shù)組
                BOOST_FOREACH(boost::property_tree::ptree::value_type &v, image_array)
                {
                  std::stringstream s;
                  write_json(s, v.second);
                  std::string image_item = s.str();
                }
              }
              catch (ptree_error & e)
              {
                return 2;
              }
              return 0;
            }

            2. 構(gòu)造json

            int InsertJson()
            {
              std::string str = "{\"code\":0,\"images\":[{\"url\":\"fmn057/20111221/1130/head_kJoO_05d9000251de125c.jpg\"},{\"url\":\"fmn057/20111221/1130/original_kJoO_05d9000251de125c.jpg\"}]}";
              using namespace boost::property_tree;

              std::stringstream ss(str);
              ptree pt;
              try{    
                read_json(ss, pt);
              }
              catch(ptree_error & e) {
                return 1; 
              }

              // 修改/增加一個(gè)key-value,key不存在則增加
              pt.put("upid", "00001");

              // 插入一個(gè)數(shù)組
              ptree exif_array;
              ptree array1, array2, array3;
              array1.put("Make", "NIKON");
              array2.put("DateTime", "2011:05:31 06:47:09");
              array3.put("Software", "Ver.1.01");
              exif_array.push_back(std::make_pair("", array1));
              exif_array.push_back(std::make_pair("", array2));
              exif_array.push_back(std::make_pair("", array3));

            //   exif_array.push_back(std::make_pair("Make", "NIKON"));
            //   exif_array.push_back(std::make_pair("DateTime", "2011:05:31 06:47:09"));
            //   exif_array.push_back(std::make_pair("Software", "Ver.1.01"));

              pt.put_child("exifs", exif_array);
              std::stringstream s2;
              write_json(s2, pt);
              std::string outstr = s2.str();

              return 0;
            }

            三. 兩種解析庫的使用經(jīng)驗(yàn)

            1. 用boost::property_tree解析字符串遇到"\/"時(shí)解析失敗,而jsoncpp可以解析成功,要知道'/'前面加一個(gè)'\'是JSON標(biāo)準(zhǔn)格式。

            2. boost::property_tree的read_json和write_json在多線程中使用會引起崩潰。

            針對1,可以在使用boost::property_tree解析前寫個(gè)函數(shù)去掉"\/"中的'\',針對2,在多線程中同步一下可以解決。

            我的使用心得:使用boost::property_tree不僅可以解析json,還可以解析xml,info等格式的數(shù)據(jù)。對于解析json,使用boost::property_tree解析還可以忍受,但解析xml,由于遇到問題太多只能換其它庫了。

            posted @ 2014-05-26 00:36 不會飛的鳥 閱讀(925) | 評論 (0)編輯 收藏

            2014年3月6日 #

            [轉(zhuǎn)]字符串匹配的Boyer-Moore算法

            上一篇文章,我介紹了KMP算法

            但是,它并不是效率最高的算法,實(shí)際采用并不多。各種文本編輯器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法

            Boyer-Moore算法不僅效率高,而且構(gòu)思巧妙,容易理解。1977年,德克薩斯大學(xué)的Robert S. Boyer教授和J Strother Moore教授發(fā)明了這種算法。

            下面,我根據(jù)Moore教授自己的例子來解釋這種算法。

            1.

            假定字符串為"HERE IS A SIMPLE EXAMPLE",搜索詞為"EXAMPLE"。

            2.

            首先,"字符串"與"搜索詞"頭部對齊,從尾部開始比較。

            這是一個(gè)很聰明的想法,因?yàn)槿绻膊孔址黄ヅ?,那么只要一次比較,就可以知道前7個(gè)字符(整體上)肯定不是要找的結(jié)果。

            我們看到,"S"與"E"不匹配。這時(shí),"S"就被稱為"壞字符"(bad character),即不匹配的字符。我們還發(fā)現(xiàn),"S"不包含在搜索詞"EXAMPLE"之中,這意味著可以把搜索詞直接移到"S"的后一位。

            3.

            依然從尾部開始比較,發(fā)現(xiàn)"P"與"E"不匹配,所以"P"是"壞字符"。但是,"P"包含在搜索詞"EXAMPLE"之中。所以,將搜索詞后移兩位,兩個(gè)"P"對齊。

            4.

            我們由此總結(jié)出"壞字符規(guī)則"

              后移位數(shù) = 壞字符的位置 - 搜索詞中的上一次出現(xiàn)位置

            如果"壞字符"不包含在搜索詞之中,則上一次出現(xiàn)位置為 -1。

            以"P"為例,它作為"壞字符",出現(xiàn)在搜索詞的第6位(從0開始編號),在搜索詞中的上一次出現(xiàn)位置為4,所以后移 6 - 4 = 2位。再以前面第二步的"S"為例,它出現(xiàn)在第6位,上一次出現(xiàn)位置是 -1(即未出現(xiàn)),則整個(gè)搜索詞后移 6 - (-1) = 7位。

            5.

            依然從尾部開始比較,"E"與"E"匹配。

            6.

            比較前面一位,"LE"與"LE"匹配。

            7.

            比較前面一位,"PLE"與"PLE"匹配。

            8.

            比較前面一位,"MPLE"與"MPLE"匹配。我們把這種情況稱為"好后綴"(good suffix),即所有尾部匹配的字符串。注意,"MPLE"、"PLE"、"LE"、"E"都是好后綴。

            9.

            比較前一位,發(fā)現(xiàn)"I"與"A"不匹配。所以,"I"是"壞字符"。

            10.

            根據(jù)"壞字符規(guī)則",此時(shí)搜索詞應(yīng)該后移 2 - (-1)= 3 位。問題是,此時(shí)有沒有更好的移法?

            11.

            我們知道,此時(shí)存在"好后綴"。所以,可以采用"好后綴規(guī)則"

              后移位數(shù) = 好后綴的位置 - 搜索詞中的上一次出現(xiàn)位置

            舉例來說,如果字符串"ABCDAB"的后一個(gè)"AB"是"好后綴"。那么它的位置是5(從0開始計(jì)算,取最后的"B"的值),在"搜索詞中的上一次出現(xiàn)位置"是1(第一個(gè)"B"的位置),所以后移 5 - 1 = 4位,前一個(gè)"AB"移到后一個(gè)"AB"的位置。

            再舉一個(gè)例子,如果字符串"ABCDEF"的"EF"是好后綴,則"EF"的位置是5 ,上一次出現(xiàn)的位置是 -1(即未出現(xiàn)),所以后移 5 - (-1) = 6位,即整個(gè)字符串移到"F"的后一位。

            這個(gè)規(guī)則有三個(gè)注意點(diǎn):

              (1)"好后綴"的位置以最后一個(gè)字符為準(zhǔn)。假定"ABCDEF"的"EF"是好后綴,則它的位置以"F"為準(zhǔn),即5(從0開始計(jì)算)。

             ?。?)如果"好后綴"在搜索詞中只出現(xiàn)一次,則它的上一次出現(xiàn)位置為 -1。比如,"EF"在"ABCDEF"之中只出現(xiàn)一次,則它的上一次出現(xiàn)位置為-1(即未出現(xiàn))。

             ?。?)如果"好后綴"有多個(gè),則除了最長的那個(gè)"好后綴",其他"好后綴"的上一次出現(xiàn)位置必須在頭部。比如,假定"BABCDAB"的"好后綴"是"DAB"、"AB"、"B",請問這時(shí)"好后綴"的上一次出現(xiàn)位置是什么?回答是,此時(shí)采用的好后綴是"B",它的上一次出現(xiàn)位置是頭部,即第0位。這個(gè)規(guī)則也可以這樣表達(dá):如果最長的那個(gè)"好后綴"只出現(xiàn)一次,則可以把搜索詞改寫成如下形式進(jìn)行位置計(jì)算"(DA)BABCDAB",即虛擬加入最前面的"DA"。

            回到上文的這個(gè)例子。此時(shí),所有的"好后綴"(MPLE、PLE、LE、E)之中,只有"E"在"EXAMPLE"還出現(xiàn)在頭部,所以后移 6 - 0 = 6位。

            12.

            可以看到,"壞字符規(guī)則"只能移3位,"好后綴規(guī)則"可以移6位。所以,Boyer-Moore算法的基本思想是,每次后移這兩個(gè)規(guī)則之中的較大值。

            更巧妙的是,這兩個(gè)規(guī)則的移動位數(shù),只與搜索詞有關(guān),與原字符串無關(guān)。因此,可以預(yù)先計(jì)算生成《壞字符規(guī)則表》和《好后綴規(guī)則表》。使用時(shí),只要查表比較一下就可以了。

            13.

            繼續(xù)從尾部開始比較,"P"與"E"不匹配,因此"P"是"壞字符"。根據(jù)"壞字符規(guī)則",后移 6 - 4 = 2位。

            14.

            從尾部開始逐位比較,發(fā)現(xiàn)全部匹配,于是搜索結(jié)束。如果還要繼續(xù)查找(即找出全部匹配),則根據(jù)"好后綴規(guī)則",后移 6 - 0 = 6位,即頭部的"E"移到尾部的"E"的位置。

            posted @ 2014-03-06 21:47 不會飛的鳥 閱讀(364) | 評論 (0)編輯 收藏

            [轉(zhuǎn)]字符串匹配的KMP算法

            字符串匹配是計(jì)算機(jī)的基本任務(wù)之一。

            舉例來說,有一個(gè)字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一個(gè)字符串"ABCDABD"?

            許多算法可以完成這個(gè)任務(wù),Knuth-Morris-Pratt算法(簡稱KMP)是最常用的之一。它以三個(gè)發(fā)明者命名,起頭的那個(gè)K就是著名科學(xué)家Donald Knuth。

            這種算法不太容易理解,網(wǎng)上有很多解釋,但讀起來都很費(fèi)勁。直到讀到Jake Boxer的文章,我才真正理解這種算法。下面,我用自己的語言,試圖寫一篇比較好懂的KMP算法解釋。

            1.

            首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一個(gè)字符與搜索詞"ABCDABD"的第一個(gè)字符,進(jìn)行比較。因?yàn)锽與A不匹配,所以搜索詞后移一位。

            2.

            因?yàn)锽與A不匹配,搜索詞再往后移。

            3.

            就這樣,直到字符串有一個(gè)字符,與搜索詞的第一個(gè)字符相同為止。

            4.

            接著比較字符串和搜索詞的下一個(gè)字符,還是相同。

            5.

            直到字符串有一個(gè)字符,與搜索詞對應(yīng)的字符不相同為止。

            6.

            這時(shí),最自然的反應(yīng)是,將搜索詞整個(gè)后移一位,再從頭逐個(gè)比較。這樣做雖然可行,但是效率很差,因?yàn)槟阋?搜索位置"移到已經(jīng)比較過的位置,重比一遍。

            7.

            一個(gè)基本事實(shí)是,當(dāng)空格與D不匹配時(shí),你其實(shí)知道前面六個(gè)字符是"ABCDAB"。KMP算法的想法是,設(shè)法利用這個(gè)已知信息,不要把"搜索位置"移回已經(jīng)比較過的位置,繼續(xù)把它向后移,這樣就提高了效率。

            8.

            怎么做到這一點(diǎn)呢?可以針對搜索詞,算出一張《部分匹配表》(Partial Match Table)。這張表是如何產(chǎn)生的,后面再介紹,這里只要會用就可以了。

            9.

            已知空格與D不匹配時(shí),前面六個(gè)字符"ABCDAB"是匹配的。查表可知,最后一個(gè)匹配字符B對應(yīng)的"部分匹配值"為2,因此按照下面的公式算出向后移動的位數(shù):

              移動位數(shù) = 已匹配的字符數(shù) - 對應(yīng)的部分匹配值

            因?yàn)?6 - 2 等于4,所以將搜索詞向后移動4位。

            10.

            因?yàn)榭崭衽cC不匹配,搜索詞還要繼續(xù)往后移。這時(shí),已匹配的字符數(shù)為2("AB"),對應(yīng)的"部分匹配值"為0。所以,移動位數(shù) = 2 - 0,結(jié)果為 2,于是將搜索詞向后移2位。

            11.

            因?yàn)榭崭衽cA不匹配,繼續(xù)后移一位。

            12.

            逐位比較,直到發(fā)現(xiàn)C與D不匹配。于是,移動位數(shù) = 6 - 2,繼續(xù)將搜索詞向后移動4位。

            13.

            逐位比較,直到搜索詞的最后一位,發(fā)現(xiàn)完全匹配,于是搜索完成。如果還要繼續(xù)搜索(即找出全部匹配),移動位數(shù) = 7 - 0,再將搜索詞向后移動7位,這里就不再重復(fù)了。

            14.

            下面介紹《部分匹配表》是如何產(chǎn)生的。

            首先,要了解兩個(gè)概念:"前綴"和"后綴"。 "前綴"指除了最后一個(gè)字符以外,一個(gè)字符串的全部頭部組合;"后綴"指除了第一個(gè)字符以外,一個(gè)字符串的全部尾部組合。

            15.

            "部分匹配值"就是"前綴"和"后綴"的最長的共有元素的長度。以"ABCDABD"為例,

             ?。?A"的前綴和后綴都為空集,共有元素的長度為0;

             ?。?AB"的前綴為[A],后綴為[B],共有元素的長度為0;

             ?。?ABC"的前綴為[A, AB],后綴為[BC, C],共有元素的長度0;

             ?。?ABCD"的前綴為[A, AB, ABC],后綴為[BCD, CD, D],共有元素的長度為0;

             ?。?ABCDA"的前綴為[A, AB, ABC, ABCD],后綴為[BCDA, CDA, DA, A],共有元素為"A",長度為1;

             ?。?ABCDAB"的前綴為[A, AB, ABC, ABCD, ABCDA],后綴為[BCDAB, CDAB, DAB, AB, B],共有元素為"AB",長度為2;

             ?。?ABCDABD"的前綴為[A, AB, ABC, ABCD, ABCDA, ABCDAB],后綴為[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的長度為0。

            16.

            "部分匹配"的實(shí)質(zhì)是,有時(shí)候,字符串頭部和尾部會有重復(fù)。比如,"ABCDAB"之中有兩個(gè)"AB",那么它的"部分匹配值"就是2("AB"的長度)。搜索詞移動的時(shí)候,第一個(gè)"AB"向后移動4位(字符串長度-部分匹配值),就可以來到第二個(gè)"AB"的位置。

            posted @ 2014-03-06 21:46 不會飛的鳥 閱讀(269) | 評論 (0)編輯 收藏

            [轉(zhuǎn)]進(jìn)程與線程的一個(gè)簡單解釋

            進(jìn)程(process)和線程(thread)是操作系統(tǒng)的基本概念,但是它們比較抽象,不容易掌握。

            最近,我讀到一篇材料,發(fā)現(xiàn)有一個(gè)很好的類比,可以把它們解釋地清晰易懂。

            1.

            計(jì)算機(jī)的核心是CPU,它承擔(dān)了所有的計(jì)算任務(wù)。它就像一座工廠,時(shí)刻在運(yùn)行。

            2.

            假定工廠的電力有限,一次只能供給一個(gè)車間使用。也就是說,一個(gè)車間開工的時(shí)候,其他車間都必須停工。背后的含義就是,單個(gè)CPU一次只能運(yùn)行一個(gè)任務(wù)。

            3.

            進(jìn)程就好比工廠的車間,它代表CPU所能處理的單個(gè)任務(wù)。任一時(shí)刻,CPU總是運(yùn)行一個(gè)進(jìn)程,其他進(jìn)程處于非運(yùn)行狀態(tài)。

            4.

            一個(gè)車間里,可以有很多工人。他們協(xié)同完成一個(gè)任務(wù)。

            5.

            線程就好比車間里的工人。一個(gè)進(jìn)程可以包括多個(gè)線程。

            6.

            車間的空間是工人們共享的,比如許多房間是每個(gè)工人都可以進(jìn)出的。這象征一個(gè)進(jìn)程的內(nèi)存空間是共享的,每個(gè)線程都可以使用這些共享內(nèi)存。

            7.

            可是,每間房間的大小不同,有些房間最多只能容納一個(gè)人,比如廁所。里面有人的時(shí)候,其他人就不能進(jìn)去了。這代表一個(gè)線程使用某些共享內(nèi)存時(shí),其他線程必須等它結(jié)束,才能使用這一塊內(nèi)存。

            8.

            一個(gè)防止他人進(jìn)入的簡單方法,就是門口加一把鎖。先到的人鎖上門,后到的人看到上鎖,就在門口排隊(duì),等鎖打開再進(jìn)去。這就叫"互斥鎖"(Mutual exclusion,縮寫 Mutex),防止多個(gè)線程同時(shí)讀寫某一塊內(nèi)存區(qū)域。

            9.

            還有些房間,可以同時(shí)容納n個(gè)人,比如廚房。也就是說,如果人數(shù)大于n,多出來的人只能在外面等著。這好比某些內(nèi)存區(qū)域,只能供給固定數(shù)目的線程使用。

            10.

            這時(shí)的解決方法,就是在門口掛n把鑰匙。進(jìn)去的人就取一把鑰匙,出來時(shí)再把鑰匙掛回原處。后到的人發(fā)現(xiàn)鑰匙架空了,就知道必須在門口排隊(duì)等著了。這種做法叫做"信號量"(Semaphore),用來保證多個(gè)線程不會互相沖突。

            不難看出,mutex是semaphore的一種特殊情況(n=1時(shí))。也就是說,完全可以用后者替代前者。但是,因?yàn)閙utex較為簡單,且效率高,所以在必須保證資源獨(dú)占的情況下,還是采用這種設(shè)計(jì)。

            11.

            操作系統(tǒng)的設(shè)計(jì),因此可以歸結(jié)為三點(diǎn):

            (1)以多進(jìn)程形式,允許多個(gè)任務(wù)同時(shí)運(yùn)行;

            (2)以多線程形式,允許單個(gè)任務(wù)分成不同的部分運(yùn)行;

            (3)提供協(xié)調(diào)機(jī)制,一方面防止進(jìn)程之間和線程之間產(chǎn)生沖突,另一方面允許進(jìn)程之間和線程之間共享資源。

            posted @ 2014-03-06 21:44 不會飛的鳥 閱讀(236) | 評論 (0)編輯 收藏

            [轉(zhuǎn)]相似圖片搜索的原理(二)

            昨天,我在isnowfy的網(wǎng)站看到,還有其他兩種方法也很簡單,這里做一些筆記。

            一、顏色分布法

            每張圖片都可以生成顏色分布的直方圖(color histogram)。如果兩張圖片的直方圖很接近,就可以認(rèn)為它們很相似。

            任何一種顏色都是由紅綠藍(lán)三原色(RGB)構(gòu)成的,所以上圖共有4張直方圖(三原色直方圖 + 最后合成的直方圖)。

            如果每種原色都可以取256個(gè)值,那么整個(gè)顏色空間共有1600萬種顏色(256的三次方)。針對這1600萬種顏色比較直方圖,計(jì)算量實(shí)在太大了,因此需要采用簡化方法??梢詫?~255分成四個(gè)區(qū):0~63為第0區(qū),64~127為第1區(qū),128~191為第2區(qū),192~255為第3區(qū)。這意味著紅綠藍(lán)分別有4個(gè)區(qū),總共可以構(gòu)成64種組合(4的3次方)。

            任何一種顏色必然屬于這64種組合中的一種,這樣就可以統(tǒng)計(jì)每一種組合包含的像素?cái)?shù)量。

            上圖是某張圖片的顏色分布表,將表中最后一欄提取出來,組成一個(gè)64維向量(7414, 230, 0, 0, 8, ..., 109, 0, 0, 3415, 53929)。這個(gè)向量就是這張圖片的特征值或者叫"指紋"。

            于是,尋找相似圖片就變成了找出與其最相似的向量。這可以用皮爾遜相關(guān)系數(shù)或者余弦相似度算出。

            二、內(nèi)容特征法

            除了顏色構(gòu)成,還可以從比較圖片內(nèi)容的相似性入手。

            首先,將原圖轉(zhuǎn)成一張較小的灰度圖片,假定為50x50像素。然后,確定一個(gè)閾值,將灰度圖片轉(zhuǎn)成黑白圖片。

              

            如果兩張圖片很相似,它們的黑白輪廓應(yīng)該是相近的。于是,問題就變成了,第一步如何確定一個(gè)合理的閾值,正確呈現(xiàn)照片中的輪廓?

            顯然,前景色與背景色反差越大,輪廓就越明顯。這意味著,如果我們找到一個(gè)值,可以使得前景色和背景色各自的"類內(nèi)差異最小"(minimizing the intra-class variance),或者"類間差異最大"(maximizing the inter-class variance),那么這個(gè)值就是理想的閾值。

            1979年,日本學(xué)者大津展之證明了,"類內(nèi)差異最小"與"類間差異最大"是同一件事,即對應(yīng)同一個(gè)閾值。他提出一種簡單的算法,可以求出這個(gè)閾值,這被稱為"大津法"(Otsu's method)。下面就是他的計(jì)算方法。

            假定一張圖片共有n個(gè)像素,其中灰度值小于閾值的像素為 n1 個(gè),大于等于閾值的像素為 n2 個(gè)( n1 + n2 = n )。w1 和 w2 表示這兩種像素各自的比重。

              w1 = n1 / n

              w2 = n2 / n

            再假定,所有灰度值小于閾值的像素的平均值和方差分別為 μ1 和 σ1,所有灰度值大于等于閾值的像素的平均值和方差分別為 μ2 和 σ2。于是,可以得到

              類內(nèi)差異 = w1(σ1的平方) + w2(σ2的平方)

              類間差異 = w1w2(μ1-μ2)^2

            可以證明,這兩個(gè)式子是等價(jià)的:得到"類內(nèi)差異"的最小值,等同于得到"類間差異"的最大值。不過,從計(jì)算難度看,后者的計(jì)算要容易一些。

            下一步用"窮舉法",將閾值從灰度的最低值到最高值,依次取一遍,分別代入上面的算式。使得"類內(nèi)差異最小"或"類間差異最大"的那個(gè)值,就是最終的閾值。具體的實(shí)例和Java算法,請看這里

            有了50x50像素的黑白縮略圖,就等于有了一個(gè)50x50的0-1矩陣。矩陣的每個(gè)值對應(yīng)原圖的一個(gè)像素,0表示黑色,1表示白色。這個(gè)矩陣就是一張圖片的特征矩陣。

            兩個(gè)特征矩陣的不同之處越少,就代表兩張圖片越相似。這可以用"異或運(yùn)算"實(shí)現(xiàn)(即兩個(gè)值之中只有一個(gè)為1,則運(yùn)算結(jié)果為1,否則運(yùn)算結(jié)果為0)。對不同圖片的特征矩陣進(jìn)行"異或運(yùn)算",結(jié)果中的1越少,就是越相似的圖片。

            posted @ 2014-03-06 21:42 不會飛的鳥 閱讀(346) | 評論 (0)編輯 收藏

            [轉(zhuǎn)]相似圖片搜索的原理(一)

            上個(gè)月,Google把"相似圖片搜索"正式放上了首頁。

            你可以用一張圖片,搜索互聯(lián)網(wǎng)上所有與它相似的圖片。點(diǎn)擊搜索框中照相機(jī)的圖標(biāo)。

            一個(gè)對話框會出現(xiàn)。

            你輸入網(wǎng)片的網(wǎng)址,或者直接上傳圖片,Google就會找出與其相似的圖片。下面這張圖片是美國女演員Alyson Hannigan。

            上傳后,Google返回如下結(jié)果:

            類似的"相似圖片搜索引擎"還有不少,TinEye甚至可以找出照片的拍攝背景。

            ==========================================================

            這種技術(shù)的原理是什么?計(jì)算機(jī)怎么知道兩張圖片相似呢?

            根據(jù)Neal Krawetz博士的解釋,原理非常簡單易懂。我們可以用一個(gè)快速算法,就達(dá)到基本的效果。

            這里的關(guān)鍵技術(shù)叫做"感知哈希算法"(Perceptual hash algorithm),它的作用是對每張圖片生成一個(gè)"指紋"(fingerprint)字符串,然后比較不同圖片的指紋。結(jié)果越接近,就說明圖片越相似。

            下面是一個(gè)最簡單的實(shí)現(xiàn):

            第一步,縮小尺寸。

            將圖片縮小到8x8的尺寸,總共64個(gè)像素。這一步的作用是去除圖片的細(xì)節(jié),只保留結(jié)構(gòu)、明暗等基本信息,摒棄不同尺寸、比例帶來的圖片差異。

             

            第二步,簡化色彩。

            將縮小后的圖片,轉(zhuǎn)為64級灰度。也就是說,所有像素點(diǎn)總共只有64種顏色。

            第三步,計(jì)算平均值。

            計(jì)算所有64個(gè)像素的灰度平均值。

            第四步,比較像素的灰度。

            將每個(gè)像素的灰度,與平均值進(jìn)行比較。大于或等于平均值,記為1;小于平均值,記為0。

            第五步,計(jì)算哈希值。

            將上一步的比較結(jié)果,組合在一起,就構(gòu)成了一個(gè)64位的整數(shù),這就是這張圖片的指紋。組合的次序并不重要,只要保證所有圖片都采用同樣次序就行了。

             =  = 8f373714acfcf4d0

            得到指紋以后,就可以對比不同的圖片,看看64位中有多少位是不一樣的。在理論上,這等同于計(jì)算"漢明距離"(Hamming distance)。如果不相同的數(shù)據(jù)位不超過5,就說明兩張圖片很相似;如果大于10,就說明這是兩張不同的圖片。

            具體的代碼實(shí)現(xiàn),可以參見Wote用python語言寫的imgHash.py。代碼很短,只有53行。使用的時(shí)候,第一個(gè)參數(shù)是基準(zhǔn)圖片,第二個(gè)參數(shù)是用來比較的其他圖片所在的目錄,返回結(jié)果是兩張圖片之間不相同的數(shù)據(jù)位數(shù)量(漢明距離)。

            這種算法的優(yōu)點(diǎn)是簡單快速,不受圖片大小縮放的影響,缺點(diǎn)是圖片的內(nèi)容不能變更。如果在圖片上加幾個(gè)文字,它就認(rèn)不出來了。所以,它的最佳用途是根據(jù)縮略圖,找出原圖。

            實(shí)際應(yīng)用中,往往采用更強(qiáng)大的pHash算法和SIFT算法,它們能夠識別圖片的變形。只要變形程度不超過25%,它們就能匹配原圖。這些算法雖然更復(fù)雜,但是原理與上面的簡便算法是一樣的,就是先將圖片轉(zhuǎn)化成Hash字符串,然后再進(jìn)行比較。

            posted @ 2014-03-06 21:42 不會飛的鳥 閱讀(318) | 評論 (0)編輯 收藏

            [轉(zhuǎn)]TF-IDF與余弦相似性的應(yīng)用(三):自動摘要

            有時(shí)候,很簡單的數(shù)學(xué)方法,就可以完成很復(fù)雜的任務(wù)。

            這個(gè)系列的前兩部分就是很好的例子。僅僅依靠統(tǒng)計(jì)詞頻,就能找出關(guān)鍵詞相似文章。雖然它們算不上效果最好的方法,但肯定是最簡便易行的方法。

            今天,依然繼續(xù)這個(gè)主題。討論如何通過詞頻,對文章進(jìn)行自動摘要(Automatic summarization)。

            如果能從3000字的文章,提煉出150字的摘要,就可以為讀者節(jié)省大量閱讀時(shí)間。由人完成的摘要叫"人工摘要",由機(jī)器完成的就叫"自動摘要"。許多網(wǎng)站都需要它,比如論文網(wǎng)站、新聞網(wǎng)站、搜索引擎等等。2007年,美國學(xué)者的論文《A Survey on Automatic Text Summarization》(Dipanjan Das, Andre F.T. Martins, 2007)總結(jié)了目前的自動摘要算法。其中,很重要的一種就是詞頻統(tǒng)計(jì)。

            這種方法最早出自1958年的IBM公司科學(xué)家H.P. Luhn的論文《The Automatic Creation of Literature Abstracts》。

            Luhn博士認(rèn)為,文章的信息都包含在句子中,有些句子包含的信息多,有些句子包含的信息少。"自動摘要"就是要找出那些包含信息最多的句子。

            句子的信息量用"關(guān)鍵詞"來衡量。如果包含的關(guān)鍵詞越多,就說明這個(gè)句子越重要。Luhn提出用"簇"(cluster)表示關(guān)鍵詞的聚集。所謂"簇"就是包含多個(gè)關(guān)鍵詞的句子片段。

            上圖就是Luhn原始論文的插圖,被框起來的部分就是一個(gè)"簇"。只要關(guān)鍵詞之間的距離小于"門檻值",它們就被認(rèn)為處于同一個(gè)簇之中。Luhn建議的門檻值是4或5。也就是說,如果兩個(gè)關(guān)鍵詞之間有5個(gè)以上的其他詞,就可以把這兩個(gè)關(guān)鍵詞分在兩個(gè)簇。

            下一步,對于每個(gè)簇,都計(jì)算它的重要性分值。

            以前圖為例,其中的簇一共有7個(gè)詞,其中4個(gè)是關(guān)鍵詞。因此,它的重要性分值等于 ( 4 x 4 ) / 7 = 2.3。

            然后,找出包含分值最高的簇的句子(比如5句),把它們合在一起,就構(gòu)成了這篇文章的自動摘要。具體實(shí)現(xiàn)可以參見《Mining the Social Web: Analyzing Data from Facebook, Twitter, LinkedIn, and Other Social Media Sites》(O'Reilly, 2011)一書的第8章,python代碼見github。

            Luhn的這種算法后來被簡化,不再區(qū)分"簇",只考慮句子包含的關(guān)鍵詞。下面就是一個(gè)例子(采用偽碼表示),只考慮關(guān)鍵詞首先出現(xiàn)的句子。

              Summarizer(originalText, maxSummarySize):

                // 計(jì)算原始文本的詞頻,生成一個(gè)數(shù)組,比如[(10,'the'), (3,'language'), (8,'code')...]
                wordFrequences = getWordCounts(originalText)

                // 過濾掉停用詞,數(shù)組變成[(3, 'language'), (8, 'code')...]
                contentWordFrequences = filtStopWords(wordFrequences)

                // 按照詞頻進(jìn)行排序,數(shù)組變成['code', 'language'...]
                contentWordsSortbyFreq = sortByFreqThenDropFreq(contentWordFrequences)

                // 將文章分成句子
                sentences = getSentences(originalText)

                // 選擇關(guān)鍵詞首先出現(xiàn)的句子
                setSummarySentences = {}
                foreach word in contentWordsSortbyFreq:
                  firstMatchingSentence = search(sentences, word)
                  setSummarySentences.add(firstMatchingSentence)
                  if setSummarySentences.size() = maxSummarySize:
                    break

                // 將選中的句子按照出現(xiàn)順序,組成摘要
                summary = ""
                foreach sentence in sentences:
                  if sentence in setSummarySentences:
                    summary = summary + " " + sentence

                return summary

            類似的算法已經(jīng)被寫成了工具,比如基于Java的Classifier4J庫的SimpleSummariser模塊、基于C語言的OTS庫、以及基于classifier4J的C#實(shí)現(xiàn)python實(shí)現(xiàn)。

            posted @ 2014-03-06 21:37 不會飛的鳥 閱讀(278) | 評論 (0)編輯 收藏

            僅列出標(biāo)題  下一頁
            久久久青草久久久青草| 久久精品国产亚洲沈樵| 久久人人爽人爽人人爽av| 午夜精品久久久久久久无码| 色婷婷狠狠久久综合五月| 一本久道久久综合狠狠爱| 91精品国产91久久综合| 国产真实乱对白精彩久久| 久久久久亚洲AV成人网| 婷婷久久综合九色综合绿巨人 | 久久天天躁狠狠躁夜夜avapp| 无码超乳爆乳中文字幕久久 | 无码国内精品久久综合88| 伊人色综合久久天天人手人婷 | 国产69精品久久久久777| 久久影院午夜理论片无码| 人妻精品久久久久中文字幕一冢本| 久久青青草原综合伊人| 狠狠色综合网站久久久久久久高清| 久久久久中文字幕| 色综合久久久久无码专区| 午夜福利91久久福利| 99久久婷婷国产一区二区| 久久综合给合久久国产免费 | 久久精品国产精品亚洲| 精品久久久久久亚洲精品| 亚洲精品第一综合99久久 | 久久婷婷五月综合97色| 久久久精品日本一区二区三区| 99国产欧美精品久久久蜜芽| 四虎国产精品成人免费久久| 国产精品久久久99| 四虎国产永久免费久久| 99久久99这里只有免费费精品| 久久综合久久综合亚洲| 久久久99精品一区二区| 丁香久久婷婷国产午夜视频| 久久天堂电影网| 久久亚洲精品视频| 久久精品成人| 88久久精品无码一区二区毛片|