Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
range_minimum_query.h
Go to the documentation of this file.
1// Copyright 2010-2025 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
14// The range minimum query problem is a range query problem where queries ask
15// for the minimum of all elements in ranges of the array.
16// The problem is divided into two phases:
17// - precomputation: the data structure is given an array A of n elements.
18// - query: the data structure must answer queries min(A, begin, end),
19// where min(A, begin, end) = min_{i in [begin, end)} A[i].
20// This file has an implementation of the sparse table approach to solving the
21// problem, for which the precomputation takes O(n*log(n)) time and memory,
22// and further queries take O(1) time.
23// Reference: https://en.wikipedia.org/wiki/Range_minimum_query.
24//
25// The data structure allows to have multiple arrays at the same time, and
26// to reset the arrays.
27//
28// Usage, single range:
29// RangeMinimumQuery rmq({10, 100, 30, 300, 70});
30// rmq.GetMinimumFromRange(0, 5); // Returns 10.
31// rmq.GetMinimumFromRange(2, 4); // Returns 30.
32//
33// Usage, multiple ranges:
34// RangeMinimumQuery rmq({10, 100, 30, 300, 70});
35// rmq.GetMinimumFromRange(0, 5); // Returns 10.
36// rmq.GetMinimumFromRange(2, 4); // Returns 30.
37//
38// // We add another array {-3, 10, 5, 2, 15, 3}.
39// const int begin2 = rmq.TablesSize();
40// for (const int element : {-3, 10, 5, 2, 15, 3}) {
41// rmq.PushBack(element);
42// }
43// rmq.MakeSparseTableFromNewElements();
44// rmq.GetMinimumFromRange(begin2 + 0, begin2 + 5); // Returns -3.
45// rmq.GetMinimumFromRange(begin2 + 2, begin2 + 4); // Returns 2.
46// rmq.GetMinimumFromRange(begin2 + 4, begin2 + 6); // Returns 3.
47// // The previous array can still be queried.
48// rmq.GetMinimumFromRange(1, 3); // Returns 30.
49//
50// // Forbidden, query ranges can only be within the same array.
51// rmq.GetMinimumFromRange(3, 9); // Undefined.
52//
53// rmq.Clear();
54// // All arrays have been removed, so no range query can be made.
55// rmq.GetMinimumFromRange(0, 5); // Undefined.
56//
57// // Add a new range.
58// for (const int element : {0, 3, 2}) {
59// rmq.PushBack(element);
60// }
61// rmq.MakeSparseTableFromNewElements();
62// // Queries on the new array can be made.
63//
64// Note: There are other space/time tradeoffs for this problem, but they are
65// generally worse in terms of the constants in the O(1) query time, moreover
66// their implementation is generally more involved.
67//
68// Implementation: The idea is to cache every min(A, i, i+2^k).
69// Provided this information, we can answer all queries in O(1): given a pair
70// (i, j), first find the maximum k such that i + 2^k < j, then use
71// min(A, i, j) = std::min(min(A, i, i+2^k), min(A, j-2^k, j)).
72
73#ifndef OR_TOOLS_UTIL_RANGE_MINIMUM_QUERY_H_
74#define OR_TOOLS_UTIL_RANGE_MINIMUM_QUERY_H_
75
76#include <algorithm>
77#include <functional>
78#include <numeric>
79#include <utility>
80#include <vector>
81
82#include "absl/log/check.h"
83#include "ortools/util/bitset.h"
84
85namespace operations_research {
86template <typename T, typename Compare = std::less<T>>
88 public:
90 // This class uses the first two rows of cache_ to know the number of new
91 // elements, which at any moment is cache_[1].size() - cache_[0].size().
92 cache_.resize(2);
93 };
94 explicit RangeMinimumQuery(std::vector<T> array);
95 RangeMinimumQuery(std::vector<T> array, Compare cmp);
96
97 // This type is neither copyable nor movable.
100
101 // Returns the minimum (w.r.t. Compare) arr[x], where x is contained in
102 // [begin_index, end_index).
103 // The range [begin_index, end_index) can only cover elements that were new
104 // at the same call to MakeTableFromNewElements().
105 // When calling this method, there must be no pending new elements, i.e. the
106 // last method called apart from TableSize() must not have been PushBack().
107 T RangeMinimum(int begin, int end) const;
108
109 void PushBack(T element) { cache_[0].push_back(element); }
110
111 // Generates the sparse table for all new elements, i.e. elements that were
112 // added with PushBack() since the latest of these events: construction of
113 // this object, a previous call to MakeTableFromNewElements(), or a call to
114 // Clear().
115 // The range of new elements [begin, end), with begin the Size() at the
116 // latest event, and end the current Size().
118
119 // Returns the number of elements in sparse tables, excluding new elements.
120 int TableSize() const { return cache_[1].size(); }
121
122 // Clears all tables. This invalidates all further range queries on currently
123 // existing tables. This does *not* release memory held by this object.
124 void Clear() {
125 for (auto& row : cache_) row.clear();
126 }
127
128 // Returns the concatenated sequence of all elements in all arrays.
129 const std::vector<T>& array() const;
130
131 private:
132 // cache_[k][i] = min_{j in [i, i+2^k)} arr[j].
133 std::vector<std::vector<T>> cache_;
134 Compare cmp_;
135};
136
137// RangeMinimumIndexQuery is similar to RangeMinimumQuery, but
138// GetMinimumIndexFromRange returns the index for which the minimum is attained.
139template <typename T, typename Compare = std::less<T>>
141 public:
142 explicit RangeMinimumIndexQuery(std::vector<T> array);
143 RangeMinimumIndexQuery(std::vector<T> array, Compare cmp);
144
145 // This type is neither copyable nor movable.
148
149 // Returns an index idx from [begin, end) such that arr[idx] is the minimum
150 // value of arr over the interval [begin, end).
151 int GetMinimumIndexFromRange(int begin, int end) const;
152
153 // Returns the original array.
154 const std::vector<T>& array() const;
155
156 private:
157 // Returns a vector with values 0, 1, ... n - 1 for a given n.
158 static std::vector<int> CreateIndexVector(int n);
159 struct IndexComparator {
160 bool operator()(int lhs_idx, int rhs_idx) const;
161 const std::vector<T> array;
162 Compare cmp;
163 } cmp_;
165};
166
167// RangeMinimumQuery implementation
168template <typename T, typename Compare>
170 : RangeMinimumQuery(std::move(array), Compare()) {}
171
172template <typename T, typename Compare>
174 Compare cmp)
175 : cache_(2), cmp_(std::move(cmp)) {
176 // This class uses the first two rows of cache_ to know the number of new
177 // elements.
178 cache_[0] = std::move(array);
180}
181
182template <typename T, typename Compare>
184 DCHECK_LE(0, begin);
185 DCHECK_LT(begin, end);
186 DCHECK_LE(end, cache_[1].size());
187 DCHECK_EQ(cache_[0].size(), cache_[1].size());
188 const int layer = MostSignificantBitPosition32(end - begin);
189 DCHECK_LT(layer, cache_.size());
190 const int window = 1 << layer;
191 const T* row = cache_[layer].data();
192 DCHECK_LE(end - window, cache_[layer].size());
193 return std::min(row[begin], row[end - window], cmp_);
194}
195
196// Reminder: The task is to fill cache_ so that for i in [begin, end),
197// cache_[k][i] = min(arr, i, i+2^k) for every k <= Log2(n) and i <= n-2^k.
198// Note that cache_[k+1][i] = min(cache_[k][i], cache_[k][i+2^k]), hence every
199// row can be efficiently computed from the previous.
200template <typename T, typename Compare>
202 const int new_size = cache_[0].size();
203 const int old_size = cache_[1].size();
204 if (old_size >= new_size) return;
205 // This is the minimum number of rows needed to store the sequence of
206 // new elements, there may be more rows in the cache.
207 const int num_rows = 1 + MostSignificantBitPosition32(new_size - old_size);
208 if (cache_.size() < num_rows) cache_.resize(num_rows);
209 // Record the new number of elements, wastes just size(T) space.
210 cache_[1].resize(new_size);
211
212 for (int row = 1; row < num_rows; ++row) {
213 const int half_window = 1 << (row - 1);
214 const int last_col = new_size - 2 * half_window;
215 if (cache_[row].size() <= last_col) cache_[row].resize(last_col + 1);
216 for (int col = old_size; col <= last_col; ++col) {
217 cache_[row][col] = std::min(cache_[row - 1][col],
218 cache_[row - 1][col + half_window], cmp_);
219 }
220 }
221}
222
223template <typename T, typename Compare>
224inline const std::vector<T>& RangeMinimumQuery<T, Compare>::array() const {
225 return cache_[0];
226}
227
228// RangeMinimumIndexQuery implementation
229template <typename T, typename Compare>
233
234template <typename T, typename Compare>
236 Compare cmp)
237 : cmp_({std::move(array), std::move(cmp)}),
238 rmq_(CreateIndexVector(cmp_.array.size()), cmp_) {}
239
240template <typename T, typename Compare>
242 int begin, int end) const {
243 DCHECK_LT(begin, end);
244 return rmq_.RangeMinimum(begin, end);
245}
246
247template <typename T, typename Compare>
248inline bool RangeMinimumIndexQuery<T, Compare>::IndexComparator::operator()(
249 int lhs_idx, int rhs_idx) const {
250 return cmp(array[lhs_idx], array[rhs_idx]);
251}
252
253template <typename T, typename Compare>
254std::vector<int> RangeMinimumIndexQuery<T, Compare>::CreateIndexVector(int n) {
255 std::vector<int> result(n, 0);
256 std::iota(result.begin(), result.end(), 0);
257 return result;
258}
259
260template <typename T, typename Compare>
261inline const std::vector<T>& RangeMinimumIndexQuery<T, Compare>::array() const {
262 return cmp_.array;
263}
264} // namespace operations_research
265#endif // OR_TOOLS_UTIL_RANGE_MINIMUM_QUERY_H_
RangeMinimumIndexQuery(const RangeMinimumIndexQuery &)=delete
This type is neither copyable nor movable.
RangeMinimumIndexQuery(std::vector< T > array)
RangeMinimumIndexQuery implementation.
int GetMinimumIndexFromRange(int begin, int end) const
const std::vector< T > & array() const
Returns the original array.
RangeMinimumIndexQuery & operator=(const RangeMinimumIndexQuery &)=delete
const std::vector< T > & array() const
Returns the concatenated sequence of all elements in all arrays.
RangeMinimumQuery & operator=(const RangeMinimumQuery &)=delete
RangeMinimumQuery(const RangeMinimumQuery &)=delete
This type is neither copyable nor movable.
int TableSize() const
Returns the number of elements in sparse tables, excluding new elements.
In SWIG mode, we don't want anything besides these top-level includes.
ClosedInterval::Iterator end(ClosedInterval interval)
int MostSignificantBitPosition32(uint32_t n)
Definition bitset.h:276
ClosedInterval::Iterator begin(ClosedInterval interval)
STL namespace.