Создание комбинаций, которые не имеют больше одного пересекающегося элемента - PullRequest
17 голосов
/ 02 июня 2010

Я ищу создание особого типа комбинации, в которой нет двух наборов, имеющих более одного пересекающегося элемента. Позвольте мне объяснить на примере:

Допустим, у нас есть набор из 9 букв, который содержит A, B, C, D, E, F, G, H и I

Если вы создадите стандартные неповторяющиеся комбинации из трех букв, у вас будет наборы 9C3. Они будут содержать наборы, такие как ABC, ABD, BCD и т. Д. Я ищу для создания наборов, которые имеют не более 1 общей буквы. Так что в этом примере мы получим следующие наборы:

ABC, ADG, AEI, AFH, BEH, BFG, BDI, CFI, CDH, CEG, DEF и GHI - обратите внимание, что если вы берете любые два набора, то не более 1 повторяющейся буквы.

Какой будет хороший способ генерировать такие наборы? Это должно быть масштабируемое решение, чтобы я мог сделать это для набора из 1000 букв с размером поднабора 4.

Любая помощь высоко ценится.

Спасибо

Ответы [ 5 ]

11 голосов
/ 02 июня 2010

Скажем, у вас есть n букв (или студентов, или что-то еще), и каждую неделю вы хотите разделить их на подмножества размера k (всего n/k подмножеств каждую неделю). Этот метод будет генерировать почти n/k подмножества каждую неделю - ниже я покажу, как расширить его, чтобы генерировать ровно n/k подмножества.


Генерация подмножеств (без разделения)

Первый выбор p, наибольшее простое число <= n / k. </p>

Рассмотрим каждую упорядоченную пару (a, b) такую, что

0 <= a < k
0 <= b < p

Мы можем сопоставить каждую пару с одной из наших букв; таким образом, мы можем сопоставить p*k <= n букв таким образом (опять же, я покажу ниже, как отобразить ровно n букв)

(0,0) => 'A'
(0,1) => 'B'
...
(0,p-1) => 'F'
(1,0)   => 'G'
(1,1)   => 'H'
...
(k-1,p-1) => 's'

Теперь, учитывая

0 <= w < p  
0 <= i < p

Мы можем создать набор S w (i) наших упорядоченных пар. Каждое соединение в S w (i) будет представлять одну букву (в соответствии с нашим отображением выше), а набор S w (i) сам по себе представляет одну «группировку букв», иначе. одно подмножество размера k.

Формула для S w (i) равна

S<sub>w</sub>(i) = {(0,i mod p), (1,(w+i) mod p), (2,(2w+i) mod p),..., ((k-1),((k-1)*w+i) mod p)}
      = { (x,y) | 0 <= x < k and y = w*x + i (mod p)}

Если мы меняем w и i по всем возможным значениям, мы получаем p 2 полных наборов. Когда мы возьмем любой два из этих наборов, они будут иметь не более одного пересекающегося элемента.

Как это работает

Скажем, у нас есть два набора S w1 (i 1 ) и S w2 (i 2 ). Если S w1 (i 1 ) и S w2 (i 2 ) имеют более одного общего элемента, то существует минимум два x такие, что

w<sub>1</sub>*x + i<sub>1</sub> = w<sub>2</sub>*x + i<sub>2</sub> (mod p)  
(w<sub>1</sub>-w<sub>2</sub>)*x + (i<sub>1</sub>-i<sub>2</sub>) = 0 (mod p)

Однако любой, кто принимается за модульную арифметику, знает, что если p простое число, либо x имеет уникальное решение, либо (w 1 = w 2 и i 1 = i 2 ); таким образом, не может быть более одного x, и S w1 (i 1 ) и S w2 (i 2 ) могут иметь не более одного пересекающегося элемента.

* * Анализ тысяча восемьдесят-одна * * тысяча восемьдесят-две

Поскольку p < n/k, по Теорема Чебышева (которая утверждает, что между x и 2x есть простое число для x> 3)

n/2k < p <= n/k

Таким образом, этот метод генерирует как минимум (n / 2k) 2 подмножеств букв, хотя на практике p будет ближе к n / k, поэтому число будет ближе к (n / k) 2 . Поскольку простая верхняя граница для максимально возможных таких подмножеств равна n(n-1)/(k(k-1)) (см. Комментарий BlueRaja ниже), это означает, что алгоритм является асимптотически оптимальным и будет генерировать около оптимального количества множеств (даже в наихудший случай, он не сгенерирует менее чем 1/4 оптимальной суммы; см. Комментарий ниже)


Разметка

Теперь вы хотите сгруппировать буквы в разделы каждую неделю: каждую неделю все буквы включаются ровно в одну группу.
Мы делаем это, позволяя w фиксироваться на определенном значении (представляющем неделю) и позволяя i варьироваться от 0 до p-1.

Доказательство

Рассмотрим группы, которые мы создали:

S<sub>w</sub>(i) = { (x,y) | 0 <= x < k and y = w*x + i (mod p)}

Допустим, w является фиксированным и i изменяется от 0 до p-1. Тогда мы получим p наборов:

S<sub>w</sub>(0), S<sub>w</sub>(1), ..., S<sub>w</sub>(p-1)

Теперь, скажем, S w (i 1 ) и S w (i 2 ) (с i 1 = / = i 2 ) пересекаются; то

w*x + i<sub>1</sub> = w*x + i<sub>2</sub> (mod p)

для некоторого x, и, следовательно, i 1 = i 2 . Таким образом, S w (i 1 ) и S w (i 2 ) не пересекаются.

Поскольку ни один из наших наборов не пересекается, и их ровно p (каждый с k элементами), наши наборы образуют разбиение k * p букв.


Создание n / k подмножеств каждую неделю

Самым большим недостатком этого метода является то, что он генерирует наборы для p * k букв, а не n букв. Если нельзя пропустить последние буквы (как в вашем случае, когда буквы являются студентами), есть два способа генерировать ровно n / k подмножеств каждую неделю:

  1. Найдите набор простых чисел p 1 , p 2 , p 3 , ..., который суммирует в точности n / k , Тогда мы можем рассматривать каждую группу из p i k букв как независимый алфавит, так что вместо того, чтобы находить подмножества p k букв, мы находим одну группу подмножеств для p 1 * k букв, другая группа подмножеств для p 2 * k букв, другая группа ...
    Это имеет тот недостаток, что письма из одной группы никогда не будут сопряжены с письмами из другой группы, что уменьшает общее количество генерируемых подмножеств. К счастью, если n четное, по гипотезе Гольдбаха † вам понадобится не более двух групп (если n нечетно, вам понадобится только три максимум )
    Этот метод гарантирует подмножества размера k, но не генерирует столько подмножеств.
    Хотя это не доказано, известно, что это верно для каждого смехотворно большого числа, которое вы, вероятно, встретите для этой проблемы

  2. Другой вариант - использовать наименьшее простое число p> = n / k. Это даст вам p * k> = n букв - после генерации подмножеств просто выбросьте лишние буквы. Таким образом, в итоге это дает вам несколько подмножеств с размером Этот метод генерирует как минимум столько же подмножеств, что и исходный метод, но некоторые могут иметь размер


Пример * * тысяча сто девяносто восемь Возьмем n = 15, k = 3. То есть, у нас 15 учеников, и мы делаем группы по три. Для начала выберем наибольшее простое число p <= n / k. n / k <em>равно простое число (повезло нам!) , поэтому p = 5. Мы отображаем 15 студентов в упорядоченные пары (a, b), описанные выше, давая нам (каждое письмо - студент): (0,0) = A (0,1) = B (0,2) = C (0,3) = D (0,4) = E (1,0) = F (1,1) = G (1,2) = H (1,3) = I (1,4) = J (2,0) = K (2,1) = L (2,2) = M (2,3) = N (2,4) = O Метод генерирует 25 групп по три. Таким образом, поскольку нам нужно планировать n / k = 5 групп каждую неделю, мы можем запланировать 5 недель деятельности (5 групп в неделю * 5 недель = 25 групп). Для недели 0 мы создаем раздел как S<sub>0</sub>(i), for i = 0 to 4. S<sub>0</sub>(0) = { (0,0), (1,0), (2,0) } = AFK S<sub>0</sub>(1) = { (0,1), (1,1), (2,1) } = BGL S<sub>0</sub>(2) = { (0,2), (1,2), (2,2) } = CHM S<sub>0</sub>(3) = { (0,3), (1,3), (2,3) } = DIN S<sub>0</sub>(4) = { (0,4), (1,4), (2,4) } = EJO Для 4 недели это будет S<sub>4</sub>(i) for i = 0 to 4. S<sub>4</sub>(0) = { (0,0), (1, (4*1 + 0) mod 5), (2, (2*4 + 0) mod 5) } = { (0,0), (1,4), (2,3) } = AJN S<sub>4</sub>(1) = { (0,1), (1, (4*1 + 1) mod 5), (2, (4*2 + 1) mod 5) } = { (0,1), (1,0), (2,4) } = BFO S<sub>4</sub>(2) = { (0,2), (1, (4*1 + 2) mod 5), (2, (4*2 + 2) mod 5) } = { (0,2), (1,1), (2,0) } = CGK S<sub>4</sub>(3) = { (0,3), (1, (4*1 + 3) mod 5), (2, (4*2 + 3) mod 5) } = { (0,3), (1,2), (2,1) } = DHL S<sub>4</sub>(4) = { (0,4), (1, (4*1 + 4) mod 5), (2, (4*2 + 4) mod 5) } = { (0,4), (1,3), (2,2) } = EIM Вот расписание на все 5 недель: Week: 0 S<sub>0</sub>(0) ={(0,0) (1,0) (2,0) } = AFK S<sub>0</sub>(1) ={(0,1) (1,1) (2,1) } = BGL S<sub>0</sub>(2) ={(0,2) (1,2) (2,2) } = CHM S<sub>0</sub>(3) ={(0,3) (1,3) (2,3) } = DIN S<sub>0</sub>(4) ={(0,4) (1,4) (2,4) } = EJO Week: 1 S<sub>1</sub>(0) ={(0,0) (1,1) (2,2) } = AGM S<sub>1</sub>(1) ={(0,1) (1,2) (2,3) } = BHN S<sub>1</sub>(2) ={(0,2) (1,3) (2,4) } = CIO S<sub>1</sub>(3) ={(0,3) (1,4) (2,0) } = DJK S<sub>1</sub>(4) ={(0,4) (1,0) (2,1) } = EFL Week: 2 S<sub>2</sub>(0) ={(0,0) (1,2) (2,4) } = AHO S<sub>2</sub>(1) ={(0,1) (1,3) (2,0) } = BIK S<sub>2</sub>(2) ={(0,2) (1,4) (2,1) } = CJL S<sub>2</sub>(3) ={(0,3) (1,0) (2,2) } = DFM S<sub>2</sub>(4) ={(0,4) (1,1) (2,3) } = EGN Week: 3 S<sub>3</sub>(0) ={(0,0) (1,3) (2,1) } = AIL S<sub>3</sub>(1) ={(0,1) (1,4) (2,2) } = BJM S<sub>3</sub>(2) ={(0,2) (1,0) (2,3) } = CFN S<sub>3</sub>(3) ={(0,3) (1,1) (2,4) } = DGO S<sub>3</sub>(4) ={(0,4) (1,2) (2,0) } = EHK Week: 4 S<sub>4</sub>(0) ={(0,0) (1,4) (2,3) } = AJN S<sub>4</sub>(1) ={(0,1) (1,0) (2,4) } = BFO S<sub>4</sub>(2) ={(0,2) (1,1) (2,0) } = CGK S<sub>4</sub>(3) ={(0,3) (1,2) (2,1) } = DHL S<sub>4</sub>(4) ={(0,4) (1,3) (2,2) } = EIM Более практичный пример

В вашем случае n = 1000 студентов и k = 4 в каждой группе. Таким образом, мы выбираем p как наибольшее простое число <= (n / k = 1000/4 = 250), поэтому p = 241. Без учета изменений, приведенных выше в разделе <strong>«Создание подмножеств n / k каждую неделю» , этот метод создаст расписание для 961 студента продолжительностью 241 неделя.

(Верхняя граница для максимально возможного числа подмножеств будет 1000 * 999 / (4 * 3) = 83250, хотя фактическое число, вероятно, меньше этого. Тем не менее, этот метод генерирует 58081 подмножеств, или около 70% от теоретического максимума!)

Если мы используем первый метод выше, чтобы сгенерировать расписание для ровно 1000 учеников, мы берем p 1 = 113, p 2 = 137 (так что p 1 + р 2 = н / к). Таким образом, мы можем сгенерировать (113) ^ 2 + (137) ^ 2 = 31 538 подмножеств учеников, которых хватит на 113 недель.

Если мы используем второй метод выше, чтобы сгенерировать расписание для ровно 1000 учеников, мы берем р = 251. Это даст нам расписание для 1004 учеников на 251 неделю; мы удаляем 4 фантомных ученика из расписания каждую неделю. Как правило, это приводит к тому, что четыре группы по 3 в неделю (хотя маловероятно, возможно, например, иметь одну группу из 2 и две группы по 3). Группы с <4 студентами всегда будут иметь число учеников, кратное 4, так что вы можете вручную распределить этих учеников по четырем группам, что может привести к тому, что двое из этих учеников снова будут вместе позже в другой группе. </p>


Заключительные мысли

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

Эта проблема подпадает под категорию систем с ограниченным множеством в комбинаторике. См. этот документ для получения дополнительной информации, особенно в главах 1 и 2. Поскольку это файл postscript, вам потребуется gsview или что-то еще для его просмотра.

3 голосов
/ 04 июня 2010

Мне пришлось добавить еще один ответ, так как другой был слишком длинным.

Если у вас есть следующие ограничения:

1) Вам нужны группы по 4 в неделю.

2) Каждая группа в определенную неделю не разделена, и каждый студент находится в одной группе.

3) Если два студента находятся в одной группе, они не могут быть в одной группе в будущем.

Если вы строите граф G следующим образом:

1) Каждый студент - это узел.

2) Два студента соединяются ребром, если они не были вместе в группе.

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

Ваша проблема: Вам нужно найти охватывающий подграф G, такой, чтобы он представлял собой объединение копий K_4 или, другими словами, разбиение на K_4s.

К сожалению, похоже, что эта проблема NP-Hard: Точное покрытие с 4-мя наборами (то есть NP-Complete) может быть уменьшено до вашей задачи (точно так же, как Точное покрытие с 3-мя наборами может быть уменьшено для разбиения на треугольники). .

Возможно, это поможет дать некоторые алгоритмы аппроксимации: http://www.siam.org/proceedings/soda/2010/SODA10_122_chany.pdf

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

Точное покрытие: http://en.wikipedia.org/wiki/Exact_cover

Разбиение на треугольники: https://noppa.tkk.fi/noppa/kurssi/t-79.5103/viikkoharjoitukset/T-79_5103_solutions_5.pdf

Точное покрытие по 4 наборам = Учитывая набор S размером 4 м и набор C из 4-элементных подмножеств S, существует ли подмножество C 'из C, такое, что каждый элемент S появляется точно один раз в C' .

К сожалению, похоже, вам, возможно, придется изменить некоторые ограничения.

1 голос
/ 02 июня 2010

Вот подход, который удовлетворит заявленные вами требования. Делает ли он то, что вы хотите, я не знаю.

  1. На большом листе бумаги нарисуйте правильную сетку с минимум 250 квадратами.
  2. Пометьте стороны квадратов буквами в вашем алфавите (250 квадратов х 4 стороны == 1000).
  3. Каждый квадрат определяет одно из ваших подмножеств. Каждый из них имеет одну сторону (т.е. одну букву) только с каждым из своих (до) 4 соседей. Ни одна сторона (т.е. буква) не разделяется более чем на 2 квадрата (подмножества).

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

Другой подход, о котором я подумал:

  1. На большом листе бумаги нарисуйте N точек, где N - размер вашего алфавита. Пометьте каждую точку одной из букв алфавита.
  2. Соедините любые n точек, где n - размер требуемых подмножеств. Это ваше первое подмножество.
  3. Выберите одну из уже подключенных точек, подключите ее к (n-1) большему количеству не связанных точек. Это ваше следующее подмножество.
  4. Повторяйте шаг 3, пока не закончите.

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

РЕДАКТИРОВАТЬ: Легко преобразовать мой первый подход в форму, которая, более очевидно, алгоритм на графике. Смоделируйте каждое подмножество как узел, а каждую букву в алфавите - как ребро. Постройте график, в котором каждый узел имеет степень n (количество элементов в подмножестве), и каждая буква (ребро) используется один раз.

1 голос
/ 02 июня 2010

Вот некоторые наброски алгоритма.

  1. Сначала найдите все пары: AB BC CD DE EF FG GH HI AC BD CE DF EG FH GI AD BE CF DG EH FI AE BF CG DH EI AF BG CH DI AG BH CI АХ БИ AI

Они могут храниться в массиве из шести (n-1)

  1. Теперь начните пытаться объединять последовательные пары, используя следующие правила: а. Две пары можно объединять только при наличии общего символа. б. Комбинация возможна, когда пара, образованная выходом из общего символа, также доступна. например если мы хотим объединить AB и AC, нам нужно проверить, доступен ли BC. с. Когда вышеуказанные правила будут выполнены, мы объединяем две пары в тройку (например, AB и AC, объединенные в ABC) и помечаем все три пары, как AB, AC и BC, как недоступные.

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

Пример: 1. объединить AB + AC -> ABC; Отметить AB, AC и BC недоступно. 2. объединить AD + AE -> AED; Отметить AD, AE и DE недоступно. 3. объединить AF + AG -> AFG; Марки AF, AG и FG недоступны. ..

0 голосов
/ 02 июня 2010

@ khuss

Тот же метод может быть обобщен. Но это не линейный алгоритм и может быть экспоненциальным.

Например: когда размер подмножества равен 4, мы выбираем 2 или более пар с 4 уникальными символами.

например. «AB and CD» или «AB, AC & AD» только при соблюдении следующих условий:

  1. Доступны все пары, образованные персонажами из 4-х кортежей. например если мы хотим сформировать ABCD, используя AB, AC & AD, тогда все пары, выделенные A, B, C & D, то есть AB, AC, AD, BC, BD, CD, все должны быть доступны.
  2. Если условие 1 выполнено, тогда мы формируем ABCD и помечаем пары C (4,2) = 6 как недоступные.

Мы продолжаем, как и прежде, до тех пор, пока не будет сформировано больше 4-х кортежей или больше не будет доступных пар.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...