F# и уменьшение сложности алгоритма поиска пересечений всех
От: zverjuga Беларусь  
Дата: 17.02.20 16:19
Оценка:
есть следующая задачка. найти все возможные пересечения в списке, которые содержит в себе "дороги", то есть объекты, состоящие из координат. нарисовал координатную сетку 8х8 и на ней 4 горизонтальные и 3 вертикальные линии. в коде это выглядит как то так

let rh1 =
        road "rh1" [ // дорога содержит имя и список точек
                        point (1., 7., 0.) [BEGIN; COORDINATE]; // точка point (x,y,z) - координата, [] - содержит массив типов точки (не имеет значения в данном случае)
                        point (3., 7., 0.) [COORDINATE];
                        point (5., 7., 0.) [COORDINATE];
                        point (7., 7., 0.) [COORDINATE];
                        point (8., 7., 0.) [COORDINATE; TRAFFICLIGHT; END];
                    ]

    let rh2 =
        road "rh2" [
                        point (1., 5., 0.) [BEGIN; COORDINATE];
                        point (3., 5., 0.) [COORDINATE];
                        point (5., 5., 0.) [COORDINATE];
                        point (7., 5., 0.) [COORDINATE];
                        point (8., 5., 0.) [COORDINATE; TRAFFICLIGHT; END];
                    ]

    let rh3 =
        road "rh3" [
                        point (1., 3., 0.) [BEGIN; COORDINATE];
                        point (3., 3., 0.) [COORDINATE];
                        point (5., 3., 0.) [COORDINATE];
                        point (7., 3., 0.) [COORDINATE];
                        point (8., 3., 0.) [COORDINATE; TRAFFICLIGHT; END];
                    ]

    let rh4 =
        road "rh4" [
                        point (1., 1., 0.) [BEGIN; COORDINATE];
                        point (3., 1., 0.) [COORDINATE];
                        point (5., 1., 0.) [COORDINATE];
                        point (7., 1., 0.) [COORDINATE];
                        point (8., 1., 0.) [COORDINATE; TRAFFICLIGHT; END];
                    ]

    let rv1 =
        road "rv1" [
                        point (3., 8., 0.) [BEGIN; COORDINATE];
                        point (3., 7., 0.) [COORDINATE];
                        point (3., 5., 0.) [COORDINATE];
                        point (3., 3., 0.) [COORDINATE];
                        point (3., 1., 0.) [COORDINATE];
                        point (3., 0., 0.) [END; COORDINATE; TRAFFICLIGHT];
                    ]

    let rv2 =
        road "rv2" [
                        point (5., 8., 0.) [BEGIN; COORDINATE];
                        point (5., 7., 0.) [COORDINATE];
                        point (5., 5., 0.) [COORDINATE];
                        point (5., 3., 0.) [COORDINATE];
                        point (5., 1., 0.) [COORDINATE];
                        point (5., 0., 0.) [END; COORDINATE; TRAFFICLIGHT];
                    ]

    let rv3 =
        road "rv3" [
                        point (7., 8., 0.) [BEGIN; COORDINATE];
                        point (7., 7., 0.) [COORDINATE];
                        point (7., 5., 0.) [COORDINATE];
                        point (7., 3., 0.) [COORDINATE];
                        point (7., 1., 0.) [COORDINATE];
                        point (7., 0., 0.) [END; COORDINATE; TRAFFICLIGHT];
                    ]


далее вызываю соответствующий метод для получения множества всех возможных пересечений (должно быть 12) пересечений

let crossrods = Road.CrossRoads [rh1; rh2; rh3; rh4; rv1; rv2; rv3]

crossrods
|> Seq.iter (fun x -> printfn "position x: %f, y: %f" x.p.x x.p.y)


код алгоритма привожу ниже (весь тип не привожу, только то, что нужно)

let (|Includes|DoesntIncludes|) (point, list) = 
    let tryPoint = List.tryFind (fun c2 ->
                                    if point == c2.p then
                                        true
                                    else
                                        false) list
    match tryPoint with
        | Some(x) -> Includes x
        | _ -> DoesntIncludes

static member CrossRoads2 r1 r2 =

    r1.pl
    |> List.filter (fun c1 -> match (c1.p, r2.pl) with
                                          | Includes(_) -> true
                                          | _ -> false
                               )

static member CrossRoads (roads: Road list) =

    let rec inCross r roads res = 
        match roads with
        | h::t when h.n <> r.n ->
            let nextRes = Seq.append res (Road.CrossRoads2 r h)
            inCross r t nextRes
        | _::t ->
            inCross r t res
        | [] -> res

    roads
        |> Seq.map (fun x -> inCross x roads Seq.empty)
        |> Seq.collect (fun r -> r)
        |> Seq.distinct // это фиксит проблему двойного результата


код работает корректно, но результаты удваиваются, то есть итоговый список содержал дважды правильные ответы. проблема фиксится путем добавления
|> Seq.distinct


поскрипев мозгами, прихожу к выводу, что двойные результаты обусловлены тем, что алгоритм дважды проходит один и тот же путь (сначала он горизонтальную линии пересекает с вертикальной, а потом вертикальную с горизонтальной). думаю, что по идее проблему также можно пофиксить и мемоизацией, но:

1. оттого сложность алгоритма все равно не уменьшится. все равно сначала нужно получить результат, а потом смотреть мемоизацию
2. опять таки не совсем понятно, как эту мемоизацию в данном случае использовать и будет ли она работать лучше или быстрее дистинкта.

отсюда возникает вопрос, как можно все таки избежать мемоизации и одновременно не просчитывать один и тот же путь дважды?
ну и в принципе? как можно сделать алгоритм менее сложным.

спасибо
проклятый антисутенерский закон
Отредактировано 17.02.2020 16:25 zverjuga . Предыдущая версия . Еще …
Отредактировано 17.02.2020 16:24 zverjuga . Предыдущая версия .
Отредактировано 17.02.2020 16:23 zverjuga . Предыдущая версия .
Отредактировано 17.02.2020 16:23 zverjuga . Предыдущая версия .
Re: F# и уменьшение сложности алгоритма поиска пересечений всех
От: samius Япония http://sams-tricks.blogspot.com
Дата: 17.02.20 18:12
Оценка:
Здравствуйте, zverjuga, Вы писали:

Z>есть следующая задачка. найти все возможные пересечения в списке, которые содержит в себе "дороги", то есть объекты, состоящие из координат. нарисовал координатную сетку 8х8 и на ней 4 горизонтальные и 3 вертикальные линии. в коде это выглядит как то так


Z>отсюда возникает вопрос, как можно все таки избежать мемоизации и одновременно не просчитывать один и тот же путь дважды?

Z>ну и в принципе? как можно сделать алгоритм менее сложным.

Насколько я понял задачу, нужно просто перейти от списка точек в дороге к списку дорог, проходящих через точку. Т.е. для каждой точки каждой дороги, посмотреть, зарегистрирована ли она уже в словаре, и если нет, то добавить точку с дорогой, а если да, то добавить дорогу к списку дорог, проходящей через точку. Сложность линейная, если допустить что добавление и проверка точки в словаре имеют константную сложность.
Re[2]: F# и уменьшение сложности алгоритма поиска пересечений всех
От: zverjuga Беларусь  
Дата: 17.02.20 19:37
Оценка:
Здравствуйте, samius, Вы писали:

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


Z>>есть следующая задачка. найти все возможные пересечения в списке, которые содержит в себе "дороги", то есть объекты, состоящие из координат. нарисовал координатную сетку 8х8 и на ней 4 горизонтальные и 3 вертикальные линии. в коде это выглядит как то так


Z>>отсюда возникает вопрос, как можно все таки избежать мемоизации и одновременно не просчитывать один и тот же путь дважды?

Z>>ну и в принципе? как можно сделать алгоритм менее сложным.

S>Насколько я понял задачу, нужно просто перейти от списка точек в дороге к списку дорог, проходящих через точку. Т.е. для каждой точки каждой дороги, посмотреть, зарегистрирована ли она уже в словаре, и если нет, то добавить точку с дорогой, а если да, то добавить дорогу к списку дорог, проходящей через точку. Сложность линейная, если допустить что добавление и проверка точки в словаре имеют константную сложность.


фактически, это и есть мемоизация. идея понятна, спасибо. основная проблема в том, что сама точка сверяется с другой точкой практически в самом конце алгоритма. то есть с точки зрения производительности думаю улучшения никакого не будет (или минимальное), так как по сути нет разницы, сравнить две точки или проверить, есть ли она уже в словаре.

но похоже, что другого выхода и нет. разве что кардинально не пересмотреть сам принцип работы алгоритма.
проклятый антисутенерский закон
Re[3]: F# и уменьшение сложности алгоритма поиска пересечений всех
От: samius Япония http://sams-tricks.blogspot.com
Дата: 17.02.20 21:46
Оценка: 4 (1)
Здравствуйте, zverjuga, Вы писали:

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


Z>фактически, это и есть мемоизация. идея понятна, спасибо. основная проблема в том, что сама точка сверяется с другой точкой практически в самом конце алгоритма. то есть с точки зрения производительности думаю улучшения никакого не будет (или минимальное), так как по сути нет разницы, сравнить две точки или проверить, есть ли она уже в словаре.


Сравнить две точки или проверить наличие в словаре — словарь будет слегка дороже. Но когда речь идет о поиске точки в списке точек, что происходит в коде — это уже другая алгоритмическая сложность.

Z>но похоже, что другого выхода и нет. разве что кардинально не пересмотреть сам принцип работы алгоритма.

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