Название: Выделить кластеры Отправлено: Igors от Март 02, 2012, 12:56 Добрый день
Есть контейнер и есть ф-ция принимающая 2 элемента и возвращающая true если они "связаны" напр Код Отношение связи не транзитивно, напр Код
Нужно разделить исходный контейнер на какое-то число (как получится) новых контейнеров (не удаляя и не создавая новых элементов) так чтобы выполнялись условия: - для любых 2 элементов из разных контейнеров Linked вернет false - для любого элемента в его контейнере найдется хотя бы один с которым Linked вернет true Пример: в исходном контейнере точки принадлежащие кубу. Ф-ция Linked сравнивает угол между нормалями, результат - 6 новых контейнеров, в каждом из которых точки на 1 грани куба. А вот если бы точки принадлежали сфере (вместо куба) то вероятно ничего бы не поделилось Можно ли это сделать без унылого перебора - ну или хоть как-то ускорить? Спасибо Название: Re: Выделить кластеры Отправлено: Tonal от Март 05, 2012, 12:23 Это более точно называется нахождением связности.
Детально описано в первой главе первого тома Р. Седжвика «Фундаментальные алгоритмы на С++». Вот на хабре статейка: http://habrahabr.ru/blogs/algorithm/104772/ Название: Re: Выделить кластеры Отправлено: Tonal от Март 06, 2012, 09:39 Ну да при условии, что все элементы различны мы можем написать быстрый алгоритм связности без использования деревьев - только несколько списочков - очередей. :)
Идея в том, что как если экземпляр попал в какое-то множество (контейнер) его не нужно тестировать (вызывать Linked) с остальными членами этого множества. Код для иллюстрации: Код Компиляция и запуск: Код: $ ghc Cohesion.hs Название: Re: Выделить кластеры Отправлено: Igors от Март 06, 2012, 13:03 Это более точно называется нахождением связности. Фундаментальную теорию пока не читал. Статейку посмотрел но пока не очень понял - ну это нормально, спасибоДетально описано в первой главе первого тома Р. Седжвика «Фундаментальные алгоритмы на С++». Вот на хабре статейка: http://habrahabr.ru/blogs/algorithm/104772/ Идея в том, что как если экземпляр попал в какое-то множество (контейнер) его не нужно тестировать (вызывать Linked) с остальными членами этого множества. Это понятно. Как сделано сейчас (псевдокод)Код Ну используются списки (вместо векторов) но суть та же. Пока не вижу причем здесь дерево и как ускориться Название: Re: Выделить кластеры Отправлено: Tonal от Март 07, 2012, 09:03 Контейнер, куда добавлять можно вовсе не искать.
Мы берём первый элемент x и делим оставшиеся на тех кто входит в группу "таких как x" (текущая группа) и остаток. Далее, для каждого элемента из текущей группы повторяем с остатком то же самое добавляя новые элементы в текущую группу и уменьшая остаток. Как только все все элементы из текущей группы обработаны - переходим в начало. :) Таким образом, мы всегда знаем контейнер в который добавляем. Вот это в коде, смотри: Код
Название: Re: Выделить кластеры Отправлено: Igors от Март 09, 2012, 12:49 Мы берём первый элемент x и делим оставшиеся на тех кто входит в группу "таких как x" (текущая группа) и остаток. Ну и что мы выиграли? Пусть простейший случай: точки принадлежат 2 граням куба. Начали очень хорошо - напр первая тысяча точек вся на первой грани. Добавили в первый выходной контейнер. И вот теперь попалась точка на второй грани. Сделали тысячу проверок - никак в первый контейнер не добавить, создаем второй. Следующая точка опять на второй грани. Да, мы может и быстро добавили ее во второй контейнер, НО никто не обещал что она не может входить и в первый - опять полетела тысяча проверок, вот где тормоза. Если я что-то не понял - поясните.Далее, для каждого элемента из текущей группы повторяем с остатком то же самое добавляя новые элементы в текущую группу и уменьшая остаток. Как только все все элементы из текущей группы обработаны - переходим в начало. :) Таким образом, мы всегда знаем контейнер в который добавляем. Название: Re: Выделить кластеры Отправлено: Tonal от Март 11, 2012, 10:19 1 .Берём первую точку и добавляем её в выходной первый контейнер.
Теперь тестируем её со всеми точками и добавляем в этот же контейнер всю 1000 с первой грани. 2. Далее, берём какую-нибудь точку добавленную в выходной контейнер (но не первую). Её уже не нужно тестировать не только с первой, но и с остальными 998-ю точками контейнера Стало быть тестируем её и точки не попавшие в контейнер - если кого-то нашли добавляем в выходной контейнер. 3. Опять берём ещё не обработанную точку из выходного контейнера и проделываем то же самое, что со второй. 4. Когда у нас будет обработан таким образом весь выходной контейнер, в остатке не останится ни одной точки которая могла бы к нему принадлежать. Тут мы или заканчиваем работу - если в остатке ничего нет, или возвращаемся к шагну 1. :) |