From b4aba0b29ac1ccf03dfe0cc7d589481a039aae86 Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Wed, 25 Jun 2025 01:19:44 +0200 Subject: [PATCH 01/12] Add files via upload --- src/geometry/nearest_points_blocks_example.png | Bin 0 -> 89714 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/geometry/nearest_points_blocks_example.png diff --git a/src/geometry/nearest_points_blocks_example.png b/src/geometry/nearest_points_blocks_example.png new file mode 100644 index 0000000000000000000000000000000000000000..15a90ce2fb450b5affe1c4d8725b175604b1767b GIT binary patch literal 89714 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%0A_;q(_nF5RnwUw@+rVds~o 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 literal 0 HcmV?d00001 From 4da1ca11f400056f78cc8d8f26d85904361dc930 Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Wed, 25 Jun 2025 01:23:35 +0200 Subject: [PATCH 02/12] Update nearest_points.md - add randomized algorithm Explanation of randomized algorithms for closest pair of points. --- src/geometry/nearest_points.md | 139 +++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index 0c8abc259..d470ea0f2 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -162,6 +162,145 @@ mindist = 1E20; rec(0, n); ``` +## Linear time randomized algorithms + +### A linear time (with high probability) algorithm + +An alternative method arises from a very simple idea to heuristically improve the runtime: We can divide the plane into a grid of $d \times d$ squares, then it is only required to test distances between same-block or adjacent-block points (unless all squares are disconnected from each other, we will avoid this by design), since any other pair has larger distance that the two points in the same square. + +

+ Example of the squares strategy +
+ + +We will consider only the squares containing at least one point. Denote by $n_1, n_2, \dots, n_k$ the number of points in each of the $k$ remaining squares. Assuming at least two points are in the same or in adjacent squares, the time complexity is $\Theta(\sum_{i=1}^{k} n_i^2)$. + +**Proof.** For the $i$-th square containing $n_i$ points, the number of pairs inside is $\Theta(n_i^2)$. If the $i$-th square is adjacent to the $j$-th square, then we also perform $n_i n_j \le \max(n_i, n_j)^2 \le n_i^2 + n_j^2$ distance comparisons. Notice that each cube has at most $8$ adjacent cubes, so we can bound the sum of all comparisons by $\Theta(\sum_{i=1}^{k} n_i^2)$. $\quad \blacksquare$ + +Now we need to decide on how to set $d$ so that it minimizes $\Theta(\sum_{i=1}^{k} n_i^2)$. + +#### Choosing d + +We need $d$ to be an approximation of the minimum distance $d$, and the trick is to just sample $n$ distances randomly and choose $d$ to be the smallest of these distances. We now prove that with high probability this has linear cost. + +**Proof.** Assume with a particular choice of $d$, the resulting squares have $C \coloneqq \sum_{i=1}^{k} n_i^2 = \lambda n$. What is the probability that such $d$ survives the sampling of $n$ independent distances? If a single pair among the sampled ones has distance smaller than $d$, this arrangement is not possible. Inside a square, at least half of the pairs would raise a smaller distance, so we have $\sum_{i=1}^{k} \frac{1}{2} {n_i \choose 2}$ pairs which yield a smaller final $d$. This is, approximately, $\frac{1}{4} \sum_{i=1}^{k} n_i^2 = \frac{\lambda}{4} n$. On the other hand, there are about $\frac{1}{2} n^2$ pairs that can be sampled. We have that the probability of sampling a pair with distance smaller than $d$ is at least (approximately) $\frac{\lambda n / 4}{n^2 / 2} = \frac{\lambda/2}{n}$, so the probability of at least one such pair being chosen during the $n$ rounds (and therefore avoiding this situation) is $1 - (1 - \frac{\lambda/2}{n})^n \approx 1 - e^{-\lambda/2}$. This goes to $1$ as $\lambda$ increases. $\quad \blacksquare$ + +#### Implementation of the algorithm + +The advantage of this algorithm is that it is straightforward to implement, but still has good performance in practise. + +```{.cpp file=nearest_pair_randomized} +using ll = long long; +using ld = long double; + +struct RealPoint { + ld x, y; + RealPoint() {} + RealPoint(T x_, T y_) : x(x_), y(y_) {} +}; +using pt = RealPoint; + +struct CustomHash { + size_t operator()(const pair& p) const { + static const uint64_t C = chrono::steady_clock::now().time_since_epoch().count(); + return C ^ ((p.first << 32) ^ p.second); + } +}; + +ld dist(pt a, pt b) { + ld dx = a.x - b.x; + ld dy = a.y - b.y; + return sqrt(dx*dx + dy*dy); +} + +pair closest_pair_of_points_rand_reals(vector P) { + const ld eps = 1e-9; + + int n = int(P.size()); + assert(n >= 2); + unordered_map,vector,CustomHash> grid; + grid.reserve(n); + + mt19937 rd(chrono::system_clock::now().time_since_epoch().count()); + uniform_int_distribution dis(0, n-1); + + ld d = dist(P[0], P[1]); + pair closest = {P[0], P[1]}; + + auto consider_pair = [&](const pt& a, const pt& b) -> void { + ld ab = dist(a, b); + if (ab + eps < d) { + d = ab; + closest = {a, b}; + } + }; + + for (int i = 0; i < n; ++i) { + int j = dis(rd); + int k = dis(rd); + while (j == k) + k = dis(rd); + consider_pair(P[j], P[k]); + } + + for (const pt& p : P) + grid[{ll(p.x/d), ll(p.y/d)}].push_back(p); + + for (const auto& it : grid) { // same block + int k = int(it.second.size()); + for (int i = 0; i < k; ++i) { + for (int j = i+1; j < k; ++j) + consider_pair(it.second[i], it.second[j]); + } + } + + for (const auto& it : grid) { // adjacent blocks + auto coord = it.first; + for (int dx = 0; dx <= 1; ++dx) { + for (int dy = -1; dy <= 1; ++dy) { + if (dx == 0 and dy == 0) continue; + pair neighbour = { + coord.first + dx, + coord.second + dy + }; + for (const pt& p : it.second) { + if (not grid.count(neighbour)) continue; + for (const pt& q : grid.at(neighbour)) + candidate_closest(p, q); + } + } + } + } + + return closest; +} +``` + + +### A randomized algorithm with expected linear time + +Now we introduce a different randomized algorithm which is less practical but very easy to show that it runs in expected linear time. + +- Permute the $n$ points randomly +- Take $\delta \coloneqq \operatorname{dist}(p_1, p_2)$ +- Partition the plane in squares of side $\delta/2$ +- For $i = 1,2,\dots,n$: + - Take the square corresponding to $p_i$ + - Interate over the $25$ squares within two steps to our square in the grid of squares partitioning the plane + - If some $p_j$ in those squares has $\operatorname{dist}(p_j, p_i) < \delta$, then + - Recompute the partition and squares with $\delta \coloneqq \operatorname{dist}(p_j, p_i)$ + - Store points $p_1, \dots, p_i$ in the corresponding squares + - else, store $p_i$ in the corresponding square +- output $\delta$ + +While this algorithm may look slow, because of recomputing everything multiple times, we can show the total expected cost is linear. + +**Proof.** Let $X_i$ the random variable that is $1$ when point $p_i$ causes a change of $\delta$ and a recomputation of the data structures, and $0$ if not. It is easy to show that the cost is $O(n + \sum_{i=1}^{n} i X_i)$, since on the $i$-th step we are considering only the first $i$ points. However, turns out that $\Pr(X_i = 1) \le \frac{2}{i}$. This is because on the $i$-th step, $\delta$ is the distance of the closest pair in $\{p_1,\dots,p_i\}$, and $\Pr(X_i = 1)$ is the probability of $p_i$ belonging to the closest pair, which only happens in $2(i-1)$ pairs out of the $i(i-1)$ possible pairs (assuming all distances are different), so the probability is at most $\frac{2(i-1)}{i(i-1)} = \frac{2}{i}$, since we previously shuffled the points uniformly. + +We can therefore see that the expected cost is +$$O(n + \sum_{i=1}^{n} i \Pr(X_i = 1)) \le O(n + \sum_{i=1}^{n} i \frac{2}{i}) = O(3n) = O(n) \quad \quad \blacksquare $$ + + ## Generalization: finding a triangle with minimal perimeter The algorithm described above is interestingly generalized to this problem: among a given set of points, choose three different points so that the sum of pairwise distances between them is the smallest. From ad322f5b37c40d59429baaf43e53d79dd5a1ab0c Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Thu, 26 Jun 2025 21:29:50 +0200 Subject: [PATCH 03/12] Update nearest_points.md - Complete proof of linear expected time --- src/geometry/nearest_points.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index d470ea0f2..d2bf8f740 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -164,9 +164,9 @@ rec(0, n); ## Linear time randomized algorithms -### A linear time (with high probability) algorithm +### A randomized algorithm with linear expected time -An alternative method arises from a very simple idea to heuristically improve the runtime: We can divide the plane into a grid of $d \times d$ squares, then it is only required to test distances between same-block or adjacent-block points (unless all squares are disconnected from each other, we will avoid this by design), since any other pair has larger distance that the two points in the same square. +An alternative method arises from a very simple idea to heuristically improve the runtime: We can divide the plane into a grid of $d \times d$ squares, then it is only required to test distances between same-block or adjacent-block points (unless all squares are disconnected from each other, but we will avoid this by design), since any other pair has larger distance that the two points in the same square.
Example of the squares strategy @@ -183,7 +183,18 @@ Now we need to decide on how to set $d$ so that it minimizes $\Theta(\sum_{i=1}^ We need $d$ to be an approximation of the minimum distance $d$, and the trick is to just sample $n$ distances randomly and choose $d$ to be the smallest of these distances. We now prove that with high probability this has linear cost. -**Proof.** Assume with a particular choice of $d$, the resulting squares have $C \coloneqq \sum_{i=1}^{k} n_i^2 = \lambda n$. What is the probability that such $d$ survives the sampling of $n$ independent distances? If a single pair among the sampled ones has distance smaller than $d$, this arrangement is not possible. Inside a square, at least half of the pairs would raise a smaller distance, so we have $\sum_{i=1}^{k} \frac{1}{2} {n_i \choose 2}$ pairs which yield a smaller final $d$. This is, approximately, $\frac{1}{4} \sum_{i=1}^{k} n_i^2 = \frac{\lambda}{4} n$. On the other hand, there are about $\frac{1}{2} n^2$ pairs that can be sampled. We have that the probability of sampling a pair with distance smaller than $d$ is at least (approximately) $\frac{\lambda n / 4}{n^2 / 2} = \frac{\lambda/2}{n}$, so the probability of at least one such pair being chosen during the $n$ rounds (and therefore avoiding this situation) is $1 - (1 - \frac{\lambda/2}{n})^n \approx 1 - e^{-\lambda/2}$. This goes to $1$ as $\lambda$ increases. $\quad \blacksquare$ +**Proof.** Imagine the disposition of points in squares with a particular choice of $d$, say $x$. Consider $d$ a random variable, resulting from our sampling of distances. Let's define $C(x) = \sum_{i=1}^{k(x)} n_i(x)^2$ as the cost estimation for a particular disposition when we choose $d=x$. Now, let's define $\lambda(x)$ such that $C(x) = \lambda(x) \, n$. What is the probability that such choice $x$ survives the sampling of $n$ independent distances? If a single pair among the sampled ones has distance smaller than $x$, this arrangement will be replaced by the smaller $d$. Inside a square, at least a quarter of the pairs would raise a smaller distance (imagine four subsquares in every square, and use the pigeonhole principle), so we have $\sum_{i=1}^{k} \frac{1}{4} {n_i \choose 2}$ pairs which yield a smaller final $d$. This is, approximately, $\frac{1}{8} \sum_{i=1}^{k} n_i^2 = \frac{1}{8} \lambda(x) n$. On the other hand, there are about $\frac{1}{2} n^2$ pairs that can be sampled. We have that the probability of sampling a pair with distance smaller than $x$ is at least (approximately) +$$\frac{\lambda(x) n / 8}{n^2 / 2} = \frac{\lambda(x)/4}{n}$$ +so the probability of at least one such pair being chosen during the $n$ rounds (and therefore finding a smaller $d$) is +$$1 - \left(1 - \frac{\lambda(x)/4}{n}\right)^n \ge 1 - e^{-\lambda(x)/4}$$ +(we have used that $(1 + x)^n \le e^{xn}$ for any real number $x$, check https://en.wikipedia.org/wiki/Bernoulli%27s_inequality#Related_inequalities).
Notice this goes to $1$ exponentially as $\lambda(x)$ increases. This hints that $\lambda$ will be small usually. + + +We have shown that $\Pr(d \le x) \ge 1 - e^{-\lambda(x)/4}$, or equivalently, $\Pr(d \ge x) \le e^{-\lambda(x)/4}$. We need to know $\Pr(\lambda(d) \ge \text{something})$ to be able to estimate its expected value. We notice that $\lambda(d) \ge \lambda(x) \iff d \ge x$. This is because making the squares smaller only reduces the number of points in each square (splits the points into other squares), and this keeps reducing the sum of squares. Therefore, +$$\Pr(\lambda(d) \ge \lambda(x)) = \Pr(d \ge x) \le e^{-\lambda(x)/4} \implies \Pr(\lambda(d) \ge t) \le e^{-t/4} \implies \mathbb{E}[\lambda(d)] \le \int_{0}^{+\infty} e^{-t/4} \, \mathrm{d}t = 4$$ +(we have used that $E[X] = \int_0^{+\infty} \Pr(X \ge x) \, \mathrm{d}x$, check https://math.stackexchange.com/a/1690829). + +Finally, $\mathbb{E}[C(d)] = \mathbb{E}[\lambda(d) \, n] \le 4n$, and the expected running time is $O(n)$, with a reasonable constant factor. $\quad \blacksquare$ #### Implementation of the algorithm @@ -277,7 +288,7 @@ pair closest_pair_of_points_rand_reals(vector P) { ``` -### A randomized algorithm with expected linear time +### An alternative randomized linear expected time algorithm Now we introduce a different randomized algorithm which is less practical but very easy to show that it runs in expected linear time. From f79a21148de2bf1037ee38bef290718495591e75 Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Thu, 26 Jun 2025 21:31:23 +0200 Subject: [PATCH 04/12] Update nearest_points.md - small mistakes in writing --- src/geometry/nearest_points.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index d2bf8f740..067eee4ef 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -181,7 +181,7 @@ Now we need to decide on how to set $d$ so that it minimizes $\Theta(\sum_{i=1}^ #### Choosing d -We need $d$ to be an approximation of the minimum distance $d$, and the trick is to just sample $n$ distances randomly and choose $d$ to be the smallest of these distances. We now prove that with high probability this has linear cost. +We need $d$ to be an approximation of the minimum distance $d$, and the trick is to just sample $n$ distances randomly and choose $d$ to be the smallest of these distances. We now prove that the expected running time is linear. **Proof.** Imagine the disposition of points in squares with a particular choice of $d$, say $x$. Consider $d$ a random variable, resulting from our sampling of distances. Let's define $C(x) = \sum_{i=1}^{k(x)} n_i(x)^2$ as the cost estimation for a particular disposition when we choose $d=x$. Now, let's define $\lambda(x)$ such that $C(x) = \lambda(x) \, n$. What is the probability that such choice $x$ survives the sampling of $n$ independent distances? If a single pair among the sampled ones has distance smaller than $x$, this arrangement will be replaced by the smaller $d$. Inside a square, at least a quarter of the pairs would raise a smaller distance (imagine four subsquares in every square, and use the pigeonhole principle), so we have $\sum_{i=1}^{k} \frac{1}{4} {n_i \choose 2}$ pairs which yield a smaller final $d$. This is, approximately, $\frac{1}{8} \sum_{i=1}^{k} n_i^2 = \frac{1}{8} \lambda(x) n$. On the other hand, there are about $\frac{1}{2} n^2$ pairs that can be sampled. We have that the probability of sampling a pair with distance smaller than $x$ is at least (approximately) $$\frac{\lambda(x) n / 8}{n^2 / 2} = \frac{\lambda(x)/4}{n}$$ From c2eba24a1b074a2561f0ad718d944c9f8f4fe311 Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Thu, 26 Jun 2025 21:37:58 +0200 Subject: [PATCH 05/12] Update nearest_points.md - improve image size and latex spacing for correct visualization --- src/geometry/nearest_points.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index 067eee4ef..454b62595 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -169,7 +169,7 @@ rec(0, n); An alternative method arises from a very simple idea to heuristically improve the runtime: We can divide the plane into a grid of $d \times d$ squares, then it is only required to test distances between same-block or adjacent-block points (unless all squares are disconnected from each other, but we will avoid this by design), since any other pair has larger distance that the two points in the same square.
- Example of the squares strategy + Example of the squares strategy
@@ -184,14 +184,20 @@ Now we need to decide on how to set $d$ so that it minimizes $\Theta(\sum_{i=1}^ We need $d$ to be an approximation of the minimum distance $d$, and the trick is to just sample $n$ distances randomly and choose $d$ to be the smallest of these distances. We now prove that the expected running time is linear. **Proof.** Imagine the disposition of points in squares with a particular choice of $d$, say $x$. Consider $d$ a random variable, resulting from our sampling of distances. Let's define $C(x) = \sum_{i=1}^{k(x)} n_i(x)^2$ as the cost estimation for a particular disposition when we choose $d=x$. Now, let's define $\lambda(x)$ such that $C(x) = \lambda(x) \, n$. What is the probability that such choice $x$ survives the sampling of $n$ independent distances? If a single pair among the sampled ones has distance smaller than $x$, this arrangement will be replaced by the smaller $d$. Inside a square, at least a quarter of the pairs would raise a smaller distance (imagine four subsquares in every square, and use the pigeonhole principle), so we have $\sum_{i=1}^{k} \frac{1}{4} {n_i \choose 2}$ pairs which yield a smaller final $d$. This is, approximately, $\frac{1}{8} \sum_{i=1}^{k} n_i^2 = \frac{1}{8} \lambda(x) n$. On the other hand, there are about $\frac{1}{2} n^2$ pairs that can be sampled. We have that the probability of sampling a pair with distance smaller than $x$ is at least (approximately) + $$\frac{\lambda(x) n / 8}{n^2 / 2} = \frac{\lambda(x)/4}{n}$$ + so the probability of at least one such pair being chosen during the $n$ rounds (and therefore finding a smaller $d$) is + $$1 - \left(1 - \frac{\lambda(x)/4}{n}\right)^n \ge 1 - e^{-\lambda(x)/4}$$ + (we have used that $(1 + x)^n \le e^{xn}$ for any real number $x$, check https://en.wikipedia.org/wiki/Bernoulli%27s_inequality#Related_inequalities).
Notice this goes to $1$ exponentially as $\lambda(x)$ increases. This hints that $\lambda$ will be small usually. We have shown that $\Pr(d \le x) \ge 1 - e^{-\lambda(x)/4}$, or equivalently, $\Pr(d \ge x) \le e^{-\lambda(x)/4}$. We need to know $\Pr(\lambda(d) \ge \text{something})$ to be able to estimate its expected value. We notice that $\lambda(d) \ge \lambda(x) \iff d \ge x$. This is because making the squares smaller only reduces the number of points in each square (splits the points into other squares), and this keeps reducing the sum of squares. Therefore, + $$\Pr(\lambda(d) \ge \lambda(x)) = \Pr(d \ge x) \le e^{-\lambda(x)/4} \implies \Pr(\lambda(d) \ge t) \le e^{-t/4} \implies \mathbb{E}[\lambda(d)] \le \int_{0}^{+\infty} e^{-t/4} \, \mathrm{d}t = 4$$ + (we have used that $E[X] = \int_0^{+\infty} \Pr(X \ge x) \, \mathrm{d}x$, check https://math.stackexchange.com/a/1690829). Finally, $\mathbb{E}[C(d)] = \mathbb{E}[\lambda(d) \, n] \le 4n$, and the expected running time is $O(n)$, with a reasonable constant factor. $\quad \blacksquare$ From 5a2e3493130642d71f013b710d3d5f6ee1dffb74 Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Thu, 26 Jun 2025 21:43:19 +0200 Subject: [PATCH 06/12] Update nearest_points.md - avoid \coloneqq and add links --- src/geometry/nearest_points.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index 454b62595..794256466 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -183,7 +183,7 @@ Now we need to decide on how to set $d$ so that it minimizes $\Theta(\sum_{i=1}^ We need $d$ to be an approximation of the minimum distance $d$, and the trick is to just sample $n$ distances randomly and choose $d$ to be the smallest of these distances. We now prove that the expected running time is linear. -**Proof.** Imagine the disposition of points in squares with a particular choice of $d$, say $x$. Consider $d$ a random variable, resulting from our sampling of distances. Let's define $C(x) = \sum_{i=1}^{k(x)} n_i(x)^2$ as the cost estimation for a particular disposition when we choose $d=x$. Now, let's define $\lambda(x)$ such that $C(x) = \lambda(x) \, n$. What is the probability that such choice $x$ survives the sampling of $n$ independent distances? If a single pair among the sampled ones has distance smaller than $x$, this arrangement will be replaced by the smaller $d$. Inside a square, at least a quarter of the pairs would raise a smaller distance (imagine four subsquares in every square, and use the pigeonhole principle), so we have $\sum_{i=1}^{k} \frac{1}{4} {n_i \choose 2}$ pairs which yield a smaller final $d$. This is, approximately, $\frac{1}{8} \sum_{i=1}^{k} n_i^2 = \frac{1}{8} \lambda(x) n$. On the other hand, there are about $\frac{1}{2} n^2$ pairs that can be sampled. We have that the probability of sampling a pair with distance smaller than $x$ is at least (approximately) +**Proof.** Imagine the disposition of points in squares with a particular choice of $d$, say $x$. Consider $d$ a random variable, resulting from our sampling of distances. Let's define $C(x) := \sum_{i=1}^{k(x)} n_i(x)^2$ as the cost estimation for a particular disposition when we choose $d=x$. Now, let's define $\lambda(x)$ such that $C(x) = \lambda(x) \, n$. What is the probability that such choice $x$ survives the sampling of $n$ independent distances? If a single pair among the sampled ones has distance smaller than $x$, this arrangement will be replaced by the smaller $d$. Inside a square, at least a quarter of the pairs would raise a smaller distance (imagine four subsquares in every square, and use the pigeonhole principle), so we have $\sum_{i=1}^{k} \frac{1}{4} {n_i \choose 2}$ pairs which yield a smaller final $d$. This is, approximately, $\frac{1}{8} \sum_{i=1}^{k} n_i^2 = \frac{1}{8} \lambda(x) n$. On the other hand, there are about $\frac{1}{2} n^2$ pairs that can be sampled. We have that the probability of sampling a pair with distance smaller than $x$ is at least (approximately) $$\frac{\lambda(x) n / 8}{n^2 / 2} = \frac{\lambda(x)/4}{n}$$ @@ -191,14 +191,14 @@ so the probability of at least one such pair being chosen during the $n$ rounds $$1 - \left(1 - \frac{\lambda(x)/4}{n}\right)^n \ge 1 - e^{-\lambda(x)/4}$$ -(we have used that $(1 + x)^n \le e^{xn}$ for any real number $x$, check https://en.wikipedia.org/wiki/Bernoulli%27s_inequality#Related_inequalities).
Notice this goes to $1$ exponentially as $\lambda(x)$ increases. This hints that $\lambda$ will be small usually. +(we have used that $(1 + x)^n \le e^{xn}$ for any real number $x$, check [this Wikipedia page](https://en.wikipedia.org/wiki/Bernoulli%27s_inequality#Related_inequalities)).
Notice this goes to $1$ exponentially as $\lambda(x)$ increases. This hints that $\lambda$ will be small usually. We have shown that $\Pr(d \le x) \ge 1 - e^{-\lambda(x)/4}$, or equivalently, $\Pr(d \ge x) \le e^{-\lambda(x)/4}$. We need to know $\Pr(\lambda(d) \ge \text{something})$ to be able to estimate its expected value. We notice that $\lambda(d) \ge \lambda(x) \iff d \ge x$. This is because making the squares smaller only reduces the number of points in each square (splits the points into other squares), and this keeps reducing the sum of squares. Therefore, $$\Pr(\lambda(d) \ge \lambda(x)) = \Pr(d \ge x) \le e^{-\lambda(x)/4} \implies \Pr(\lambda(d) \ge t) \le e^{-t/4} \implies \mathbb{E}[\lambda(d)] \le \int_{0}^{+\infty} e^{-t/4} \, \mathrm{d}t = 4$$ -(we have used that $E[X] = \int_0^{+\infty} \Pr(X \ge x) \, \mathrm{d}x$, check https://math.stackexchange.com/a/1690829). +(we have used that $E[X] = \int_0^{+\infty} \Pr(X \ge x) \, \mathrm{d}x$, check [the proof](https://math.stackexchange.com/a/1690829)). Finally, $\mathbb{E}[C(d)] = \mathbb{E}[\lambda(d) \, n] \le 4n$, and the expected running time is $O(n)$, with a reasonable constant factor. $\quad \blacksquare$ @@ -299,13 +299,13 @@ pair closest_pair_of_points_rand_reals(vector P) { Now we introduce a different randomized algorithm which is less practical but very easy to show that it runs in expected linear time. - Permute the $n$ points randomly -- Take $\delta \coloneqq \operatorname{dist}(p_1, p_2)$ +- Take $\delta := \operatorname{dist}(p_1, p_2)$ - Partition the plane in squares of side $\delta/2$ - For $i = 1,2,\dots,n$: - Take the square corresponding to $p_i$ - Interate over the $25$ squares within two steps to our square in the grid of squares partitioning the plane - If some $p_j$ in those squares has $\operatorname{dist}(p_j, p_i) < \delta$, then - - Recompute the partition and squares with $\delta \coloneqq \operatorname{dist}(p_j, p_i)$ + - Recompute the partition and squares with $\delta := \operatorname{dist}(p_j, p_i)$ - Store points $p_1, \dots, p_i$ in the corresponding squares - else, store $p_i$ in the corresponding square - output $\delta$ From e09c0424b8ce340a5aff0118076c8f87016273fb Mon Sep 17 00:00:00 2001 From: Michael Hayter Date: Sat, 28 Jun 2025 14:31:49 -0400 Subject: [PATCH 07/12] Update nearest_points.md Quick edits. Rendering --- src/geometry/nearest_points.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index 794256466..f39de98ed 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -166,7 +166,7 @@ rec(0, n); ### A randomized algorithm with linear expected time -An alternative method arises from a very simple idea to heuristically improve the runtime: We can divide the plane into a grid of $d \times d$ squares, then it is only required to test distances between same-block or adjacent-block points (unless all squares are disconnected from each other, but we will avoid this by design), since any other pair has larger distance that the two points in the same square. +An alternative method arises from a very simple idea to heuristically improve the runtime: We can divide the plane into a grid of $d \times d$ squares, then it is only required to test distances between same-block or adjacent-block points (unless all squares are disconnected from each other, but we will avoid this by design), since any other pair has a larger distance than the two points in the same square.
Example of the squares strategy @@ -191,14 +191,14 @@ so the probability of at least one such pair being chosen during the $n$ rounds $$1 - \left(1 - \frac{\lambda(x)/4}{n}\right)^n \ge 1 - e^{-\lambda(x)/4}$$ -(we have used that $(1 + x)^n \le e^{xn}$ for any real number $x$, check [this Wikipedia page](https://en.wikipedia.org/wiki/Bernoulli%27s_inequality#Related_inequalities)).
Notice this goes to $1$ exponentially as $\lambda(x)$ increases. This hints that $\lambda$ will be small usually. +(we have used that $(1 + x)^n \le e^{xn}$ for any real number $x$, check [Bernoulli inequalities](https://en.wikipedia.org/wiki/Bernoulli%27s_inequality#Related_inequalities)).
Notice this goes to $1$ exponentially as $\lambda(x)$ increases. This hints that $\lambda$ will be small usually. We have shown that $\Pr(d \le x) \ge 1 - e^{-\lambda(x)/4}$, or equivalently, $\Pr(d \ge x) \le e^{-\lambda(x)/4}$. We need to know $\Pr(\lambda(d) \ge \text{something})$ to be able to estimate its expected value. We notice that $\lambda(d) \ge \lambda(x) \iff d \ge x$. This is because making the squares smaller only reduces the number of points in each square (splits the points into other squares), and this keeps reducing the sum of squares. Therefore, $$\Pr(\lambda(d) \ge \lambda(x)) = \Pr(d \ge x) \le e^{-\lambda(x)/4} \implies \Pr(\lambda(d) \ge t) \le e^{-t/4} \implies \mathbb{E}[\lambda(d)] \le \int_{0}^{+\infty} e^{-t/4} \, \mathrm{d}t = 4$$ -(we have used that $E[X] = \int_0^{+\infty} \Pr(X \ge x) \, \mathrm{d}x$, check [the proof](https://math.stackexchange.com/a/1690829)). +(we have used that $E[X] = \int_0^{+\infty} \Pr(X \ge x) \, \mathrm{d}x$, check [Stackexchange proof](https://math.stackexchange.com/a/1690829)). Finally, $\mathbb{E}[C(d)] = \mathbb{E}[\lambda(d) \, n] \le 4n$, and the expected running time is $O(n)$, with a reasonable constant factor. $\quad \blacksquare$ @@ -315,7 +315,7 @@ While this algorithm may look slow, because of recomputing everything multiple t **Proof.** Let $X_i$ the random variable that is $1$ when point $p_i$ causes a change of $\delta$ and a recomputation of the data structures, and $0$ if not. It is easy to show that the cost is $O(n + \sum_{i=1}^{n} i X_i)$, since on the $i$-th step we are considering only the first $i$ points. However, turns out that $\Pr(X_i = 1) \le \frac{2}{i}$. This is because on the $i$-th step, $\delta$ is the distance of the closest pair in $\{p_1,\dots,p_i\}$, and $\Pr(X_i = 1)$ is the probability of $p_i$ belonging to the closest pair, which only happens in $2(i-1)$ pairs out of the $i(i-1)$ possible pairs (assuming all distances are different), so the probability is at most $\frac{2(i-1)}{i(i-1)} = \frac{2}{i}$, since we previously shuffled the points uniformly. We can therefore see that the expected cost is -$$O(n + \sum_{i=1}^{n} i \Pr(X_i = 1)) \le O(n + \sum_{i=1}^{n} i \frac{2}{i}) = O(3n) = O(n) \quad \quad \blacksquare $$ +$$O(n + \sum_{i=1}^{n} i \Pr(X_i = 1)) \le O(n + \sum_{i=1}^{n} i \frac{2}{i}) = O(3n) = O(n) \quad \quad \blacksquare$$ ## Generalization: finding a triangle with minimal perimeter From 9b2611cdd4c16dd1b2d28c7bb8be38fb94a5abcd Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Sat, 28 Jun 2025 22:13:26 +0200 Subject: [PATCH 08/12] Update nearest_points.md - block equation spacing for correct rendering --- src/geometry/nearest_points.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index f39de98ed..2db764a3d 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -315,6 +315,7 @@ While this algorithm may look slow, because of recomputing everything multiple t **Proof.** Let $X_i$ the random variable that is $1$ when point $p_i$ causes a change of $\delta$ and a recomputation of the data structures, and $0$ if not. It is easy to show that the cost is $O(n + \sum_{i=1}^{n} i X_i)$, since on the $i$-th step we are considering only the first $i$ points. However, turns out that $\Pr(X_i = 1) \le \frac{2}{i}$. This is because on the $i$-th step, $\delta$ is the distance of the closest pair in $\{p_1,\dots,p_i\}$, and $\Pr(X_i = 1)$ is the probability of $p_i$ belonging to the closest pair, which only happens in $2(i-1)$ pairs out of the $i(i-1)$ possible pairs (assuming all distances are different), so the probability is at most $\frac{2(i-1)}{i(i-1)} = \frac{2}{i}$, since we previously shuffled the points uniformly. We can therefore see that the expected cost is + $$O(n + \sum_{i=1}^{n} i \Pr(X_i = 1)) \le O(n + \sum_{i=1}^{n} i \frac{2}{i}) = O(3n) = O(n) \quad \quad \blacksquare$$ From fc76d12f824b8230a49da2015467d8095a77a68c Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Sat, 28 Jun 2025 22:20:28 +0200 Subject: [PATCH 09/12] Update nearest_points.md - try improve format --- src/geometry/nearest_points.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index 2db764a3d..02ac5672a 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -302,12 +302,12 @@ Now we introduce a different randomized algorithm which is less practical but ve - Take $\delta := \operatorname{dist}(p_1, p_2)$ - Partition the plane in squares of side $\delta/2$ - For $i = 1,2,\dots,n$: - - Take the square corresponding to $p_i$ - - Interate over the $25$ squares within two steps to our square in the grid of squares partitioning the plane - - If some $p_j$ in those squares has $\operatorname{dist}(p_j, p_i) < \delta$, then - - Recompute the partition and squares with $\delta := \operatorname{dist}(p_j, p_i)$ - - Store points $p_1, \dots, p_i$ in the corresponding squares - - else, store $p_i$ in the corresponding square + - Take the square corresponding to $p_i$ + - Interate over the $25$ squares within two steps to our square in the grid of squares partitioning the plane + - If some $p_j$ in those squares has $\operatorname{dist}(p_j, p_i) < \delta$, then + - Recompute the partition and squares with $\delta := \operatorname{dist}(p_j, p_i)$ + - Store points $p_1, \dots, p_i$ in the corresponding squares + - else, store $p_i$ in the corresponding square - output $\delta$ While this algorithm may look slow, because of recomputing everything multiple times, we can show the total expected cost is linear. @@ -316,7 +316,7 @@ While this algorithm may look slow, because of recomputing everything multiple t We can therefore see that the expected cost is -$$O(n + \sum_{i=1}^{n} i \Pr(X_i = 1)) \le O(n + \sum_{i=1}^{n} i \frac{2}{i}) = O(3n) = O(n) \quad \quad \blacksquare$$ +$$O\left(n + \sum_{i=1}^{n} i \Pr(X_i = 1)\right) \le O\left(n + \sum_{i=1}^{n} i \frac{2}{i}\right) = O(3n) = O(n) \quad \quad \blacksquare$$ ## Generalization: finding a triangle with minimal perimeter From 937d47bd2e8529c47ec3be04799b4f1b1fd858db Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Sat, 28 Jun 2025 22:42:29 +0200 Subject: [PATCH 10/12] Update nearest_points.md - explanation of the implementation --- src/geometry/nearest_points.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index 02ac5672a..e3e738b70 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -204,7 +204,7 @@ Finally, $\mathbb{E}[C(d)] = \mathbb{E}[\lambda(d) \, n] \le 4n$, and the expect #### Implementation of the algorithm -The advantage of this algorithm is that it is straightforward to implement, but still has good performance in practise. +The advantage of this algorithm is that it is straightforward to implement, but still has good performance in practise. We first sample $n$ distances and set $d$ as the minimum of the distances. Then we insert points into the "blocks" by using a hash table from 2D coordinates to a vector of points. Finally, just compute distances between same-block pairs and adjacent-block pairs. Hash table operations have $O(1)$ expected time cost, and therefore our algorithm retains the $O(n)$ expected time cost with an increased constant. ```{.cpp file=nearest_pair_randomized} using ll = long long; From 4d47823ffc221c6f07fc64562c397ae359727dfa Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Sat, 28 Jun 2025 22:57:36 +0200 Subject: [PATCH 11/12] Update nearest_points.md - put proofs in dropdowns --- src/geometry/nearest_points.md | 51 ++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index e3e738b70..01d744fb4 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -175,7 +175,8 @@ An alternative method arises from a very simple idea to heuristically improve th We will consider only the squares containing at least one point. Denote by $n_1, n_2, \dots, n_k$ the number of points in each of the $k$ remaining squares. Assuming at least two points are in the same or in adjacent squares, the time complexity is $\Theta(\sum_{i=1}^{k} n_i^2)$. -**Proof.** For the $i$-th square containing $n_i$ points, the number of pairs inside is $\Theta(n_i^2)$. If the $i$-th square is adjacent to the $j$-th square, then we also perform $n_i n_j \le \max(n_i, n_j)^2 \le n_i^2 + n_j^2$ distance comparisons. Notice that each cube has at most $8$ adjacent cubes, so we can bound the sum of all comparisons by $\Theta(\sum_{i=1}^{k} n_i^2)$. $\quad \blacksquare$ +??? info "Proof" + For the $i$-th square containing $n_i$ points, the number of pairs inside is $\Theta(n_i^2)$. If the $i$-th square is adjacent to the $j$-th square, then we also perform $n_i n_j \le \max(n_i, n_j)^2 \le n_i^2 + n_j^2$ distance comparisons. Notice that each cube has at most $8$ adjacent cubes, so we can bound the sum of all comparisons by $\Theta(\sum_{i=1}^{k} n_i^2)$. $\quad \blacksquare$ Now we need to decide on how to set $d$ so that it minimizes $\Theta(\sum_{i=1}^{k} n_i^2)$. @@ -183,24 +184,25 @@ Now we need to decide on how to set $d$ so that it minimizes $\Theta(\sum_{i=1}^ We need $d$ to be an approximation of the minimum distance $d$, and the trick is to just sample $n$ distances randomly and choose $d$ to be the smallest of these distances. We now prove that the expected running time is linear. -**Proof.** Imagine the disposition of points in squares with a particular choice of $d$, say $x$. Consider $d$ a random variable, resulting from our sampling of distances. Let's define $C(x) := \sum_{i=1}^{k(x)} n_i(x)^2$ as the cost estimation for a particular disposition when we choose $d=x$. Now, let's define $\lambda(x)$ such that $C(x) = \lambda(x) \, n$. What is the probability that such choice $x$ survives the sampling of $n$ independent distances? If a single pair among the sampled ones has distance smaller than $x$, this arrangement will be replaced by the smaller $d$. Inside a square, at least a quarter of the pairs would raise a smaller distance (imagine four subsquares in every square, and use the pigeonhole principle), so we have $\sum_{i=1}^{k} \frac{1}{4} {n_i \choose 2}$ pairs which yield a smaller final $d$. This is, approximately, $\frac{1}{8} \sum_{i=1}^{k} n_i^2 = \frac{1}{8} \lambda(x) n$. On the other hand, there are about $\frac{1}{2} n^2$ pairs that can be sampled. We have that the probability of sampling a pair with distance smaller than $x$ is at least (approximately) - -$$\frac{\lambda(x) n / 8}{n^2 / 2} = \frac{\lambda(x)/4}{n}$$ - -so the probability of at least one such pair being chosen during the $n$ rounds (and therefore finding a smaller $d$) is - -$$1 - \left(1 - \frac{\lambda(x)/4}{n}\right)^n \ge 1 - e^{-\lambda(x)/4}$$ - -(we have used that $(1 + x)^n \le e^{xn}$ for any real number $x$, check [Bernoulli inequalities](https://en.wikipedia.org/wiki/Bernoulli%27s_inequality#Related_inequalities)).
Notice this goes to $1$ exponentially as $\lambda(x)$ increases. This hints that $\lambda$ will be small usually. - - -We have shown that $\Pr(d \le x) \ge 1 - e^{-\lambda(x)/4}$, or equivalently, $\Pr(d \ge x) \le e^{-\lambda(x)/4}$. We need to know $\Pr(\lambda(d) \ge \text{something})$ to be able to estimate its expected value. We notice that $\lambda(d) \ge \lambda(x) \iff d \ge x$. This is because making the squares smaller only reduces the number of points in each square (splits the points into other squares), and this keeps reducing the sum of squares. Therefore, - -$$\Pr(\lambda(d) \ge \lambda(x)) = \Pr(d \ge x) \le e^{-\lambda(x)/4} \implies \Pr(\lambda(d) \ge t) \le e^{-t/4} \implies \mathbb{E}[\lambda(d)] \le \int_{0}^{+\infty} e^{-t/4} \, \mathrm{d}t = 4$$ - -(we have used that $E[X] = \int_0^{+\infty} \Pr(X \ge x) \, \mathrm{d}x$, check [Stackexchange proof](https://math.stackexchange.com/a/1690829)). - -Finally, $\mathbb{E}[C(d)] = \mathbb{E}[\lambda(d) \, n] \le 4n$, and the expected running time is $O(n)$, with a reasonable constant factor. $\quad \blacksquare$ +??? info "Proof" + Imagine the disposition of points in squares with a particular choice of $d$, say $x$. Consider $d$ a random variable, resulting from our sampling of distances. Let's define $C(x) := \sum_{i=1}^{k(x)} n_i(x)^2$ as the cost estimation for a particular disposition when we choose $d=x$. Now, let's define $\lambda(x)$ such that $C(x) = \lambda(x) \, n$. What is the probability that such choice $x$ survives the sampling of $n$ independent distances? If a single pair among the sampled ones has distance smaller than $x$, this arrangement will be replaced by the smaller $d$. Inside a square, at least a quarter of the pairs would raise a smaller distance (imagine four subsquares in every square, and use the pigeonhole principle), so we have $\sum_{i=1}^{k} \frac{1}{4} {n_i \choose 2}$ pairs which yield a smaller final $d$. This is, approximately, $\frac{1}{8} \sum_{i=1}^{k} n_i^2 = \frac{1}{8} \lambda(x) n$. On the other hand, there are about $\frac{1}{2} n^2$ pairs that can be sampled. We have that the probability of sampling a pair with distance smaller than $x$ is at least (approximately) + + $$\frac{\lambda(x) n / 8}{n^2 / 2} = \frac{\lambda(x)/4}{n}$$ + + so the probability of at least one such pair being chosen during the $n$ rounds (and therefore finding a smaller $d$) is + + $$1 - \left(1 - \frac{\lambda(x)/4}{n}\right)^n \ge 1 - e^{-\lambda(x)/4}$$ + + (we have used that $(1 + x)^n \le e^{xn}$ for any real number $x$, check [Bernoulli inequalities](https://en.wikipedia.org/wiki/Bernoulli%27s_inequality#Related_inequalities)).
Notice this goes to $1$ exponentially as $\lambda(x)$ increases. This hints that $\lambda$ will be small usually. + + + We have shown that $\Pr(d \le x) \ge 1 - e^{-\lambda(x)/4}$, or equivalently, $\Pr(d \ge x) \le e^{-\lambda(x)/4}$. We need to know $\Pr(\lambda(d) \ge \text{something})$ to be able to estimate its expected value. We notice that $\lambda(d) \ge \lambda(x) \iff d \ge x$. This is because making the squares smaller only reduces the number of points in each square (splits the points into other squares), and this keeps reducing the sum of squares. Therefore, + + $$\Pr(\lambda(d) \ge \lambda(x)) = \Pr(d \ge x) \le e^{-\lambda(x)/4} \implies \Pr(\lambda(d) \ge t) \le e^{-t/4} \implies \mathbb{E}[\lambda(d)] \le \int_{0}^{+\infty} e^{-t/4} \, \mathrm{d}t = 4$$ + + (we have used that $E[X] = \int_0^{+\infty} \Pr(X \ge x) \, \mathrm{d}x$, check [Stackexchange proof](https://math.stackexchange.com/a/1690829)). + + Finally, $\mathbb{E}[C(d)] = \mathbb{E}[\lambda(d) \, n] \le 4n$, and the expected running time is $O(n)$, with a reasonable constant factor. $\quad \blacksquare$ #### Implementation of the algorithm @@ -312,11 +314,12 @@ Now we introduce a different randomized algorithm which is less practical but ve While this algorithm may look slow, because of recomputing everything multiple times, we can show the total expected cost is linear. -**Proof.** Let $X_i$ the random variable that is $1$ when point $p_i$ causes a change of $\delta$ and a recomputation of the data structures, and $0$ if not. It is easy to show that the cost is $O(n + \sum_{i=1}^{n} i X_i)$, since on the $i$-th step we are considering only the first $i$ points. However, turns out that $\Pr(X_i = 1) \le \frac{2}{i}$. This is because on the $i$-th step, $\delta$ is the distance of the closest pair in $\{p_1,\dots,p_i\}$, and $\Pr(X_i = 1)$ is the probability of $p_i$ belonging to the closest pair, which only happens in $2(i-1)$ pairs out of the $i(i-1)$ possible pairs (assuming all distances are different), so the probability is at most $\frac{2(i-1)}{i(i-1)} = \frac{2}{i}$, since we previously shuffled the points uniformly. - -We can therefore see that the expected cost is - -$$O\left(n + \sum_{i=1}^{n} i \Pr(X_i = 1)\right) \le O\left(n + \sum_{i=1}^{n} i \frac{2}{i}\right) = O(3n) = O(n) \quad \quad \blacksquare$$ +??? info "Proof" + Let $X_i$ the random variable that is $1$ when point $p_i$ causes a change of $\delta$ and a recomputation of the data structures, and $0$ if not. It is easy to show that the cost is $O(n + \sum_{i=1}^{n} i X_i)$, since on the $i$-th step we are considering only the first $i$ points. However, turns out that $\Pr(X_i = 1) \le \frac{2}{i}$. This is because on the $i$-th step, $\delta$ is the distance of the closest pair in $\{p_1,\dots,p_i\}$, and $\Pr(X_i = 1)$ is the probability of $p_i$ belonging to the closest pair, which only happens in $2(i-1)$ pairs out of the $i(i-1)$ possible pairs (assuming all distances are different), so the probability is at most $\frac{2(i-1)}{i(i-1)} = \frac{2}{i}$, since we previously shuffled the points uniformly. + + We can therefore see that the expected cost is + + $$O\left(n + \sum_{i=1}^{n} i \Pr(X_i = 1)\right) \le O\left(n + \sum_{i=1}^{n} i \frac{2}{i}\right) = O(3n) = O(n) \quad \quad \blacksquare$$ ## Generalization: finding a triangle with minimal perimeter From 50bb91b96f6ca449fd28c83beef4baf6ef57f7a4 Mon Sep 17 00:00:00 2001 From: Izan Beltran Date: Sun, 29 Jun 2025 21:28:11 +0200 Subject: [PATCH 12/12] Update nearest_points.md - patch code error --- src/geometry/nearest_points.md | 134 ++++++++++++++++----------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/src/geometry/nearest_points.md b/src/geometry/nearest_points.md index 01d744fb4..395eda726 100644 --- a/src/geometry/nearest_points.md +++ b/src/geometry/nearest_points.md @@ -213,85 +213,85 @@ using ll = long long; using ld = long double; struct RealPoint { - ld x, y; - RealPoint() {} - RealPoint(T x_, T y_) : x(x_), y(y_) {} + ld x, y; + RealPoint() {} + RealPoint(ld x_, ld y_) : x(x_), y(y_) {} }; using pt = RealPoint; struct CustomHash { - size_t operator()(const pair& p) const { - static const uint64_t C = chrono::steady_clock::now().time_since_epoch().count(); - return C ^ ((p.first << 32) ^ p.second); - } + size_t operator()(const pair& p) const { + static const uint64_t C = chrono::steady_clock::now().time_since_epoch().count(); + return C ^ ((p.first << 32) ^ p.second); + } }; -ld dist(pt a, pt b) { - ld dx = a.x - b.x; - ld dy = a.y - b.y; - return sqrt(dx*dx + dy*dy); +ld dist(const pt& a, const pt& b) { + ld dx = a.x - b.x; + ld dy = a.y - b.y; + return sqrt(dx*dx + dy*dy); } pair closest_pair_of_points_rand_reals(vector P) { const ld eps = 1e-9; - int n = int(P.size()); - assert(n >= 2); - unordered_map,vector,CustomHash> grid; - grid.reserve(n); - - mt19937 rd(chrono::system_clock::now().time_since_epoch().count()); - uniform_int_distribution dis(0, n-1); - - ld d = dist(P[0], P[1]); - pair closest = {P[0], P[1]}; - - auto consider_pair = [&](const pt& a, const pt& b) -> void { - ld ab = dist(a, b); - if (ab + eps < d) { - d = ab; - closest = {a, b}; - } - }; - - for (int i = 0; i < n; ++i) { - int j = dis(rd); - int k = dis(rd); - while (j == k) - k = dis(rd); - consider_pair(P[j], P[k]); - } - - for (const pt& p : P) - grid[{ll(p.x/d), ll(p.y/d)}].push_back(p); - - for (const auto& it : grid) { // same block - int k = int(it.second.size()); - for (int i = 0; i < k; ++i) { - for (int j = i+1; j < k; ++j) - consider_pair(it.second[i], it.second[j]); - } - } + int n = int(P.size()); + assert(n >= 2); + unordered_map,vector,CustomHash> grid; + grid.reserve(n); + + mt19937 prng(chrono::system_clock::now().time_since_epoch().count()); + uniform_int_distribution uniform(0, n-1); + + ld d = dist(P[0], P[1]); + pair closest = {P[0], P[1]}; + + auto consider_pair = [&](const pt& a, const pt& b) -> void { + ld ab = dist(a, b); + if (ab + eps < d) { + d = ab; + closest = {a, b}; + } + }; + + for (int i = 0; i < n; ++i) { + int j = uniform(prng); + int k = uniform(prng); + while (j == k) + k = uniform(prng); + consider_pair(P[j], P[k]); + } + + for (const pt& p : P) + grid[{ll(p.x/d), ll(p.y/d)}].push_back(p); + + for (const auto& it : grid) { // same block + int k = int(it.second.size()); + for (int i = 0; i < k; ++i) { + for (int j = i+1; j < k; ++j) + consider_pair(it.second[i], it.second[j]); + } + } - for (const auto& it : grid) { // adjacent blocks - auto coord = it.first; - for (int dx = 0; dx <= 1; ++dx) { - for (int dy = -1; dy <= 1; ++dy) { - if (dx == 0 and dy == 0) continue; - pair neighbour = { - coord.first + dx, - coord.second + dy - }; - for (const pt& p : it.second) { - if (not grid.count(neighbour)) continue; - for (const pt& q : grid.at(neighbour)) - candidate_closest(p, q); - } - } - } - } - - return closest; + for (const auto& it : grid) { // adjacent blocks + auto coord = it.first; + for (int dx = 0; dx <= 1; ++dx) { + for (int dy = -1; dy <= 1; ++dy) { + if (dx == 0 and dy == 0) continue; + pair neighbour = { + coord.first + dx, + coord.second + dy + }; + for (const pt& p : it.second) { + if (not grid.count(neighbour)) continue; + for (const pt& q : grid.at(neighbour)) + consider_pair(p, q); + } + } + } + } + + return closest; } ``` pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy