#c# #.net #data-structures #.net-5
Вопрос:
Какой объект я могу использовать в C# net 5.0 для хранения следующей информации в памяти, чтобы получить самый быстрый поиск данных?
Ввод : число (длинное)
Пример упрощенных данных поиска:
1 -> 10 : ResultA
300 -> 300 : ResultB
500 -> 10000 : ResultC
235015 -> 235820 : ResultD
...
Список можно продолжить (около 3 миллионов строк данных поиска)
В приведенном выше примере данных:
Input -> output
5 -> ResultA
300 -> ResultB
400 -> Not found/null
9999 -> ResultC
1000000 -> Not found/null
Комментарии:
1. Я не уверен, есть ли что-нибудь встроенное (надеюсь, кто-то еще может сказать, есть ли), но если в диапазонах нет перекрытий, вы можете создать отсортированный список и выполнить двоичный поиск.
2. Я не уверен, работает ли/как работает сортированный список, если точное совпадение уникальных ключей недоступно. (например: при поиске «9» результатом должно быть значение «1 -> 10»
Ответ №1:
Как упоминает Лама, правильным подходом было бы использовать двоичный поиск. Это должно обеспечить достаточную производительность даже для многих миллионов диапазонов, поскольку оно масштабируется с помощью O(log n) для достаточно хорошо распределенных данных.
Если диапазоны не перекрываются, должно сработать что-то подобное:
// Assume sorted
var rangesArray = new[]
{
(1, 10, "A"),
(300, 300, "B"),
(500, 10000, "C"),
(235015, 235820, "D")
};
var rangesList = rangesArray.ToList();
var toSearchFor = 9999;
var comparer = new KeyComparer<(int, int, string), int>(p => p.Item1);
var index = rangesList.BinarySearch((toSearchFor, 0, ""), comparer);
if (index < 0) // negative values mean a exact match could not be found,
{
// Take bitwise complement to get index of the element larger than the toSearchFor
// remove one get the actual range to check
index = ~index -1;
if (index > 0 amp;amp; toSearchFor < rangesList[index].Item2 )
{
Console.WriteLine($"Found Range {index}");
}
else
{
Console.WriteLine($"Not Found");
}
}
else
{
Console.WriteLine($"Found Exact Range {index}");
}
Как написать универсальный IComparer
public class KeyComparer<T, TKey> : IComparer<T> where TKey : IComparable<TKey>
{
private readonly Func<T, TKey> selector;
public KeyComparer(Func<T, TKey> selector) => this.selector = selector;
public int Compare(T x, T y) => selector(x).CompareTo(selector(y));
}
Если у вас есть перекрывающиеся диапазоны, вам, возможно, потребуется выполнить поиск по всем меньшим индексам или использовать более продвинутую структуру поиска.
Ответ №2:
Как насчет такого подхода? Имейте в виду, что при этом учитывается только начальное значение, потому что вы сказали, что у вас есть только пробелы, но нет перекрытия. В этом случае все стало бы еще сложнее.
public static class Program
{
static void Main(string[] args)
{
var random = new Random();
// Create a list with random items.
var source = Enumerable.Range(0, 1000)
.Select(_ => new SortableObject { Start = random.Next(100000) })
.ToList();
// Sort the list by using a dedicated comparer
source.Sort(SortableObjectComparer.Default);
// Define an element to search for
var searchElement = new SortableObject { Start = 17432 };
// Get the index of the best matching item
var found = source.BinarySearch(searchElement, SortableObjectComparer.Default);
if(found < 0)
{
// No exact match, get the best candidates.
var nextElement = source[~found];
var beforeElement = source[~found - 1];
Console.WriteLine($"Element with lower start value: {beforeElement.Value}");
Console.WriteLine($"Element to search {searchElement.Value}");
Console.WriteLine($"Element with higher start value: {nextElement.Value}");
}
else
{
// Exact match
var matchingElement = source[found];
Console.WriteLine($"Element matching: {matchingElement.Value}");
Console.WriteLine($"Element to search {searchElement.Value}");
}
}
}
public class SortableObjectComparer : IComparer<SortableObject>
{
public static readonly IComparer<SortableObject> Default = new SortableObjectComparer();
public int Compare(SortableObject x, SortableObject y)
{
if (ReferenceEquals(x, y))
return 0;
if (ReferenceEquals(x, null))
return -1;
if (ReferenceEquals(y, null))
return 1;
return x.Start.CompareTo(y.Start);
}
}
public class SortableObject
{
public long Start { get; set; }
public long End { get; set; }
public string Value => $"Result {Start}";
}