EDAthon2020-P4 Floorplanning问题 BSG + Simulated Annealing解决方案

文章目录

  • 1. 概念介绍
  • 2.问题描述
  • 3.解决方案
    • 3.1 BSG 架构
    • 3.2 模拟退火
    • 3.3 具体改进
  • 4. 代码参考
  • 5.总结

1. 概念介绍

EDAthon是一个围绕硬件相关的一个比赛,涉及的面居多,每个题都是基于某个领域的经典问题,今年的题目涉及具体IP核实现,PCB布线算法,IP核布局算法,指令调度相关,神经网络的量化等
Floorplanning问题大概如下:给你若干个矩形(模块),让你不重叠的放置在平面中使得最终的包围矩形最小,当然可以加些约束,比如指定某些模块靠的近一些,或者某些模块是软的(只要保证面积不变,长宽比可以任意),或者指定模块四周要留某个单位的空余,等等
如下图所示,10个模块的最小包围矩形面积为100*100
EDAthon2020-P4 Floorplanning问题 BSG + Simulated Annealing解决方案_第1张图片

2.问题描述

题目给你n个矩形,边长为浮点数,和m对约束,约束为两个矩形尽可能的离得近,不能旋转矩形。最终输出每个矩形最终状态的左下角坐标。

PS:题目未说明n的大小,但上限不会太大,可以按照200作为上限。

输入样例如下:

8
400.0 300.0
400.0 140.0
330.0 240.0
280.0 220.0
150.0 240.0
380.0 130.0
530.0 210.0
230.0 220.0
1
1 2

输出样例如下:

0.0 0.0
400.0 0.0
0.0 300.0
400.0 140.0
680.0 140.0
330.0 380.0
0.0 540.0
530.0 510.0

此样例最终状态如下图所示:
EDAthon2020-P4 Floorplanning问题 BSG + Simulated Annealing解决方案_第2张图片
评判标准
F c o s t = ( A f + D ) / A c Fcost = ( Af+ D ) / Ac Fcost=(Af+D)/Ac
其中 F c o s t Fcost Fcost为最终代价,使其最小化, A f Af Af是最终的包围矩形面积, D D D是m对矩形间曼哈顿距离的平方和的和, A c Ac Ac是所有矩形的面积和

时空约束
R u n t i m e : 5   m i n Runtime:5 ~min Runtime:5 min
M e m o r y : 1   G B Memory:1~GB Memory:1 GB

3.解决方案

主要参考了这篇论文,可能需要梯子,也可以参考下面自行下载
BSG(Bounded slicing grid)- structure:Nakatake S, Fujiyoshi K, Murata H, et al. Module placement on BSG-structure and IC layout applications[C]//Proceedings of International Conference on Computer Aided Design. IEEE, 1996: 484-491

该论文主要提出了BSG结构,可以很巧妙的构建出模块的分布,进而可以计算每个分布的包围矩形,再结合模拟退火随机算法,最终得到的解还挺理想。
简单介绍下论文提出的架构。

3.1 BSG 架构

EDAthon2020-P4 Floorplanning问题 BSG + Simulated Annealing解决方案_第3张图片

如上右图1B是一个4*3的BSG图,首先观察该图线段很有规律,比如横向观察,第一层线段长度为2,1第二层为1,2,再往上就循环如此,纵向类似。当然这个图是可以无限放大的。每个交错的横纵线段中有很多ROOM,如图1A所示,这里的ROOM可以放一个矩形(模块)

EDAthon2020-P4 Floorplanning问题 BSG + Simulated Annealing解决方案_第4张图片
在上述的BGS图中,横向和纵向分别建图,如上图2所示,可以很巧妙的发现每个图的节点位置都是线段长度为2的中心点。我们有可以很巧妙的发现在Gh和Gv中每个room都会有一条边覆盖,进而利于我们在可以在任意的room中放置矩形(模块)。每个图都要设置超级源点和超级汇点。
EDAthon2020-P4 Floorplanning问题 BSG + Simulated Annealing解决方案_第5张图片
现在进入实战环节,假如有4个矩形 a , b , c , d a,b,c,d a,b,c,d,长宽如上图A所示,假如我们的BSG图为4*4,恰好每个模块所在的位置如上图B所示,那么我们就可以得到上图CD的Gh,Gv,如果在BSG图某个room有模块时,那么在Gh图对应边的权值应为对应模块的宽,同理Gv对应的权值应为模块的高,如果BSG图某个room没有模块,对应的Gh,Gv边权都为0。
通过Gh,Gv我们就可以得到每个模块在最终布局的具体坐标
比如 a a a模块,首先根据Gh图对应 r o o m room room源点 V 1 V1 V1(边权为8的左下角的点),从超级源点 S v Sv Sv V 1 V1 V1的最长路径为0,即表示 a a a模块最终位置的 x x x坐标为0。同理可以得到 y y y坐标为9,进而得到 a a a模块的左下角坐标,其他模块类似。
重点是我们可以直接通过Gh,Gv求得最终包围矩形的宽和长
只需要分别在Gh,Gv图中跑一边最长路径即可。利用记忆化搜索可以把时间降低到O(N),N为节点的个数。

该样例最终的布置如下:
EDAthon2020-P4 Floorplanning问题 BSG + Simulated Annealing解决方案_第6张图片
基于论文中所述的BSG结构,可以轻松的建图,8*8的图,Gh,Gv原始图如下,可能会更详细点。代码就是基于此图构建的

EDAthon2020-P4 Floorplanning问题 BSG + Simulated Annealing解决方案_第7张图片

3.2 模拟退火

首先模拟退火算法的原理具体不多介绍。

主要利用 Simulated Annealing 来确定每个模块在BSG的具体位置,以及再退火的过程中如何变化每个模块的位置。

首先是初始化,直接暴力“塞进去”,按照编号一个个进入ROOM编号。
退火过程就是随机挑选两个位置,如果两个位置都为空,再重新挑选,直到找到至少有一个位置不为空为止,然后就是交换两个ROOM的值,更新图和相关的变量,然后就是退火的老一套,如果当前Cost是上次的Cost小,则直接交换,否则随机一波值,让它有一定概率Cost变大。然后就让它随机下去,直到温度小于阈值。

3.3 具体改进

  1. 由于题目所提供的矩形边长为浮点,考虑到浮点数运算会更耗时,虽然可能得到的结果更优,但最终还是选择把题目提供的边长向上取整为整数边长来求解。
  2. 题目要求不能旋转矩形,所以降低了复杂度(因为题目准备的是可以旋转,临时删了)
  3. 由于题目提供了代价函数,所以在退火时计算代价直接用题目提供的即可
  4. 退火降温系数可以随机选取
  5. 在寻找最长路径时采用了记忆化搜索

4. 代码参考

代码很水,有手就能敲,仅供参考,可能有的函数懒得加注释了,反正C++都能看懂

#include 

using namespace std;

#define MX 100
#define pii pair
#define fi first
#define se second


vector<pii> input;                  // 输入的模型,暂定为矩形
vector<pii> Vh,Vv;                  // 顶点 对应 坐标
vector<vector<int>> graphH,graphV;  // 节点的孩子节点
map<pii,int> mGh, mGv;              // 坐标对应顶点
int Gh[MX][MX];                     // Gh 节点权重
int Gv[MX][MX];                     // Gv 节点权重
int dpH[MX*MX];                     // 求Gh最大路径记忆化数组
int dpV[MX*MX];                     // 求Gv最大路径记忆化数组
int modulesNum;                     // 模型总个数
pii table[MX*MX];                   // 随机用到的表格, fi : index, se : 摆放姿势,只考虑横竖
int vertex[MX*MX];                  // input point
pii shape;                          // 预定平面大小
int vNum;                           // 建图之后的顶点个数,G_0 为源点,G_vNum 为汇点
int changeSum;                      // 总随机的次数
int sumArea;                        // 模块总面积
vector<pii> close;                  // 某些模块靠近
int closeNum;                       // 靠近模块对的个数
clock_t startClock, endClock;       // 记时tag
double coe[10] = {0.9993, 0.9994, 0.9995, 0.9996, 0.9793, 0.9696, 0.9981, 0.9877, 0.9699, 0.9991};

int finalHeight, finalwidth;        // 最终的长宽
pii finalTable[MX*MX];              // 最终的调度结果
int finalGh[MX][MX];                // 最终的 Gh 节点权重
int finalGv[MX][MX];                // 最终的 Gv 节点权重
int finalVertex[MX*MX];
double finalCost;



void printTable();
void initPlaneSize();
void initGraphVertex();
bool isOdd(int x);
int dfsH(int u);
int dfsV(int u);
pii index2Coordinate(int idx);
pii getCoordinate(int idx, int p);
void SA();


string int2string(int x) {
    stringstream ss;
    ss << x;
    string res;
    ss >> res;
    return res;
}

char mp[200][200];

void draw(int x1, int y1, int x2, int y2) {
    if (x1 == x2) {
        for (int j = y1; j < y2; j++) {
            mp[x1][j] = '_';
        }
    } else for (int i = x1; i < x2; i++) {
        mp[i][y1] = '|';
    }
}

void printMP() {
    for (int i = 0; i < dpV[0] + 4; i++) cout << "_";cout <<endl;
    for (int i = dpH[0]; i >= 0; i--) {
        cout << "| ";
        for (int j = 0; j <= dpV[0]; j++) {
            printf("%c", mp[i][j]);
        }cout << " |\n";
    }
    cout << "|";for (int i = 0; i < dpV[0] + 3; i++) cout << "_";cout << "|";cout <<endl;
}

void printGhGvScheduing(pii _table[], int _Gh[][MX], int _Gv[][MX]) {
    puts("Table");
    for (int i = shape.fi - 2; i >= 0; --i) {
        for (int j = 0; j < shape.se - 1; ++j) {
            if (_table[i * (shape.fi - 1) + j].fi == -1) printf("None  ");
            else printf("%d_%d  ", _table[i * (shape.fi - 1) + j].fi, _table[i * (shape.fi - 1) + j].se);            
        } puts("");
    }
    
    puts("Gh");
    for (int i = shape.fi - 2; i >= 0; --i) {
        for (int j = 0; j < shape.se - 1; ++j) {
            int now = i * (shape.fi - 1) + j;
            pii tmp = getCoordinate(now, 0);
            printf("%2d ", _Gh[tmp.fi][tmp.se]);
        } puts("");
    }
    
    puts("Gv");
    for (int i = shape.fi - 2; i >= 0; --i) {
        for (int j = 0; j < shape.se - 1; ++j) {
            int now = i * (shape.fi - 1) + j;
            pii tmp = getCoordinate(now, 1);
            printf("%2d ", _Gv[tmp.fi][tmp.se]);
        } puts("");
    }

}

void printTable(pii _table[], int _vertex[]) {
    // printGhGvScheduing();
    for (int i = 0; i < 200; i++) for (int j = 0; j < 200; j++) mp[i][j] = ' ';
    for (int w = 0; w < modulesNum; w++) {
        int idx = _vertex[w];
        int x = dpV[0] - dpV[getCoordinate(idx, 1).fi];
        int y = dpH[0] - dpH[getCoordinate(idx, 0).fi];

        int height = _table[idx].se ? input[_table[idx].fi].fi : input[_table[idx].fi].se;
        int width  = _table[idx].se ? input[_table[idx].fi].se : input[_table[idx].fi].fi;

        // printf("_table[i].fi = %2d, height = %2d, width = %2d, x = %2d, y = %2d\n", _table[i].fi, height, width, x, y);

        draw(y, x, y, x + width);
        draw(y + height, x, y + height, x + width);
        draw(y, x, y + height, x);
        draw(y, x + width, y + height, x + width);

        string val = int2string(w);
        for (int i = 0; i < val.size(); i++) {
            mp[y + height / 2][x + width / 2 + i] = val[i];
        }
        printf("%d %d\n", x, y); // 记录每个模块左下角的坐标
    }
    printMP();
}

void initPlaneSize() {
    if (modulesNum > 300) {
        shape.fi = shape.se = 2 * (int)sqrt(modulesNum + 0.5);
    } else if (modulesNum > 200) {
        shape.fi = shape.se = 30;
    } else if (modulesNum > 100) {
        shape.fi = shape.se = 25;
    } else if (modulesNum > 50) {
        shape.fi = shape.se = 15;
    } else if (modulesNum > 20) {
        shape.fi = shape.se = 10;
    } else {
        shape.fi = shape.se = 8; // 8
    } 
    vNum = (int)(shape.fi * shape.se * 1.5) >> 1;
    // vNum = 1000;
}

void initGraphVertex() {
    Vh.resize(vNum);
    Vv.resize(vNum);
    graphH.resize(vNum);
    graphV.resize(vNum);
    vNum = (shape.se * shape.se + 1) / 2 + 1 ;
    bool cloIsOdd = shape.se & 1;
    
    Vh[0] = Vv[0] = make_pair(-1,-1);
    // 源点
    for (int i = 1; i <= shape.fi + 1>> 1; ++i) {
        graphH[0].emplace_back(i);
        graphV[0].emplace_back(i);
    }
    int v = 1;
    int vn = shape.se >> 1;
    for (int i = 0; i < shape.fi; ++i) {
        int st = (i & 1) ? 0 : 1;
        bool rowIsOdd = i & 1;
        for (int j = st; j < shape.se; j += 2) {
            Vh[v].fi = i;
            Vh[v].se = j;
            mGh[Vh[v]] = v;
            if (i == shape.fi - 1) {
                graphH[v++].emplace_back(vNum); // 汇点
                continue;
            }
            // left up child
            if (j != 0) {
                graphH[v].emplace_back((!cloIsOdd & rowIsOdd) ? (v + vn - 1) : (v + vn));
            }
            // right up child
            if (j != shape.se - 1) {
                graphH[v].emplace_back((!cloIsOdd & rowIsOdd) ? (v + vn) : (v + vn + 1));
            }
            v++;
        }
    }
    v = 1;
    vn = (int)shape.fi >> 1;
    bool rowIsOdd = shape.fi & 1;
    for (int i = 0; i < shape.se; ++i) {
        int st = (i & 1) ? 1 : 0;
        bool _cloIsOdd = i & 1;
        for (int j = st; j < shape.fi; j += 2) {
            Vv[v].fi = j;
            Vv[v].se = i;
            mGv[Vv[v]] = v;
            if (i == shape.se - 1) {
                graphV[v++].emplace_back(vNum);
                continue;
            }
            // down right child
            if (j != 0) {
                graphV[v].emplace_back((!_cloIsOdd & !rowIsOdd) ? (v + vn - 1) : (v + vn));
            }
            // up right child
            if (j != shape.se - 1) {
                graphV[v].emplace_back((!_cloIsOdd & !rowIsOdd) ? (v + vn) : (v + vn + 1));
            }
            v++;
        }
    }
    for (int i = 0; i < MX*MX; ++i) {
        table[i] = make_pair(-1, 0);
    }
}

bool isOdd(int x) {
    return x & 1;
}

// find the longest path
int dfsH(int _Gh[][MX], int u) {
    if (u == vNum) return 0;
    if (dpH[u] != -1) {
        return dpH[u];
    }
    int mx = -1;
    for (auto it : graphH[u]) {
        mx = max(mx, _Gh[u][it] + dfsH(_Gh, it));
    }
    return dpH[u] = mx;
}

int dfsV(int _Gv[][MX], int u) {
    if (u == vNum) return 0;
    if (dpV[u] != -1) {
        return dpV[u];
    }
    int mx = -1;
    for (auto it : graphV[u]) {
        mx = max(mx, _Gv[u][it] + dfsV(_Gv, it));
    }
    return dpV[u] = mx;
}


pii index2Coordinate(int idx) {
    return make_pair(idx / (shape.fi - 1), idx % (shape.fi - 1));
}


/**
 * @description: return corresponding coordinate
 * @param  : idx: table index
 *           p  : 0 = Vh,     1 = Vv
 * @return:  first : source vertex
 *           second: sink vertex
 */
pii getCoordinate(int idx, int p) {
    int row = idx / (shape.fi - 1);
    int col = idx % (shape.fi - 1);
    if (p == 0) {  // 判断Gh边是往左还是往右,奇数往右,偶数往左
        if (isOdd(row + col)) {
            return make_pair(mGh[make_pair(row, col)], mGh[make_pair(row + 1, col + 1)]);
        } else {
            return make_pair(mGh[make_pair(row, col + 1)], mGh[make_pair(row + 1, col)]);
        }
    } else {      // 判断Gv边是往上还是往下,奇数往下,偶数往上
        if (isOdd(row + col)) {
            return make_pair(mGv[make_pair(row + 1, col)], mGv[make_pair(row, col + 1)]);
        } else {
            return make_pair(mGv[make_pair(row, col)], mGv[make_pair(row + 1, col + 1)]);
        }
    }
}

int segment_distance(int a0, int b0, int a1, int b1) {
    return max(a0 - b1, a1 - b0);
}

double getCost(int area) {
    int sumD = 0;
    // printTable();
    for (auto it : close) {
        int u = vertex[it.fi], v = vertex[it.se];

        int height = table[u].se ? input[table[u].fi].fi : input[table[u].fi].se;
        int width  = table[u].se ? input[table[u].fi].se : input[table[u].fi].fi;

        int x0Left  = dpV[0] - dpV[getCoordinate(u, 1).fi];
        int x0Right =  dpV[0] - dpV[getCoordinate(u, 1).fi] + width;
        int y0Up   = dpH[0] - dpH[getCoordinate(u, 0).fi] + height;
        int y0Down  =  dpH[0] - dpH[getCoordinate(u, 0).fi];


        height = table[v].se ? input[table[v].fi].fi : input[table[v].fi].se;
        width  = table[v].se ? input[table[v].fi].se : input[table[v].fi].fi;

        int x1Left  = dpV[0] - dpV[getCoordinate(v, 1).fi];
        int x1Right =  dpV[0] - dpV[getCoordinate(v, 1).fi] + width;
        int y10Up   = dpH[0] - dpH[getCoordinate(v, 0).fi] + height;
        int y1Down  =  dpH[0] - dpH[getCoordinate(v, 0).fi];

        int dx = segment_distance(x0Left, x0Right, x1Left, x1Right);
        int dy = segment_distance(y0Down, y0Up, y1Down, y10Up);

        assert(dx < 0 && dy < 0);

        sumD += dx * dx + dy * dy;
    }
    return (area + sumD) * 1.0 / sumArea;

}

void SA(double coe) {

    memset(Gh, 0, sizeof Gh);
    memset(Gv, 0, sizeof Gv);
    
    for (int i = 0; i < MX*MX; i++) {
        if (table[i].fi != -1) {
            pii edgeH = getCoordinate(i, 0);
            pii edgeV = getCoordinate(i, 1);
            Gh[edgeH.fi][edgeH.se] = table[i].se ? input[table[i].fi].fi : input[table[i].fi].se;
            Gv[edgeV.fi][edgeV.se] = table[i].se ? input[table[i].fi].se : input[table[i].fi].fi;
        }
    }
    // printTable();
    memset(dpH, -1, sizeof dpH);
    memset(dpV, -1, sizeof dpV);
    int m1 = dfsH(Gh, 0), m2 = dfsV(Gv, 0);
    double oldArea = m1 * m2;
    double oldCost = getCost(oldArea);
    
	double t = 3000.0;	// Initial temperature
	while (t > 1e-14) {		
        // 随机挑选两个位置
		pii loc1 = make_pair(rand() % ((shape.fi - 1) * (shape.fi - 1)), rand() % 1);
        pii loc2 = make_pair(rand() % ((shape.fi - 1) * (shape.fi - 1)), rand() % 1);
        /**
         * 以下情况直接continue
         * 1. 两个位置都为空
         * 2. 两个位置一样,且角度也一样
         * 3. 两个位置一样,角度不一样,但是模块是一个正方形
        */
        if (table[loc1.fi].fi == -1 && table[loc2.fi].fi == -1 || loc1 == loc2 
            || loc1.fi == loc2.fi && input[table[loc1.fi].fi].fi == input[table[loc1.fi].fi].se) { 
            continue;
        }
        changeSum++;

        // 旋转某一个模块 (特判下)
        if (loc1.fi == loc2.fi) {
            // 保存 交换之前的部分值
            pii tableValue1 = table[loc1.fi];
            pii edge1H = getCoordinate(loc1.fi, 0);
            pii edge1V = getCoordinate(loc1.fi, 1);
            pii val1 = make_pair(Gh[edge1H.fi][edge1H.se], Gv[edge1V.fi][edge1V.se]);
            // 模块位置未变化所以不用改变vertex
            // vertex[table[loc1.fi].fi]

            // 更改当前的状态(只需要姿势即可),并且计算出 新的 面积
            table[loc1.fi].se = (table[loc1.fi].se^1);
            Gh[edge1H.fi][edge1H.se] = table[loc1.fi].se ? input[tableValue1.fi].fi : input[tableValue1.fi].se;
            Gv[edge1V.fi][edge1V.se] = table[loc1.fi].se ? input[tableValue1.fi].se : input[tableValue1.fi].fi;

            // 清空记忆化数组
            memset(dpH, -1, sizeof dpH);
            memset(dpV, -1, sizeof dpV);
            m1 = dfsH(Gh, 0), m2 = dfsV(Gv, 0);
            double newArea = m1 * m2;
            double newCost = getCost(newArea);

            if (newCost < finalCost) {
                finalHeight = m1;
                finalwidth = m2;
                finalCost = newCost;
                memcpy(finalTable, table, sizeof(table));
                memcpy(finalGh, Gh, sizeof(Gh));
                memcpy(finalGv, Gv, sizeof(Gv));
                memcpy(finalVertex, vertex, sizeof(vertex));
            }

            // printTable();
            double delta = newCost - oldCost;	// delta area
            if (delta < 0) {	                // 新解分配面积更小,直接接受
                oldCost = newCost;
            } else if (exp(-delta / t) * 32767 > rand()) { // 新解更次,以一定概率接受
                // int t1 = exp(-delta / t) * 32767;
                oldCost = newCost;
            } else {           // 不接受,还原状态
                table[loc1.fi] = tableValue1;
                Gh[edge1H.fi][edge1H.se] = val1.fi;
                Gv[edge1V.fi][edge1V.se] = val1.se;
            }
            t *= coe;	       //根据牛顿冷却定律降温
            continue;
        }

        // 保存 交换之前的部分值
        pii tableValue1 = table[loc1.fi];
        pii tableValue2 = table[loc2.fi];
        pii edge1H = getCoordinate(loc1.fi, 0);
        pii edge1V = getCoordinate(loc1.fi, 1);
        pii edge2H = getCoordinate(loc2.fi, 0);
        pii edge2V = getCoordinate(loc2.fi, 1);
        pii val1 = make_pair(Gh[edge1H.fi][edge1H.se], Gv[edge1V.fi][edge1V.se]);
        pii val2 = make_pair(Gh[edge2H.fi][edge2H.se], Gv[edge2V.fi][edge2V.se]);

        // 更改当前的状态并且计算出 新的 面积
        table[loc1.fi] = tableValue2;
        table[loc2.fi] = tableValue1;
        vertex[tableValue1.fi] = loc2.fi;
        vertex[tableValue2.fi] = loc1.fi;
        if (tableValue2.fi != -1) { // 把loc2的room里的module放到loc1中,且loc2不为空
            Gh[edge1H.fi][edge1H.se] = tableValue2.se ? input[tableValue2.fi].fi : input[tableValue2.fi].se;
            Gv[edge1V.fi][edge1V.se] = tableValue2.se ? input[tableValue2.fi].se : input[tableValue2.fi].fi;
        } else {
            Gh[edge1H.fi][edge1H.se] = 0;
            Gv[edge1V.fi][edge1V.se] = 0;
        }

        if (tableValue1.fi != -1) { // 把loc1的room里的module放到loc2中,且loc1不为空
            Gh[edge2H.fi][edge2H.se] = tableValue1.se ? input[tableValue1.fi].fi : input[tableValue1.fi].se;
            Gv[edge2V.fi][edge2V.se] = tableValue1.se ? input[tableValue1.fi].se : input[tableValue1.fi].fi;
        } else {
            Gh[edge2H.fi][edge2H.se] = 0;
            Gv[edge2V.fi][edge2V.se] = 0;
        }

        // 清空记忆化数组
        memset(dpH, -1, sizeof dpH);
        memset(dpV, -1, sizeof dpV);
        m1 = dfsH(Gh, 0), m2 = dfsV(Gv, 0);
        double newArea = m1 * m2;
        // printTable();
        double newCost = getCost(newArea);

        if (newCost < finalCost) {
            finalHeight = m1;
            finalwidth = m2;
            finalCost = newCost;
            memcpy(finalTable, table, sizeof(table));
            memcpy(finalGh, Gh, sizeof(Gh));
            memcpy(finalGv, Gv, sizeof(Gv));
            memcpy(finalVertex, vertex, sizeof(vertex));
        }

		double delta = newCost - oldCost;	// delta area
		if (delta < 0) {	                // 新解分配面积更小,直接接受
			oldCost = newCost;
        } else if (exp(-delta / t) * 32767 > rand()) { // 新解更次,以一定概率接受
            // int t1 = exp(-delta / t) * 32767;
            oldCost = newCost;
        } else {           // 不接受,还原状态
            table[loc1.fi] = tableValue1;
            table[loc2.fi] = tableValue2;
            vertex[tableValue1.fi] = loc1.fi;
            vertex[tableValue2.fi] = loc2.fi;
            Gh[edge1H.fi][edge1H.se] = val1.fi;
            Gv[edge1V.fi][edge1V.se] = val1.se;
            Gh[edge2H.fi][edge2H.se] = val2.fi;
            Gv[edge2V.fi][edge2V.se] = val2.se;
        }
		t *= coe;	       //根据牛顿冷却定律降温
	}
}

void printBestResult() {
    memset(dpH, -1, sizeof dpH);
    memset(dpV, -1, sizeof dpV);
    int height = dfsH(finalGh, 0); 
    int width  = dfsV(finalGv, 0);
    int newArea = height * width;

    printf("Total time (ms) : %.2f\n",(double)(endClock - startClock) * 1000 / CLOCKS_PER_SEC);
    printf("The sum changes  : %d\n",changeSum);
    printf("The minimum area : %d\n",newArea);
    printf("Area of waste    : %d\n",newArea - sumArea);
    printf("Utilization rate : %.2f%%\n", (double)(sumArea * 1.0 / newArea) * 100);
    printf("height = %d  width = %d\n",height, width);
    // for (int i = 0; i < modulesNum; i++) {
    //     printf("%d: %d %d\n", i, input[i].fi, input[i].se);
    // }
    printf("\n      --------  final state following  begin -------- \n");
    printTable(finalTable, finalVertex);
    printf("\n      --------  final state following  end -------- \n");
}


/**
 * FIXED:   1. 可以实现多矩形分配,占比率约为90%
 *          2. 最终的模块可以分配展示出来,而不是手动去画
 * 
 * TODO:    
 *          1. 调参过程需要加入自动化, 可以引入多线程去调参 
 *          2. 当有两个模块需要靠近时,该如何处理
 *          3. 处理不规则模块,例如 L-shape
 *          4. 处理软模块
*/


void initTable() {
    for (int i = 0; i < modulesNum; i++) {
        table[i].fi = i;
        table[i].se = rand() % 1;
        vertex[i] = i;
    }
}

void printRes() {
    for (int i = 0; i < modulesNum; i++) {
        int u = vertex[i];
        int x  = dpV[0] - dpV[getCoordinate(u, 1).fi];
        int y  =  dpH[0] - dpH[getCoordinate(u, 0).fi];
        printf("%d %d\n", x, y);
    }
}

int main() {
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    srand(11113);
    startClock=clock();
    cin >> modulesNum;
    initPlaneSize();
    initGraphVertex();
    finalCost = 9999999;
    for (int i = 0; i < modulesNum; ++i) {
        double x,y; 
        cin >> x >> y;
        int xx = (int)(x) + 1;
        int yy = (int)(y) + 1;
        sumArea += x * y;
        input.emplace_back(make_pair(xx,yy));
    }
    cin >> closeNum;
    for (int i = 0; i < closeNum; i++) {
        int x, y; cin >> x >> y; 
        close.emplace_back(make_pair(x, y));
    }
    initTable();

    for (int i = 0; i < 10; i++) {
        SA(coe[rand()%10]);
    }
    endClock=clock();
    printBestResult();
    // printRes();
    return 0;
}

Judge代码参考

import argparse
import matplotlib.pyplot as plt


def parse_argument():
    parser = argparse.ArgumentParser(description='JUDGER of the problem - '
                                                 '"floorplanning with optimum area utilization under constraints"')
    parser.add_argument('input_path', help='the input file path')
    parser.add_argument('output_path', help='the output file path')
    args = parser.parse_args()
    return args


def read_input_file(input_path):
    with open(input_path) as input_file:
        n = int(input_file.readline().strip())
        cells = []
        for _ in range(n):
            w, h = (float(_) for _ in input_file.readline().strip().split(' '))
            cells.append((w, h))

        m = int(input_file.readline().strip())
        nears = []
        for _ in range(m):
            a, b = (int(_) for _ in input_file.readline().strip().split(' '))
            nears.append((a, b))

    return n, cells, m, nears


def read_output_file(output_path):
    coors = []
    with open(output_path) as output_file:
        for line in output_file:
            x, y = (float(_) for _ in line.strip().split(' '))
            coors.append((x, y))

    return coors


def segment_distance(a0, b0, a1, b1):
    return max(a0 - b1, a1 - b0)


def display_placement(n, x0_lst, x1_lst, y0_lst, y1_lst, nears):
    fig, ax = plt.subplots()
    for i in range(n):
        x0, x1, y0, y1 = x0_lst[i], x1_lst[i], y0_lst[i], y1_lst[i]
        rect = plt.Rectangle((x0, y0), x1-x0, y1-y0, lw=1.0, ls='-', fc='blue', ec='black')
        ax.add_patch(rect)
        ax.annotate(str(i), ((x0 + x1) / 2.0, (y0 + y1) / 2.0),
                    color='w', weight='bold', fontsize=6, ha='center', va='center')

    for (i, j) in nears:
        x0a, x1a, y0a, y1a = x0_lst[i], x1_lst[i], y0_lst[i], y1_lst[i]
        x0b, x1b, y0b, y1b = x0_lst[j], x1_lst[j], y0_lst[j], y1_lst[j]
        plt.plot([(x0a + x1a) / 2.0, (x0b + x1b) / 2.0 ],
                 [(y0a + y1a) / 2.0, (y0b + y1b) / 2.0])

    min_x, max_x = min(x0_lst), max(x1_lst)
    min_y, max_y = min(y0_lst), max(y1_lst)
    aix_range = max(max_x - min_x, max_y - min_y) * 1.1
    center_x = (min_x + max_x) / 2.0
    center_y = (min_y + max_y) / 2.0

    ax.set_xlim([center_x - aix_range / 2.0, center_x + aix_range / 2.0])
    ax.set_ylim([center_y - aix_range / 2.0, center_y + aix_range / 2.0])
    plt.show()


def main():
    # get input and output file path
    args = parse_argument()

    # read input and output files
    n, cells, m, nears = read_input_file(args.input_path)
    coors = read_output_file(args.output_path)
    x0_lst = [coor[0] for coor in coors]
    x1_lst = [coor[0] + cell[0] for (coor, cell) in zip(coors, cells)]
    y0_lst = [coor[1] for coor in coors]
    y1_lst = [coor[1] + cell[1] for (coor, cell) in zip(coors, cells)]

    # displace placement
    display_placement(n, x0_lst, x1_lst, y0_lst, y1_lst, nears)

    # check if cells overlap
    for i in range(n-1):
        for j in range(i+1, n):
            dx = segment_distance(x0_lst[i], x1_lst[i], x0_lst[j], x1_lst[j])
            dy = segment_distance(y0_lst[i], y1_lst[i], y0_lst[j], y1_lst[j])
            if dx < 0 and dy < 0:
                raise Exception(f"The cell[{i}] and cell[{j}] overlapped.")

    # calculate the bounding rect area
    min_x, max_x = min(x0_lst), max(x1_lst)
    min_y, max_y = min(y0_lst), max(y1_lst)
    area = (max_x - min_x) * (max_y - min_y)

    # calculate the dist scores
    dist = 0
    for (i, j) in nears:
        dx = segment_distance(x0_lst[i], x1_lst[i], x0_lst[j], x1_lst[j])
        dy = segment_distance(y0_lst[i], y1_lst[i], y0_lst[j], y1_lst[j])
        ds = (dx if dx > 0 else 0) + (dy if dy > 0 else 0)
        dist += ds * ds

    norm = sum((x1 - x0) * (y1 - y0) for x0, x1, y0, y1 in zip(x0_lst, x1_lst, y0_lst, y1_lst))
    score = (area + dist) / norm
    print("%.3f" % score)


if __name__ == "__main__":
    main()

5.总结

首先是比赛关于此题的总结,总体来说还算比较充分,基本赛前准备和赛题较吻合,当然由于自己的debug能力,导致这道题拖到了4h后才交,好在最后这道题的成绩也是满分,也可能是样例比较水吧,同时也侧面反映出了自己的编码、找错能力下滑了很多。

然后是这道题的拓展,总的来看题目给的约束很松,很多约束没有涉及到,比如说提前预制某个模块在某个位置,有一些模块时软模块,等等。说实话,在赛前一直和队友讨论如何解决模块预制问题,一直没有好的解决方案,因为最终的布置图是未知的,所以没办法提前在某个区域预制模块,但是论文中提到了这一点,并且给出了相应布置图,但是具体的解决方案没有详细说,如果哪位大佬看到这里并且有好的解决方案,麻烦告知下,谢谢。
在code上我觉得还有很多地方可以优化,比如可以进入多线程进行自动调参,毕竟是个随机算法嘛,说不定某个参数就可以找个更优的结果,想进一步讨论也可以私信我。

另外真的好久不写blog了,不知道该如何描述算法或者解决方案,可能会引起极度不适,东一头西一头的,见谅啦。加油陌生人~

你可能感兴趣的:(经典问题集合,EDAthon,Floorplanning,模拟退火,SA,平面覆盖问题)