Given a list of dates in descending order, this code will find the largest date where the date is <= searchDate
.
List<CurrencyHistoricExchangeRate> history = GetOrderedHistory();
foreach (var record in history)
{
if (record.Date < searchDate)
{
return record ;
}
}
How would I write a binary search function to replace this method? I'm struggling to implement it for an inexact comparison like this.
This method is called frequently, and can contain several thousand records which is why I wish to replace it with a binary search.
Search algorithm finding the position of a target value within a sorted array. In computer science, binary search, also known as half-interval search, logarithmic search, or binary chop, is a search algorithm that finds the position of a target value within a sorted array. Binary search compares the target value to the middle element of the array.
A simple approach is to do linear search. The time complexity of above algorithm is O (n). Another approach to perform the same task is using Binary Search. Binary Search: Search a sorted array by repeatedly dividing the search interval in half. Begin with an interval covering the whole array.
Given a sorted array arr [] of n elements, write a function to search a given element x in arr []. A simple approach is to do a linear search. The time complexity of the above algorithm is O (n). Another approach to perform the same task is using Binary Search. Binary Search: Search a sorted array by repeatedly dividing the search interval in half.
This is because the worst case is reached when the search reaches the deepest level of the tree, and there are always levels in the tree for any binary search. The worst case may also be reached when the target element is not in the array. If is one less than a power of two, then this is always the case.
Given a sorted list, List<T>.BinarySearch
actually helps you find the index of item which is "equal", or "larger than" your item (presuming an ascending list and a default comparer).
This method returns:
So, first you need an inverted comparer, because your items are sorted in reverse:
class CurrencyHistoricExchangeRateComparer : IComparer<CurrencyHistoricExchangeRate>
{
public int Compare(CurrencyHistoricExchangeRate x, CurrencyHistoricExchangeRate y)
{
// this is just the opposite of the default DateTime comparer
return -x.Date.CompareTo(y.Date);
}
}
Then, you need to check if the item was actually found or not, and complement the result:
private static int FindIndex(List<CurrencyHistoricExchangeRate> list, DateTime dateTime)
{
var comparer = new CurrencyHistoricExchangeRateComparer();
var idx = list.BinarySearch(
new CurrencyHistoricExchangeRate() { Date = dateTime }, comparer);
// not found? then calculate the bitwise complement to
// get the index of the first larger element
// (this will evaluate to list.Count if there is no such element)
return (idx < 0) ? ~idx : idx;
}
Interpreting these results should then be something like:
var idx = FindIndex(history, someDate);
CurrencyHistoricExchangeRate rate = null;
if (idx < history.Count)
rate = history[idx];
else
throw new InvalidOperationException($"there are no dates smaller than {someDate}");
After playing round with it a bit I came up with this working solution:
if (history.First().Date <= date) return history.First();
var lowerIx = 0;
var upperIx = history.Count - 1;
while (true)
{
var middleIndex = lowerIx + (upperIx - lowerIx) / 2;
if (history[middleIndex].Date <= date)
{
upperIx = middleIndex;
}
else
{
lowerIx = middleIndex;
}
if (lowerIx + 1 == upperIx) break;
}
if(history[upperIx].Date > date) throw new Exception();
return history[upperIx];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With