Google OR-Tools v9.12
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>
183inline T RangeMinimumQuery<T, Compare>::RangeMinimum(int begin, int end) const {
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< int64_t > & array() const
RangeMinimumQuery(std::vector< T > array, Compare cmp)
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.
RangeMinimumQuery(std::vector< T > array)
RangeMinimumQuery implementation.
In SWIG mode, we don't want anything besides these top-level includes.
int MostSignificantBitPosition32(uint32_t n)
Definition bitset.h:277
STL namespace.