蚁群算法--背包问题

来源:互联网 发布:缩小毛孔的精华 知乎 编辑:程序博客网 时间:2024/05/16 11:34

蚁群算法,简称ACO,其入门性介绍请参看博文:http://www.cnblogs.com/Leo_wl/p/5665715.html


ACO在提出之时主要用来解决旅行商问题(即TSP,不清楚的可以百度TSP)。旅行商问题也是一个经典的NP完全问题,比较传统的解法有贪心算法等,在问题规模增长时,传统算法的求解效率大大降低。ACO的灵感来源于蚂蚁搜寻食物的过程,对于寻路问题有天然的优势,在旅行商问题上的成功应用证明了这点。

ACO最核心的两个公式如下:

  



第一个公式计算蚂蚁选择下一个要去的地点的概率。第二个公式计算蚂蚁走过每个地点后,每个地点上残留的信息素。对于不同的问题模型,这两个公式中参数含义不一样,具体计算时,过程相似,细节不同。

本文主要讨论ACO应用在求解简单的01背包问题上。关于背包问题的描述,参看我的博文遗传算法--背包问题


说了这么多,直接上代码,代码就是最好的讲解:(scala语言实现)

package main.scalaimport scala.collection.mutable.HashSetimport scala.collection.mutable.ArrayBufferimport scala.io.Source// 定义蚂蚁class Ant(val knapsack_data:ArrayBuffer[(Int, Int)]){  val item_num = knapsack_data.length // 背包中物品数量  val iter_all_item_set = new HashSet[Int]() // 每次迭代所有物品的集合(均保留下标id)  val iter_legal_selected_set = new HashSet[Int]() // 每次迭代所选的合法物品集合  var pheromones:Array[Double] = _  // 物品信息素值的引用  val LIMIT_WIGHT = 1000  // 背包的总重量限重  var iter_selected_weight = 0 // 每次迭代所选物品的总重量  var iter_selected_value = 0 // 每次迭代所选物品的总价值  def this(data:ArrayBuffer[(Int, Int)], pher:Array[Double]) = {    this(data)    pheromones = pher  }  // 根据信息素计算选择概率,选取下一个概率最大的爬行位置(物品)  private def select() = {    var sum = 0.0    for(id <- iter_all_item_set)    {      sum += (pheromones(id) * knapsack_data(id)._2 / knapsack_data(id)._1)    }    // 每个物品被选中的概率集 (概率,下标id)    val probabys = iter_all_item_set.map(id => {      val Pj = (pheromones(id) * knapsack_data(id)._2.toDouble / knapsack_data(id)._1) / sum      (Pj, id)    }).toArray    var max = (0.0, 0)    for(i <- probabys) {      if(i._1 > max._1) {        max = i      }    }    max._2  }  // 每次迭代前的处理工作  def before_each_iter() = {    iter_selected_weight = 0    iter_selected_value = 0    iter_all_item_set.clear()    iter_legal_selected_set.clear()    for(i <- 0 until item_num)    {      iter_all_item_set += i    }  }  // 蚂蚁爬行,从起始点开始,选取一条路径,即生成一个所选物品序列  def go() = {    while(iter_all_item_set.nonEmpty)    {      // 选取下一个概率最大的物品      val pos = select()      if(iter_selected_weight + knapsack_data(pos)._1 <= LIMIT_WIGHT)      {        iter_selected_weight += knapsack_data(pos)._1        iter_selected_value += knapsack_data(pos)._2        // 若不超过总重量,加入到合法序列中        iter_legal_selected_set += pos      }      iter_all_item_set -= pos    }  }  def resultString() = {    val ret = new ArrayBuffer[Int]()    for(i <- 0 until item_num)    {      ret += {        if(iter_legal_selected_set(i)) 1        else 0      }    }    ret.mkString(",")  }}object ACODemo{  val KNAPSACK_DATA_FILE = "knapsack.data"  val INIT_PHERO = 1.1 // 每个物品上信息素初始值  val ITER_RESERVE_PHERO_RATE = 0.7 // 每次迭代每个物品上信息素的保留率  val ANTS_NUM = 20 // 蚂蚁数量  val ITER_NUM = 30 // 迭代计算次数  // 读取背包问题的初始数据  def readKnapsackData(path:String) = {    val list = new ArrayBuffer[(Int, Int)]()    for(line <- Source.fromFile(path).getLines())    {      val strs = line.split(" ")      list += ((strs(0).toInt, strs(1).toInt))    }    list  }  // 初始化蚁群  def initAnts(data:ArrayBuffer[(Int, Int)], pher:Array[Double]) = {    val _ants = new ArrayBuffer[Ant]()    for(i <- 0 until ANTS_NUM)    {      _ants += new Ant(data, pher)    }    _ants  }  // 找出最优的蚂蚁个体  def findBestAnt(ants:ArrayBuffer[Ant]) = {    var best = ants(0)    for(ant <- ants)      if(ant.iter_selected_value > best.iter_selected_value)        best = ant    best  }  // 更新所有物品的信息素  def updatePheromones(pheromones:Array[Double], ants:ArrayBuffer[Ant],                       data:ArrayBuffer[(Int, Int)]) = {    for(i <- 0 until pheromones.length)    {      // 计算每个物品的信息素增量      var increment = 0.0      for(ant <- ants)      {        val delta = {          if(ant.iter_legal_selected_set(i)) data(i)._2.toDouble / ant.iter_selected_value          else 0.0        }        increment += delta      }      // 更新该物品的信息素      pheromones(i) = ITER_RESERVE_PHERO_RATE * pheromones(i) + increment    }  }  def main(args: Array[String]) = {    // 背包数据,每个元素为(weight value)    val data = readKnapsackData(KNAPSACK_DATA_FILE)    val pheromones = Array.fill(data.length)(INIT_PHERO) // 所有物品的信息素值    val ants = initAnts(data, pheromones)    for(i <- 0 until ITER_NUM)    {      for(j <- 0 until ANTS_NUM)      {        ants(j).before_each_iter()        ants(j).go()      }      val best_ant = findBestAnt(ants)      println("第" + i + "代,总重量:" + best_ant.iter_selected_weight + ",总价值:"        + best_ant.iter_selected_value + ",物品序列:" + best_ant.resultString())      updatePheromones(pheromones, ants, data)    }  }}

首先从文件“knapsack.data”中读取背包物品数据,背包物品的数据组织方式参看我另一篇博文粒子群算法--背包问题。然后初始化物品上的全局信息素、初始化蚁群,接着开始不断的迭代计算,直到迭代结束。

如何设计选择概率的计算方式和信息素的更新方式,直接影响到ACO算法性能,优化ACO通常从这两方面入手。本文在计算物品的选择概率时,只针对未选物品,已选物品的概率均为0。在更新物品的信息素时,每只蚂蚁都会影响到该物品的信息素变化,只要蚂蚁的选择路径中加入了该物品,更新时都要考虑进去。

经测试,本文中的ACO处理01背包问题,拥有较快的解收敛速度,能够比较快速的找到一个优质解。

原创粉丝点击