美文网首页
《算法图解》笔记 iii

《算法图解》笔记 iii

作者: 寒食君 | 来源:发表于2018-06-02 16:46 被阅读187次

迪克斯特拉算法

在图中,搜索最小的“段”数可以用广度优先算法,这就相当于默认每条边的权重是相同的,如果每条边的权重不同呢?那就需要用到迪克斯特拉算法。

概括来说,迪克斯特拉算法就是从起点开始,首先寻找最廉价的节点,更新其开销并标记为已处理,然然后在未处理的节点中寻找开销最小的节点,然后以此往复下去。

针对书中的这样一个问题,我把题干提取出来:目标是用乐谱换钢琴。现在乐谱可以免费换海报;海报加30元换吉他;海报加35元换架子鼓;乐谱加5元可以换唱片;唱片加15元换吉他;唱片加20元换架子鼓;吉他加20元换钢琴;架子鼓加10元换钢琴。

现在我用图把这个关系表示出来:

image

可以看出这是一个加权图,现在我们要使用迪克斯特拉算法寻找最短路径。

代码实现:

#检索图
def dijkstra_find(costs,parent,processed):
    #找到当前最廉价的节点
    node =lowest_cost_node(costs,processed)
    while node is not None:
        cost=costs[node]
        if not graph.__contains__(node):
            break
        neighbours=graph[node]
        for key in neighbours.keys():
            new_cost=cost+neighbours[key]
            if costs.__contains__(key):
                if costs[key] > new_cost:
                    costs[key] = new_cost
                    parent[key] = node
            else:
                costs[key]=new_cost
                parent[key] = node

        processed.append(node)
        node = lowest_cost_node(costs,processed)

#在开销表中寻找最廉价的节点
def lowest_cost_node(costs,processed):
    lowest_cost=float("inf")
    lowest_node=None
    for node in costs:
        cost=costs[node]
        if cost<lowest_cost and node not in processed:
            lowest_cost=cost
            lowest_node=node
    return lowest_node

if __name__ == '__main__':
    #要检索的图
    graph={}
    graph['music']={}
    graph['music']['record']=5
    graph['music']['poster']=0
    graph['record'] = {}
    graph['record']['guitar']=15
    graph['record']['drum']=20
    graph['poster'] = {}
    graph['poster']['guitar']=30
    graph['poster']['drum']=35
    graph['drum'] = {}
    graph['drum']['piano']=10
    graph['guitar'] = {}
    graph['guitar']['piano']=20

    #开销表
    infinity=float('inf')
    cost={}
    cost['record']=5
    cost['poster']=0

    #线路表
    parent={}
    parent['record']='music'
    parent['poster']='music'

    #已检索过的节点
    processed=[]

    dijkstra_find(cost,parent,processed)
    #打印父子节点表,通过父子节点表可以找到路径
    print(parent)

输出:

image

最后的最低开销表为:
| 节点 | 开销 |
---|-----|------|
| 海报 | 0 |
| 唱片 | 5 |
| 吉他 | 20 |
| 鼓 | 25 |
| 钢琴 | 35 |

父子节点表为:
| 父节点 | 子节点 |
---|-----|------|
| 乐谱 | 唱片 |
| 乐谱 | 海报 |
| 唱片 | 吉他 |
| 唱片 | 鼓 |
| 鼓 | 钢琴 |

可以看出,最优的交换的路径为:piano-drum-record-music

最低开销为:35元

贝尔曼-福德算法

在迪克特拉斯算法的基础上,我们考虑这样一种情况,假如边的权重存在负值。

在迪克特拉斯算法中,我们首先寻找最廉价的节点,更新其开销,再寻找未处理节点中最廉价的节点,以此往复。

可能出现这样一个情况:

image

在将海报标记为已处理后,开始处理唱片,但是唱片到海报的路径使得海报的开销更小,又将更新海报的开销,但是海报已经标记为已处理。那么就会出现一些问题。假如继续使用迪克特拉斯算法,最后的结果肯定是错的,大家可以更改参数试一下。为了正确解决问题,这时需要使用贝尔曼-福德算法。

贪心算法

对于一些比较复杂的问题,使用一些算法不能简单有效地解决,这时候往往会使用贪心算法:每步操作都选择局部最优解,最终得到的往往就是全局最优解。这似乎是想当然的做法,但是很多情况下真的行之有效。当然,贪心算法不适用于所有场景,但是他简单高效。因为很多情况并不需要追求完美,只要能找到大致解决问题的办法就行了。

假如我们面对这么一个问题:假设我开了一家网店,在全国各省都有生意,现在面临发快递的问题,假设现在的基础物流不是很完善,每家快运公司只能覆盖很少几个省,那么我该如何在覆盖全国34个省级行政区的情况下,选择最少的快运公司?

这个问题看似不难,其实很复杂。

现在假设有n家快运公司,那么全部的组合有2^n种可能。

| N | 2^N |
---|-----|------|
| 10 | 1024 |
| 20 | 1048576 |
| 50 | 1125899906842624 |

可以看到,假如有50家快递公司,我将要考虑1125千亿种可能。可以看到,没有算法能很快的计算出这个问题,那么我们可以使用贪心算法,求局部最优解,然后将最终得到的视为全局最优解。

那么在这个问题下如何使用贪心算法?核心在于什么是局部最优条件?可以这样:

  1. 选择一家覆盖了最多未覆盖省的公司。
  2. 重复第一步。

我们在进行测试的时候稍稍简化一下问题,将34个省改为10个省。
代码实现:

def func(company,province):
    result = set()
    province_need=province
    #当存在未覆盖的省时,循环一直继续
    while province_need:
        best_company=None
        province_coverd=set()
        #查找局部最好的选择
        for temp_company,temp_province in company.items():
            coverd=province_need & temp_province
            if len(coverd)>len(province_coverd):
                best_company=temp_company
                province_coverd=coverd
        province_need-=province_coverd
        result.add(best_company)
    return result

if __name__ == '__main__':
    province=set(["河北","山西","辽宁","吉林","黑龙江","江苏","浙江","安徽","福建","江西"])
    company={}
    company["顺丰"]=set(["河北","山西","辽宁","江苏","浙江"])
    company["圆通"]=set(["吉林","浙江"])
    company["中通"]=set(["黑龙江","江西"])
    company["韵达"]=set(["江苏","浙江","江苏"])
    company["EMS"]=set(["浙江","安徽","河北","山西"])
    company["德邦"]=set(["福建","江西","安徽"])
    select_company=func(company,province)
    print(select_company)

输出结果:


image image

相关文章

  • 《算法图解》笔记 iii

    迪克斯特拉算法 在图中,搜索最小的“段”数可以用广度优先算法,这就相当于默认每条边的权重是相同的,如果每条边的权重...

  • 算法图解读书笔记

    date: 2017-9-16 11:11:15title: 算法图解读书笔记 算法图解: http://www....

  • 算法图解 读书笔记

    date: 2017-9-16 11:11:15title: 算法图解读书笔记 算法图解: http://www....

  • 《算法图解》note 11 总结

    这是《算法图解》的第十一篇读书笔记,是一篇总结。经过1个月的时间,终于把《算法图解》看完了。个人认为,《算法图解》...

  • 代码小工蚁的#《算法图解》#学习笔记-C8贪婪算法

    代码小工蚁的#《算法图解》#学习笔记-C8贪婪算法C8 贪婪算法greedy algorithms 一、贪婪算法 ...

  • 算法图解笔记

    二分查找输入:有序列表个元素,最多步找到,与简单查找相比最多需要n步输出:找到的位置/数据结构:使用数组,不断更新...

  • 《算法图解》笔记

    7月份的时候看完这本算法入门书,学习难度比较低,很快就看完了。但是时隔两个月再回想,书中的内容已经了无印象,今天重...

  • 《算法图解》笔记

    广搜可用于求最短路线,如果节点之间的距离都差不多的话。还可以用来整合借钱关系。还可以用来在人际关系网络中寻找某个要...

  • 算法图解笔记

    书名: 算法图解出版社: 图灵出版社网址: http://www.ituring.com.cn/book/1864...

  • 《算法图解》note 10 K近邻算法

    这是《算法图解》第十篇读书笔记,内容主要是K邻近算法的介绍。 1.K近邻算法简介 K近邻算法(K-nearest ...

网友评论

      本文标题:《算法图解》笔记 iii

      本文链接:https://www.haomeiwen.com/subject/dzdqsftx.html