集体智慧编程chapter5:优化问题

优化问题1:组团旅游

五个人要乘坐航班去同一个地方,如何安排航班可以使总成本最低
思路:目标函数是一个10维list,[1,4,3,2,7,3,6,3,2,4,5,3],表示每个人乘坐第几趟航班往返

优化问题2:学生宿舍优化

10个学生选择5间宿舍10个床位,每人2个志愿,如何安排可以符合多数人的志愿
思路:将每个床位看作一个槽,十个学生选择十个槽

关键点

  1. 设置初始值
  2. 设置成本函数cost,即损失函数

可用算法

随机选择:完全随机,保留最优解

def randomptimize(domain, costf):
    best = 999999999
    bestr = None
    for i in range(10000):  # 我们打算随机产生1000次结果,从这1000次结果中选择一个最好的
        # 很显然randint是产生在一定范围内的随机数,显然由于下一句右边等号里的for,将会产生一个循环
        r = [random.randint(domain[j][0], domain[j][1]) for j in range(len(domain))]
        cost = costf(r)

        # 每次得到成本我们都判断一次,如果更低,我们就置换
        if cost < best:
            best = cost
            bestr = r
    return bestr

爬山法:每次更新一个值,保留最优解,容易陷入局部最优

def hillclimb(domain, costf):
 # Create a random solution
 sol = [random.randint(domain[i][0], domain[i][1])
        for i in range(len(domain))]
 # Main loop
 while 1:
     # Create list of neighboring solutions
     neighbors = []

     for j in range(len(domain)):
         # 对于每个元素+1或者-1
         if sol[j] > domain[j][0]:
             neighbors.append(sol[0:j] + [sol[j] + 1] + sol[j + 1:])
         if sol[j] < domain[j][1]:
             neighbors.append(sol[0:j] + [sol[j] - 1] + sol[j + 1:])

     # See what the best solution amongst the neighbors is
     current = costf(sol)
     best = current
     for j in range(len(neighbors)):
         cost = costf(neighbors[j])
         if cost < best:
             best = cost
             sol = neighbors[j]

     # If there's no improvement, then we've reached the top
     if best == current:
         break
 return sol

模拟退火算法:即使新的成本更高,也有可能更新最优解,可避免局部最优

def annealingoptimize(domain, costf, T=10000.0, cool=0.98, step=1):
  # 和爬山法一样,先产生一个随机解,然后一切的改变都从这个随机解开始
  vec = [random.randint(domain[i][0], domain[i][1]) for i in range(len(domain))]

  while T > 0.5:
      # 产生一个随机数,决定这次改变是改变数列中的哪一个随机数
      i = random.randint(0, len(domain) - 1)

      # 选择一个改变的方向,也就是说是增加还是减少
      dir = random.randint(-step, step)

      # 复制随机解,然后对随机解进行改变,然后判断到底新的解好,还是后来产生的解好
      vecb = vec[:]
      vecb[i] += dir
      # 这一段主要还是不让它超不过了最大最小值的限制
      if vecb[i] < domain[i][0]:
          vecb[i] = domain[i][0]
      elif vecb[i] > domain[i][1]:
          vecb[i] = domain[i][1]

      # 计算新产生的两次解的成本,然后对成本进行比较
      ea = costf(vec)
      eb = costf(vecb)

      # or后面:表示接受更差的结果。仔细想想,原来概率的表示是如此完成的,注意前一个random()产生的数是在0到1之间。
      if (eb < ea or random.random() < pow(math.e, -(eb - ea) / T)):
          vec = vecb

          # 没经过一次循环,改变温度,温度一改变,就会改变循环的次数和接受更差解的概率
      # 按一定比例降温
      T = T * cool

  return vec

遗传算法:交叉变异,每次保留一定的最优序列

def geneticoptimize(domain, costf, popsize=50, step=1, mutprob=0.2, elite=0.2, maxiter=100):
  # 方法中还在定义方法
  # 变异操作
  def mutate(vec):
      i = random.randint(0, len(domain) - 1)
      # 完成第增加或减少的概率各一半
      if random.random() < 0.5 and vec[i] > domain[i][0]:
          return vec[0:i] + [vec[i] - step] + vec[i + 1:]
      elif vec[i] < domain[i][1]:
          return vec[0:i] + [vec[i] + step] + vec[i + 1:]
      else:
          return vec
      # 交叉操作:貌似用python编程是好快的说,我感觉比较复杂的句子只要两句么,还是我c/c++没学好

  def crossover(r1, r2):
      # 为什么减2,其实想把这个一个数字列表划分为两段,再各取一半
      i = random.randint(1, len(domain) - 2)
      return r1[0:i] + r2[i:]

  # 构造初始种群
  pop = []
  for i in range(popsize):
      vec = [random.randint(domain[i][0], domain[i][1]) for i in range(len(domain))]
      pop.append(vec)

      # 每一代有多少优势物种,我们需要保留
  topelite = int(elite * popsize)
  # 主循环
  for i in range(maxiter):
      # print pop #但是如果不加这句会使下一句出现一个bug,就是传过去的v是None,但是我讲pop全部打印出来的话,又没有问题
      scores = [(costf(v), v) for v in pop]  # 列表里面,每一个元素都是一个元组,每一个元组是由一个数字和一个列表构成
      scores.sort()
      ranked = [v for (s, v) in scores]

      # 从中选择我们觉得优势的物种,然后保留
      pop = ranked[0:topelite]

      # 如果种群数量不够,那么我们使用变异或者配对,产生新的后代个体
      while len(pop) < popsize:
          # 变异的概率,这是由我们设定的,虽然这里是变异和配对只能选择其一,但是我认为是可以共同进行的
          if random.random() < mutprob:  # 如果这样做,就是变异的少,交叉的多吧
              # 变异
              c = random.randint(0, topelite)  # 注意是从优秀的子代中选出一个进行变异
              pop.append(mutate(ranked[c]))
          else:
              c1 = random.randint(0, topelite)  # 从优秀的子代中选择
              c2 = random.randint(0, topelite)  # 从优秀的子代中选择
              pop.append(crossover(ranked[c1], ranked[c2]))

      print(scores[0][0])  # 注意打印的是成本

  return scores[0][1]  # 这里返回的是航班序列
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值