If I understand this correctly, GetSkipHeight's complexity should come from its bit-width, not distance, so maybe we can just estimate it via triangular numbers:
B = ⌊log₂(N − 1)⌋ + 1
bound(N) = B(B + 1) / 2
This isn't as tight as the previous one, but is very simple and seems to pass.
The patch would be something like:
diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp
index d7d18cf76e..fd7b409c88 100644
--- a/src/test/skiplist_tests.cpp
+++ b/src/test/skiplist_tests.cpp
@@ -6,7 +6,7 @@
#include <test/util/random.h>
#include <test/util/setup_common.h>
-#include <cmath>
+#include <bit>
#include <vector>
#include <boost/test/unit_test.hpp>
@@ -268,6 +268,8 @@ BOOST_AUTO_TEST_CASE(skip_height_analysis)
// Build a chain
constexpr int chain_size{1'000'000};
+ constexpr int chain_bits{std::bit_width<unsigned>(chain_size - 1)}; // Bit width of the maximum height.
+ constexpr int iteration_bound{chain_bits * (chain_bits + 1) / 2}; // Conservative O(log^2(N)) bound.
std::vector<CBlockIndex> block_index(chain_size);
for (auto i{0}; i < chain_size; ++i) {
block_index[i].pprev = i == 0 ? nullptr : &block_index[i - 1];
@@ -313,12 +315,7 @@ BOOST_AUTO_TEST_CASE(skip_height_analysis)
++iteration_count;
}
- // Asserts on the number of iterations:
- // Absolute value maximum (derived empirically)
- BOOST_CHECK_LE(iteration_count, 130);
- // Dynamic bound, function of distance. Formula derived empirically.
- const auto bound_log_distance{std::ceil(8.3 * std::log(delta) / std::log(2) - 35)};
- BOOST_CHECK_LE(double(iteration_count), bound_log_distance);
+ BOOST_CHECK_LE(iteration_count, iteration_bound);
// Also check that `GetAncestor` gives the same result
auto p_ancestor{start_p.GetAncestor(target_height)};