業務的要求
- 判斷一個IP是不是屬於中國的
- 時間復雜度盡量小,適用於請求量大的業務場景
- 不要請求第三方接口,防止出現網絡異常
由於只需要判斷到國家的緯度,所以不需要用到純真IP庫這樣龐大的IP庫,通過網上的查詢,發現有個中國IP段的列表
http://ipblock.chacuo.net/down/t_txt=c_CN
可以看到IP段有2K個左右,如果用二分查詢法,時間復雜度是logN。基本上能夠滿足需求了。
實現步驟
一、 整理IP段配置
為了方便IP進行比較,這里將IP轉換為long格式。
- 把數據load進來,取第一第二行,ip2long處理
- 第一行保存在left,第二行保存在right中
- 然后根據left進行排序
最終得到的結果,如下:
<?php
#部分數據
array (
0 =>
array (
'left' => '16777472',
'right' => '16778239',
),
1 =>
array (
'left' => '16779264',
'right' => '16781311',
),
2 =>
array (
'left' => '16785408',
'right' => '16793599',
),
3 =>
array (
'left' => '16842752',
'right' => '16843007',
),
4 =>
array (
'left' => '16843264',
'right' => '16859135',
)
);
二、二分查找法
function binarySearch(Array $arr, $target) {
$low = 0;
$high = count($arr) - 1;
while($low <= $high) {
$mid = floor(($low + $high) / 2);
#找到元素
if($target>=$arr[$mid]['left'] && $target<=$arr[$mid]['right']){
return true;
}
#中元素比目標大,查找左部
if($target < $arr[$mid]['left'] ){
$high = $mid - 1;
}
#中元素比目標小,查找右部
if($target > $arr[$mid]['left'] ){
$low = $mid + 1;
}
}
#查找失敗
return false;
}
bitmap算法是否可行?
ip地址轉為long的最大值剛好是2^32-1 ,那么在redis中我們剛好可以申請一個4294967295的bitmap,占用內存為512M。
如果能夠把所有中國的IP對應的long都設置為1,那么每次查詢都只要0(1),遇到的問題是redis的setbit怎么批量設置呢?如果要一個ip設置一次的話,需要設置3億次,這個比較難實現。。