Repository: netcan/2017-HUAWEI-Codecraft Branch: master Commit: ff32fa385da9 Files: 15 Total size: 41.0 KB Directory structure: gitextract_mkuby_3i/ ├── .gitignore ├── CMakeLists.txt ├── README.md ├── cdn.cpp ├── deploy.cpp ├── deploy.h ├── gene.h ├── io.cpp ├── lib/ │ ├── lib_io.h │ └── lib_time.h ├── mcmf.cpp ├── mcmf.h ├── random.h ├── testBranch.txt └── 初赛样例参数分析.xlsx ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /spfa /gene /random /mcmf_scaling ================================================ FILE: CMakeLists.txt ================================================ # CMake 最低版本号要求 cmake_minimum_required(VERSION 2.8) # 项目信息 project(cdn) # include路径 include_directories(${PROJECT_SOURCE_DIR}/lib) # 设置可执行文件生成路径 set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../bin) # 生成debug版本 SET(CMAKE_BUILD_TYPE "release") if (CMAKE_BUILD_TYPE STREQUAL debug) add_definitions(-D_DEBUG) endif () SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -std=c++11") SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall -D_DEBUG -g -std=c++11") # 查找当前目录下的所有源文件 # 并将名称保存到 DIR_LIB_SRCS 变量 aux_source_directory(. DIR_SRCS) # 指定生成目标 add_executable(cdn ${DIR_SRCS}) ================================================ FILE: README.md ================================================ ## 心路历程 [2017华为软件精英挑战赛参赛心得](http://www.netcan666.com/2017/03/23/2017%E5%8D%8E%E4%B8%BA%E8%BD%AF%E4%BB%B6%E7%B2%BE%E8%8B%B1%E6%8C%91%E6%88%98%E8%B5%9B%E5%8F%82%E8%B5%9B%E5%BF%83%E5%BE%97/) ================================================ FILE: cdn.cpp ================================================ #include "deploy.h" #include "lib_io.h" #include "lib_time.h" #include "stdio.h" int main(int argc, char *argv[]) { print_time("Begin"); char *topo[MAX_EDGE_NUM]; int line_num; char *topo_file = argv[1]; line_num = read_file(topo, MAX_EDGE_NUM, topo_file); printf("line num is :%d \n", line_num); if (line_num == 0) { printf("Please input valid topo file.\n"); return -1; } char *result_file = argv[2]; deploy_server(topo, line_num, result_file); release_buff(topo, line_num); print_time("End"); return 0; } ================================================ FILE: deploy.cpp ================================================ #include "deploy.h" #include #include "random.h" #include "mcmf.h" #include "gene.h" typedef void (sigFunc)(int); bool runing = true; sigFunc * Signal(int signo, sigFunc *func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(signo, &act, &oact) < 0) return(SIG_ERR); return(oact.sa_handler); } /* end signal */ void timeOutHandler(int signo) { runing = false; return; } // 直连状态 unordered_set directConn() { static unordered_set direct; if(direct.empty()) { for(int u=0; u < mcmf.consumerNum; ++u) // 初始位置,直连 direct.insert(mcmf.edges[mcmf.G[u + mcmf.networkNum][0]].to); } return direct; } // XJBS bool cmp(int u1, int u2) { // 比较函数,消费降低需要的流量越低,排在越前 int u1cap = 0, u2cap = 0; for(size_t i = 0; i < mcmf.G[u1].size(); ++i) if(mcmf.isConsumer(mcmf.edges[mcmf.G[u1][i]].to)) { u1cap = mcmf.edges[mcmf.G[u1][i]].cap; break; } for(size_t i = 0; i < mcmf.G[u2].size(); ++i) if(mcmf.isConsumer(mcmf.edges[mcmf.G[u2][i]].to)) { u2cap = mcmf.edges[mcmf.G[u2][i]].cap; break; } return u1cap < u2cap; } unordered_set XJBS(bool sorted = false) { unordered_set init = directConn(); vector tmp(init.begin(), init.end()); if(sorted) sort(tmp.begin(), tmp.end(), cmp); list cdn(tmp.begin(), tmp.end()); int minCost = mcmf.minCost_Set(unordered_set(cdn.begin(), cdn.end())); // 删点 int iterationCnt = 0; for(auto itr = cdn.begin(); itr != cdn.end(); ) { int node = *itr; int cost = -1; itr = cdn.erase(itr); ++iterationCnt; if( (cost = mcmf.minCost_Set(unordered_set(cdn.begin(), cdn.end()))) < minCost && cost != -1) { minCost = cost; // printf("deleted: %d\n", node); } else { itr = cdn.insert(itr, node); // 恢复 ++itr; } // printf("cost: %d\n", minCost); } // 替换 /* for(auto itr = cdn.begin(); itr != cdn.end(); ++itr) { int u = *itr; for(size_t i = 0; i < mcmf.G[u].size(); ++i) { int cost = -1; int v = mcmf.edges[mcmf.G[u][i]].to; if(v < mcmf.networkNum) { *itr = v; // 替换 if( (cost = mcmf.minCost_Set(unordered_set(cdn.begin(), cdn.end()))) < minCost && cost != -1) { minCost = cost; printf("replace %d with %d\n", u, v); } else { *itr = u; } } size_t next = mcmf.G[u][i] + 1; if( next < mcmf.G[u].size() && mcmf.edges[next].to == v) ++i; } // printf("minCost: %d/%d\n", minCost, mcmf.consumerNum * mcmf.costPerCDN); } */ // printf("minCost: %d/%d iterationCnt: %d\n", minCost, mcmf.consumerNum * mcmf.costPerCDN, iterationCnt); // printf("cdn sz: %ld\n", cdn.size()); return unordered_set(cdn.begin(), cdn.end()); } // 按评估值从高到低选址 unordered_set evaluationSelect() { vector> evaluation; unordered_set cdn{}; for(int u = 0; u < mcmf.networkNum; ++u) evaluation.push_back(make_pair(mcmf.nodes[u].evaluation, u)); sort(evaluation.begin(), evaluation.end(), greater_equal>()); for(int i = 0; i < mcmf.networkNum; ++i) { cdn.insert(evaluation[i].second); // printf("%d: %lf\n", evaluation[i].second, evaluation[i].first); if(mcmf.minCost_Set(cdn) != -1) break; } mcmf.showRealMinCost(); return cdn; } //- GA begin int fitness(const Gene &p) { // 适应性 int cost = mcmf.minCost_Set(p.to_Set()); // printf("cost = %d\n", cost); int Total = mcmf.networkNum * mcmf.costPerCDN; if(cost == -1) return 1; else return max(1, Total - cost); } // 返回一个选中基因的下标 int select(const vector & genes) { double R = Rand.Random_Real(0, 1); double s = 0.0; for(size_t i = 0; i < genes.size(); ++i) { s += genes[i].P; // printf("%f/%f\n", s, R); if(s >= R) { // printf("select %d\n", i); return i; } } return 0; } void GA(unordered_set init = {}, int geneCnt = 20, double retain = 12, double crossP = 0.95, double mutationP = 0.25) { // 遗传算法 // 初始基因数,精英保留(geneCnt-retain),交叉率,变异率 int iterationCnt = 0; int minCost = MCMF::INF; vector genes(geneCnt); vector next_genes(geneCnt); priority_queue que; // 最大堆选出最强的那20条染色体 unordered_set initial; if(init.empty()) initial = directConn(); else initial = move(init); // 初始化基因 genes[0].set(initial, mcmf.networkNum); for(int i = 1; i < geneCnt; ++i) genes[i].reset(mcmf.networkNum); while(runing && iterationCnt < 800) { // for(int i = 0; i < geneCnt; ++i) { // printf("基因型%d: ", i); // genes[i].show(); // } // 适应度计算 int sum = 0; for(int i = 0; i < geneCnt; ++i) { genes[i].fitness = fitness(genes[i]); sum += genes[i].fitness; minCost = min(minCost, mcmf.networkNum * mcmf.costPerCDN - genes[i].fitness); que.push(genes[i]); // 最大堆 } for(int i = 0; i < geneCnt; ++i) genes[i].P = genes[i].fitness*1.0 / sum; next_genes.clear(); // 选择 for(int i = 0; i < geneCnt; ++i) { if(que.size() > retain) next_genes[i] = que.top(); else next_genes[i] = genes[select(genes)]; que.pop(); } for(int i = 0; i < geneCnt; ++i) // 复制 genes[i] = next_genes[i]; // XXOO for(int i = 0; i < geneCnt; i+=2) if(Rand.Random_Real(0, 1) < crossP) genes[i] * genes[i+1]; // 突变 for(int i = 0; i < geneCnt; ++i) if(Rand.Random_Real(0, 1) < mutationP) genes[i].mutation(); ++iterationCnt; // printf("iterationCnt: %d minCost = %d\n", iterationCnt, minCost); // break; } // mcmf.showSolution(); printf("iterationCnt=%d\n", iterationCnt); // printf("minCost: %d/%d\n\n", minCost, mcmf.consumerNum * mcmf.costPerCDN); mcmf.showRealMinCost(); } //- GA end //- 模拟退火 begin int select(const vector> & cdn) { double R = Rand.Random_Real(0, 1); double s = 0.0; for(size_t i = 0; i < cdn.size(); ++i) { s += cdn[i].second; // printf("%f/%f\n", s, R); if(s >= R) { // printf("select %d\n", i); return i; } } return 0; } unordered_set SA(unordered_setinit = {}, int innerLoop = 10, double T = 20.0, double delta = 0.99999, double poi = 0.02) { // 模拟退火,初始温度,迭代系数,0.15的增点概率 // double T = 20.0, delta = 0.99999; // 初始温度20, 0.999-0.999999 unordered_set backup, cur, best; if(init.empty()) backup = directConn(); else backup = move(init); int minCost = MCMF::INF, backCost = MCMF::INF, curCost = MCMF::INF; backCost = mcmf.minCost_Set(backup); minCost = min(minCost, backCost); int iterationCnt = 0; while(runing && T > 0.1) { for(int loop = 0; loop < innerLoop && runing; ++loop) { vector> cdn; // cdn选中的概率,概率越大,越容易被选中 double sum = 0.0; int u = -1, v = -1; // 随机选点u->v for(auto x: backup) sum += mcmf.nodes[x].evaluation; for(auto x: backup) cdn.push_back(make_pair(x, mcmf.nodes[x].evaluation / sum)); u = cdn[select(cdn)].first; cdn.clear(); sum = 0.0; for(size_t i = 0; i < mcmf.G[u].size(); ++i) { int t = mcmf.edges[mcmf.G[u][i]].to; if(t < mcmf.networkNum) sum += 100000.0 / mcmf.nodes[t].evaluation; // printf("%d %lf\n", t, mcmf.nodes[t].evaluation); } for(size_t i = 0; i < mcmf.G[u].size(); ++i) { int t = mcmf.edges[mcmf.G[u][i]].to; if(t < mcmf.networkNum) cdn.push_back(make_pair(t, (100000.0 / mcmf.nodes[t].evaluation) / sum)); } v = cdn[select(cdn)].first; for(int x: backup) { if(x == u) cur.insert(v); else cur.insert(x); } if(Rand.Random_Real(0, 1) < poi) cur.insert(Rand.Random_Int(0, mcmf.networkNum - 1)); // 增加一个点 curCost = mcmf.minCost_Set(cur); if(curCost == -1) {// 无解 cur.clear(); continue; } else { int dC = curCost - backCost; // printf("dC: %d ratio: %lf probability: %lf\n", dC, curCost * 1.0 / minCost, exp(-dC / T)); if(min(1.0, exp(-dC / T)) > Rand.Random_Real(0, 1)) {// 接受 // printf("T: %lf dC: %d ratio: %lf\n", T, dC, curCost * 1.0 / minCost); backup = move(cur); backCost = curCost; } else { cur.clear(); } if(minCost > backCost) { minCost = backCost; best = backup; #ifdef _DEBUG printf("T=%lf iterationCnt=%d\n", T, iterationCnt); mcmf.showRealMinCost(); #endif } } } T *= delta; ++iterationCnt; // printf("T=%lf iterationCnt=%d minCost = %d\n", T, iterationCnt, minCost); } #ifdef _DEBUG printf("T=%lf iterationCnt=%d\n", T, iterationCnt); mcmf.showRealMinCost(); #endif // printf("Deploy CDN(%ld):\n", backup.size()); // for(int x: backup) // printf("%d ", x); // puts("\n=====Solution======"); // mcmf.showSolution(); return best; } //- 模拟退火 end //- SAGA begin void SAGA(unordered_setinit = {}, double T = 20.0, double poi = 0.05, double delta = 0.999, int geneCnt = 26, double crossP = 0.95, double mutationP = 0.15) { // 模拟退火,初始温度,迭代系数 // double T = 20.0, delta = 0.99999; // 初始温度20, 0.999-0.999999 unordered_set initial; vector genes(geneCnt); vector next_genes(geneCnt); if(init.empty()) initial = directConn(); else initial = move(init); int minCost = MCMF::INF; // for(int i = 0; i < geneCnt; ++i) // genes[i].set(initial, mcmf.networkNum); genes[0].set(initial, mcmf.networkNum); unordered_set direct = directConn(); for(int i = 1; i < geneCnt; ++i) genes[i].reset(mcmf.networkNum); int iterationCnt = 0; // 忘记初始化了!导致段错误!! Gene elite{mcmf.networkNum}; // 精英基因 while(runing && T > 0.1) { next_genes.clear(); int fmin = MCMF::INF; for(int idx = 0; runing && idx < geneCnt; ++idx) { unordered_set s = genes[idx].to_Set(); // 每条染色体 if(s.empty()) continue; // 空集的时候需要跳过 int fi = mcmf.minCost_Set(s), fj; unordered_set cur; // 邻域 // 计算领域 //- 随机选点u int u = -1; int i = Rand.Random_Int(0, s.size() - 1); auto it = s.begin(); for(; it != s.end() && i; ++it, --i); u = *it; // - 选完了 // 随机选u->v int v = -1; do { v = mcmf.edges[mcmf.G[u][Rand.Random_Int(0, mcmf.G[u].size() - 1)]].to; // (u, v)随机选点 } while(v >= mcmf.networkNum); // 防止移动到消费节点 // - 选完v了 for(int x: s) { if(x == u) cur.insert(v); else cur.insert(x); } if(Rand.Random_Real(0, 1) < poi) cur.insert(Rand.Random_Int(0, mcmf.networkNum - 1)); // 增加一个点 // 邻域计算完毕 fj = mcmf.minCost_Set(cur); if(fj != -1) {// 有解 int dC = fj - fi; // printf("dC: %d\n", dC); if(fi == -1 || min(1, exp(-dC / T)) > Rand.Random_Real(0, 1)) {// 接受 genes[idx].set(cur, mcmf.networkNum); genes[idx].fitness = fj; if(fmin > fj) { fmin = fj; elite = genes[idx]; } } else { genes[idx].fitness = fi; // 不接收 if(fmin > fi) { fmin = fi; elite = genes[idx]; } } } else { // 无解,不接受 if(fmin > fi && fi != -1) { fmin = fi; elite = genes[idx]; } genes[idx].fitness = (fi == -1?mcmf.networkNum * mcmf.costPerCDN:fi); } } // 计算适应度 double sum = 0.0; for(int idx = 0; runing && idx < geneCnt; ++idx) { if(fmin == MCMF::INF) fmin = 0; int dC = genes[idx].fitness - fmin; genes[idx].fitness = exp(-dC / T); sum += genes[idx].fitness; } for(int idx = 0; runing && idx < geneCnt; ++idx) genes[idx].P = genes[idx].fitness / sum; // 轮盘赌选择 next_genes[0] = elite; // 精英 for(int idx = 1; runing && idx < geneCnt; ++idx) next_genes[idx] = genes[select(genes)]; for(int idx = 0; runing && idx < geneCnt; ++idx) genes[idx] = next_genes[idx]; // 洗牌,打乱顺序,考虑是否必要 // random_shuffle(genes.begin(), genes.end()); // XXOO for(int i = 0; runing && i < geneCnt; i+=2) if(Rand.Random_Real(0, 1) < crossP) genes[i] * genes[i+1]; // 突变 for(int i = 0; runing && i < geneCnt; ++i) if(Rand.Random_Real(0, 1) < mutationP) genes[i].mutation(); if(fmin != 0) minCost = min(minCost, fmin); T *= delta; ++iterationCnt; // printf("minCost: %d/%d\n\n", minCost, mcmf.consumerNum * mcmf.costPerCDN); } printf("T=%lf iterationCnt=%d\n", T, iterationCnt); // mcmf.showSolution(); // printf("minCost: %d/%d\n\n", minCost, mcmf.consumerNum * mcmf.costPerCDN); mcmf.showRealMinCost(); } //- SAGA end //- 禁忌搜索 begin // 这块没写好,效果太差 unordered_set Tabu(unordered_setinit = {}, int times = MCMF::INF) { // 禁忌搜索 typedef unordered_set X; list H; // 禁忌表,队列 pair x_best; X x_now; if(init.empty()) x_now = directConn(); else x_now = move(init); pair x_next{MCMF::INF, {}}; // 转移 H.push_back(x_best.first = mcmf.minCost_Set(x_now)); // for(int x: x_now) // printf("%d ", x); // puts(""); int iterationCnt = 0; while(runing && iterationCnt < times) { int Len = 0; for(int u: x_now) { for(size_t i = 0; i < mcmf.G[u].size() && runing; i+=2) { ++Len; int v = mcmf.edges[mcmf.G[u][i]].to; // u->v if(v < mcmf.networkNum) { X tmp{}; // 邻居 for(int uu: x_now) { if(uu != u) tmp.insert(uu); else tmp.insert(v); } int cost = mcmf.minCost_Set(tmp); if(cost == -1) continue; if(find(H.begin(), H.end(), cost) == H.end() && cost < x_next.first) { x_next.first = cost; x_next.second = move(tmp); if(x_best.first > cost) { x_best.first = cost; x_best.second = x_next.second; } } } } } H.push_back(x_next.first); // 入队 x_next.first = MCMF::INF; x_now = move(x_next.second); ++iterationCnt; while(H.size() > sqrt(Len)) H.pop_front(); } printf("iterationCnt = %d\n", iterationCnt); printf("minCost: %d/%d cdnNum: %ld\n\n", x_best.first, mcmf.consumerNum * mcmf.costPerCDN, x_best.second.size()); return x_best.second; } //- 禁忌搜索 end //- BPSO begin // 这个BPSO有坑,效果没想象中的好 double sig(double v, double Vmax, double Vmin) { // v->[0, 1] return 1/(1+ pow((Vmax - v)/(v- Vmin), 2)); } void BPSO(unordered_set init = {}, int particleCnt = 10, double Vmin = 0.0, double Vmax = 10.0, double c1 = 1.0, double c2 = 1.0) { vector particles(particleCnt); Particle pBest, gBest; int fpBest = -1, fgBest = -1, fCur; // 当代最小费用,全局最小费用 vector v[particleCnt]; // Vij unordered_set initial; if(init.empty()) initial = directConn(); // 初始状态 else initial = move(init); for(int i = 0; i < particleCnt; ++i) { if(i) particles[i].reset(mcmf.networkNum); else particles[i].set(initial, mcmf.networkNum); // 直连状态 for(int j = 0; j < mcmf.networkNum; ++j) // 初始化速度 v[i].push_back(Vmin + (Vmax - Vmin) * Rand.Random_Real(0, 1)); } // for(int i = 0; i < particleCnt; ++i) // for(int j = 0; j < mcmf.networkNum; ++j) // printf("%lf\n", v[i][j]); int iterationCnt = 0; while(runing) { for(int i = 0; i < particleCnt; ++i) { if( (fCur = mcmf.minCost_Set(particles[i].to_Set())) != -1) { if(fpBest == -1 || fpBest > fCur) { fpBest = fCur; pBest = particles[i]; } if(fgBest == -1 || fgBest > fpBest) { fgBest = fpBest; gBest = pBest; } } } for(int i = 0; i < particleCnt; ++i) { // puts("------------------------"); // printf("p[%d]: \n", i); // particles[i].show(); for(int j = 0; j < mcmf.networkNum; ++j) { v[i][j] = v[i][j] + c1 * Rand.Random_Real(0, 1) * (pBest.getBit(j) - particles[i].getBit(j)) + c2 * Rand.Random_Real(0, 1) * (gBest.getBit(j) - particles[i].getBit(j)); if(v[i][j] > Vmax) v[i][j] = Vmax; else if(v[i][j] < Vmin) v[i][j] = Vmin; // printf("sig(%lf) = %lf\n", v[i][j], sig(v[i][j], Vmax, Vmin)); if(Rand.Random_Real(0, 1) < sig(v[i][j], Vmax, Vmin)) particles[i].setBit(j, 1); else particles[i].setBit(j, 0); } // particles[i].show(); } fpBest = -1; ++iterationCnt; // printf("iterationCnt = %d\n", iterationCnt); // printf("minCost: %d/%d\n", fgBest, mcmf.consumerNum * mcmf.costPerCDN); // break; } printf("iterationCnt = %d\n", iterationCnt); printf("minCost: %d/%d\n", fgBest, mcmf.consumerNum * mcmf.costPerCDN); } //- BPSO end void deploy_server(char * topo[MAX_EDGE_NUM], int line_num,char * filename) { Signal(SIGALRM, timeOutHandler); // 启动计时器 alarm(86); mcmf.loadGraph(topo, line_num); if(mcmf.networkNum < 800){ mcmf.setCostCdnGap(80); // 不贪心降档 unordered_set s = SA(XJBS(true), 1, 500, 0.9999, 0.00); // mcmf.setCostCdnGap(1000); // 最后才贪心降档 // mcmf.minCost_Set(s); // mcmf.showRealMinCost(); // GA(XJBS(true)); // SAGA(XJBS(true), 200, 0.00, 0.99, 20, 0.95, 0.05); } else { mcmf.setCostCdnGap(0); // 不贪心降档 unordered_set s = SA(XJBS(true), 1, 500, 0.9999, 0.00); mcmf.setCostCdnGap(1000); // 最后才贪心降档 mcmf.minCost_Set(s); mcmf.showRealMinCost(); } // SA(Tabu({}, 20)); // GA(XJBS(true)); // SAGA(); // BPSO(XJBS(true)); // XJBS(); // 初始解{},初始温度,增点概率,迭代系数,基因数,交叉率,变异率 // if(mcmf.networkNum < 200) { // mcmf.setCostPerCdnMethod(false); // 动态变动 // SAGA(XJBS(), 2000, 0.00, 0.99, 30, 0.8, 0.05); // } // else if(mcmf.networkNum < 500) { // mcmf.setCostPerCdnMethod(false); // 服务器费用固定 // SAGA(XJBS(false), 2000, 0.00, 0.99, 50, 0.8, 0.05); // } // else { // mcmf.setCostPerCdnMethod(false); // 服务器费用固定 // SAGA(XJBS(true), 20, 0.00, 0.999, 6, 0.8, 0.05); // } // unordered_set cdn{ // 0, 45, 55, 56, 60, 78, 105, 107, 133, 134, 142, 152, 161, 177, 236, 242, 245, 274, 278, 290, 291, 296, 314, 333, 343, 359, 373, 389, 390, 394, 409, 411, 416, 445, 458, 460, 467, 470, 495, 497, 515, 518, 526, 527, 538, 556, 557, 570, 577, 582, 586, 597, 615, 617, 625, 640, 641, 650, 656, 657, 666, 669, 683, 688, 697, 700, 714, 724, 751, 767, 804, 835, 847, 872, 883, 894, 920, 934, 940, 952, 970, 984, 991, 993, 1002, 1017, 1019, 1029, 1031, 1032, 1034, 1053, 1056, 1070, 1076, 1080, 1090, 1103, 1109, 1110, 1118, 1119, 1184, 1187 // }; // mcmf.setCostCdnGap(1000); // mcmf.minCost_Set(cdn); // mcmf.showRealMinCost(); //- test /* double T = 20.0, delta = 0.99999, poi = 0.02; double bestT = T, bestDelta = delta, bestPoi = poi; int minCost = MCMF::INF; int cost = 0; // for(; T <= 100.0; T+=1) { for(poi = 0.01; poi <= 1; poi += 0.01) { alarm(88); if( (cost = SA({}, T,delta, poi)) < minCost && cost != -1) { minCost = cost; bestT = T; bestDelta = delta; bestPoi = poi; } puts("--------------------"); printf("bestT = %lf/%lf bestDelta = %lf/%lf bestPoi = %lf/%lf minCost = %d\n", bestT, T, bestDelta, delta, bestPoi, poi, minCost); puts("--------------------"); runing = true; } */ //- test End // 开始计算 write_result(mcmf.outputPath(), filename); } ================================================ FILE: deploy.h ================================================ #ifndef __ROUTE_H__ #define __ROUTE_H__ #include "lib_io.h" #include #include #include #include #include #include #include #include void deploy_server(char * graph[MAX_EDGE_NUM], int edge_num, char * filename); #endif ================================================ FILE: gene.h ================================================ /************************************************************************* > File Name: gene.cpp > Author: Netcan > Blog: http://www.netcan666.com > Mail: 1469709759@qq.com > Created Time: 2017-03-26 Sun 20:31:48 CST ************************************************************************/ #ifndef __GENE__ #define __GENE__ #include #include #include #include #include #include #include "random.h" using namespace std; class Gene { private: int len; // 长度,0-1200 bitset<10000+5> code; public: double fitness; // 适应度/费用 double P; // 选中概率 Gene(): len(0), code(), fitness(0), P(0) {} inline void reset(int len) { // 重置,亦即随机 this->len = len; this->P = this->fitness = 0; for(int i = 0; i < len; ++i) code[i] = Rand.Random_Int(0, 1); } Gene(int len): len(len), fitness(0), P(0) {} inline void operator*(Gene &b) { // 交叉,同时改变2条染色体 int end = Rand.Random_Int(1, len); // 交换的位置,交换一边就行了,因为另一边不动,这里交换两边 int begin = Rand.Random_Int(0, end - 1); // printf("begin = %d end = %d len = %d\n", begin, end, len); for(int i = begin; i < end; ++i) if(code[i] != b.code[i]) { code[i] = !code[i]; b.code[i] = !b.code[i]; } } inline void set(unordered_set &s, int len) { this->len = len; std::vector ss(s.begin(), s.end()); sort(ss.begin(), ss.end()); // 排序 size_t j = 0; for(int i = 0; i < len && j < ss.size(); ++i) { if(ss[j] == i) { code[i] = 1; ++j; } else // 忘记置0了 code[i] = 0; } } inline bool operator<(const Gene &b) const { // 最小堆用 return this->fitness < b.fitness; } inline void operator=(const Gene &b) { // 赋值 this->len = b.len; this->fitness = b.fitness; this->P = b.P; code = b.code; } inline bool operator==(const Gene &b)const { // 判断序列是否相等 return code == b.code; } inline void mutation(int loc = -1) { // 突变,[0, len) if(loc == -1) loc = Rand.Random_Int(0, len - 1); else if(loc >= len) return; // printf("loc = %d\n", loc); code[loc] = !code[loc]; } inline void show() const { for(int i = 0; i < len; ++i) { if(i != 0 && i % 8 == 0) printf(","); printf(code[i]?"1":"0"); } puts(""); } inline unordered_set to_Set() const { unordered_set S; for(int i = 0; i < len; ++i) if(code[i]) S.insert(i); return S; } inline bool getBit(int loc) { return code[loc]; } inline void setBit(int loc, bool x) { code[loc] = x; } }; typedef Gene Particle; #endif // int main(void) { // Gene ga(10); // Gene gb(10); // ga.show(); // gb.show(); // ga * gb; // ga.show(); // gb.show(); // ga.show(); // ga.mutation(); // ga.show(); // for(auto x: ga.to_Set()) { // printf("%d\n", x); // } // return 0; // } ================================================ FILE: io.cpp ================================================ #include #include #include #include #include #include #include #include #include #define MAX_LINE_LEN 55000 #define INLINE static __inline #ifdef _DEBUG #define PRINT printf #else #define PRINT(...) #endif INLINE void write_file(const bool cover, const char * const buff, const char * const filename); void print_time(const char *head) { #ifdef _DEBUG struct timeb rawtime; struct tm * timeinfo; ftime(&rawtime); timeinfo = localtime(&rawtime.time); static int ms = rawtime.millitm; static unsigned long s = rawtime.time; int out_ms = rawtime.millitm - ms; unsigned long out_s = rawtime.time - s; ms = rawtime.millitm; s = rawtime.time; if (out_ms < 0) { out_ms += 1000; out_s -= 1; } printf("%s date/time is: %s \tused time is %lu s %d ms.\n", head, asctime(timeinfo), out_s, out_ms); #endif } int read_file(char ** const buff, const unsigned int spec, const char * const filename) { FILE *fp = fopen(filename, "r"); if (fp == NULL) { PRINT("Fail to open file %s, %s.\n", filename, strerror(errno)); return 0; } PRINT("Open file %s OK.\n", filename); char line[MAX_LINE_LEN + 2]; unsigned int cnt = 0; while ((cnt < spec) && !feof(fp)) { line[0] = 0; if (fgets(line, MAX_LINE_LEN + 2, fp) == NULL) continue; if (line[0] == 0) continue; buff[cnt] = (char *)malloc(MAX_LINE_LEN + 2); strncpy(buff[cnt], line, MAX_LINE_LEN + 2 - 1); buff[cnt][MAX_LINE_LEN + 1] = 0; cnt++; } fclose(fp); PRINT("There are %d lines in file %s.\n", cnt, filename); return cnt; } void write_result(const char * const buff,const char * const filename) { // 以覆盖的方式写入 write_file(1, buff, filename); } void release_buff(char ** const buff, const int valid_item_num) { for (int i = 0; i < valid_item_num; i++) free(buff[i]); } INLINE void write_file(const bool cover, const char * const buff, const char * const filename) { if (buff == NULL) return; const char *write_type = cover ? "w" : "a";//1:覆盖写文件,0:追加写文件 FILE *fp = fopen(filename, write_type); if (fp == NULL) { PRINT("Fail to open file %s, %s.\n", filename, strerror(errno)); return; } PRINT("Open file %s OK.\n", filename); fputs(buff, fp); fputs("\n", fp); fclose(fp); } ================================================ FILE: lib/lib_io.h ================================================ #ifndef __LIB_IO_H__ #define __LIB_IO_H__ #define MAX_EDGE_NUM (2000 * 20) //读取文件并按行输出到buff。 //buff为一个指针数组,每一个元素是一个字符指针,对应文件中一行的内容。 //spec为允许解析的最大行数。 extern int read_file(char ** const buff, const unsigned int spec, const char * const filename); //将result缓冲区中的内容写入文件,写入方式为覆盖写入 extern void write_result(const char * const buff,const char * const filename); //释放读文件的缓冲区 extern void release_buff(char ** const buff, const int valid_item_num); #endif ================================================ FILE: lib/lib_time.h ================================================ #ifndef __LIB_TIME_H__ #define __LIB_TIME_H__ //打印时间。入参为打印信息头 void print_time(const char * const head); #endif ================================================ FILE: mcmf.cpp ================================================ /************************************************************************* > File Name: spfa.cpp > Author: Netcan > Blog: http://www.netcan666.com > Mail: 1469709759@qq.com > Created Time: 2017-03-21 Tue 21:25:20 CST ************************************************************************/ #include "mcmf.h" char MCMF::topo[50000*1000*6]; void MCMF::getPath(int cost) { if(cost != -1 && cost < solutionPath.first) { solutionPath.first = cost; solutionPath.second.clear(); // 记得清理 vector tmpPath; memset(vis, 0, sizeof(vis[0]) * Vn); findPath(tmpPath, superSource, INF, INF); } } int MCMF::findPath(vector & tmpPath, int u, int minFlow, int totalFlow) { // dfs,深搜路径,路径上的最小流量,总流量 if(vis[u]) return 0; else if(isConsumer(u)) { // 到达消费节点,找到一条路径 solutionPath.second.push_back(tmpPath); vector &b = solutionPath.second.back(); b.push_back(u - networkNum); // 转换为消费节点的id b.push_back(minFlow); b.push_back(servers[nodes[b.front()].bestCdnId].level); // 档次 return minFlow; } vis[u] = true; if(u < superSource) tmpPath.push_back(u); int tf = totalFlow; for(size_t i = 0; i < G[u].size(); ++i) { Edge &e = edges[G[u][i]]; if(e.flow > 0) { // 流过的流量>0 // printf("%d->%d flow: %d\n", e.from, e.to, e.flow); int v = e.to; if(!vis [v]) { if(totalFlow > 0) { int t = findPath(tmpPath, v, min(minFlow, min(totalFlow, e.flow)), min(totalFlow, e.flow)); e.flow -= t; totalFlow -= t; } else break; } } } vis[u] = false; if(u < superSource) tmpPath.pop_back(); return tf; } int MCMF::aug(int u, int minFlow, int &tmpCost, int &cost) { if(u == superSink) { // 到达终点 cost += tmpCost * minFlow; return minFlow; } vis[u] = true; int tf = minFlow; for(size_t i = 0; i < G[u].size(); ++i) { Edge &e = edges[G[u][i]]; if( (e.cap - e.flow) && !e.cost && !vis[e.to]) { int d = aug(e.to, min(tf, (e.cap - e.flow)), tmpCost, cost); e.flow += d; edges[G[u][i] ^ 1].flow -= d; tf -= d; if(! tf) return minFlow; } } return minFlow - tf; } bool MCMF::modLabel(int &tmpCost) { int d = INF; for(int u=0; u < Vn; ++u) // 遍历完全部节点 if(vis[u]) { for(size_t i = 0; i < G[u].size(); ++i) { Edge &e = edges[G[u][i]]; if( (e.cap - e.flow) && !vis[e.to] && e.cost < d) d = e.cost; } } if(d == INF) return false; for(int u=0; u < Vn; ++u) if(vis[u]) { for(size_t i = 0; i < G[u].size(); ++i) { edges[G[u][i]].cost -= d; edges[G[u][i] ^ 1].cost += d; } } tmpCost += d; return true; // SLF优化 /* memset(d, 0x3f, sizeof(int) * Vn); d[superSink] = 0; static deque que; que.push_back(superSink); while(que.size()) { int dt, u = que.front(); que.pop_front(); for(size_t i = 0; i < G[u].size(); ++i) { Edge &e = edges[G[u][i]], &re = edges[G[u][i] ^ 1]; if( (re.cap - re.flow) && (dt = d[u] - e.cost) < d[e.to] ) (d[e.to] = dt) <= d[que.size() ? que.front() : 0] ? que.push_front(e.to) : que.push_back(e.to); } } for(int u=0; u<=superSink; ++u) for(size_t i = 0; i < G[u].size(); ++i) { Edge &e = edges[G[u][i]]; e.cost += d[e.to] - d[u]; } tmpCost += d[superSource]; return d[superSource] < INF; */ } void MCMF::AddEdge(int from, int to, int cap, int cost) { edges.push_back(Edge(to, cap, 0, cost)); edges.push_back(Edge(from, 0, 0, -cost)); int m = edges.size(); G[from].push_back(m - 2); G[to].push_back(m - 1); if(from < networkNum) { if(isConsumer(to)) // 网络节点直连消费节点,计算需要的总共流量 needFlow += cap; if(to < superSource) nodes[from].nodeFlow += cap; } } void MCMF::showSolution() const{ int totalFlow = 0; for(const auto &x : solutionPath.second) { for(vector::const_iterator i = x.begin(); i != x.end() - 1; ++i) { if(i == x.begin()) printf("%d", *i); else printf("->%d", *i); } totalFlow += x.back(); printf(" flow: %d\n", x.back()); } printf("Flow :%d/%d Cost: %d/%d\n", totalFlow, needFlow, solutionPath.first, costPerCDN * consumerNum); } void MCMF::loadGraph(char * topo[MAX_EDGE_NUM], int line_num) { sscanf(topo[0], "%d%d%d", &networkNum, &edgeNum, &consumerNum); // 网络节点数量 网络链路数量 消费节点数量 // solutionPath.first = consumerNum * costPerCDN; // 待求 superSource = consumerNum + networkNum; // 超级源点、汇点 superSink = consumerNum + networkNum + 1; Vn = superSink + 1; int a, b, c, d, maxCap = 0; int i; for(i = 2; i < line_num && !isspace(topo[i][0]); ++i) { sscanf(topo[i], "%d%d%d", &a, &b, &c); // 服务器硬件档次ID 输出能力 硬件成本 servers.push_back(Server(a, b, c)); // printf("level: %d outFlow: %d cost: %d\n", a, b, c); } // printf("maxFlowServer level: %d outFlow: %d cost: %d\n", maxFlowServer.level, maxFlowServer.outFlow, maxFlowServer.cost); for(++i; i < line_num && !isspace(topo[i][0]); ++i) { sscanf(topo[i], "%d%d", &a, &b); // 网络节点ID 部署成本 nodes[a].deployCost = b; // printf("node: %d cost: %d\n", a, b); } for(++i; i < line_num && !isspace(topo[i][0]); ++i) { sscanf(topo[i], "%d%d%d%d", &a, &b, &c, &d); // 链路起始节点ID 链路终止节点ID 总带宽大小 单位网络租用费 AddEdge(a, b, c, d); AddEdge(b, a, c, d); // printf("u: %d v: %d bandwidth: %d cost: %d\n", a, b, c, d); } for(++i; i < line_num; ++i) { sscanf(topo[i], "%d%d%d", &a, &b, &c); // 消费节点ID 相连网络节点ID 视频带宽消耗需求 AddEdge(b, a + networkNum, c, 0); // 与网络节点相连 AddEdge(a + networkNum, superSink, c, 0); // 与汇点相连 maxCap = max(maxCap, c); // printf("consumer: %d connect: %d need: %d\n", a, b, c); // vector path{to, from, bandwidth}; // 直连策略 // solutionPath.second.push_back(move(path)); } sort(servers.begin(), servers.end()); maxFlowServer = servers.back(); // vector::iterator it; // if( (it = lower_bound(servers.begin(), servers.end(), maxCap)) != servers.end()) // >= 最大档 // maxFlowServer = *it; // 存放下标,nodes输出路径的时候用 // else maxFlowServer = servers.back(); // 最大的level // printf("l: %d\n", maxFlowServer.level); costPerCDN = maxFlowServer.cost; // 以最大档次的费用为准 edgeNum = edges.size(); // 边数 solutionPath.first = INF; calcEvaluation(); } const char* MCMF::outputPath() { // getPath(solutionPath.first, true); // 放到最后才遍历路径,提高性能 // showRealMinCost(); char buffer[10]; char *pt = topo, *pb = buffer; snprintf(buffer, sizeof(buffer), "%ld\n\n", solutionPath.second.size()); while(*pb && (*pt++ = *pb++)); for(auto &x: solutionPath.second) { for(auto it = x.begin(); it != x.end(); ++it) { snprintf(buffer, sizeof(buffer), it == x.begin() ? "%d":" %d", *it); pb = buffer; while(*pb && (*pt++ = *pb++)); } *pt++ = '\n'; } *--pt = 0; return topo; } MCMF mcmf; ================================================ FILE: mcmf.h ================================================ /************************************************************************* > File Name: spfa.h > Author: Netcan > Blog: http://www.netcan666.com > Mail: 1469709759@qq.com > Created Time: 2017-03-22 Wed 12:33:02 CST ************************************************************************/ #ifndef __MCMF__ #define __MCMF__ #include #include #include #include #include #include #include #include #include #include #include "deploy.h" using namespace std; extern bool runing; class MCMF{ private: struct Edge{ int to, cap, flow ,cost, oldCost; Edge() {} Edge(int to, int cap, int flow, int cost): to(to), cap(cap), flow(flow), cost(cost), oldCost(cost) {} }; struct Server { int level, outFlow, cost; Server(int level, int outFlow, int cost): level(level), outFlow(outFlow), cost(cost) {} Server() { level = outFlow = cost = 0; } bool operator<(const Server &b) const { if(this->outFlow != b.outFlow) return this->outFlow < b.outFlow; else return this->cost < b.cost; } bool operator<(int flow) const { return this->outFlow < flow; } }; static const int N = 20000+5; static char topo[50000*1000*6]; // 网络路径数量不得超过300000条, 单条路径的节点数量不得超过10000个, 所有数值必须为大于等于0的整数,数值大小不得超过1000000。 int Vn, superSource, superSink; // 总节点数,超级源点/汇点,需要的流量 size_t minCostCdnGap = 50; // 当cdn小于这个数的时候,进行贪心降档 int d[N]; bool vis[N]; // 标记数组 vector servers; // 服务器 Server maxFlowServer; #ifdef _DEBUG int realMinCost = INF; // 保存真实的最小费用,最后打印,调试用 #endif pair>> solutionPath; // 当前可行解,路径 // ZKW算法 int aug(int u, int minFlow, int &tmpCost, int &cost); bool modLabel(int &tmpCost); inline void reset() { // 还原初始状态,删除源点 for(size_t i = 0; i < G[superSource].size(); ++i) G[edges[G[superSource][i]].to].pop_back(); // 删除链接超源的边 for(int i=G[superSource].size() * 2; i > 0; --i) edges.pop_back(); // 删除超源的边 for(size_t i = 0; i < edges.size(); ++i) { edges[i].cost = edges[i].oldCost; edges[i].flow = 0; // 重置流量 } G[superSource].clear(); } inline void calcEvaluation() { // 评估函数,评估值越小越好 for(int u = 0; u < networkNum; ++u) nodes[u].evaluation = nodes[u].deployCost * 100 / nodes[u].nodeFlow; } int findPath(vector & tmpPath, int u, int minFlow, int totalFlow); void getPath(int cost); inline int pathFlowCost() { // 路径流量费 int cost = 0, flow = 0; int tmpCost = 0; do { int f; do { memset(vis, 0, sizeof(vis[0]) * Vn); f = aug(superSource, INF, tmpCost, cost); flow += f; } while(f); } while(modLabel(tmpCost)); if(flow < needFlow) return -1; return cost; // SLF优化 /* while(modLabel(tmpCost)) do bzero(vis, sizeof(vis)); while(aug(superSource, INF, tmpCost, cost)); */ } inline int minCost(const unordered_set &cdn) { // 调用setCDN后再调用minCost!! 注意不能连续调用多次minCost!!! int cost = pathFlowCost(), cdnCost = 0, cdnFlow = 0, minCdnFlowCost = INF; if(cost == -1) return -1; vector> diff; // 存放差/节点名 unordered_map eId; // 存放超源到cdn的边的下标 bool downShift = false; if(cdn.size() < minCostCdnGap) downShift = true; for (size_t i = 0; i < G[superSource].size(); i++) { // 降档 Edge &e = edges[G[superSource][i]]; if(e.flow == 0) continue; vector::iterator it; if( (it = lower_bound(servers.begin(), servers.end(), e.flow)) != servers.end()) // >= 降档 nodes[e.to].bestCdnId = it - servers.begin(); // 存放下标,nodes输出路径的时候用 else nodes[e.to].bestCdnId = servers.size() - 1; // 最大的level if(downShift) { eId[e.to] = G[superSource][i]; e.cap = servers[nodes[e.to].bestCdnId].outFlow; cdnFlow += servers[nodes[e.to].bestCdnId].outFlow; // 计算总费用 } cdnCost += servers[nodes[e.to].bestCdnId].cost; // 计算总费用 // printf("%d e.flow: %d/%d(%d)\n", e.to, e.flow, servers[nodes[e.to].bestCdnId].outFlow, servers[nodes[e.to].bestCdnId].level); } minCdnFlowCost = min(minCdnFlowCost, cost + cdnCost); // 更新总费用 // printf("minCdnFlowCost %d\n", minCdnFlowCost); if(downShift) { bool exit = false; while(! exit) { diff.clear(); for (size_t i = 0; i < G[superSource].size(); i++) { const Edge &e = edges[G[superSource][i]]; if(e.flow == 0) continue; // printf("u: %d e.flow: %d/%d(%d)\n", e.to, e.flow, servers[nodes[e.to].bestCdnId].outFlow, servers[nodes[e.to].bestCdnId].level); if(nodes[e.to].bestCdnId == 0) diff.push_back(make_pair(0, e.to)); else if(e.flow == servers[nodes[e.to].bestCdnId - 1].outFlow) { // 直接可以降档 diff.push_back(make_pair(INF, e.to)); } else diff.push_back(make_pair( (servers[nodes[e.to].bestCdnId].cost - servers[nodes[e.to].bestCdnId - 1].cost) / (e.flow - servers[nodes[e.to].bestCdnId - 1].outFlow) , e.to)); } sort(diff.begin(), diff.end(), greater>()); for(size_t i = 0; i < diff.size(); ++i) { int u = diff[i].second; if(nodes[u].bestCdnId == 0) continue; for(size_t j = 0; j < edges.size(); ++j) { edges[j].cost = edges[j].oldCost; edges[j].flow = 0; // 重置流量 } int dc = servers[nodes[u].bestCdnId].cost - servers[nodes[u].bestCdnId - 1].cost; int df = servers[nodes[u].bestCdnId].outFlow - servers[nodes[u].bestCdnId - 1].outFlow; if(cdnFlow - df >= needFlow) { // 预判 cdnCost -= dc; // 更新Cdn费用 cdnFlow -= df; edges[eId[u]].cap = servers[nodes[u].bestCdnId - 1].outFlow; --nodes[u].bestCdnId; cost = pathFlowCost(); if(cost == -1 || cost + cdnCost > minCdnFlowCost) { // 降档失败 cdnCost += dc; cdnFlow += df; edges[eId[u]].cap = servers[nodes[u].bestCdnId + 1].outFlow; ++nodes[u].bestCdnId; if(cost == -1){ exit = true; break; } } else {// 降档成功,有解 minCdnFlowCost = cost + cdnCost; break; // printf("success: %d\n", u); } } else { exit = true; break; } } // printf("minCdnFlowCost %d\n", minCdnFlowCost); // printf("%d diff: %d\n", u, diff[i].first); // printf("cdnFlow: %d cdnCost: %d needFlow: %d minCdnFlowCost: %d\n", cdnFlow, cdnCost, needFlow, minCdnFlowCost); } } cost = minCdnFlowCost; // 计算部署费用 #ifdef _DEBUG int realCost = cost; #endif for(auto c: cdn) { cost += nodes[c].deployCost; #ifdef _DEBUG realCost += nodes[c].deployCost; #endif } #ifdef _DEBUG realMinCost = min(realMinCost, realCost); #endif if(cost < solutionPath.first) { if(downShift) { for(size_t j = 0; j < edges.size(); ++j) { edges[j].cost = edges[j].oldCost; edges[j].flow = 0; // 重置流量 } pathFlowCost(); } // 打印档次 /* vector> v; for (size_t i = 0; i < G[superSource].size(); i++) { // 降档 Edge &e = edges[G[superSource][i]]; v.push_back(make_pair(e.to, G[superSource][i])); } sort(v.begin(), v.end()); for(size_t i = 0; i < v.size(); ++i) { Edge &e = edges[v[i].second]; // printf("%d e.flow: %d/%d(%d)\n", e.to, e.flow, servers[nodes[e.to].bestCdnId].outFlow, servers[nodes[e.to].bestCdnId].level); printf("%d\t%d\n", e.to, servers[nodes[e.to].bestCdnId].level); } */ getPath(cost); // 更新方案 } return cost; } inline void setCdn(const unordered_set & cdn) { reset(); for(int x: cdn) AddEdge(superSource, x, maxFlowServer.outFlow, 0); } public: struct Node{ int deployCost; // 节点部署费用 int nodeFlow; // 每个节点的流量 int bestCdnId; // 存放每个节点最适合的服务器档次(下标) double evaluation; // 每个节点的评估值 Node() { deployCost = bestCdnId = nodeFlow = evaluation = 0; } } nodes[10000 + 5]; vector G[N]; // 图 vector edges; // 边集 int networkNum, edgeNum, consumerNum, needFlow, costPerCDN = 0; static const int INF = 0x3f3f3f3f; friend class MCMF_SCALING; void inline showRealMinCost() { #ifdef _DEBUG printf("\x1B[31mReal minCost: %d/%d\x1B[0m\n", realMinCost, consumerNum * costPerCDN); #endif } inline bool isConsumer(int u) { return u >= networkNum && u < superSource; } MCMF() { needFlow = 0; }; inline void setCostCdnGap(int x) { minCostCdnGap = x; } void AddEdge(int from, int to, int cap, int cost); void showSolution() const; void loadGraph(char * topo[MAX_EDGE_NUM], int line_num); const char* outputPath(); inline int minCost_Set(const unordered_set &cdn) { setCdn(cdn); return minCost(cdn); } }; extern MCMF mcmf; #endif ================================================ FILE: random.h ================================================ /************************************************************************* > File Name: random.cpp > Author: Netcan > Blog: http://www.netcan666.com > Mail: 1469709759@qq.com > Created Time: 2017-03-27 Mon 07:40:54 CST ************************************************************************/ #ifndef __RANDOM__ #define __RANDOM__ #include #include class Random { private: std::default_random_engine generator; public: // Random(time_t t = time(NULL)): generator(t) {} inline uint32_t Random_Int(uint32_t min, uint32_t max) { // [min, max] return std::uniform_int_distribution{min, max}(generator); } inline double Random_Real(double min, double max) { // [min, max) return std::uniform_real_distribution{min, max}(generator); } }; Random Rand; #endif ================================================ FILE: testBranch.txt ================================================ hello world netcan