Как более быстро искать подходящий диапазон в списке?
От: mDmitriy Россия  
Дата: 25.09.17 11:43
Оценка:
Всем привет!

Есть таблица с записями такой структуры (числовые диапазоны [FirstNumber..EndNumber] не пересекаются):
internal class Range
{
    internal ulong FirstNumber;
    internal ulong EndNumber;
    internal string Data;
|

Записей несколько сотен тысяч
Надо загнать ее в память (в какую-нибудь структуру) и быстро по числу найти запись с диапазоном, в который входит это число
Что посоветуете в смысле алгоритма поиска? Нужен быстрый, перебором не пойдет...
Спасибо
Re: Как более быстро искать подходящий диапазон в списке?
От: LWhisper  
Дата: 25.09.17 12:00
Оценка: 4 (2)
Здравствуйте, mDmitriy, Вы писали:

D>Есть таблица с записями такой структуры (числовые диапазоны [FirstNumber..EndNumber] не пересекаются):

D>Записей несколько сотен тысяч
D>Надо загнать ее в память (в какую-нибудь структуру) и быстро по числу найти запись с диапазоном, в который входит это число
D>Что посоветуете в смысле алгоритма поиска? Нужен быстрый, перебором не пойдет...

Отсортировать записи по диапазонам и искать методом половинного деления.
Re: Как более быстро искать подходящий диапазон в списке?
От: RushDevion Россия  
Дата: 25.09.17 12:03
Оценка:
На вскидку — сбалансированное бинарное дерево и слегка адаптированный алгоритм поиска (чтобы учитывал верхнюю границу).
Поиск будет за O(log n).
Re: Как более быстро искать подходящий диапазон в списке?
От: Qulac Россия  
Дата: 25.09.17 12:35
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Всем привет!


D>Есть таблица с записями такой структуры (числовые диапазоны [FirstNumber..EndNumber] не пересекаются):

D>
D>internal class Range
D>{
D>    internal ulong FirstNumber;
D>    internal ulong EndNumber;
D>    internal string Data;
D>|
D>

D>Записей несколько сотен тысяч
D>Надо загнать ее в память (в какую-нибудь структуру) и быстро по числу найти запись с диапазоном, в который входит это число
D>Что посоветуете в смысле алгоритма поиска? Нужен быстрый, перебором не пойдет...
D>Спасибо

Смотри interval tree
Программа – это мысли спрессованные в код
Re: Как более быстро искать подходящий диапазон в списке?
От: Danchik Украина  
Дата: 25.09.17 14:44
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Всем привет!


D>Есть таблица с записями такой структуры (числовые диапазоны [FirstNumber..EndNumber] не пересекаются):

D>
D>internal class Range
D>{
D>    internal ulong FirstNumber;
D>    internal ulong EndNumber;
D>    internal string Data;
D>|
D>

D>Записей несколько сотен тысяч
D>Надо загнать ее в память (в какую-нибудь структуру) и быстро по числу найти запись с диапазоном, в который входит это число
D>Что посоветуете в смысле алгоритма поиска? Нужен быстрый, перебором не пойдет...
D>Спасибо

Посмотри CompositeRange в библиотеке CodeJam.
Там же экстеншин GetIntersection должен выдать пересечнеие.
Я не пользовался, может и перформанс на таких обьемах не дать. Но алгоритмы пересечнений сравнений и тд. там присутствуют.
Re: Как более быстро искать подходящий диапазон в списке?
От: Sinix  
Дата: 25.09.17 18:03
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Всем привет!

D>Что посоветуете в смысле алгоритма поиска? Нужен быстрый, перебором не пойдет...
D>Спасибо

В CodeJam действительно есть класс для работы с диапазонами, CompositeRange, но он недоделан в плане производительности, руки не доходят.
Зато в CodeJam.Experimental есть то, что тебе нужно — IntervalTree.
Можно не тащить библиотеку, а просто допилить код под твою структуру данных. Всё, что требуется — чтобы входные данные были отсортированы по левой границе, затем по правой.

В плане перфоманса в принципе норм, но можно и ускорить, если заморочиться, перфтесты:
// 10k ranges
          Method |      Mean |    StdDev | Scaled | Scaled-StdDev | GcAllocations |
---------------- |----------:|----------:|-------:|--------------:|--------------:|
       Intersect | 38.232 us | 5.2904 us |   1.00 |          0.00 |         208 B |
   IntersectTree |  3.665 us | 0.2707 us |  0.097 |        0.0162 |         176 B |
 IntersectCostin |  9.058 us | 0.9607 us |   0.24 |         0.044 |       5.36 KB |

// 100k ranges
          Method |         Mean |     StdDev | Scaled | Scaled-StdDev | GcAllocations |
---------------- |-------------:|-----------:|-------:|--------------:|--------------:|
       Intersect | 3,740.194 us | 87.1295 us |   1.00 |          0.00 |         208 B |
   IntersectTree |     8.625 us |  0.3551 us | 0.0023 |      0.000109 |         176 B |
 IntersectCostin |    20.239 us |  1.7635 us | 0.0054 |       0.00048 |      11.05 KB |

us — микросекунды

Intersect — CompositeRange<T>, там O(n) со всеми вытекающими.
IntersectTree — тот самый IntervalTree
IntersectCostin — типовой код из интернета aka "не умею в перфоманс", взят отсюда: https://code.google.com/archive/p/intervaltree/
См на аллокации. 5-10kb на каждый вызов — ну ппц же
Re[2]: Как более быстро искать подходящий диапазон в списке?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 26.09.17 06:43
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Зато в CodeJam.Experimental есть то, что тебе нужно — IntervalTree.


А чего оно в experimental?
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re: Как более быстро искать подходящий диапазон в списке?
От: VladCore  
Дата: 26.09.17 07:24
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Всем привет!


D>Есть таблица с записями такой структуры (числовые диапазоны [FirstNumber..EndNumber] не пересекаются):

D>
D>internal class Range
D>{
D>    internal ulong FirstNumber;
D>    internal ulong EndNumber;
D>    internal string Data;
D>|
D>

D>Записей несколько сотен тысяч
D>Надо загнать ее в память (в какую-нибудь структуру) и быстро по числу найти запись с диапазоном, в который входит это число
D>Что посоветуете в смысле алгоритма поиска? Нужен быстрый, перебором не пойдет...
D>Спасибо

https://msdn.microsoft.com/ru-ru/library/a1s5syxa(v=vs.110).aspx
public int BinarySearch(
    int index,
    int count,
    T item,
    IComparer<T> comparer
)


В компарере сравниваете только FirstNumber. Сможете написать его сами?
Метод вернет или индекс элемента у которого some == FirstNumber, или индекс ближайшего элемента.
Если второе то проверте ближайший элемент списка.

P.S. Это если апдейтов нет между поисками. Если апдейты есть, то тут уже написали — ищите реализацию B-дерева про которое Sinix выше написал.

P.P.S. по моему сначала вам надо бы хотя бы википедию прочитать про бинарный поиск и бинарные деревья.
Отредактировано 26.09.2017 7:38 VladCore . Предыдущая версия . Еще …
Отредактировано 26.09.2017 7:32 VladCore . Предыдущая версия .
Re[3]: Как более быстро искать подходящий диапазон в списке?
От: Sinix  
Дата: 26.09.17 11:55
Оценка:
Здравствуйте, AndrewVK, Вы писали:

S>>Зато в CodeJam.Experimental есть то, что тебе нужно — IntervalTree.


AVK>А чего оно в experimental?

Дакакобычно, руки не доходят.
На неделе дойдёт, надеюсь)
Re: конфликт сборок
От: Codechanger Россия  
Дата: 26.09.17 14:24
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Всем привет!


D>Есть таблица с записями такой структуры (числовые диапазоны [FirstNumber..EndNumber] не пересекаются):

D>
D>internal class Range
D>{
D>    internal ulong FirstNumber;
D>    internal ulong EndNumber;
D>    internal string Data;
D>|
D>

D>Записей несколько сотен тысяч
D>Надо загнать ее в память (в какую-нибудь структуру) и быстро по числу найти запись с диапазоном, в который входит это число
D>Что посоветуете в смысле алгоритма поиска? Нужен быстрый, перебором не пойдет...
D>Спасибо

Судя по косвенным признакам, ищем бины. В целом делается как : массив бьется на несколько десятков партишнов, у которых задается минимальный/максимальный диапазон. Поиск в два этапа — сначала находим нужный партишн, потом ищем в нем прямым перебором.
Re[2]: конфликт сборок
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 26.09.17 18:26
Оценка:
Здравствуйте, Codechanger, Вы писали:

C>Судя по косвенным признакам, ищем бины. В целом делается как : массив бьется на несколько десятков партишнов, у которых задается минимальный/максимальный диапазон. Поиск в два этапа — сначала находим нужный партишн, потом ищем в нем прямым перебором.


Ну то есть interval tree врукопашную. Советовать подобное хардкодное решение можно только зная распределение диапазонов в реальной задаче.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re: Как более быстро искать подходящий диапазон в списке?
От: artelk  
Дата: 27.09.17 09:25
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>числовые диапазоны [FirstNumber..EndNumber] не пересекаются

Достаточно SortedSet с ключем по EndNumber.
Для поиска GetViewBetween + FirstOrDefault.
Re[2]: Как более быстро искать подходящий диапазон в списке?
От: artelk  
Дата: 27.09.17 09:31
Оценка:
Здравствуйте, Sinix, Вы писали:

S>В CodeJam действительно есть класс для работы с диапазонами, CompositeRange, но он недоделан в плане производительности, руки не доходят.

S>Зато в CodeJam.Experimental есть то, что тебе нужно — IntervalTree.

http://blogs.solidq.com/en/sqlserver/static-relational-interval-tree
Вот такую штуку, наверно, имеет смысл и для коллекции в оперативной памяти провернуть.
Re[3]: конфликт сборок
От: Codechanger Россия  
Дата: 27.09.17 10:01
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Ну то есть interval tree врукопашную. Советовать подобное хардкодное решение можно только зная распределение диапазонов в реальной задаче.


Если это то, о чем я думаю, то там распределение более-менее известно.
Re[4]: конфликт сборок
От: mDmitriy Россия  
Дата: 27.09.17 12:35
Оценка:
Здравствуйте, Codechanger, Вы писали:
C>Если это то, о чем я думаю, то там распределение более-менее известно.
нет, это не бины
это ip-адреса в ulong
распределение неизвестно совсем, так как диапазоны приходят снаружи
Re[2]: Как более быстро искать подходящий диапазон в списке?
От: mDmitriy Россия  
Дата: 27.09.17 12:37
Оценка:
Здравствуйте, artelk, Вы писали:
A>Достаточно SortedSet с ключем по EndNumber.
A>Для поиска GetViewBetween + FirstOrDefault.
отсюда можно подробнее?
аргументом поиска является число, результатом — одна запись, в диапазон которой это число входит
Re[5]: конфликт сборок
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 27.09.17 20:04
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>это ip-адреса в ulong


А почему в ulong?
Вобщем, два варианта:
1) Если диапазоны пересекающиеся и изменять их нельзя — interval tree
2) Если непересекающиеся (пересекающиеся можно нормализовать к такому виду, в алгоритмах обсуждали алгоритм, Кодт посоветовал вполне годный с небольшими доработками вариант) — количество вариантов расширяется, банальный табличный вариант с О(1) но ужором памяти, binary search и хеш-таблицы.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[6]: конфликт сборок
От: mDmitriy Россия  
Дата: 28.09.17 08:03
Оценка:
Здравствуйте, AndrewVK, Вы писали:
AVK>А почему в ulong?
ну пока в ulong
там еще про IP6 надо думать в перспективе
AVK>Вобщем, два варианта:
AVK>2) Если непересекающиеся (пересекающиеся можно нормализовать к такому виду, в алгоритмах обсуждали алгоритм, Кодт посоветовал вполне годный с небольшими доработками вариант) — количество вариантов расширяется, банальный табличный вариант с О(1) но ужором памяти, binary search и хеш-таблицы.
непересекающиеся
склоняюсь к binary search и кешированию найденного
память да, пожрет
Re[7]: конфликт сборок
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 28.09.17 20:04
Оценка:
Здравствуйте, mDmitriy, Вы писали:

AVK>>А почему в ulong?

D>ну пока в ulong
D>там еще про IP6 надо думать в перспективе

На ipv6 ulong все равно не хватит, там в два раза больше.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
Re[3]: Как более быстро искать подходящий диапазон в списке?
От: artelk  
Дата: 28.09.17 23:15
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Здравствуйте, artelk, Вы писали:

A>>Достаточно SortedSet с ключем по EndNumber.
A>>Для поиска GetViewBetween + FirstOrDefault.
D>отсюда можно подробнее?
D>аргументом поиска является число, результатом — одна запись, в диапазон которой это число входит

Пример:
Отсортированные по EndNumber (и одновременно по FirstNumber, т.к. непересекающиеся) интервалы:
[1..5], [7..13], [14..25], [30..42]

Ищем интервал, включающий точку 20.
intervals.GetViewBetween(new Range{EndNumber=20}, intervals.Max) == [14..25], [30..42]
Смотрим только первый [14..25]. Подходит, возвращаем.

Ищем интервал, включающий точку 6.
intervals.GetViewBetween(new Range{EndNumber=6}, intervals.Max) == [7..13], [14..25], [30..42]
Смотрим только первый [7..13]. Не подходит, т.к. FirstNumber у него больше 6.

Т.е.
var intervals = new SortedSet<Range>(Comparer<Range>.Create((x,y) => x.EndNumber.CompareTo(y.EndNumber)));

Range Find(int n)
{
  if(intervals.Count == 0) return null;
  var range = intervals.GetViewBetween(new Range{EndNumber=n}, intervals.Max).FirstOrDefault();
  return range != null && range.FirstNumber <= n ? range = null;
}


Писал в браузере, перепроверь тестом.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.