Skip to content

Commit

Permalink
feat: algo/numeric/bin_search_integer.hpp
Browse files Browse the repository at this point in the history
  • Loading branch information
arumakan1727 committed Aug 2, 2023
1 parent 34674a2 commit 8b5a6e1
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/armkn/algo/numeric/bin_search_integer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once
#include <cstddef>
#include <iostream>
#include <type_traits>
#include <utility>

/// `p(ok)` を満たし、かつ `ng_` に最も近い `ok` を二分探索で求める。
/// - 戻り値の値域は `min(ok_,ng_) - 1 <= ok <= max(ok_,ng_) + 1`
/// - `p(x) == true` (`∀x ∈ [ok_, ng_]`) ならば `ng_` を返す。
/// - `p(x) == false` (`∀x ∈ [ok_, ng_]`) ならば `[ok_, ng_]` の範囲外で `ok_` に最も近い値を返す。
template <
class Int,
class PredicateFn,
class SignedInt = std::make_signed_t<Int>,
std::enable_if_t<std::is_integral_v<Int>, std::nullptr_t> = nullptr>
SignedInt bin_search_integer(const Int ok_, const Int ng_, PredicateFn&& p) {
SignedInt ng, ok;
if (ng_ < ok_) {
ng = ng_ - 1;
ok = ok_ + 1;
while (ok - ng > 1) {
auto mid = ng + ((ok - ng) >> 1);
(p((Int)mid) ? ok : ng) = mid;
}
} else {
ng = ng_ + 1;
ok = ok_ - 1;
while (ng - ok > 1) {
auto mid = ok + ((ng - ok) >> 1);
(p((Int)mid) ? ok : ng) = mid;
}
}

#ifdef ARMKN_DEBUG
if (ng == (SignedInt)ok_) {
std::clog << "\x1b[31;1m[Warn] ALL p(x) made FALSE\x1b[;m";
std::clog << " initial range: (ok_,ng_)=(" << ok_ << ", " << ng_ << ") / result: (ok,ng)=("
<< ok << ", " << ng << ')' << std::endl;
}
#endif
return ok;
}
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_executable(tests
armkn/algo/mapping/cycle_finder.cpp
armkn/algo/mapping/doubling.cpp
armkn/algo/mapping/doubling_with_monoid.cpp
armkn/algo/numeric/bin_search_integer.cpp
armkn/dp/k_choose_prod_sum.cpp
armkn/dsu/union_find.cpp
armkn/fmt/printer.cpp
Expand Down
28 changes: 28 additions & 0 deletions tests/armkn/algo/numeric/bin_search_integer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <armkn/algo/numeric/bin_search_integer.hpp>
#include <armkn/util/alias/stdint.hpp>
#include <catch2/catch_test_macros.hpp>

#include <utility>
#include <vector>

#define fn(expr) ([&](u32 i) { return (expr); })

TEST_CASE("bin_search_integer") {
std::vector<int> a{1, 3, 3, 3, 3, 6};

CHECK(bin_search_integer(5u, 0u, fn(a[i] >= 1)) == 0);
CHECK(bin_search_integer(5u, 0u, fn(a[i] >= 2)) == 1);
CHECK(bin_search_integer(5u, 0u, fn(a[i] >= 3)) == 1);
CHECK(bin_search_integer(5u, 0u, fn(a[i] >= 4)) == 5);
CHECK(bin_search_integer(5u, 0u, fn(a[i] >= 5)) == 5);
CHECK(bin_search_integer(5u, 0u, fn(a[i] >= 6)) == 5);
CHECK(bin_search_integer(5u, 0u, fn(a[i] >= 7)) == 6);

CHECK(bin_search_integer(0u, 5u, fn(a[i] < 1)) == -1);
CHECK(bin_search_integer(0u, 5u, fn(a[i] < 2)) == 0);
CHECK(bin_search_integer(0u, 5u, fn(a[i] < 3)) == 0);
CHECK(bin_search_integer(0u, 5u, fn(a[i] < 4)) == 4);
CHECK(bin_search_integer(0u, 5u, fn(a[i] < 5)) == 4);
CHECK(bin_search_integer(0u, 5u, fn(a[i] < 6)) == 4);
CHECK(bin_search_integer(0u, 5u, fn(a[i] < 7)) == 5);
}

0 comments on commit 8b5a6e1

Please sign in to comment.