Сканирующая прямая
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 07.09.23 07:02
Оценка:
Приветствую.
В табличке накопились временные отрезки и сейчас хочется посчитать количество дней, которые покрывались хотя бы одним отрезком (начало или конец отрезка — включительно).
Погуглил — находится алгоритм "сканирующей прямой". Например, тут: Длина объединения отрезков
1. Получается, нужно делать выборку всех данных? Так данных может быть много, то, думаю, будет проще не делать предварительную сортировку, а тупо идти по дням, выбирая самые длинные отрезки.
2. Может есть лучше алгоритм?
Вселенная бесконечна как вширь, так и вглубь.
Re: Сканирующая прямая
От: BlackEric http://black-eric.lj.ru
Дата: 07.09.23 10:42
Оценка:
Здравствуйте, Real 3L0, Вы писали:

R3>Приветствую.

R3>В табличке накопились временные отрезки и сейчас хочется посчитать количество дней, которые покрывались хотя бы одним отрезком (начало или конец отрезка — включительно).
R3>Погуглил — находится алгоритм "сканирующей прямой". Например, тут: Длина объединения отрезков
R3>1. Получается, нужно делать выборку всех данных? Так данных может быть много, то, думаю, будет проще не делать предварительную сортировку, а тупо идти по дням, выбирая самые длинные отрезки.
R3>2. Может есть лучше алгоритм?

Тут сложно что-то подсказать, не видя как у вас организованы данные
https://github.com/BlackEric001
Re[2]: Сканирующая прямая
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 07.09.23 12:57
Оценка:
Здравствуйте, BlackEric, Вы писали:

BE>Тут сложно что-то подсказать, не видя как у вас организованы данные


SQLite. Одна таблица. Два поля в формате DATETIME.
Вселенная бесконечна как вширь, так и вглубь.
Re[3]: Сканирующая прямая
От: BlackEric http://black-eric.lj.ru
Дата: 07.09.23 14:32
Оценка:
Здравствуйте, Real 3L0, Вы писали:

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


BE>>Тут сложно что-то подсказать, не видя как у вас организованы данные


R3>SQLite. Одна таблица. Два поля в формате DATETIME.


Насколько я понимаю, если отрезки перекрываются, без сортировки нормально работать не будет.
https://github.com/BlackEric001
Re[4]: Сканирующая прямая
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 07.09.23 14:50
Оценка:
Здравствуйте, BlackEric, Вы писали:

BE>Насколько я понимаю, если отрезки перекрываются, без сортировки нормально работать не будет.


Т.е. другого алгоритма нет?
Вселенная бесконечна как вширь, так и вглубь.
Re[4]: Сканирующая прямая
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 07.09.23 14:57
Оценка:
Здравствуйте, BlackEric, Вы писали:

BE>Насколько я понимаю, если отрезки перекрываются, без сортировки нормально работать не будет.


Если количество записей большое, то может быть попадос по памяти.

Если уж всё равно идти перебором по всем записям то, по-моему, проще без сортировки, а тупо поиском:
1. берём самый первый отрезок (и самый длинный)
2. считаем его дни
3. берём отрезок, в который попадает конец предыдущего отрезка
3.1. если такого нет, то берём следующий ближайший отрезок (и самый длинный)
4. считаем его дни
5. go to 3.

Тут не нужно на клиента выгружать все отрезки, но нужно больше обращаться в БД.
Вселенная бесконечна как вширь, так и вглубь.
Re[5]: Сканирующая прямая
От: BlackEric http://black-eric.lj.ru
Дата: 07.09.23 15:35
Оценка:
Здравствуйте, Real 3L0, Вы писали:


R3>Тут не нужно на клиента выгружать все отрезки, но нужно больше обращаться в БД.


Ну да, сортировать в бд, как иначе то искать?
https://github.com/BlackEric001
Re: Сканирующая прямая
От: Olaf Россия  
Дата: 08.09.23 07:50
Оценка: +1
Здравствуйте, Real 3L0, Вы писали:

R3>Приветствую.

R3>....
R3>2. Может есть лучше алгоритм?

А в чем заключается задача? Не уверен, что правильно понял.

Существует набор отрезков времени t1 и t2. Необходимо найти непрерывную последовательность таких отрезков?
Re[2]: Сканирующая прямая
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 08.09.23 08:17
Оценка:
Здравствуйте, Olaf, Вы писали:

O>А в чем заключается задача? Не уверен, что правильно понял.

O>Существует набор отрезков времени t1 и t2. Необходимо найти непрерывную последовательность таких отрезков?

посчитать количество дней, которые покрывались хотя бы одним отрезком
Вселенная бесконечна как вширь, так и вглубь.
Re[5]: Сканирующая прямая
От: paucity  
Дата: 08.09.23 15:14
Оценка:
Здравствуйте, Real 3L0, Вы писали:

R3>Если уж всё равно идти перебором по всем записям то, по-моему, проще без сортировки, а тупо поиском:

R3>1. берём самый первый отрезок (и самый длинный)

А чтоб отрезок был и первый и самый длинный, что обеспечит?
Re[6]: Сканирующая прямая
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 08.09.23 15:24
Оценка:
Здравствуйте, paucity, Вы писали:

P>А чтоб отрезок был и первый и самый длинный, что обеспечит?


select top 1 start, finish
from table
order by start, finish desc
Вселенная бесконечна как вширь, так и вглубь.
Re: Сканирующая прямая
От: tsitan  
Дата: 08.09.23 15:36
Оценка: 2 (1)
Здравствуйте, Real 3L0, Вы писали:

R3>Приветствую.

R3>В табличке накопились временные отрезки и сейчас хочется посчитать количество дней, которые покрывались хотя бы одним отрезком (начало или конец отрезка — включительно).
R3>Погуглил — находится алгоритм "сканирующей прямой". Например, тут: Длина объединения отрезков
R3>1. Получается, нужно делать выборку всех данных? Так данных может быть много, то, думаю, будет проще не делать предварительную сортировку, а тупо идти по дням, выбирая самые длинные отрезки.
R3>2. Может есть лучше алгоритм?

я бы пробовал 2 варианта:
первый — с индексированными столбцами
Создаём временную таблицу с календарём
DAY_ID DAY_start DAY_end
01/01/2020 — 01/01/2020 00:00:00 — 01/01/2020 23:59:59
...
31/12/2023 — 31/12/2023 00:00:00 — 31/12/2023 23:59:59
Выбирем день если начало или конец дня между началом и концом отрезков.
На год 366 записей. Соответственно 366+366 индексированных поисков на год.
На выходе — дни по которым были записи.

Второй
Группировка по начальному дню и максимальному для начала дню окончания.

Для записей типа
01/01/2020 07:11:22 — 23/02/2020 14:36:27
01/01/2020 12:36:45 — 08/03/2020 14:36:27

вводим функции начала и конца дня
для начала обрезаем время а концу устанавливаем окончание дня

группируем по началу дня начала и максимальному окончанию дня окончания
На выходе группировки получим для каждого дня начала
01/01/2020 00:00:00 — 08/03/2020 23:59:59
для года на выходе не более 365 записей.
В конце уже можно по первому варианту по календарю пройтись.


если точность нужна на доли секунды, то рассмотреть вариант с полуоткрытыми интервалами [начало; конец).
DAY_ID DAY_start DAY_end
01/01/2020 — 01/01/2020 00:00:00 — 02/01/2020 00:00:00
Re[7]: Сканирующая прямая
От: paucity  
Дата: 08.09.23 16:30
Оценка:
Здравствуйте, Real 3L0, Вы писали:

P>>А чтоб отрезок был и первый и самый длинный, что обеспечит?


R3>
R3>select top 1 start, finish
R3>from table
R3>order by start, finish desc
R3>


Я конечно давно не брал в руки шашек, но разве Order — не сортировка, которой ты хочешь избежать?
Re[8]: Сканирующая прямая
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 08.09.23 20:04
Оценка:
Здравствуйте, paucity, Вы писали:

P>Я конечно давно не брал в руки шашек, но разве Order — не сортировка, которой ты хочешь избежать?


Я хотел избежать выгрузки всех данных на клиента. А это — просто получение одной записи.
Вселенная бесконечна как вширь, так и вглубь.
Re[3]: Сканирующая прямая
От: Olaf Россия  
Дата: 08.09.23 20:28
Оценка:
Здравствуйте, Real 3L0, Вы писали:

R3>посчитать количество дней, которые покрывались хотя бы одним отрезком


Мы можем посчитать количество дней на одном отрезке t2 — t1 в днях.
Не могу понять, как связаны остальные отрезки друг с другом в рамках этой задачи, т.е. что нужно сделать дальше, зная разницу в днях на каждом отрезке.
Re[9]: Сканирующая прямая
От: watchmaker  
Дата: 08.09.23 23:13
Оценка: 4 (1) +1
Здравствуйте, Real 3L0, Вы писали:

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


P>>Я конечно давно не брал в руки шашек, но разве Order — не сортировка, которой ты хочешь избежать?


R3>Я хотел избежать выгрузки всех данных на клиента. А это — просто получение одной записи.


Так и алгоритм "сканирующей прямой" тоже на клиент вернёт тебе только одно число, а сортировка и обработка будет на стороне sql сервера делаться. Непонятно, что ещё на клиента ты хочешь получать.

Пример для таблицы Intervals(A, B) с полуоткрытыми целочисленными интервалами:
WITH 
 Bounds(Point, Diff) AS (
     SELECT A AS Point, 1 as Diff from Intervals
     UNION ALL 
     SELECT B AS Point, -1 as Diff from Intervals
 ),
 Balance(IntervalBalance, IntervalLength) AS (
    SELECT 
        SUM(Diff) OVER w0 AS IntervalBalance,
        Point - lag(Point) OVER w0 as IntervalLength
    FROM Bounds
    WINDOW w0 AS (ORDER BY Point ASC, Diff DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW)
 )
SELECT SUM(IntervalLength) As LengthOfUnion FROM Balance WHERE IntervalBalance > 0;

(в зависимости от объёма и движка, на практике можно ещё PARTITION BY в окно, очевидно, добавить, чтобы обрабатывать непересекающиеся части таблицы параллельно, но идея и так должна быть понятна)
Re[2]: Сканирующая прямая
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 09.09.23 09:26
Оценка:
Здравствуйте, tsitan, Вы писали:

T>первый — с индексированными столбцами

T>Создаём временную таблицу с календарём

Да, думал про создание дополнительной таблицы с днями. Вариант всем хорош, кроме того, что нужно её поддерживать в актуальном состоянии.

T>Второй

T>Группировка по начальному дню и максимальному для начала дню окончания.
T>для года на выходе не более 365 записей.

Это как сделать? Я про группировку.
Вселенная бесконечна как вширь, так и вглубь.
Re[10]: Сканирующая прямая
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 09.09.23 09:32
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Так и алгоритм "сканирующей прямой" тоже на клиент вернёт тебе только одно число, а сортировка и обработка будет на стороне sql сервера делаться.


Нет. Там в алгоритме нужно идти по прямой времени и делать n++, если отрезок начался, и n--, если отрезок кончился.
Соответственно, если в дне n>0, то этот день считаем.
Вселенная бесконечна как вширь, так и вглубь.
Re[11]: Сканирующая прямая
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.09.23 12:35
Оценка: 4 (1)
Здравствуйте, Real 3L0, Вы писали:
R3>Нет. Там в алгоритме нужно идти по прямой времени и делать n++, если отрезок начался, и n--, если отрезок кончился.
R3>Соответственно, если в дне n>0, то этот день считаем.
Решение от watchmaker делает именно это. Только на стороне сервера, а не на стороне клиента.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Сканирующая прямая
От: Real 3L0 Россия http://prikhodko.blogspot.com
Дата: 30.09.23 11:08
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Пример для таблицы Intervals(A, B) с полуоткрытыми целочисленными интервалами:

W>
WITH 
W> Bounds(Point, Diff) AS (
W>     SELECT A AS Point, 1 as Diff from Intervals
W>     UNION ALL 
W>     SELECT B AS Point, -1 as Diff from Intervals
W> ),
W> Balance(IntervalBalance, IntervalLength) AS (
W>    SELECT 
W>        SUM(Diff) OVER w0 AS IntervalBalance,
W>        Point - lag(Point) OVER w0 as IntervalLength
W>    FROM Bounds
W>    WINDOW w0 AS (ORDER BY Point ASC, Diff DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW)
W> )
W>SELECT SUM(IntervalLength) As LengthOfUnion FROM Balance WHERE IntervalBalance > 0;
W>


Спасибо, начинает доходить идея.
Правда, я с оконными функциями не дружу.

В таком виде запрос возвращает 0, хотя данные точно есть.
A и B — имеют тип datetime (sqlite).
Нужно учитывать, что если:
* A и B находятся в одном и том же дне, то этот день тоже считаем;
* A и B находятся в разных днях, то эти дни считаем включительно.
Вселенная бесконечна как вширь, так и вглубь.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.