zoukankan      html  css  js  c++  java
  • GeoHash

      查找是我们经常会碰到的问题,以前我做过一个这样的算法,在有序的数列(80万条左右),这批数据是根据维度由小到大排序的,寻找已知数据的位置,并且所相应的运算,由于这个算法要在嵌入式系统中做,如果一次在内存中载入80万条数据是不明智的。而且这个计算过程是每秒都要做一次,如果不在内存中载入数据时效性上又无法满足,那么只能根据已知数据在整个数列中的位置,然后将这个位置附近的数据载入内存。这个算法与在百度地图上查找最近的饭店基本是一个意思。我们需要对查找的数据库做处理,一维数据要变成二维数据,这是我自己的想法,将80万条数据放在一个个的方框里。然后确定每一个方框边界的经度值与维度值,如下图所示,然后将这些边界值根据经度与维度分别做两张索引表,当然这个表也是有序的,然后根据自己当前的经纬度坐标值(lat,lon)分别用二分法查找自己在表里的位置,得出自己坐标点所在方框的坐标。然后将方框里的数据全部读入内存。当然里面还有边界问题,实际上需要将自己所在方框的周围8个方框都读入内存。如下图所示:

    这个其实就是哈希散列的思想。这个方法是可行的,而且已经实现,不过有一个问题是,如何将这80万比数据变成一个一个方框的数据结构按照线性保存起来。这个存储结构应该是这样的:

    这是一个示意图,估算一下大概的计算量,如果要将80万笔数据分别根据经度维度一个一个对比放入框框中,这个时间复杂度是无法接受的。同样这也要设计一个哈希函数来完成这个工作,下面说重点:

    所谓的GeoHash算法。

    我们已知经纬度(22.527051,113.918646)先用逼近算法对其进行编码,具体做法如下:

    以经度为例,value =113.918646,大于中间值为1,小于中间值为0。这个区间如何得来的呢?要知道经度的范围是[180,-180],那么中间值就是0,判断是在[180,0]还是[0,-180]如果在[180,0]就是1,然后缩小区间,在[180,0]这个区间

    与中间值90比较,以此类推。最后得到编码值是:11010 00100,一共10位,为什么是10位与后面混合编码转换成base32编码有关。

    同理得到维度22.527051的编码值是:10100 00000,最后要将纬度编码与经度编码混合,偶数放经度,奇数放纬度,这个可能不好理解我做一张图:

    合成之后在转换成Base32编码,Base32编码是0-9、b-z(去掉a, i, l, o),这种编码方式是32以内的十进制,正好是5位二进制数,所以之前经纬度编码选择的是10位,因此正好可以换成2个字符。如下表:

    最后可得出对应的值:ws10。

    这个GeoHash算法网上有很多,我参考的是这个:http://www.cnblogs.com/LBSer/p/3310455.html

    至于为什么这样编码里面有解释自己理解吧。我用c写了一下实现,不具有普遍性。

    void GeoHash(double lat,double lon,int flag,char* hashCode)
    {
        double left,right,middle,value;
        int ret_lat,ret_lon,temp,temp1;
        int mixLatLon=0;
        right = 180;
        left = -180;
        ret_lat = ret_lon = 0;
        temp = flag;
        value = lon;
        while(flag){
            middle = (float)((right+left)/2);
            if(value>middle){
                ret_lon |= (1<<(flag-1));
                left = middle;
            }else if(value < middle){
                ret_lon |= (0<<(flag-1));
                right = middle;
            }else{
                ret_lon |= (1<<(flag-1));
                left = middle;
            }
            flag--;
        }
        flag = temp;
        right = 90;
        left = -90;
        value = lat;
        printf("value:%f
    ",value);
        while(flag){
            middle = (float)((right+left)/2);
            if(value>middle){
                ret_lat |= (1<<(flag-1));
                left = middle;
            }else if(value < middle){
                ret_lat |= (0<<(flag-1));
                right = middle;
            }else{
                ret_lat |= (1<<(flag-1));
                left = middle;
            }
            flag--;
        }
        flag = temp;
        flag = flag*2;
        temp1 = temp;
        while(temp1){
            if(ret_lon & (1<<(temp1-1))){
                mixLatLon |= (1<<(flag-1));
            }else{
                mixLatLon |= (0<<(flag-1));
            }
            flag--;
            if(ret_lat & (1<<(temp1-1))){
                mixLatLon |= (1<<(flag-1));
            }else{
                mixLatLon |= (0<<(flag-1));
            }
            temp1--;
            flag--;
        }
        flag = 4;
        while(flag){    
            hashCode[flag-1] = ToBase32(mixLatLon&0x1F);
            mixLatLon >>= 5;
            flag--;
        }
    }
    
    char ToBase32(int dec)
    {
        char ret;
        switch(dec){
            case 0:ret='0';break;
            case 1:ret='1';break;
            case 2:ret='2';break;
            case 3:ret='3';break;
            case 4:ret='4';break;
            case 5:ret='5';break;
            case 6:ret='6';break;
            case 7:ret='7';break;
            case 8:ret='8';break;
            case 9:ret='9';break;
            case 10:ret='b';break;
            case 11:ret='c';break;
            case 12:ret='d';break;
            case 13:ret='e';break;
            case 14:ret='f';break;
            case 15:ret='g';break;
            case 16:ret='h';break;
            case 17:ret='j';break;
            case 18:ret='k';break;
            case 19:ret='m';break;
            case 20:ret='n';break;
            case 21:ret='p';break;
            case 22:ret='q';break;
            case 23:ret='r';break;
            case 24:ret='s';break;
            case 25:ret='t';break;
            case 26:ret='u';break;
            case 27:ret='v';break;
            case 28:ret='w';break;
            case 29:ret='x';break;
            case 30:ret='y';break;
            case 31:ret='z';break;
            default:ret='a';
        }
        return ret;
    }

    调用代码:

    int main(int argc, char *argv[]) {
       char hashCode[4];
        GeoHash(22.527051,113.918646,10,hashCode);
        printf("hashCode:%s",hashCode);
        printf("
    ");
        system("pause");
        return 0;
    }

    结果是:ws10

    这里碰到一个问题,在c中,如果想要函数返回一个字符串有哪些做法呢:

    1.使用堆空间,返回申请的堆地址,注意释放
    2.函数参数传递指针,返回该指针
    3.返回函数内定义的静态变量(共享)
    4.返回全局变量
    另外注意一点,在c中,浮点数运算要注意,如果定义的是float,如果超过范围会自动变成double。例如如果我定义函数是void GeoHash(float lat,float lon,int flag,char* hashCode);
    而在调用的时候传入的实参超出范围这个时候会变成double,而在函数体中保存实参的变量lat与lon定义的是float,这个时候就会出现double 强制转换成float,因引起精度损失出错。
    这个只不过是自己的流水账,偶尔有一些心得,错误的地方概不负责
  • 相关阅读:
    常见sql注入的防范总结
    Hadoop各个组件与端口
    Jenkins HA高可用参考
    zookeeper的主要应用
    Jenkins常见REST API(便于将Jenkins集成到其他系统)
    使用pscp/pslurp批量并发分发/回收文件
    kv数据库对比总结
    /usr/bin/curl: Argument list too long的解决方法
    优秀的开源监控系统梳理
    Linux socat轻松实现TCP/UDP端口转发
  • 原文地址:https://www.cnblogs.com/ashitaka/p/6007539.html
Copyright © 2011-2022 走看看