算法概论 project | Capacitated Facility Location Problem —— 贪心算法、模拟退火、禁忌搜索

代码及结果

python代码:anneal.py

贪心算法:

模拟退火:

禁忌搜索:


目录

题目描述

题意分析

样例格式

贪心算法

  • python代码(部分)
  • Detailed solution (部分)

模拟退火

  • python代码(部分)
  • Detailed solution (部分)

禁忌搜索

  • python代码(部分)
  • Detailed solution (部分)

题目描述

Suppose there are n facilities and m customers. We wish to choose:

  1. which of the n facilities to open
  2. the assignment of customers to facilities

The objective is to minimize the sum of the opening cost and the assignment cost.
The total demand assigned to a facility must not exceed its capacity.


题意分析

可以这样理解:

在这里插入图片描述

有n个facility, 每个facility具有一个capacity,如果开启这个facility,那么就会产生一个open cost。

有m个customer, 每个customer具有一个demond,customer可以自由选择去哪个facility, 但是选择这个facility的所有customer的demond之和不能大于它的capacity。每当customer选定一个facility,就会产生一个assign cost。

题目还有个隐含条件:所有customer的demond都要满足。

因此,总的cost可以表示为:所有开启了的facility的open cost之和 + 所有customer选择facility所产生的assign cost之和

我们的目标是令总的cost最小。


样例格式

在这里插入图片描述

在这里插入图片描述

例如:
在这里插入图片描述


1 贪心算法

我们可以让customer按顺序选择facility,当一个customer在选择时,它可以从所有facility中选择最优惠的那个。那么什么样的facility才是最优惠的呢?我目前想到三种方案:

  • assign cost 最小的
  • assign cost + open cost 最小的
  • assign cost (如果该facility未开启,+ open cost) 最小的

实践证明,第一种方案得到的cost最小。第二种方案由于未考虑facility已开启的情况,导致错过了更优惠的facility,而第三种方案,由于过于在意facility是否开启,最终更倾向于已开启的facility,导致错过了很多开启后可能更优惠的facility。


1.1 python代码(部分)

def greedy():
    global total_cost
    // 对于每个customer
    for i in range(len(assign[0])):
        min_cost = -1
        fac = -1
        cus = -1
        // 对于每个facility
        for j in range(len(assign)):
            // facility的剩余capacity要能满足customer的demond
            if get_fac_cap(j) < customers[i].demond:
                continue

            temp_cost = assign_cost[j][i]
            // temp_cost = temp_cost + facilities[j].open_cost

            // 选择最优惠的facility
            if min_cost == -1 or temp_cost < min_cost:
                fac = j
                cus = i
                min_cost = temp_cost

        // 如果没有facility能满足该customer的demond, return
        if min_cost == -1:
            print "false!"
            return 
        // 选择该facility
        else:
            facilities[fac].open = 1
            assign[fac][cus] = 1
            customers[cus].assign = fac


1.2 Detailed solution (部分)

p1
在这里插入图片描述

p2
在这里插入图片描述

p3
在这里插入图片描述

p4
在这里插入图片描述

p5
在这里插入图片描述


2 模拟退火

贪心算法看起来已经帮我们找到了一个最低的cost,但是这个cost很有可能是局部最优的,而不是全局最优的。至少,我们的贪心算法没有考虑顾客的顺序,如果让顾客按另一种顺序选择facility,也许cost会更低。

为了跳出这个局部最优解,我们在这个解的基础上,随机选择一个customer和一个facility,如果customer当前选择的不是这个facility,而且该facility还有足够的capacity来满足customer的demond,那么就计算让customer改选到这个facility的cost,如果这个新的cost低于旧的cost,那么就真的让这个customer改选到这个facility,否则,我们也按一定的概率接受这次改选。

刚开始时,我们可以大胆地接受cost更高的改选,随着温度降低,我们接受这种改选的概率也会越来越低。


2.1 python代码(部分)

def run_anneal():
    global total_cost
    print "\n-----\nanneal\n-----"
    readFile()
    // 在贪心算法得到的解的基础上进行计算
    greedy()
    total_cost = get_total_cost()
    anneal()
    print_result()
    
def anneal():
    global t, total_cost
    min_cost = total_cost
    while t > 1:
        for i in range(200):
            // 随机选择一个customer和一个facility
            customer, facility_from, facility_to = random_choice()
            // 计算新的cost
            new_cost = cal_new_cost(facility_from, facility_to, customer)
            // 当new_cost小于当前最优解时,接受该解,或者以一定概率接受比当前解要差的解
            if new_cost < min_cost or np.random.rand() < np.exp(-(new_cost-min_cost)/t):
                accept(facility_from, facility_to, customer)
                if new_cost < total_cost:
                    min_cost = new_cost
        // 降温
        t = t * 0.9
    total_cost = min_cost

2.2 Detailed solution (部分)

p1

模拟退火
在这里插入图片描述

贪心算法
在这里插入图片描述

p2

模拟退火
在这里插入图片描述

贪心算法
在这里插入图片描述

p3

模拟退火
在这里插入图片描述

贪心算法
在这里插入图片描述

p4

模拟退火
在这里插入图片描述

贪心算法
在这里插入图片描述

p5

模拟退火
在这里插入图片描述

贪心算法
在这里插入图片描述

对比可知,模拟退火比简单的贪心算法要进步很多。


3 禁忌搜索

模拟退火也有不足,就比如兔子在爬山,一只兔子的力量是渺小的,而一群兔子可以互相转告,哪里的山已经找过,并且找过的每一座山他们都留下一只兔子做记号。他们制定了下一步去哪里寻找的策略。这就是禁忌搜索。

禁忌搜索也将在贪心算法的基础上进行计算。

首先建立一个候选集,也就是一组改选的方式,从中选出最优的,如果这种该选方式已经出现在了禁忌条目中,那么,如果新的cost小于历史上最优的cost,就破除禁忌,接受这次改选,否则就继续按优劣顺序另选一个。

当然,从候选集中选出的最优并不一定会比历史最优更低,只要找到这个最优的,我们就接受它。最终的结果为历史最优解。

当禁忌表已满,要加入新的禁忌条目时,移除最早加入的条目。


3.1 python代码(部分)

def tabu():
    global total_cost, history_min_cost, tabu_customers, tabu_facilities
    history_min_cost = total_cost

    for n in range(cus_num * fac_num * 4 / 5):
        now_min_cost = 99999
        fac_from = facilities[0]
        fac_to = facilities[0]
        cus = customers[0]

        // 在当前最优解的基础上,在候选集中寻找cost更低,
        // 且不在禁忌表中(或者比历史最低cost更低)的exchange
        now_min_cost, fac_from, fac_to, cus = find_best_change(now_min_cost, fac_from, fac_to, cus)
        // 更新禁忌表
        update_tabu_list(fac_to, cus)
        // 接受该更改
        accept(fac_from, fac_to, cus)

        // 当新的cost小于历史最低cost时,更新历史最低cost,并记录此时的facilities和customers状况
        if now_min_cost < history_min_cost:
            history_min_cost = now_min_cost
            tabu_facilities = facilities
            tabu_customers = customers

3.2 Detailed solution (部分)

p1

禁忌搜索
在这里插入图片描述

模拟退火
在这里插入图片描述

贪心算法
在这里插入图片描述

p2

禁忌搜索
在这里插入图片描述

模拟退火
在这里插入图片描述

贪心算法
在这里插入图片描述

p3

禁忌搜索
在这里插入图片描述

模拟退火
在这里插入图片描述

贪心算法
在这里插入图片描述

p4

禁忌搜索
在这里插入图片描述

模拟退火
在这里插入图片描述

贪心算法
在这里插入图片描述

p5

禁忌搜索
在这里插入图片描述

模拟退火
在这里插入图片描述

贪心算法
在这里插入图片描述

看来禁忌搜索又比模拟退火好很多呀~

猜你喜欢

转载自blog.csdn.net/cat_xing/article/details/85165720