Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
random_graph.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstdint>
18#include <memory>
19#include <utility>
20#include <vector>
21
22#include "absl/container/flat_hash_set.h"
23#include "absl/memory/memory.h"
24#include "absl/random/bit_gen_ref.h"
25#include "absl/random/random.h"
27#include "ortools/base/types.h"
28
29namespace util {
30
31namespace {
32// This function initializes the graph used by GenerateRandomMultiGraph() and
33// GenerateRandomMultiGraph(), given their arguments.
34// See the .h for documentation on those arguments.
35std::unique_ptr<StaticGraph<>> CreateGraphMaybeReserved(int num_nodes,
36 int num_arcs,
37 bool finalized,
38 absl::BitGenRef gen) {
39 std::unique_ptr<StaticGraph<>> graph;
40 // We either "reserve" the number of nodes and arcs or not, depending on
41 // randomness and on the "finalized" bit (if false, we can't assume that the
42 // user won't add some nodes or arcs after we return the graph, so we can't
43 // cap those).
44 if (finalized && absl::Bernoulli(gen, 1.0 / 2)) {
45 graph = std::make_unique<StaticGraph<>>(num_nodes, num_arcs);
46 } else {
47 graph = std::make_unique<StaticGraph<>>();
48 graph->AddNode(num_nodes - 1);
49 }
50 return graph;
51}
52} // namespace
53
54std::unique_ptr<StaticGraph<>> GenerateRandomMultiGraph(int num_nodes,
55 int num_arcs,
56 bool finalized,
57 absl::BitGenRef gen) {
58 std::unique_ptr<StaticGraph<>> graph =
59 CreateGraphMaybeReserved(num_nodes, num_arcs, finalized, gen);
60 if (num_nodes != 0) {
61 CHECK_GT(num_nodes, 0);
62 CHECK_GE(num_arcs, 0);
63 graph->AddNode(num_nodes - 1);
64 for (int a = 0; a < num_arcs; ++a) {
65 graph->AddArc(absl::Uniform(gen, 0, num_nodes),
66 absl::Uniform(gen, 0, num_nodes));
67 }
68 } else {
69 CHECK_EQ(num_arcs, 0);
70 }
71 if (finalized) graph->Build();
72 return graph;
73}
74
75namespace {
76// Parameterized method to generate both directed and undirected simple graphs.
77std::unique_ptr<StaticGraph<>> GenerateRandomSimpleGraph(int num_nodes,
78 int num_arcs,
79 bool finalized,
80 bool directed,
81 absl::BitGenRef gen) {
82 CHECK_GE(num_nodes, 0);
83 // For an undirected graph, the number of arcs should be even: a->b and b->a.
84 CHECK(directed || (num_arcs % 2 == 0));
85 const int64_t max_num_arcs =
86 static_cast<int64_t>(num_nodes) * (num_nodes - 1);
87 CHECK_LE(num_arcs, max_num_arcs);
88 std::unique_ptr<StaticGraph<>> graph =
89 CreateGraphMaybeReserved(num_nodes, num_arcs, finalized, gen);
90
91 // If the number of arcs is greater than half the possible arcs of the graph,
92 // we generate the inverse graph and convert non-arcs to arcs.
93 if (num_arcs > max_num_arcs / 2) {
94 std::unique_ptr<StaticGraph<>> inverse_graph =
95 GenerateRandomSimpleGraph(num_nodes, max_num_arcs - num_arcs,
96 /*finalized=*/true, directed, gen);
97 std::vector<bool> node_mask(num_nodes, false);
98 for (int from = 0; from < num_nodes; ++from) {
99 for (const int to : (*inverse_graph)[from]) {
100 node_mask[to] = true;
101 }
102 for (int to = 0; to < num_nodes; ++to) {
103 if (node_mask[to]) {
104 node_mask[to] = false; // So that the mask is reset to all false.
105 } else if (to != from) {
106 graph->AddArc(from, to);
107 }
108 }
109 }
110 if (finalized) graph->Build();
111 return graph;
112 }
113
114 // We use a trivial algorithm: pick an arc at random, uniformly, and add it to
115 // the graph unless it was already added. As we sometimes have to discard an
116 // arc, we expect to do this slightly more times than the desired number "m"
117 // of distinct arcs. But in the worst case, which is when m = M/2 (where M =
118 // N*(N-1) is the number of possible arcs), the expected number of steps is
119 // only ln(2)*M ~ 0.69*M, to produce 0.5*M arcs. So it's fine.
120 //
121 // Proof: The expected number of steps to get "m" distinct arcs across the M
122 // possible arcs is M/M + M/(M-1) + M/(M-2) + ... + M/(M-m+1), which is equal
123 // to M * (H(M) - H(M-m)), where H(x) is the harmonic sum up to x.
124 // H(M) - H(M-m) converges to ln(M) - ln(M-m) = ln(1 + m/(M-m)) as M grows,
125 // which stricly grows with m and is equal to ln(2) in the worst case m=M/2.
126 //
127 // NOTE(user): If some specialized users want a uniform generation method
128 // that uses less memory (this one uses a flat hash map on the arcs, which
129 // uses significant memory), it could be done. Reach out to me.
130 absl::flat_hash_set<std::pair<int, int>> arc_set;
131 // To detect bad user-provided random number generator which could lead to
132 // infinite loops, we bound the number of iterations to a value well beyond
133 // the expected number of iterations (which is less than 0.69 * max_num_arcs).
134 int64_t num_iterations = 0;
135 const int64_t max_num_iterations = 1000 + max_num_arcs;
136 while (graph->num_arcs() < num_arcs) {
137 ++num_iterations;
138 CHECK_LE(num_iterations, max_num_iterations)
139 << "The random number generator supplied to GenerateRandomSimpleGraph()"
140 << " is likely biased or broken.";
141 const int tail = absl::Uniform(gen, 0, num_nodes);
142 const int head = absl::Uniform(gen, 0, num_nodes);
143 if (tail == head) continue;
144 if (directed) {
145 if (!arc_set.insert({tail, head}).second) continue;
146 graph->AddArc(tail, head);
147 } else { // undirected
148 const std::pair<int, int> arc = {
149 std::min(tail, head),
150 std::max(tail, head)}; // Canonic edge representative.
151 if (!arc_set.insert(arc).second) continue;
152 graph->AddArc(tail, head);
153 graph->AddArc(head, tail);
154 }
155 }
156 if (finalized) graph->Build();
157 return graph;
158}
159} // namespace
160
161std::unique_ptr<StaticGraph<>> GenerateRandomDirectedSimpleGraph(
162 int num_nodes, int num_arcs, bool finalized, absl::BitGenRef gen) {
163 return GenerateRandomSimpleGraph(num_nodes, num_arcs, finalized,
164 /*directed=*/true, gen);
165}
166
167std::unique_ptr<StaticGraph<>> GenerateRandomUndirectedSimpleGraph(
168 int num_nodes, int num_edges, bool finalized, absl::BitGenRef gen) {
169 return GenerateRandomSimpleGraph(num_nodes, 2 * num_edges, finalized,
170 /*directed=*/false, gen);
171}
172
173} // namespace util
int64_t a
Definition table.cc:44
GraphType graph
int arc
A collections of i/o utilities for the Graph classes in ./graph.h.
std::unique_ptr< StaticGraph<> > GenerateRandomMultiGraph(int num_nodes, int num_arcs, bool finalized, absl::BitGenRef gen)
std::unique_ptr< StaticGraph<> > GenerateRandomDirectedSimpleGraph(int num_nodes, int num_arcs, bool finalized, absl::BitGenRef gen)
std::unique_ptr< StaticGraph<> > GenerateRandomUndirectedSimpleGraph(int num_nodes, int num_edges, bool finalized, absl::BitGenRef gen)
trees with all degrees equal to
int head
int tail