$\newcommand{\O}{\mathrm{O}}$
ランダムグラフを生成するアルゴリズム. $1$ つ目は簡単なものの寄せ集め(Erdős–Rényi (ER)モデルとか) で 2 つ目は次数列が与えられたときにそれを満たすような(連結な)グラフを一様ランダムで生成するアルゴリズムになっている.
元論文は "Efficient Generation of Large Random Networks" [Batagelj Brandes 2005] および "Efficient and simple generation of random simple connected graphswith prescribed degree sequence" [Viger, Latapy 2011]
(注) verify してないのでちゃんとやらないと...
struct pair_hash { template <class T1, class T2> size_t operator() (const pair<T1, T2>& p) const { size_t lhs = hash<T1>()(p.first), rhs = hash<T2>()(p.second); return lhs^(rhs+0x9e3779b9+(lhs<<6)+(lhs>>2)); } }; // (多重辺、自己ループも許した)ランダムグラフ (計算量 O(m)) void random_graph(const int node_size, const int edge_size, vector<vector<int> >& graph){ random_device rnd; mt19937 mt(rnd()); uniform_int_distribution<> _rand(0, node_size-1); for(int i = 0; i < edge_size; i++){ int from = _rand(mt), to = _rand(mt); graph[from].push_back(to), graph[to].push_back(from); } } // G(n, p) (各辺が確率 p で存在) (平均計算量 O(np)) void given_probability_random_simple_graph(const int node_size, const double prob, vector<vector<int> >& graph){ random_device rnd; mt19937 mt(rnd()); uniform_real_distribution<double> _rand(0.0, 1.0); int v = 1, w = -1; while(v < node_size){ w += 1 + floor(log(1.0 -_rand(mt))/log(1.0 - prob)); while(w >= v && v < node_size) w -= v, v++; if(v < node_size) graph[v].push_back(w), graph[w].push_back(v); } } // G(n, m) ランダムな単純グラフ (平均計算量 O(m)) void random_simple_graph(const int node_size, int edge_size, vector<vector<int> >& graph){ assert(edge_size <= (long long)(node_size)*(node_size-1)/2); bool complement = false; if((edge_size > (long long)(node_size)*(node_size-1)/4)){ complement = true; edge_size = (long long)(node_size)*(node_size-1)/2 - edge_size; } unordered_set<pair<int, int>, pair_hash> edge_set; random_device rnd; mt19937 mt(rnd()); uniform_int_distribution<> rand1(0, node_size-1), rand2(0, node_size-2); for(int i = 0; i < edge_size; i++){ while(true){ int from = rand1(mt), to = rand2(mt); if(to < from) swap(from, to); else to++; if(edge_set.find({from, to}) != edge_set.end()) continue; if(!complement){ graph[from].push_back(to), graph[to].push_back(from); } edge_set.insert({from, to}); break; } } if(complement){ for(int i = 0; i < node_size-1; i++){ for(int j = i+1; j < node_size; j++){ if(edge_set.find({i, j}) == edge_set.end()){ graph[i].push_back(j), graph[j].push_back(i); } } } } } // ランダムな単純連結グラフ (平均計算量 O(m)) void random_simple_connected_graph(const int node_size, int edge_size, vector<vector<int> >& graph){ assert(edge_size >= node_size - 1); assert(edge_size <= (long long)(node_size)*(node_size-1)/2); unordered_set<pair<int, int>, pair_hash> edge_set; random_device rnd; mt19937 mt(rnd()); vector<int> ver(node_size); iota(ver.begin(), ver.end(), 0); shuffle(ver.begin(), ver.end(), mt); for(int i = 1; i < node_size; i++){ int u = mt() % i; graph[ver[u]].push_back(ver[i]), graph[ver[i]].push_back(ver[u]); edge_set.insert({min(ver[i], ver[u]), max(ver[i], ver[u])}); } edge_size -= node_size - 1; bool complement = false; if((edge_size > (long long)(node_size)*(node_size-1)/4)){ complement = true; edge_size = (long long)(node_size)*(node_size-1)/2 - edge_size; } uniform_int_distribution<> rand1(0, node_size-1), rand2(0, node_size-2); for(int i = 0; i < edge_size; i++){ while(true){ int from = rand1(mt), to = rand2(mt); if(to < from) swap(from, to); else to++; if(edge_set.find({from, to}) != edge_set.end()) continue; if(!complement){ graph[from].push_back(to), graph[to].push_back(from); } edge_set.insert({from, to}); break; } } if(complement){ for(int i = 0; i < node_size-1; i++){ for(int j = i+1; j < node_size; j++){ if(edge_set.find({i, j}) == edge_set.end()){ graph[i].push_back(j), graph[j].push_back(i); } } } } } // ランダムな木 (計算量 O(n)) void random_tree(const int node_size, vector<vector<int> >& graph){ random_device rnd; mt19937 mt(rnd()); vector<int> ver(node_size); iota(ver.begin(), ver.end(), 0); shuffle(ver.begin(), ver.end(), mt); for(int i = 1; i < node_size; i++){ int u = mt() % i; graph[ver[u]].push_back(ver[i]), graph[ver[i]].push_back(ver[u]); } } // 一様ランダムな木 (計算量 O(n log n)) void uniformly_random_tree(const int node_size, vector<vector<int> >& graph){ random_device rnd; mt19937 mt(rnd()); uniform_int_distribution<> _rand(0, node_size - 1); vector<int> prufer_code(node_size - 2); vector<int> used(node_size, 0); set<int> unused; for(int i = 0; i < node_size - 2; ++i){ prufer_code[i] = _rand(mt); ++used[prufer_code[i]]; } for(int i = 0; i < node_size; ++i){ if(!used[i]) unused.insert(i); } for(int i = 0; i < node_size - 2; ++i){ const int ver = *unused.begin(); graph[prufer_code[i]].push_back(ver); graph[ver].push_back(prufer_code[i]); unused.erase(unused.begin()); if(--used[prufer_code[i]] == 0){ unused.insert(prufer_code[i]); } } const int ver1 = *unused.begin(), ver2 = *next(unused.begin(), 1); graph[ver1].push_back(ver2); graph[ver2].push_back(ver1); }
// 次数列 d とグラフ G(vector<vecotr<int> >型で予め頂点数分の配列を確保する), 連結であるべきかを与える. // TIME は適宜設定する. class GraphGenerator { private: int V; bool connected; vector<unordered_set<int> > G; vector<vector<pair<int, int> > > off_tree; vector<pair<int, int> > population; vector<int> visit, non_tree, tree; chrono::high_resolution_clock::time_point start; random_device rnd; mt19937 mt; const int TIME = 1000; // (ms) static constexpr double UP = 1.13108324944; static constexpr double DOWN = 0.92371260217; int _time() const noexcept { chrono::high_resolution_clock::time_point end = chrono::high_resolution_clock::now(); return (int)chrono::duration_cast<chrono::milliseconds>(end - start).count(); } bool check() const noexcept { if(*max_element(degree.begin(), degree.end()) >= V) return false; if(*min_element(degree.begin(), degree.end()) <= -(!connected)) return false; long long edge_size = accumulate(degree.begin(), degree.end(), 0LL); if(edge_size % 2 || (connected && edge_size < 2 * (V - 1))) return false; return true; } void add_edge(const int u, const int v) noexcept { G[u].insert(v), G[v].insert(u); } void erase_edge(const int u, const int v) noexcept { G[u].erase(v), G[v].erase(u); } void swap_edge(const pair<int, int>& p, const pair<int, int>& q) noexcept { erase_edge(p.first, p.second), erase_edge(q.first, q.second); add_edge(p.first, q.first), add_edge(p.second, q.second); } bool construct_init_graph() noexcept { const int node_size = (int)degree.size(); const int max_degree = *max_element(degree.begin(), degree.end()); if(max_degree > node_size - 1) return false; if(accumulate(degree.begin(), degree.end(), 0LL) % 2 == 1) return false; vector<stack<int> > bucket(max_degree + 1); vector<int> deg_cnt(max_degree + 1, 0); for(int i = 0; i < node_size; ++i) bucket[degree[i]].push(i); vector<pair<int, int> > deg_seq; for(int i = max_degree; i >= 1; --i){ deg_cnt[i] = (int)bucket[i].size(); while(!bucket[i].empty()){ deg_seq.emplace_back(i, bucket[i].top()), bucket[i].pop(); } } while(!deg_seq.empty()){ int pos = 0, rem = deg_seq.back().first, cur_degree; const int ver = deg_seq.back().second; --deg_cnt[rem], deg_seq.pop_back(); stack<int> update; while(rem > 0){ if(pos >= (int)deg_seq.size()) return false; cur_degree = deg_seq[pos].first; const int start = max(pos, pos + deg_cnt[cur_degree] - rem); for(int i = start; i < pos + deg_cnt[cur_degree]; ++i){ add_edge(ver, deg_seq[i].second); --deg_seq[i].first, update.push(cur_degree); } pos += deg_cnt[cur_degree], rem -= deg_cnt[cur_degree]; } while(!update.empty()){ --deg_cnt[update.top()], ++deg_cnt[update.top() - 1], update.pop(); } while(!deg_seq.empty() && deg_seq.back().first == 0) deg_seq.pop_back(); } return true; } void dfs(const int u, const int p, const int c) noexcept { visit[u] = 1; for(int v : G[u]){ if(!visit[v]) dfs(v, u, c); else if(v != p && visit[v] == 1) off_tree[c].emplace_back(u, v); } visit[u] = 2; } void detect_component() noexcept { int comp_size = 0; for(int i = 0; i < V; i++){ if(!visit[i]){ off_tree.push_back(vector<pair<int, int> >()); dfs(i, -1, comp_size++); if(off_tree.back().empty()){ off_tree.back().emplace_back(min(i, *G[i].begin()), -max(i, *G[i].begin())); } } } for(int i = 0; i < comp_size; i++){ if(off_tree[i][0].second < 0) tree.push_back(i); else non_tree.push_back(i); } } void transform_to_connected_graph() noexcept { detect_component(); while(!tree.empty()){ int id = non_tree.back(); while(!off_tree[id].empty() && !tree.empty()){ pair<int, int> p = off_tree[id].back(), q = off_tree[tree.back()][0]; q.second = -q.second; swap_edge(p, q); off_tree[id].pop_back(), tree.pop_back(); } if(off_tree[id].empty()) non_tree.pop_back(); } while((int)non_tree.size() >= 2){ int q = non_tree.back(); non_tree.pop_back(); int p = non_tree.back(); non_tree.pop_back(); pair<int, int> e1 = off_tree[p].back(), e2 = off_tree[q].back(); off_tree[p].pop_back(), off_tree[q].pop_back(); swap_edge(e1, e2); off_tree[q].emplace_back(e1.first, e2.first); for(auto& e : off_tree[p]) off_tree[q].push_back(e); non_tree.push_back(q); } } void dfs(const int u, const int p, int& ver_sm, vector<bool>& _visit) const noexcept { _visit[u] = true, ver_sm++; for(int v : G[u]) if(!_visit[v]) dfs(v, u, ver_sm, _visit); } bool IsConnected() const noexcept { int ver_sm = 0; vector<bool> _visit(V, false); dfs(0, -1, ver_sm, _visit); return (ver_sm == V); } bool check_swap_edge(const pair<int, int>& p, const pair<int, int>& q) noexcept { if(G[p.first].find(q.first) != G[p.first].end()) return false; if(G[p.second].find(q.second) != G[p.second].end()) return false; erase_edge(p.first, p.second), erase_edge(q.first, q.second); add_edge(p.first, q.first), add_edge(p.second, q.second); return true; } bool transition(pair<int, int> p, pair<int, int> q, bool flag) noexcept { if(p.first == q.second || p.second == q.first || p.second == q.second) return false; if(flag) swap(q.first, q.second); return check_swap_edge(p, q); } void shuffle_graph() noexcept { for(int i = 0; i < V; i++) for(int j = 0; j < degree[i]; j++) population.emplace_back(i, j); uniform_int_distribution<> _rand(0, (int)population.size()-1); int window = 1; double ex_window = 1.0; while(true){ vector<unordered_set<int> > tmpG; if(connected) tmpG = G; bool finish = false, done = false; for(int i = 0; i < window; i++){ pair<int, int> id1 = {0, 0}, id2 = {0, 0}; while(id1.first == id2.first) id1 = population[_rand(mt)], id2 = population[_rand(mt)]; unordered_set<int>::iterator it1 = G[id1.first].begin(), it2 = G[id2.first].begin(); advance(it1, id1.second), advance(it2, id2.second); done |= transition({id1.first, *it1}, {id2.first, *it2}, mt() % 2); if(_time() > TIME){ finish = true; break; } } if(!connected || !done || IsConnected()){ loop_count += window, ex_window *= UP, window = round(ex_window); } else G = tmpG, ex_window *= DOWN, window = max((int)round(ex_window), 1); if(finish) break; } } void construct_graph() noexcept { vector<int> index(V); iota(index.begin(), index.end(), 0); shuffle(index.begin(), index.end(), mt); for(int i : index) for(int to : G[i]) graph[i].push_back(index[to]); for(int i = 0; i < V; i++) for(int to : G[i]) graph[i].push_back(to); } public: int loop_count; vector<vector<int> >& graph; const vector<int>& degree; GraphGenerator(const vector<int>& d, vector<vector<int> >& g, bool _connected=false) noexcept : V((int)d.size()), connected(_connected), G(V), visit(V, 0), start(chrono::high_resolution_clock::now()), mt(rnd()), loop_count(0), graph{g}, degree{d}{ assert(check()); assert(construct_init_graph()); if(connected) assert(IsConnected()); shuffle_graph(); construct_graph(); } };
verify していません(verify 問題を知らない)
verify をちゃんとやらないとなぁという気はしている