基于GEOHASH算法的附近点搜索实现(一)

8 篇文章 0 订阅

1. 引入

最近在参加学校的计算机仿真大赛,时间好像有点不够,所以只完成了前面的一部分最基础的功能,中途还是选择了放弃。但是之前的部分的确觉得完成得还不错,在这里分享一下。题目是要完成一个宇宙飞船加油点的分配调度系统。完成的部分是给定坐标附近点的搜索。

2. 算法使用原因

我们要完成的一个是二维附近点搜索的算法。就是在给出若干个加油点的二维坐标,然后再给你一个当前坐标,你要搜索出距离当前坐标最近的一个加油站的坐标点。在考虑二维的附近点搜索时,最原始的方法肯定是将所有的加油点的坐标都加入到list中.然后遍历所有的节点,判断哪个节点的坐标距离自己最近。但是这样操作的话,由于我们要进行多次的附近点搜索,这样每次搜索的成本就会相当大,比如我们搜索 N次,一共有 M 个加油点,复杂度将达到 N*M ,降低搜索效率。
所以我们应该降低每次的搜索效率。然后想过要使用建立哈希表进行搜索,这样成本基本花在了建表上,搜索所花费的时间就会少很多。但是按照自己的想法,去建一个哈希表的确比较困难,而且当地图比较大时,空间复杂度会十分的高,因此这种方法还是需要改进。然后我查阅了一些资料,参阅到一些附近点搜索的经典算法,也就是将要介绍的GeoHash算法。能够将坐标变成特定的编码,然后进行对应哈希,还能够根据编码的前缀,来进行判断两点是否在附近。

3. 算法介绍

在一篇很经典的文章中有所介绍GeoHash下附链接:
http://blog.nosqlfan.com/html/1811.html
此博客中,简要地说明GeoHash算法的思想:

  1. 将地图四分,也就是分成左上、右上、左下、右下四个部分,然后对应的地图块的编码后面追加"01","11","00","10"
    |01|11|
    |---|---|
    |00|10|
  2. 然后再将四分后的各个地图块,重复步骤1,不断地进行四分,编码也会两位两位地进行增加。直到地图块不能再进行四分。
  3. 得到对应的每个坐标的编码。

这就是GeoHash最基础的算法,能够将地图上的每个坐标点都进行编码。然后根据这些编码,很快就可以发现一个规律,因为是按着每个地图4分的,所以这地图四分前的编码是相同的,即使4分之后,这4块地图还是有着相同的前缀,因此,我们可以根据编码的最长相同前缀,去找出距离最近的加油站的坐标。
但是!这个算法还是有很大的缺点的:
由于GeoHash是将区域划分为一个个规则矩形,并对每个矩形进行编码,会导致以下问题,比如红色的点是我们的位置,绿色的两个点分别是附近的两加油点,但是在查询的时候会发现距离较远加油站的GeoHash编码与我们一样(因为在同一个GeoHash区域块上),而较近加油站的GeoHash编码与我们不一致。这个问题往往产生在边界处。
错误情况
因此,我们需要对算法进行改进,解决的思路很简单,我们查询时,除了使用定位点的GeoHash编码进行匹配外,还使用周围8个区域的GeoHash编码,这样可以避免这个问题。

4. 需要解决的问题

  1. 坐标值转化为GeoHash编码值
  2. 根据当前区域的GeoHash,推算出周围8个方位区域块的的GeoHash值。
  3. 将这8个区域块中所有加油点进行储存,并且一一计算它们到当前坐标的距离,并且计算出最短距离的点。
  4. 考虑存储结构,以及算法实现。

5. 算法实现

因为项目开发的时候要可视化,所以当时就选择了使用C#。接下来的实现代码,都是C#编写的。

1. 坐标值转化为GeoHash

首先我们要知道当前地图大小为多少,取横坐标、纵坐标的中值,然后区分出4个区域,然后按照坐标所落到的区域,将对应的两位编号追加到地图的编码后,然后再将当前地图横坐标、纵坐标、都除以二,加上根据分块后地图改变的编号,以及坐标改变值,作为参数继续递归。递归结束的条件为地图的精度都已经减少为1的坐标的。
以下为实现代码:

//xb,yb分别为地图的横坐标和总坐标大小
//a,b,分别为给定点的横坐标、以及纵坐标
//code,为存储编码的字符串
public static void Encode(StringBuilder code, int xb, int yb, int a, int b)
{
    if (xb == 1 && yb == 1)
        return;
    if (a < xb / 2 && b < yb / 2)
    {
        code.Append("00");
        Encode(code, xb / 2, yb / 2, a, b);
    }
    else if (a < xb / 2 && b >= yb / 2)
    {
        code.Append("01");
        Encode(code, xb / 2, yb / 2, a, b - yb / 2);
    }
    else if (a >= xb / 2 && b < yb / 2)
    {
        code.Append("10");
        Encode(code, xb / 2, yb / 2, a - xb / 2, b);
    }
    else if (a >= xb / 2 && b >= yb / 2)
    {
        code.Append("11");
        Encode(code, xb / 2, yb / 2, a - xb / 2, b - yb / 2);
    }
    return;
}

2. 根据当前的编码,计算其余8个方位的编码

这个的实现其实也很简单。首先,我们其实先只需要搜寻实现上下左右4个方位的编码,那么根据一些小组合,剩下的8个方位,也同样能够计算得到了。
那么我们就要计算4个方位毗邻的编码。情况其实也很简单。
有一些区域的上级区域,与自己并不相同,因此前缀也肯定不相同。所以我们判断到上级区域不相同时,就要进行对上级区域的转变。但是要对上级区域进行改变的时候,发现上级的上级区域也不同,那又要再深一层地去改变。这样一直下去,就会同样形成一个递归的过程。用语言很那解释,但是大家把各个坐标的编码表都计算出来并且显示后,就很容易能够找到规律。
下面贴上代码供大家参考。

        public static void FindRight(StringBuilder code, int len)
        {
            if (len <= 0)
                return;
            if (code[len - 2] == '1')
            {
                FindRight(code, len - 2);
                code[len - 2] = '0';
            }
            else
            {
                code[len - 2] = '1';
            }
            return;
        }
 
        public static void FindLeft(StringBuilder code, int len)
        {
            if (len <= 0)
                return;
            if (code[len - 2] == '0')
            {
                FindLeft(code, len - 2);
                code[len - 2] = '1';
            }
            else
            {
                code[len - 2] = '0';
            }
            return;
        }
 
        public static void FindDown(StringBuilder code, int len)
        {
            if (len <= 0)
                return;
            if (code[len - 1] == '1')
            {
                FindDown(code, len - 2);
                code[len - 1] = '0';
            }
            else
            {
                code[len - 1] = '1';
            }
            return;
        }
 
 
        public static void Findup(StringBuilder code, int len)
        {
            if (len <= 0)
                return;
            if (code[len - 1] == '0')
            {
                Findup(code, len - 2);
                code[len - 1] = '1';
            }
            else
            {
                code[len - 1] = '0';
            }
            return; 
        }


https://www.cnblogs.com/subaiBlog/p/5410590.html


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Python中实现geohash算法,你可以使用geohash库。首先,你需要确保已经安装了geohash库。你可以使用pip命令进行安装,命令如下:pip install geohash。如果安装成功后,仍然无法导入geohash模块并提示ImportError: No module named 'geohash'的错误,你可以尝试以下方法进行修复:将Geohash文件名改为geohash,然后在geohash文件夹下的__init__.py文件中将from geohash import decode_exactly, decode, encode改为from .geohash import decode_exactly, decode, encode(在geohash前面加一个'.')。这样应该可以解决导入模块的问题。[1] 一旦你成功导入了geohash库,你就可以使用它来进行geohash算法实现。例如,你可以使用decode_exactly函数来将geohash字符串解码为经度和纬度的坐标。例如,你可以使用以下代码来解码geohash字符串"wm6nc":print(geohash.decode_exactly("wm6nc")),这将返回一个包含经度、纬度、经度精度和纬度精度的元组。(30.73974609375, 104.12841796875, 0.02197265625, 0.02197265625)[2] geohash库还提供了其他功能模块,如距离度量和几何计算。距离度量模块提供了与距离相关的函数,如distance和dimensions。几何模块提供了将多边形转换为geohash列表的函数,如polygon_to_geohashgeohash_to_polygon。这些功能可以帮助你在地理区域中进行近似地理差异的计算。你可以使用shapely库进行几何计算[3]。 综上所述,要在Python中实现geohash算法,你可以使用geohash库,并根据需要使用其提供的不同功能模块。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值