【算法】Hash算法之FNV算法


HASH算法介紹

  • Hash,一般翻譯做“散列”,也有直接音譯為“哈希”的,就是把任意長度的輸入(又叫做預映射, pre-image),通過散列算法,變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間通常遠小於輸入的空間,不同的輸入可能會散列成相同的輸出,而不可能從散列值來唯一地確定輸入值。

  • 數學表述為:h = H(M) ,其中H( )--單向散列函數,M--任意長度明文,h--固定長度散列值。

HASH算法的實際應用-加密

  • 常見的哈希加密算法:MD5,SHA-1,SHA-2,SHA-256,SHA-X(系列)

  • 1) 文件校驗:MD5 Hash算法的“數字指紋”特性,使它成為目前應用最廣泛的一種文件完整性校驗和(Checksum)算法,不少Unix系統有提供計算md5 checksum的命令;

  • 2) 數字簽名:在這種簽名協議中,雙方必須事先協商好雙方都支持的Hash函數和簽名算法。簽名方先對該數據文件進行計算其散列值,然后再對很短的散列值結果--如Md5是16個字節,SHA1是20字節,用非對稱算法進行數字簽名操作。對方在驗證簽名時,也是先對該數據文件進行計算其散列值,然后再用非對稱算法驗證數字簽名; (實際是HASH+非對稱加密)

  • 3) 鑒權協議:需要鑒權的一方,向將被鑒權的一方發送隨機串(“挑戰”),被鑒權方將該隨機串和自己的鑒權口令字一起進行 Hash 運算后,返還鑒權方,鑒權方將收到的Hash值與在己端用該隨機串和對方的鑒權口令字進行 Hash 運算的結果相比較(“認證”),如相同,則可在統計上認為對方擁有該口令字,即通過鑒權。(摘要)

  • HASH加密算法與其他加密算法的主要不同點是:哈希(Hash)算法是一種單向密碼體制,即只有   加密過程,沒有解密過程

HASH算法的實際應用-查找

  • 常見的哈希查找算法:BKDRHash,APHash,DJBHash,JSHash,RSHash,SDBMHash等

  • 1) 基本思想是:以數據對象的關鍵詞 key 為自變量,通過一個確定的函數關系 h ,計算出對應的函數值 (𝑘𝑒𝑦)h(key) ,把這個值解釋為數據對象的存儲地址,並按此存放,即“=(𝑘𝑒𝑦)存儲位置=h(key)”。

  • Gdb下函數符號實際對應的是一個內存地址,映射大小為32bit或64bit(即32位系統或64位系統)

FNV算法介紹

  • FNV哈希算法全名為Fowler-Noll-Vo算法,是以三位發明人Glenn Fowler,Landon Curt Noll,Phong Vo的名字來命名的,最早在1991年提出

  • 特點和用途:FNV能快速hash大量數據並保持較小的沖突率,它的高度分散使它適用於hash一些非常相近的字符串,比如URL,hostname,文件名,text,IP地址等。

  • 適用范圍:比較適用於字符串比較短的哈希場景

   FNV哈希算法有如下兩種,FNV-1a相比FNV-1,散列分布更好。二者不同點為:for循環兩行代碼的順序相反

   

  哈希函數一般適用移位和乘除法來實現。哈希函數一般都比較精簡,算法復雜度比較低。哈希函數的移位和乘除法可能會導致數據丟失,這也是哈希不可逆的原因 

FNV算法說明-1

  • hash值:一個n位的unsigned int型hash值,初始值為offset_basis.

  • offset_basis:初始的哈希值,該值在最早的版本中是0,為了增強哈希的可靠性,后續修改為非0的值,通過如下算法獲取

  

  參見《生成offset_basis.py》

FNV算法說明-2

  octet_of_data:8位數據(即一個字節):即需要被哈希的字符串

  FNV_prime:FNV用於散列的質數(質數在哈希算法中發揮着重要作用,在一般使用的哈希除留余數法中: H(key) = key MOD p,p也要求是一個質數(質數也稱為素數))

  • 32 bit FNV_prime = 224 + 28 + 0x93 = 16777619

  • 64 bit FNV_prime = 240 + 28 + 0xb3 = 1099511628211

  • 128 bit FNV_prime = 288 + 28 + 0x3b = 309485009821345068724781371

  • 256 bit FNV_prime = 2168 + 28 + 0x63 = 374144419156711147060143317175368453031918731002211

  • 512 bit FNV_prime = 2344 + 28 + 0x57 = 35835915874844867368919076489095108449946327955754392558399825615420669938882575126094039892345713852759

FNV算法使用

  • 支持將字符串哈希為32/64/128/256/512/1024bit的數字

  • 支持將字符串哈希為任意bit的數字,比如11/33bit

  • 支持將字符串哈希為特定范圍的數字,比如[0,99999]

將字符串哈希為32bit的數字

hash = offset_basis 

for each octet_of_data to be hashed

    hash = hash xor octet_of_data

    hash = hash * FNV_prime

return hash

  參見《生成32位哈希.py》

將字符串哈希為特定范圍的數字

將字符串哈希為[0,999]的數字
#define TRUE_HASH_SIZE ((u_int32_t)50000) /* range top plus 1 */
#define FNV_32_PRIME ((u_int32_t)16777619)
#define FNV1_32_INIT ((u_int32_t)2166136261)
#define MAX_32BIT ((u_int32_t)0xffffffff) /* largest 32 bit unsigned value */
#define RETRY_LEVEL ((MAX_32BIT / TRUE_HASH_SIZE) * TRUE_HASH_SIZE)
u_int32_t hash;
void *data;
size_t data_len;

hash = fnv_32_buf(data, data_len, FNV1_32_INIT);
while (hash >= RETRY_LEVEL) {
    hash = (hash * FNV_32_PRIME) + FNV1_32_INIT;
}
hash %= TRUE_HASH_SIZE;

 

  參見《生成任意范圍哈希.py》,代碼附件鏈接

Java代碼Demo

  1 /**
  2  * FNV算法
  3  * 參考:https://www.cnblogs.com/charlieroro/archive/2018/03/01/8486941.html
  4  */
  5 public class FNC {
  6 
  7     public static int FNV1_32_HASH(String data) {
  8         //  p = FNV_prime:FNV用於散列的質數
  9         final int p = 16777619;
 10         // hash = offset_basis:初始的哈希值
 11         int hash = (int)2166136261L;
 12         for(int i=0;i<data.length();i++) {
 13             hash = (hash ^ data.charAt(i)) * p;
 14         }
 15         hash += hash << 13;
 16         hash ^= hash >> 7;
 17         hash += hash << 3;
 18         hash ^= hash >> 17;
 19         hash += hash << 5;
 20         return hash;
 21     }
 22 
 23     /**
 24      * 測試散列IP的情況
 25      */
 26     @Test
 27     public void testIP_FNV1_32_HASH() {
 28         // 總個數
 29         long totalIp = 0;
 30         // 小於0個數
 31         long ltCount = 0;
 32         // 等於0個數
 33         long eqCount = 0;
 34         // 大於0個數
 35         long gtCount = 0;
 36         int maxIpHash = 0;
 37         int minIpHash = 0;
 38 
 39         SortedMap<Integer, Long> sortedMap = new TreeMap<>();
 40         // 生成ip
 41 //        int[] ip = new int[]{0, 0, 0, 0};
 42         long startTime = System.currentTimeMillis();
 43         for (int i = 0; i < 100; i++) {
 44             for (int j = 0; j < 100; j++) {
 45                 for (int k = 0; k < 100; k++) {
 46                     for (int l = 0; l < 100; l++) {
 47 //                        ip[0] = i;
 48 //                        ip[1] = j;
 49 //                        ip[2] = k;
 50 //                        ip[3] = l;
 51                         StringBuilder ip = new StringBuilder();
 52                         ip.append(i);
 53                         ip.append(".");
 54                         ip.append(j);
 55                         ip.append(".");
 56                         ip.append(k);
 57                         ip.append(".");
 58                         ip.append(l);
 59                         int hash = FNC.FNV1_32_HASH(ip.toString());
 60 
 61                         // 統計
 62                         // 小於0個數
 63                         if(hash < 0) {
 64                             minIpHash = Math.min(hash, minIpHash);
 65                             ltCount++;
 66                         } else if (hash == 0) {
 67                             eqCount++;
 68                         } else {
 69                             maxIpHash = Math.max(hash, maxIpHash);
 70                             gtCount++;
 71                             // 計算正數的區間
 72                             int key = hash / (Integer.MAX_VALUE / 100);
 73                             if (key == 100) {
 74                                 System.out.println("key = " + key + "\thash = " + hash);
 75                             }
 76                             sortedMap.put(key, sortedMap.getOrDefault(key, 0l) + 1);
 77                         }
 78                         totalIp++;
 79                     }
 80                 }
 81             }
 82         }
 83 
 84         // ----------------------
 85         long endTime = System.currentTimeMillis();
 86         System.out.println("endTime - startTime = " + (endTime - startTime));
 87 
 88         System.out.println("totalIp = " + totalIp);
 89         System.out.println("Integer.MIN_VALUE = " + Integer.MIN_VALUE);
 90         System.out.println("Integer.MAX_VALUE = " + Integer.MAX_VALUE);
 91         System.out.println("minIpHash = " + minIpHash);
 92         System.out.println("maxIpHash = " + maxIpHash);
 93         System.out.println("ltCount = " + ltCount);
 94         System.out.println("eqCount = " + eqCount);
 95         System.out.println("gtCount = " + gtCount);
 96         long[] mapArr = new long[]{Long.MAX_VALUE, 0l, 0l, 0l};
 97         sortedMap.forEach((k, v) -> {
 98             long min = Math.min(v, mapArr[0]);
 99             if (min != mapArr[0]) {
100                 mapArr[1] = mapArr[0];
101                 mapArr[0] = min;
102             }
103             long max = Math.max(v, mapArr[3]);
104             if (max != mapArr[3]) {
105                 mapArr[2] = mapArr[3];
106                 mapArr[3] = max;
107             }
108             System.out.println("(k, v) = " + "("+k+", "+v+")");
109         });
110         System.out.println("Arrays.toString(mapArr) = " + Arrays.toString(mapArr));
111         System.out.println("-------------------");
112 //        System.out.println("256*256*256*256 = " + 256l*256*256*256);
113     }
114 } 

 

參考:http://www.isthe.com/chongo/tech/comp/fnv/index.html#lazy-mod

原文連接:https://www.cnblogs.com/charlieroro/archive/2018/03/01/8486941.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM