【分享】二分逼近算法 高效解析800万大数据之区域分布

qqzeng-ip 2014-05-27 10:20:52
加精
最新IP地址数据库 来自 qqzeng.com

利用二分逼近法(bisection method) ,解析800多万IP 只需几十秒, 比较高效!

原来的顺序查找算法 效率比较低

 readonly string ipBinaryFilePath = "qqzengipdb.dat";
readonly byte[] dataBuffer, indexBuffer;
readonly uint[] index = new uint[256];
readonly int dataLength;
public IpLocation()
{
try
{
FileInfo file = new FileInfo(ipBinaryFilePath);
dataBuffer = new byte[file.Length];
using (var fin = new FileStream(file.FullName, FileMode.Open, FileAccess.Read))
{
fin.Read(dataBuffer, 0, dataBuffer.Length);
}
var offset_len = BytesToLong(dataBuffer[0], dataBuffer[1], dataBuffer[2], dataBuffer[3]); //Big Endian
indexBuffer = new byte[offset_len];
Array.Copy(dataBuffer, 4, indexBuffer, 0, offset_len);
dataLength = (int)offset_len;
for (int loop = 0; loop < 256; loop++)
{
//索引 四字节 LITTLE_ENDIAN
index[loop] = BytesToLong(indexBuffer[loop * 4 + 3], indexBuffer[loop * 4 + 2], indexBuffer[loop * 4 + 1], indexBuffer[loop * 4]);
}
}
catch { }
}

public string[] Find(string ip)
{
var ips = ip.Split('.');
uint ip_prefix = uint.Parse(ips[0]);
uint find_uint32 = BytesToLong(byte.Parse(ips[0]), byte.Parse(ips[1]), byte.Parse(ips[2]), byte.Parse(ips[3]));//BIG_ENDIAN

// LITTLE_ENDIAN
int max_len = 0;
int resultOffset =-1;
int resultLegth =-1;

uint start = index[ip_prefix] * 8 + 1024;
if (ip_prefix != 255)
{
max_len = (int)index[ip_prefix + 1] * 8 + 1024;
}
else
{
max_len = (int)index[255] * 8 + 1024+1;
}

for (; start < max_len; start += 8)
{
// 前四位 结束ip 后三位 偏移 最后一位 内容长度
uint endipNum = BytesToLong(indexBuffer[start + 0], indexBuffer[start + 1], indexBuffer[start + 2], indexBuffer[start + 3]);//BIG_ENDIAN
if (endipNum >= find_uint32)
{
resultOffset =(int) BytesToLong((byte)0, indexBuffer[start + 6], indexBuffer[start + 5], indexBuffer[start + 4]);//LITTLE_ENDIAN
resultLegth = 0xFF & indexBuffer[start + 7];// 长度
break;
}
}
if (resultOffset==-1||resultLegth==-1)
{
return new string[] {"N/A"};
}
var areaBytes = new byte[resultLegth];
Array.Copy(dataBuffer, dataLength + resultOffset - 1024, areaBytes, 0, resultLegth);
return Encoding.UTF8.GetString(areaBytes).Split(' ');
}


private static uint BytesToLong(byte a, byte b, byte c, byte d)
{

return ((uint)a << 24) | ((uint)b << 16) | ((uint)c << 8) | (uint)d;
}


public static string long2IP(long longIP)
{
StringBuilder sb = new StringBuilder("");
sb.Append(longIP >> 24);
sb.Append(".");
//将高8位置0,然后右移16为

sb.Append((longIP & 0x00FFFFFF) >> 16);
sb.Append(".");

sb.Append((longIP & 0x0000FFFF) >> 8);
sb.Append(".");
sb.Append((longIP & 0x000000FF));

return sb.ToString();

}
  
}



改进版 采用二分逼近 算法(类似二分查找,但又不同) 性能提升很大

 public string[] Find(string ip)
{
var ips = ip.Split('.');
uint ip_prefix = uint.Parse(ips[0]);
uint find_uint32 = BytesToLong(byte.Parse(ips[0]), byte.Parse(ips[1]), byte.Parse(ips[2]), byte.Parse(ips[3]));//BIG_ENDIAN
uint max_len = 0;
int resultOffset = -1;
int resultLegth = -1;
uint start = index[ip_prefix];
if (ip_prefix != 255)
{
max_len = index[ip_prefix + 1];
}
else
{
max_len = index[255];
}
uint num = max_len - start;
uint my_index = BinarySearch(start, max_len, find_uint32);
start = my_index * 8 + 1024;
resultOffset = (int)BytesToLong((byte)0, indexBuffer[start + 6], indexBuffer[start + 5], indexBuffer[start + 4]);//LITTLE_ENDIAN
resultLegth = 0xFF & indexBuffer[start + 7];// 长度

if (resultOffset == -1 || resultLegth == -1)
{
return new string[] { "N/A" };
}
var areaBytes = new byte[resultLegth];
Array.Copy(dataBuffer, dataLength + resultOffset - 1024, areaBytes, 0, resultLegth);
return Encoding.UTF8.GetString(areaBytes).Split(' ');
}



/// <summary>
/// 二分逼近
/// </summary>
public uint BinarySearch(uint low, uint high, uint k)
{
uint M = 0;
while (low <= high)
{
uint mid = (low + high) / 2;
uint endipNum = GetStartIp(mid);
if (endipNum >= k)
{
M = mid; //mid有可能是解
high = mid - 1;
}
else
low = mid + 1;
}
return M;
}


有了上面高效算法 解析出来800多万数据 也很快 再用一个简单的ling 统计一下即可

  var cn_result= from r in list
group r by r.cn into g
select new { key = g.Key, cnt = g.Count() };

800多万数据 统计组图






微信


详情:http://zengxiangzhan.cnblogs.com/p/qqzengipdb-dat.html

...全文
2968 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
提香 2014-11-06
  • 打赏
  • 举报
回复
Trust-Me 2014-05-30
  • 打赏
  • 举报
回复
抱歉,第一眼看成了 二逼分近法
  • 打赏
  • 举报
回复
这题目好nb,一定要看看内容。
Change_L 2014-05-29
  • 打赏
  • 举报
回复
看错标题的飘过
yzf86211861 2014-05-28
  • 打赏
  • 举报
回复
MARK好像很厉害的样子
bfdeh 2014-05-28
  • 打赏
  • 举报
回复
引用 12 楼 hell_007 的回复:
[quote=引用 9 楼 vnvlyp 的回复:] 我能说我看成了二逼算法了吗。。 还以为是什么高级新算法。。
我也是[/quote]我也是。
  • 打赏
  • 举报
回复
引用 10 楼 zengzhan 的回复:
二分逼近 原理和二分查找 差不多 稍微有些不同 一般情况下都是二分给定的数据中有的数字,但是这儿行不通 可以用二分逼近 要往大的逼近
你大概上学时没有学过数据结构课程,谁也没搜索只能处理“数字”。所有排序查找算法,都是针对“可比大小”的元素进行,而不是什么“针对数字”进行。
xwj 2014-05-28
  • 打赏
  • 举报
回复
学习了,谢谢!!
laoer_2002 2014-05-28
  • 打赏
  • 举报
回复
谢谢楼主分享
vnvlyp 2014-05-27
  • 打赏
  • 举报
回复
我能说我看成了二逼算法了吗。。 还以为是什么高级新算法。。
_VINCE_ 2014-05-27
  • 打赏
  • 举报
回复
感谢分享。。。。
vb2014 2014-05-27
  • 打赏
  • 举报
回复
前排,混分数
yzf86211861 2014-05-27
  • 打赏
  • 举报
回复
学习啊啊啊啊啊啊
caroliang2014 2014-05-27
  • 打赏
  • 举报
回复
学习当中。。。。。。
  • 打赏
  • 举报
回复
引用 2 楼 heprisoner_901562469 的回复:
用二分法应该排序先吧
二分法肯定要排序,不排序还怎么二分
qqzeng-ip 2014-05-27
  • 打赏
  • 举报
回复
引用 2 楼 heprisoner_901562469 的回复:
用二分法应该排序先吧
IP库里面已经是排好序了的
  • 打赏
  • 举报
回复
用二分法应该排序先吧
淡淡的活着 2014-05-27
  • 打赏
  • 举报
回复
yibey 2014-05-27
  • 打赏
  • 举报
回复
其实我也中枪,看成二逼算法了。。。
jimil 2014-05-27
  • 打赏
  • 举报
回复
引用 15 楼 diaodiaop 的回复:
首先我来说下 你这广告打的真好.... 别跟我说不是,,我呵呵..... 后来我发现 竟然还要购买... 我会说我直接downloadstring ip138拿地址吗? 人家的可是实时更新的.. 好吧 频繁访问 封我IP...不过我又找到了淘宝的api..更不错..返回不仅仅是城市. 更主要的 是不花钱..

using System;
using System.Net;

namespace App_Code
{
    public class AddressHelper
    {
        public static string PostIP = "http://ip.taobao.com/service/getIpInfo.php?ip=";
        public static AddressInfo GetAddress(string ip)
        {
            var addr = "";
            try
            {
                using (WebClient c = new WebClient())
                {
                    string s = c.DownloadString(PostIP + ip);
                    addr = s;
                }
                return Newtonsoft.Json.JsonConvert.DeserializeObject<AddressInfo>(addr);
            }
            catch (Exception)
            {
                return new AddressInfo(){
                    code = 0,
                    data = new data() { 
                        region="未知省份",
                        city = "未知城市"
                    }
                };
            }
        }
    }
    [Serializable]
    public class AddressInfo
    {
        public int code { get; set; }
        public data data { get; set; }
    }
    [Serializable]
    public class data
    {
        public string country { get; set; }
        public string country_id { get; set; }
        public string area { get; set; }
        public string area_id { get; set; }
        /// <summary>
        /// 省份
        /// </summary>
        public string region { get; set; }
        public string region_id { get; set; }
        /// <summary>
        /// 市
        /// </summary>
        public string city { get; set; }
        public string city_id { get; set; }
        public string county { get; set; }
        public string county_id { get; set; }
        public string isp { get; set; }
        public string isp_id { get; set; }
        public string ip { get; set; }

    }
}
大家可以访问下..
哥们好激动,什么广告什么买?他不是共享代码吗?
加载更多回复(6)

62,046

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧