Алгоритм/структура данных для определения того, какие из множества множеств являются подмножествами другого множества

Аннотация Описание:

У меня есть набор строк, назовите его «активным набором», а набор наборов строк — назовите его «возможный набор». Когда к активному набору добавляется новая строка, наборы из возможного набора могут внезапно стать подмножествами активного набора, потому что в активном наборе не хватает только этой строки, чтобы быть надмножеством одного из возможных. Мне нужен алгоритм, чтобы эффективно находить их, когда я добавляю новую строку в активный набор. Бонусные баллы, если та же самая структура данных позволяет мне эффективно определять, какие из этих возможных наборов недействительны (больше не являются подмножеством), когда строка удаляется из активного набора.

(Причина, по которой я сформулировал проблему, описанную ниже, в терминах наборов и подмножеств строк в аннотации выше, заключается в том, что язык, на котором я пишу это (Io), является динамически типизированным. Объекты имеют поле «тип», но это строка с именем типа объекта в ней.)

Фон:

В моем игровом движке у меня есть GameObjects, к которым можно добавить несколько типов объектов Representation. Например, если GameObject имеет физическое присутствие, к нему может быть добавлено PhysicsRepresentation (или нет, если это не сплошной объект). К нему могут быть добавлены различные типы GraphicsRepresentations, такие как сетка или эффект частиц (и вы можете иметь более одного, если у вас есть несколько визуальных эффектов, прикрепленных к одному и тому же игровому объекту).

Смысл этого в том, чтобы разделить подсистемы, но вы не можете полностью разделить все: например, когда GameObject имеет как PhysicsRepresentation, так и GraphicsRepresentation, что-то должно создать 3-й объект, который соединяет положение GraphicsRepresentation с местоположением объекта. Представление физики. Чтобы служить этой цели, сохраняя при этом все компоненты отдельными, у меня есть объекты Interaction. Объект Interaction инкапсулирует сквозные знания о том, как должны взаимодействовать два компонента системы.

Но чтобы защитить GameObject от необходимости слишком много знать о представлениях и взаимодействиях, GameObject просто предоставляет общий реестр, в котором объекты-прототипы взаимодействия могут регистрироваться для вызова, когда в GameObject присутствует определенная комбинация представлений. Когда к GameObject добавляется новое Представление, GameObject должен просмотреть свой реестр и активировать только те объекты Interaction, которые вновь активируются благодаря наличию нового Представления, а также существующие Представления.

Я просто застрял на том, какую структуру данных следует использовать для этого реестра и как его искать.

Исправления:

Наборы строк не обязательно сортируются, но я могу сохранить их отсортированными.

Хотя Взаимодействие чаще всего происходит между двумя Представлениями, я не хочу ограничивать его этим; У меня должна быть возможность иметь взаимодействия, которые запускаются с 3 или более различными представлениями, или даже взаимодействия, которые запускаются только на основе 1 представления.

Я хочу оптимизировать это для максимально быстрого добавления/удаления представлений.

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


person Dennis    schedule 31.12.2011    source источник
comment
Какова максимальная мощность ваших наборов?   -  person Sergey Kalinichenko    schedule 31.12.2011
comment
Действительно ли важно, насколько быстро вы можете добавлять и удалять представления? Как часто они действительно будут меняться во время игры? Десяток раз при запуске в качестве представлений добавляются, затем один раз каждый раз, когда пользователь нажимает кнопку с надписью Переслать дисплей на мой телефон, а затем Снова воспроизвести на компьютере ? Я должен предположить, что события маршрутизации вверх и вниз по слоям будут происходить миллионы раз для каждого уровня добавления/удаления представления...   -  person sarnold    schedule 31.12.2011
comment
@sarnold — всякий раз, когда GameObject создается или уничтожается, добавляется / удаляется несколько представлений. Таким образом, каждый раз, когда появляется новая пуля, каждый раз, когда разыгрывается заклинание и т. д., вы можете добавлять 3 или 4 представления при создании игрового объекта и удалять их при уничтожении игрового объекта (чтобы объекты взаимодействия могли выполнять очистку, если необходимый).   -  person Dennis    schedule 31.12.2011
comment
@dasblinkenlight - Мощность моих сетов невелика. Я не знаю заранее, сколько видов когда-либо будет, но сейчас будет примерно 4 разных вида представления и 4 разных вида взаимодействия. Так что да (как сказал Сарнольд), это может не иметь большого значения.   -  person Dennis    schedule 31.12.2011
comment
Но я на самом деле все еще интересуюсь этой проблемой в абстрактном виде, потому что после этого у меня есть планы сделать таким образом более крупную систему. Это будет планирующий ИИ, который будет использовать ту же архитектуру объектов + взаимодействия; в основном вы бы сказали планирующему ИИ, что у меня есть эти объекты, которые поддерживают эти взаимодействия, и придумать способы их объединения. Мощность этих наборов будет намного выше; но мне не нужно было бы находить все взаимодействия в случае планирования ИИ, только некоторые.   -  person Dennis    schedule 31.12.2011
comment
Круто, спасибо за объяснение. :) Удачного программирования!   -  person sarnold    schedule 01.01.2012


Ответы (2)


Если ваши наборы действительно малы, лучшим представлением будет использование битовых наборов. Во-первых, вы строите карту из строк в последовательные целые числа 0..N, где N — количество различных строк. Затем вы строите свои наборы с помощью побитового ИЛИ 1<<k в число. Это позволяет превратить ваши операции над множествами в побитовые операции, которые выполняются очень быстро (пересечение — это &, объединение — это | и т. д.).

Вот пример: допустим, у вас есть два набора, A={quick, brown, fox} и B={brown, lazy, dog}. Во-первых, вы строите карту строки в число, например:

quick - 0
brown - 1
fox   - 2
lazy  - 3
dog   - 4

Тогда ваши наборы станут A=00111b и B=11010b. Их пересечение — A&B = 00010b, а их объединение — A|B = 11111b. Вы знаете, что набор X является подмножеством набора Y, если X == X&Y.

person Sergey Kalinichenko    schedule 31.12.2011
comment
Я действительно разрывался, отметить ли ваш ответ как принятый или ответ Макдауэллы. Ваше решение проще и, безусловно, является лучшим выбором для небольшого N. В конечном итоге я решил отметить ответ Макдауэллы, потому что я действительно хотел знать, есть ли способ сделать это, не требующий тестирования каждого из них, и его ссылку на Алгоритм Рете — это общий алгоритм, на который я надеялся. Я просто хотел сообщить вам, что с таким же успехом могу считать и этот ответ принятым. - person Dennis; 31.12.2011
comment
@Dennis Использование Rete для набора из четырех ... восьми объектов звучит как излишество огромных масштабов: удар кувалдой по мухе рядом с ним выглядел бы ненормально :) Однажды я попытался реализовать Rete (как забавное упражнение) и быстро научился что это сложно. Я имею в виду, серьезно сложный. В любом случае, удачи! - person Sergey Kalinichenko; 31.12.2011
comment
Я думаю ты прав; Я изменил принятый ответ на этот; было бы несправедливо представить фактический простой случай, который мне нужно решить, а затем отклонить ответ, потому что это неправильная сложность большого O для общего случая. Я тоже думал о битовых флагах в целых числах, прежде чем задал вопрос, поэтому я думаю, что это подтверждение того, что это был правильный путь с самого начала. - person Dennis; 02.01.2012

Один из способов сделать это — сохранить для каждого подмножества подсчет того, сколько его строк не было в основном наборе, и сопоставить строки со списками подмножеств, содержащих эту строку, чтобы вы могли обновлять подсчеты, когда вы добавляете или удаляете новую строку в активном наборе и замечаете, когда счетчик падает до нуля.

Эта проблема напоминает мне запуск правил в системе, основанной на правилах, когда факт становится истинным, что соответствует новой строке, добавляемой в активный набор. Многие из этих систем используют http://en.wikipedia.org/wiki/Rete_algorithm. http://www.jboss.org/drools/drools-expert.html является системой с открытым исходным кодом, основанной на правилах, хотя в наши дни похоже, что вокруг нее много корпоративных систем.

person mcdowella    schedule 31.12.2011
comment
Спасибо за ссылку на алгоритм Rete. Это, вероятно, излишне для простого случая, но я, возможно, захочу использовать его в будущем для общего случая. - person Dennis; 02.01.2012