Go查找经纬度+优先级队列调度算法

445 阅读3分钟

在写东西的时候有一个想法:类似于网上商城发货,怎样根据订单目的地址的不同选择最优的仓库发货地址。

最直接的想法是给根据仓库地址选择最近的一个,百度地图提供了获取经纬度的API,通过调用接口来获取地址的经纬度.((12条消息) go实现访问百度地图api实现经纬度逆解析出地理位置收集_harderczw的博客-CSDN博客)。

但同时我们需要考虑一种情况,如果最近的仓库由于某种原因无法发货,那么就顺延按照第二近的仓库进行发货吗?理论上当然可以。但是我们也要考虑一些别的因素,例如第二近仓库的持有状态,第二近仓库本身所辐射的地区订单数量是否供不应求需要向其第二近的仓库进行调货? 我们可以通过构造优先级队列,或是构造决策树来综合考虑这些因素从而选择出最优的仓库

//存储信息
type Address struct {
   MODEL
   ProductID uint64 `json:"product_id" gorm:"product_id;not null"`
   DepotID   uint64 `json:"depot_id" gorm:"depot_id"` //仓库编号
   Count     uint   `json:"count" gorm:"count"`       //仓库持有量
}

```
//仓库信息
type Depot struct {
   MODEL
   DepotID   uint64  `json:"depot_id" gorm:"depot_id"`
   Path      string  `json:"path" gorm:"path;type:varchar(50);not null"` //地址
   Longitude float64 `json:"longitude" gorm:"longitude"`                 //经度
   Latitude  float64 `json:"latitude" gorm:"latitude"`                   //纬度
   Priority  int     `json:"priority" gorm:"priority;default:0"`         //优先级
}

通过传来的目的地址获取经纬度

type JWData struct {
   Lng float64   // 经度
   Lat float64   //纬度
}

type CtiyJWData struct {
   Location   JWData
   Precise    int
   Confidence int
   Level      string
}

type BodyData struct {
   Status string
   Result CtiyJWData
}

func Get_JWData_By_Ctiy(strCtiy string) (float64, float64) {
   //通过调用百度地图提供的API获取经纬度
   resp, err := http.Get("http://api.map.baidu.com/geocoder?address=" + strCtiy + "&output=json&key=pckg0S4gcS65cSZbRdlxyb4kTq3DIAsQ&city=" + strCtiy)
   if err != nil {
      return 0.0, 0.0
   }
   body, err := ioutil.ReadAll(resp.Body)
   if err != nil {
      return 0.0, 0.0
   }
   // 解析数据
   st := &BodyData{}
   json.Unmarshal(body, &st)
   //返回城市经纬度
   return st.Result.Location.Lat, st.Result.Location.Lng
}
// url
http://api.map.baidu.com/geocoder?address=地址&output=输出格式类型&key=用户密钥&city=城市名
http://api.map.baidu.com/geocoder?address=%E6%88%90%E9%83%BD&output=json&key=pckg0S4gcS65cSZbRdlxyb4kTq3DIAsQ&city=%E6%88%90%E9%83%BD

成功实现(以上海为例)

1664012611(1).png

附:在MySQL中通过经纬度坐标找出最近的位置

SELECT id,code,name,city_name,address,
	(6371 * acos(cos(radians(31.2433336586)) * cos(radians(latitude)) * cos(radians(longitude) - radians(121.4579772949)) + sin(radians(31.2433336586)) * sin(radians(latitude)))) AS distance 
FROM bmz_site ORDER BY distance LIMIT 0,10;
 
/*北京大成39.8995965929,116.4919853210 上海宝地31.2433336586,121.4579772949*/
接下来如何实现仓库的优先级队列呢?

heap是常用的实现优先队列的方法。heap包对任意实现了heap接口的类型提供堆操作。堆结构继承自sort.Interface, 而sort.Interface,需要实现三个方法:Len() int / Less(i, j int) bool / Swap(i, j int) 再加上堆接口定义的两个方法:Push(x interface{}) / Pop() interface{}。故只要实现了这五个方法,便定义了一个堆。数据结构STL——golang实现优先队列priority_queue - 知乎 (zhihu.com)

//Queue 是我们在优先队列中管理的东西
type Queue struct {
   DepotID  uint64  //仓库id
   Distance float64 //距离
   Priority int     //仓库优先级
   Amount   uint    //仓库持有货物量
   Index    int     //在堆中的索引
}


//优先级队列
type PriorityQueue []*Queue

//设置优先队列的比较器
func (pq PriorityQueue) Less(i, j int) bool {
   // 我们希望 Pop 给我们最高而不是最低的优先级,所以我们使用比这里更大的优先级。
   //赋予距离80%权重,仓库优先级15%权重,仓库持有量5%权重设置优先级队列的比较器
   return (1/pq[i].Distance*80 + float64(pq[i].Priority*15+int(pq[i].Amount)*5)) >
      (1/pq[j].Distance*80 + float64(pq[j].Priority*15+int(pq[j].Amount)*5))
}

func (pq *PriorityQueue) Push(x interface{}) {
   n := len(*pq)
   item := x.(*Queue)
   item.Index = n
   *pq = append(*pq, item)
   return
}

func (pq *PriorityQueue) Pop() interface{} {
   old := *pq
   n := len(old)
   item := old[n-1]
   old[n-1] = nil  // 避免内存泄漏
   item.Index = -1 // 为了安全
   *pq = old[0 : n-1]
   return item
}


// GenQueue 生成优先级队列
func GenQueue(pg *PriorityQueue) {
   heap.Init(pg)
}

```