美文网首页算法编程算法Go语言
基础-5:人人都应该懂点的随机算法

基础-5:人人都应该懂点的随机算法

作者: CodingTech | 来源:发表于2017-03-14 09:41 被阅读257次

    概述

    随机算法是当前工业界和学术界都比较热的一个话题,从机器学习、数据挖掘到现在热得发烫的深度学习,无一没有随机算法的影子。随机算法起源于人类天性:赌博,最初用来解决一些不确定问题,现在已逐渐发展为一门独立的学科,深入到工程领域各学科。本文以一个简单的例子对基本的随机算法作简单介绍,深入了解请研读随机算法

    寻宝问题

    寻宝问题描述如下:

    假设有10个山头,其中3个山头里面分别有价值10个金币宝藏,每个山头都有1个精灵守护,要进山寻宝,必须给精灵3个金币,当在一个山头找到宝藏后,精灵不允许继续再寻宝,哪个山头有宝藏未知,是否应该寻宝,采用何种策略寻宝?

    因为每个山里面是否有宝藏是未知的,不妨对这10个山里面是否有宝藏进行编号,设为{0, 0, 0, 1, 0, 0, 1, 0, 0, 1},其中的0表示没有宝藏,1表示有宝藏。

    因为宝藏未知,按照什么样的顺序访问山头也是未知的。在这种情况下,有两种方案:

    • 情况1: 给山头排个序,然后依次访问;
    • 情况2: 随机访问;

    对于情况1,假设排序为p=<0, 0, 0, 1, 0, 0, 1, 0, 0, 1>,则挖到宝藏会亏本2个金币。也就是说,一旦确定了访问山头的次序,并且这个次序是一个亏本的次序,则必然亏本。

    对于情况2,随机访问有是什么样子呢?在这里不作具体的数学分析(分析也比较简单,利用离散均匀分布,计算期望)。随机访问一般有两种算法,一种是lasvegas算法,一种是monter carlo算法,随机算法的本质是算法的某一步骤中引入随机函数确定的某个值,从而导致不同相同的输入可能得到不同的结果。

    lasvegas算法针对寻宝问题的代码为:

    func LasVegas(input []int, key int) int {
        retCount := 0
        r := rand.New(rand.NewSource(time.Now().UnixNano()))
    
        idxs := make([]int, 0)
        for i, _ := range input {
            idxs = append(idxs, i)
        }
    
        for count := 0; count < len(input); count++ {
            // 从未选择的山头中选择一个
            i := r.Intn(len(idxs))
            retCount++
            if input[idxs[i]] == key {
                fmt.Println("===========")
                return 10 - 3*retCount
            }
            idxs = lefts(idxs, idxs[i])
        }
        return 0 - 3*retCount
    }
    

    分析上面的代码,lasvegas算法的本质是只要存在解(在本例的寻宝中),会一直尝试,直到得到正确解。也就是:只要存在解,lasvegas算法最终一定会得到解。

    如果寻宝问题中,山头数量足够多,而宝藏山头特别少,则按照lasvegas算法可能会亏损特别严重,得不偿失,因此,monter carlo算法对尝试的次数作了限制,这本质上就是一种尝试-止损原则,即先确定一个止损位,按照在止损范围内尝试获利。其代码为:

    func MC(input []int, upperTryNum int, key int) int {
        retCount := 0
        r := rand.New(rand.NewSource(time.Now().UnixNano()))
    
        idxs := make([]int, 0)
        for i, _ := range input {
            idxs = append(idxs, i)
        }
    
        for count := 0; count < len(input); count++ {
            if retCount <= upperTryNum {
                retCount++
                i := r.Intn(len(idxs))
                if input[i] == key {
                    return 10 - 3*retCount
                }
    
                idxs = lefts(idxs, idxs[i])
            }
        }
    
        return 0 - upperTryNum
    }
    

    其中的upperTryNum就是尝试的次数。

    通常情况下,Monte Carlo不一定会得到解,但是其损失是可以预见。针对寻宝这个例子,在本人机器上分别运行10次,其获益金币数结果为(每次运行结果都不同):
    lvs(lasvegas): [4 1 -2 4 -8 7 7 7 4 1] , mcs(monter carlo): [4 7 7 7 7 7 7 1 7 7]

    大家可以在自己的机器上观测运行结果( https://github.com/bjutJohnson/Algorithms/tree/master/src/probabilistic ) ,可以把尝试次数设置得更多一些,然后结合数学期望进行分析。

    小结

    随机算法虽然起源于不确定性问题,但是对于规模庞大的确定性问题如围棋、解密度空间稀疏等,都有与之相应的解决方案,特别是求解次优解的情况。

    相关文章

      网友评论

        本文标题:基础-5:人人都应该懂点的随机算法

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