组网算法单机原型(C++实现) 郝伟 2020/09/15 [TOC]

1. 说明

本文通过C++代码,介绍了组网算法的核心思想,并提供了单机版C++实现代码,用于演示了组网算法的主要逻辑。由于需求或需求理解可能有变化,所以以下代码根据实际情况会有一定调整。

2. C++实现

#include <iostream>
#include <vector>
#include <tuple>
#include <random>
#include <list>
#include <string>
#include <regex>
#include <map>
#include<stack>

// 预定义的内容
#define CountryCount 8
using uint = unsigned int;
using namespace std;

// Note: string variables in args should be in c_str() fromat.
template<typename ... Args>
string format(const std::string &format, Args ... args) {
    size_t size = snprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0'
 unique_ptr<char[]> buf(new char[size]);
    snprintf(buf.get(), size, format.c_str(), args ...);
    return string(buf.get(), buf.get() + size - 1); // remove '\0'
}

// 拆分字符串
vector<string> split(const string &in, const string &delimit) {
    regex re{delimit};
    return vector<string>{sregex_token_iterator(in.begin(), in.end(), re, -1), sregex_token_iterator()};
}

// 产生一个 [0, count) 间的随机整数
uint nextInt(int count) {
    static std::default_random_engine e(0);
    return count > 0 ? e() % count : 0;
}


// 节点类型
enum NodeType {
    Undefined = 0,     // 未定义
 GateWay = 1,       // 网关节点
 ManagerNode = 2,   // 注册节点
 RouterNode = 3,    // 路由节点
 ExecutionNode = 4, // 远控节点
};

enum LinkType {
    HighSpeed = 1,
    HighSecurity = 2,
};


// 国家编号
enum Countries {
    CHN = 1, // 中国
 RUS = 2, // 俄罗斯
 ITA = 3, // 意大利
 JPN = 4, // 日本
 KOR = 5, // 韩国
 USA = 6, // 美国
 GBR = 7, // 英国
 ESP = 8 // 西班牙
};

string NodeTypeNames[5]{"Undefined", "GateWay", "ManagerNode", "RouterNode", "ExecutionNode"};

string CountryNames[9]{"Undefined", "China", "Russa", "Italy", "Japan", "Korea", "Amarica", "Britain", "Spain"};


// 用于表示筛选条件
class Condition {
public:
    uint pre_area = 0;       // 在链路上的目标国家的前一国家,为0时表示不指定。
 uint tar_area = 0;       // 目标节点IP
 uint to_jumps = 0;       // 去的跳数,不包括pre_area和tar_area
 uint back_jumps = 0;     // 返回跳数
 uint linkType = HighSecurity;  // 链路类型

 Condition() {}

    Condition(uint tar, uint jumps) {
        tar_area = tar;
        back_jumps = jumps / 2;
        to_jumps = jumps - back_jumps - 1;
    }

    Condition(uint tar, uint _to_jumps, uint _from_jumps) {
        tar_area = tar;
        to_jumps = _to_jumps;
        back_jumps = _from_jumps;
    }

    Condition(uint pre, uint tar, uint _to_jumps, uint _from_jumps) {
        pre_area = pre;
        tar_area = tar;
        to_jumps = _to_jumps;
        back_jumps = _from_jumps;
    }

    Condition(uint pre, uint tar, uint _to_jumps, uint _from_jumps, uint linktype) {
        pre_area = pre;
        tar_area = tar;
        to_jumps = _to_jumps;
        back_jumps = _from_jumps;
        linkType = linktype;
    }

    // 返回总路国家,包括去的路由节点,pre, tar 和 回来国家
 uint size() {
        uint count = to_jumps + back_jumps + 1;
        if (pre_area > 0)
            count++;
        return count;
    }
};

// 用于表示网络中的节点,包括四类节点,通过节点类型区分
class Node {
public:
    uint ip = 0;           // IP地址
 double lat = 0;        // 纬度
 double lon = 0;        // 经度
 uint country = 0;      // 国家
 uint nodeType = 0;     // 节点类型
 bool online = true;    // 是否在线
 uint bandwidth = 1024; // 带宽
 uint latecy = 100;     // 延时
 int politics = 0;      // 政域值,取值范围为 [-9, 9]

 vector<Node *> mnodes;  // 注册节点列表
 vector<Node *> rnodes;  // 路由节点列表

 Node() {}

    Node(int ip) { this->ip = ip; }

    static double getDistance(double lat1, double long1, double lat2, double long2) {
        return 6371 * acos(sin(lat1 * 0.01745329) * sin(lat2 * 0.01745329) +
                           cos(lat1 * 0.01745329) * cos(lat2 * 0.01745329) * cos((long1 - long2) * 0.01745329));
    }

    double getDistance(Node *n) {
        return getDistance(lat, lon, n->lat, n->lon);
    }


    // ip, country, nodetype, politics, latitude, longtitude
 string toString() {
        return format("ip=%2d: %s(politics=%2d), # of rnodes=%d", ip, CountryNames[country].c_str(), politics,
                      rnodes.size());
    }

    // ip,country,nodetype,politics,online,bandwidth,latecy,latitude,longtitude
 string toString1() {
        return format("%2d,%d,%d,%2d,%d,%4d,%3d,%6.2f,%6.2f",
                      ip, country, nodeType, politics, online, bandwidth, latecy, lat, lon);
    }

    void fromString1(string s) {
        auto ss = split(s, ",");
        ip = atoi(ss.at(0).c_str());
        country = atoi(ss.at(1).c_str());
        nodeType = atoi(ss.at(2).c_str());
        politics = atoi(ss.at(3).c_str());
        online = atoi(ss.at(4).c_str());
        bandwidth = atoi(ss.at(5).c_str());
        latecy = atoi(ss.at(6).c_str());
        lat = atof(ss.at(7).c_str());
        lon = atof(ss.at(8).c_str());
    }

    void print() {
        cout << "------- " << toString() << " -------" << endl;
        for (auto n : rnodes)
            cout << "\t" << n->toString1() << endl;
        cout << endl;
    }

    // copy properties except ip
 void copy(Node *n) {
        this->country = n->country;
        this->politics = n->politics;
        this->bandwidth = n->bandwidth;
        this->latecy = n->latecy;
        this->lat = n->lat;
        this->lon = n->lon;
        this->online = n->online;
        this->nodeType = n->nodeType;
    }

    // 根据带宽,延时,是否在线等选择路由节点。
 Node *selectRnode() {
        int ip = 0;


        return rnodes.size() > 0 ? rnodes.at(nextInt(rnodes.size())) : nullptr;
    }


    // 本函数用于测试,用于产生连续IP的Node
 static Node *newInstance() {
        static int ip = 0;
        auto n = new Node();
        n->ip = ++ip;
        return n;
    }
};


class algo {
public:
    // 初始化整个网络,这个网络分布在8个国家,每个国家有3-10个路由节点
 // 通过添加注册和路由节点,组成一个树型结构
 static Node *generate_network() {

        tuple<uint, double, double, int> countryInfo[8]
                {
                        make_tuple(CHN, 116.40, 39.87, 9),
                        make_tuple(JPN, 139.41, 36.43, -3),
                        make_tuple(KOR, 127.45, 36.17, -2),
                        make_tuple(USA, -76.53, 38.86, -9),
                        make_tuple(RUS, -296.30, 58.88, 5),
                        make_tuple(GBR, -361.34, 53.20, -7),
                        make_tuple(ITA, 15.34, 41.03, -5),
                        make_tuple(ESP, -3.34, 39.38, -5)
                };

        auto gw = new Node();
        for (int i = 0; i < CountryCount; i++) {
            // 在本层循环中,首先生成一个注册节点,然后再为注册节点生成多个路由节点
 // 生成一个注册节点
 auto manager_node = Node::newInstance();
            manager_node->nodeType = NodeType::ManagerNode;
            manager_node->country = get<0>(countryInfo[i]);
            manager_node->lat = get<1>(countryInfo[i]);
            manager_node->lon = get<2>(countryInfo[i]);
            manager_node->politics = get<3>(countryInfo[i]);
            gw->mnodes.push_back(manager_node);

            // 生成3-10个路由节点,并添加至注册节点下(rnodes)
 for (int j = 0, count = nextInt(8) + 3; j < count; j++) {
                auto router_node = Node::newInstance();
                router_node->copy(manager_node);
                router_node->nodeType = NodeType::RouterNode;
                router_node->lat = manager_node->lat + 0.01 * nextInt(100);
                router_node->lon = manager_node->lon + 0.01 * nextInt(100);
                router_node->bandwidth = 100 + nextInt(2000);
                router_node->latecy = 10 + nextInt(190);
                router_node->online = nextInt(100) > 14; // 85% 的在线率
 manager_node->rnodes.push_back(router_node);
            }
        }

        // 因为任意注册节点都需要知道其他注册节点,所以本操作为广播操作通知所有其他注册节点自己的节点
 for (int i = 0; i < CountryCount; i++)
            for (uint j = 0; j < gw->mnodes.size(); j++)
                gw->mnodes.at(i)->mnodes.push_back(gw->mnodes.at(j));

        return gw;
    }


    // 返回指定节点的路由节点数
 static uint size(Node *_node) {
        uint size = 0;
        for (auto n : _node->mnodes)
            size += n->rnodes.size() + 1;
        return size;
    }

    // 收集所有路由节点
 static vector<Node *> collectNodes(Node *_node) {
        vector<Node *> nodes;
        for (auto n : _node->mnodes) {
            nodes.push_back(n);
            for (auto n1 : n->rnodes) {
                nodes.push_back(n1);
            }
        }
        return nodes;
    }

    // 根据条件F从nodes中选出一个节点
 template<typename F>
    static Node *pickNode(vector<Node *> &nodes, F fun) {
        Node *p = nullptr;
        for (int i = nodes.size() - 1; i >= 0; i--) {
            if (fun(nodes[i])) {
                p = nodes[i];
                nodes.erase(nodes.begin() + i);
                break;
            }
        }
        return p;
    }

    // 根据输入的国家,找到对应的节点
 static Node *findNodeByCountry(vector<Node *> &mnodes, uint contry) {
        return pickNode(mnodes, [contry](Node *n) -> bool { return n->country == contry; });
    }

    // 返回所有的组合
 static vector<vector<Node *>> getPermutation(vector<Node *> &nodes) {
        vector<vector<Node *>> res;
        vector<Node *> dstlist;
        traverse(dstlist, nodes, res);
        return res;
    }

    static void traverse(vector<Node *> &dstlist, vector<Node *> &srclist, vector<vector<Node *>> &res) {
        if (srclist.size() == 0) {
            vector<Node *> nodes;
            for (auto n : dstlist)
                nodes.push_back(n);
            res.push_back(nodes);
        } else {
            for (uint i = 0; i < srclist.size(); i++) {
                auto n = srclist[i];
                srclist.erase(srclist.begin() + i);
                dstlist.push_back(n);
                traverse(dstlist, srclist, res);
                dstlist.erase(dstlist.begin() + dstlist.size() - 1);
                srclist.insert(srclist.begin() + i, n);
            }
        }
    }

    static double getLinkDistance(vector<Node *> &nodes, vector<Node *> &tars, int to_jumps) {
        double distance = 0;
        vector<Node *> link;
        for (auto n : nodes)
            link.push_back(n);
        for (int i = 0; i < tars.size(); i++)
            link.insert(link.begin() + to_jumps + i, tars[i]);

        for (int i = 0; i < link.size() - 1; i++)
            distance += link[i]->getDistance(link[i + 1]);

        return distance;
    }


    // TODO:从给定的注册节点中,根据某些条件,选择出cout个路由节点
 static vector<Node *> getRNodes(Node *rnode, uint count) {
        vector<Node *> nodes;
        for (int i = 0; i < count; i++)
            nodes.push_back(rnode->rnodes[i]);
        return nodes;
    }

    // 从现有的国家中选择合适的区域。
 static vector<Node *> selectCountries(vector<Node *> &mnodes, Condition con) {
        vector<Node *> link; // 结果列表

 map<uint, Node *> nodemap; // 以IP为key, mnode为值的哈希表
 for (auto n : mnodes)
            nodemap[n->ip] = n;

        // 输出查看,其中first 表示 key, second 表示 value
 for (auto it = nodemap.begin(); it != nodemap.end(); ++it)
            cout << it->first << " => " << it->second->toString() << '\n';

        // 找到目标区域的注册节点
 vector<Node *> tars; // 目标区域注册节点,可能是1个只有tar,也可能是2个,为 pre+tar
 Node *tar = findNodeByCountry(mnodes, con.tar_area);
        if (tar != nullptr) {
            tars.push_back(tar);
            cout << "target: " << tar->toString() << endl;
        }

        // 添加目标前一节点(如果存在)
 if (con.pre_area > 0) {
            Node *pre = findNodeByCountry(mnodes, con.pre_area);
            if (pre != nullptr) {
                tars.insert(tars.begin(), pre);
                cout << "pre: " << pre->toString() << endl;
            }
        }


        // 选择 total 个国家,并且保证政域差尽可能大
 vector<Node *> cs; // 找到的候选国家列表
 uint to = con.to_jumps;
        uint back = con.back_jumps;
        uint total = to + back;
        int count = 0;
        while (!checkCandiatesOk(cs) && ++count < 10)
            cs = selectCandiates(mnodes, total);

        if (count > 10) {
            cout << "failed to select nodes." << endl;
            return link;
        }

        auto nodes = getPermutation(cs);  // 返回cs路径中,所有的节点的全排列的多条路径
 //打印出nodes看结果
// for (int i = 0; i < nodes.size(); ++i) {
// for (auto n: nodes[i])
// cout << n->ip << " ";
// cout << endl;
// }

 // 找到路径最短的节点
 auto ns = nodes[0];
        for (int i = 1; i < nodes.size(); i++) {
            double dis = getLinkDistance(ns, tars, to);
            double disi = getLinkDistance(nodes[i], tars, to);
            if (disi < dis)
                ns = nodes[i];
        }

        // 将目标国家插入至结果中,例如将T插入: [A, B, C] -> [A, B, T, C]
 for (int i = 0; i < tars.size(); i++)
            cs.insert(cs.begin() + to + i, tars[i]);

        // 每个区域选择2个节点
 int num = 2;

        for (int i = 0; i < cs.size(); i++) {
            if (cs[i]->ip == tars[0]->ip || cs[i]->ip == tars[tars.size() - 1]->ip)
                link.push_back(cs[i]);
            else {
                for (auto n : getRNodes(cs[i], num))
                    link.push_back(n);
            }
        }

        // TODO: 判断总个数是否满足要求

 return link;
    }

    // 从给定的所有节点中,随机选择 count 个候选国家
 // 筛选的规则是先添加所有节点,然后随机删除,直到个数与count相等
 static vector<Node *> selectCandiates(vector<Node *> &nodes, uint count) {
        vector<Node *> res;
        // 先添加所有节点
 for (auto n : nodes)
            res.push_back(n);
        // 随机删除,直到个数下count相等
 while (res.size() > count)
            res.erase(res.begin() + nextInt(res.size())); // 随机删除一个
 return res;
    }

    // 检测选中的国家列表是否满足条件,目前的条件是从只要有正有负即可
 static bool checkCandiatesOk(vector<Node *> &cs) {
        int max = 0, min = 0;
        for (auto n : cs) {
            if (n->politics > max)
                max = n->politics;
            if (n->politics < min)
                min = n->politics;
        }
        return max > 0 && min < 0;
    }
};


int main() {
    auto gw = algo::generate_network();
    for (auto n : gw->mnodes)
        n->print();
    // auto nodelist = algo::collectNodes(gw);


 Condition con1{USA, 5};
    auto res = algo::selectCountries(gw->mnodes, con1);

    cout << "results:" << endl;
    for (auto n : res)
        cout << n->toString() << endl;
}

/*
 c++ map 操作
 empty(), size(), erase 删除元素, insert 插入元素, swap 交换两个map容器内容, clear 清除容器
 emplace 构造并插入元素, ref: https://blog.csdn.net/qq_39735236/article/details/82688904
 * */

3. 运行结果

C:\Users\Hao\CLionProjects\test2\cmake-build-debug\netalgo.exe
------- ip= 1: China(politics= 9), # of rnodes=7 -------
2,1,3, 9,1,1860,173,116.79, 40.20
3,1,3, 9,0,1597, 33,116.67, 39.90
4,1,3, 9,1,1499,178,117.06, 40.43
5,1,3, 9,1, 894, 73,116.96, 40.55
6,1,3, 9,1, 354, 24,116.59, 40.78
7,1,3, 9,1,1649,190,117.09, 40.56
8,1,3, 9,1,1460, 75,116.52, 40.50
------- ip= 9: Japan(politics=-3), # of rnodes=9 -------
10,4,3,-3,1, 388,148,140.40, 36.54
11,4,3,-3,1, 871,167,140.37, 37.32
12,4,3,-3,1,1771,170,140.36, 37.25
13,4,3,-3,0, 211, 40,140.14, 36.84
14,4,3,-3,1, 427,139,139.47, 37.19
15,4,3,-3,1, 875,110,140.04, 37.17
16,4,3,-3,1,1112,147,139.73, 37.11
17,4,3,-3,1,1448,111,139.51, 37.23
18,4,3,-3,1, 590,172,139.57, 37.24
------- ip=19: Korea(politics=-2), # of rnodes=3 -------
20,5,3,-2,1,1963, 39,127.78, 36.86
21,5,3,-2,1,1835,153,127.64, 36.95
22,5,3,-2,1, 516,116,128.03, 36.52
------- ip=23: Amarica(politics=-9), # of rnodes=8 -------
24,6,3,-9,1,2046, 36,-76.41, 38.91
25,6,3,-9,1,1208,121,-75.69, 39.36
26,6,3,-9,1,1184, 24,-76.45, 39.61
27,6,3,-9,0,1619, 70,-76.09, 39.20
28,6,3,-9,1,1763, 23,-76.39, 39.61
29,6,3,-9,1,1349, 99,-76.20, 39.06
30,6,3,-9,1,1331,147,-76.42, 39.14
31,6,3,-9,1,2070, 64,-75.69, 38.87
------- ip=32: Russa(politics= 5), # of rnodes=3 -------
33,2,3, 5,1,1670, 70,-295.61, 59.15
34,2,3, 5,1, 169,115,-295.46, 59.07
35,2,3, 5,0, 742, 51,-295.58, 59.50
------- ip=36: Britain(politics=-7), # of rnodes=10 -------
37,7,3,-7,1,1750,163,-360.63, 53.31
38,7,3,-7,1,1426, 29,-360.68, 54.07
39,7,3,-7,1, 791, 50,-361.28, 54.14
40,7,3,-7,1,1218,141,-360.92, 53.95
41,7,3,-7,1, 878, 90,-360.73, 53.32
42,7,3,-7,1,1782,137,-361.06, 53.59
43,7,3,-7,1,1796, 98,-360.46, 53.98
44,7,3,-7,1, 295, 12,-361.16, 53.86
45,7,3,-7,1,1042, 84,-361.22, 53.97
46,7,3,-7,1, 706,154,-361.16, 54.15
------- ip=47: Italy(politics=-5), # of rnodes=8 -------
48,3,3,-5,1,1612, 15, 15.98, 41.03
49,3,3,-5,0,1302, 44, 15.46, 41.81
50,3,3,-5,1, 545,178, 15.57, 41.39
51,3,3,-5,1, 721,153, 15.74, 41.18
52,3,3,-5,1, 141, 71, 15.52, 41.24
53,3,3,-5,1,2092,183, 15.66, 41.35
54,3,3,-5,1, 388, 20, 16.20, 41.40
55,3,3,-5,1,2065,195, 15.64, 41.16
------- ip=56: Spain(politics=-5), # of rnodes=3 -------
57,8,3,-5,1,1135,115, -2.82, 39.70
58,8,3,-5,1, 803,187, -2.60, 40.11
59,8,3,-5,1, 147, 75, -2.86, 39.58
1 => ip= 1: China(politics= 9), # of rnodes=7
9 => ip= 9: Japan(politics=-3), # of rnodes=9
19 => ip=19: Korea(politics=-2), # of rnodes=3
23 => ip=23: Amarica(politics=-9), # of rnodes=8
32 => ip=32: Russa(politics= 5), # of rnodes=3
36 => ip=36: Britain(politics=-7), # of rnodes=10
47 => ip=47: Italy(politics=-5), # of rnodes=8
56 => ip=56: Spain(politics=-5), # of rnodes=3
target: ip=23: Amarica(politics=-9), # of rnodes=8
results:
ip= 2: China(politics= 9), # of rnodes=0
ip= 3: China(politics= 9), # of rnodes=0
ip=10: Japan(politics=-3), # of rnodes=0
ip=11: Japan(politics=-3), # of rnodes=0
ip=23: Amarica(politics=-9), # of rnodes=8
ip=33: Russa(politics= 5), # of rnodes=0
ip=34: Russa(politics= 5), # of rnodes=0
ip=57: Spain(politics=-5), # of rnodes=0
ip=58: Spain(politics=-5), # of rnodes=0
Process finished with exit code 0

results matching ""

    No results matching ""