Skip to content

Commit

Permalink
Merge pull request #76 from kzrnm/feature/binary_search
Browse files Browse the repository at this point in the history
Optimize BinarySearch
  • Loading branch information
kzrnm authored Mar 24, 2022
2 parents 779060c + 9324711 commit ba3ef65
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 44 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [Unreleased]
### Changed
- Optimize BinarySearch

## [1.11.2] - 2022-03-20
### Changed
- SourceExpander 5.0.0
Expand Down
125 changes: 81 additions & 44 deletions Source/AtCoderLibrary/STL/Extension/BinarySearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,189 +11,226 @@ private struct DefaultComparer<T> : IComparer<T> where T : IComparable<T>
[MethodImpl(256)]
public int Compare(T x, T y) => x.CompareTo(y);
}
private interface IOk
{
bool Ok(int c);
}
private struct L : IOk
{
[MethodImpl(256)]
public bool Ok(int c) => c >= 0;
}
private struct U : IOk
{
[MethodImpl(256)]
public bool Ok(int c) => c > 0;
}

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します。
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int LowerBound<T, TOp>(this T[] a, T v, TOp cmp) where TOp : IComparer<T> => BinarySearch(a.AsSpan(), v, cmp, true);
public static int LowerBound<T>(this T[] a, T v) where T : IComparable<T>
=> LowerBound(a.AsSpan(), v, default(DefaultComparer<T>));

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します。
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int LowerBound<T>(this T[] a, T v) where T : IComparable<T> => BinarySearch(a.AsSpan(), v, default(DefaultComparer<T>), true);
public static int LowerBound<T>(this IList<T> a, T v) where T : IComparable<T>
=> LowerBound(a, v, default(DefaultComparer<T>));

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int UpperBound<T, TOp>(this T[] a, T v, TOp cmp) where TOp : IComparer<T> => BinarySearch(a.AsSpan(), v, cmp, false);
public static int LowerBound<T>(this Span<T> a, T v) where T : IComparable<T>
=> LowerBound(a, v, default(DefaultComparer<T>));

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int UpperBound<T>(this T[] a, T v) where T : IComparable<T> => BinarySearch(a.AsSpan(), v, default(DefaultComparer<T>), false);
public static int LowerBound<T>(this ReadOnlySpan<T> a, T v) where T : IComparable<T>
=> LowerBound(a, v, default(DefaultComparer<T>));

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します。
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int LowerBound<T, TOp>(this IList<T> a, T v, TOp cmp) where TOp : IComparer<T> => BinarySearch(a, v, cmp, true);
public static int LowerBound<T, TOp>(this T[] a, T v, TOp cmp) where TOp : IComparer<T>
=> BinarySearch<T, TOp, L>(a.AsSpan(), v, cmp);

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します。
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int LowerBound<T>(this IList<T> a, T v) where T : IComparable<T> => BinarySearch(a, v, default(DefaultComparer<T>), true);
public static int LowerBound<T, TOp>(this IList<T> a, T v, TOp cmp) where TOp : IComparer<T>
=> BinarySearch<T, TOp, L>(a, v, cmp);

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int UpperBound<T, TOp>(this IList<T> a, T v, TOp cmp) where TOp : IComparer<T> => BinarySearch(a, v, cmp, false);
public static int LowerBound<T, TOp>(this Span<T> a, T v, TOp cmp) where TOp : IComparer<T>
=> BinarySearch<T, TOp, L>(a, v, cmp);

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int UpperBound<T>(this IList<T> a, T v) where T : IComparable<T> => BinarySearch(a, v, default(DefaultComparer<T>), false);
public static int LowerBound<T, TOp>(this ReadOnlySpan<T> a, T v, TOp cmp) where TOp : IComparer<T>
=> BinarySearch<T, TOp, L>(a, v, cmp);

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int LowerBound<T, TOp>(this Span<T> a, T v, TOp cmp) where TOp : IComparer<T> => BinarySearch(a, v, cmp, true);
public static int UpperBound<T>(this T[] a, T v) where T : IComparable<T>
=> UpperBound(a.AsSpan(), v, default(DefaultComparer<T>));

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int LowerBound<T>(this Span<T> a, T v) where T : IComparable<T> => BinarySearch(a, v, default(DefaultComparer<T>), true);
public static int UpperBound<T>(this IList<T> a, T v) where T : IComparable<T>
=> UpperBound(a, v, default(DefaultComparer<T>));

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します。
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int UpperBound<T, TOp>(this Span<T> a, T v, TOp cmp) where TOp : IComparer<T> => BinarySearch(a, v, cmp, false);
public static int UpperBound<T>(this Span<T> a, T v) where T : IComparable<T>
=> UpperBound(a, v, default(DefaultComparer<T>));

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します。
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int UpperBound<T>(this Span<T> a, T v) where T : IComparable<T> => BinarySearch(a, v, default(DefaultComparer<T>), false);
public static int UpperBound<T>(this ReadOnlySpan<T> a, T v) where T : IComparable<T>
=> UpperBound(a, v, default(DefaultComparer<T>));

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int LowerBound<T, TOp>(this ReadOnlySpan<T> a, T v, TOp cmp) where TOp : IComparer<T> => BinarySearch(a, v, cmp, true);
public static int UpperBound<T, TOp>(this T[] a, T v, TOp cmp) where TOp : IComparer<T>
=> BinarySearch<T, TOp, U>(a.AsSpan(), v, cmp);

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> 以上の値が現れる最小のインデックスを取得します
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int LowerBound<T>(this ReadOnlySpan<T> a, T v) where T : IComparable<T> => BinarySearch(a, v, default(DefaultComparer<T>), true);
public static int UpperBound<T, TOp>(this IList<T> a, T v, TOp cmp) where TOp : IComparer<T>
=> BinarySearch<T, TOp, U>(a, v, cmp);

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します。
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int UpperBound<T, TOp>(this ReadOnlySpan<T> a, T v, TOp cmp) where TOp : IComparer<T> => BinarySearch(a, v, cmp, false);
public static int UpperBound<T, TOp>(this Span<T> a, T v, TOp cmp) where TOp : IComparer<T>
=> BinarySearch<T, TOp, U>(a, v, cmp);

/// <summary>
/// <paramref name="a"/> の要素のうち、<paramref name="v"/> より大きい値が現れる最小のインデックスを取得します。
/// </summary>
/// <remarks>
/// <para>制約: <paramref name="a"/> がソート済み</para>
/// <para>計算量: O(log <paramref name="n"/>)</para>
/// <para>計算量: O(n)</para>
/// </remarks>
[MethodImpl(256)]
public static int UpperBound<T>(this ReadOnlySpan<T> a, T v) where T : IComparable<T> => BinarySearch(a, v, default(DefaultComparer<T>), false);
public static int UpperBound<T, TOp>(this ReadOnlySpan<T> a, T v, TOp cmp) where TOp : IComparer<T>
=> BinarySearch<T, TOp, U>(a, v, cmp);

[MethodImpl(256)]
private static int BinarySearch<T, TOp>(this IList<T> a, T v, TOp cmp, bool isLowerBound)
private static int BinarySearch<T, TOp, TOk>(this IList<T> a, T v, TOp cmp)
where TOp : IComparer<T>
where TOk : IOk
{
int ok = a.Count;
int ng = -1;
while (ok - ng > 1)
{
var m = (ok + ng) >> 1;
var c = cmp.Compare(a[m], v);
if (c > 0 || (c == 0 && isLowerBound)) ok = m;
if (default(TOk).Ok(c)) ok = m;
else ng = m;
}
return ok;
}
[MethodImpl(256)]
private static int BinarySearch<T, TOp>(this ReadOnlySpan<T> a, T v, TOp cmp, bool isLowerBound)
private static int BinarySearch<T, TOp, TOk>(this ReadOnlySpan<T> a, T v, TOp cmp)
where TOp : IComparer<T>
where TOk : IOk

{
int ok = a.Length;
int ng = -1;
while (ok - ng > 1)
{
var m = (ok + ng) >> 1;
var c = cmp.Compare(a[m], v);
if (c > 0 || (c == 0 && isLowerBound)) ok = m;
if (default(TOk).Ok(c)) ok = m;
else ng = m;
}
return ok;
Expand Down

0 comments on commit ba3ef65

Please sign in to comment.