2015-06-17 3 views
7

Для набора A = {1, 2, 3, ..., n}. Это называется разбиением множества A, набор k<=n элементов, которые соблюдают следующие теоремы:генерировать все перегородки набора

а) объединение всех разделов A является A

б) пересечение 2 перегородок A является пустое множество (они не могут использовать одни и те же элементы).

Например. A = {1, 2,... n} У нас есть разделы: {1, 2, 3} {1, 2} {3} {1, 3} {2} {2, 3} {1} {1} {2} {3}

Эти теоретические концепции представлены в моем учебнике алгоритмов (кстати, этот подраздел является частью «по отслеживанию» главы). Я должен найти алгоритм для генерации всех разделов данного набора. Я боролся с этой проблемой весь день, и я не мог найти решение. Можете ли вы объяснить мне, как работает этот алгоритм? Кроме того, можете ли вы дать мне псевдо-кодовый эскиз алгоритма?

+3

Подсказка: предположим, что у вас есть весь набор всех разделов {1, ..., n-1}. (BTW, отдельные наборы в разделе обычно называются «частями».) Есть ли способ генерировать каждый раздел из {1, ..., n} из них, введя элемент n в каждый раздел так или иначе? Будет ли это генерировать каждый раздел {1, ..., n} один раз и только один раз? –

+0

Я думаю, что вы правы, но я не мог понять точный алгоритм. Например, если я хочу сгенерировать все разделы 'A_3 = {1, 2, 3}', тогда мне нужно найти способ вставить '3' во все разделы' A_2 = {1, 2} '. Затем разделы 'A_2' являются' {1, 2} 'и' {1} {2} '. До сих пор я прав? И для больших значений, 'A_4', я должен вставить' 4' во все разделы 'A_3', а затем вставить' 3' во все разделы 'A_2'? – cristid9

+1

Да, вы правы до сих пор. Две вещи, которые следует помнить: 1. Когда раздел содержит более одной части (например, '{1} {2}' в вашем примере), вам нужно попробовать добавить новый элемент в каждую часть - так что вы создадите совершенно новый * раздел * для каждого * часть *. 2. Вам также необходимо создать разделы, в которых новый номер находится в отдельной части. –

ответ

2

Вы можете попробовать рекурсивный ответ, если ваш набор не большой (или еще стека использования):

Принцип заключается в следующем, у вас есть функция, которая отплатить:

rec_func(SET) = List of List of Set 

И работать следующим образом:

rec_func(SET) = 
    if SET = {empty}: 
    // if no element, easy to give the answer 
    return([[]]) 
    else: 
    // 1. Remove one element from the set : 'a' to this set 
    a = SET.pop() 
    // 2. Call rec_func : 
    list_of_list_of_set = rec_func(SET\'a') 
    response = [] 
    // 3. For every possibilities given by the function add the element 'a' : 
    For every list_of_set in list_of_list_of_set : 
     // Case 1, you add 'a' to list_of_set 
     response.push([{'a'} + list_of_set]) 
     // Case 2, for every set, you create a copy where you add 'a' 
     for every set in list_of_set: 
      response.push([{set,'a'} + list_of_set\set]) 

    // The function return the list of list of set created.   
    return(response) 
+0

@ cristid9 Извините, неправильные манипуляции, я писал ответ ... –

+0

@ cristid9 Если вам нужна более конкретная реализация, дайте мне язык, с которым вы знакомы, чтобы написать функцию. –

+0

Я знаком с C, но все, что я хотел, - это общая концепция. – cristid9

14

Существует важное наблюдение (в комментарии), что разбиение множества n элементов могут быть представлены в виде целого последовательности форма [p , & hellip; р п], где р я это номер раздела элемента я. Для такой последовательности, чтобы быть действительным, он должен подчиняться правилам, которые р является 1, и для каждого J где 1 < Jп, есть некоторые я < J такое, что р J≤ рi + 1. Или, другими словами, в любом префиксе последовательности никакое целое число не пропускается.

В настоящее время, существует стандартный алгоритм для перечисления ограниченных последовательностей в лексикографическом порядке, который состоит из следующих действий:

  • Start с наименьшей последовательности.
  • Чтобы найти следующую последовательность в лексикографическом порядке:
    • сканирование в обратном направлении по последовательности и найти самый правый «incrementable» элемент. (Инкрементный элемент - это такой элемент, что существует некоторый более крупный элемент, который может быть заменен для этого элемента, а полученная подпоследовательность до этой точки является префиксом по меньшей мере одной допустимой последовательности.)
    • Измените этот элемент на следующий более крупный элемент, который является жизнеспособным (т. е. приводит к действительному префиксу, как указано выше), а затем заполняет оставшиеся элементы справа (если есть) с наименьшими возможными значениями.
    • Если нет добавочного элемента, то перечисление завершается.

С несколькими положениями о поиске в incrementable элемента, этот алгоритм в худшем случае O (п) и часто O(), когда элемент будет увеличиваться, как правило, близка до конца последовательности. (Например, использование этого алгоритма для перечисления перестановки - это O (1), чтобы найти следующую перестановку, если вы можете найти «следующий элемент» в O (1).)

Чтобы применить этот алгоритм к мы наблюдаем следующее:

  1. Наименьшая возможная последовательность [1, & hellip; 1]
  2. Элемент ря является incrementable при условии, что:
    • ря < н
    • Существует несколько J < я такой, что pi & equals; рJ
  3. Самый маленький суффикс жизнеспособного префикса [1, & hellip; 1]

Другой способ задания состояния при наблюдении 2 является то, что элемент является incrementable, если его значение не п или это первый элемент в последовательности с ее значением.Мы можем сделать это определение в O (1), если мы также сохраним последовательность [m , & hellip; м п], где м равно 0, а м я это максимум мя -1 и ря -1. м тривиально поддерживать, и это позволяет нам переписать условие incrementability как просто рямя.

Легко видеть, что Следующая Partition могут быть реализованы в O (п) времени, но, как это бывает и так, что он амортизируется время O (1). Грубо говоря, это потому, что в большинстве случаев последний элемент последовательности является инкрементальным.

+0

Будет ли этот алгоритм генерировать «изоморфные» разделы? Например, будет ли он генерировать как {1, 1, 1, 2}, так и {2, 2, 2, 1} (которые определяют один и тот же раздел)? – Zack

+0

@zack: Нет, не будет. Правило действительности (которое было неправильно, но теперь исправлено) гарантирует, что генерируется только одна из каждой изоморфной группы разделов. – rici

+4

Этот ответ - ужасно недооцененный драгоценный камень. Жаль, что я не смог бы проголосовать за него больше. – kjo

0

Я работал над эффективным алгоритмом, который создает все разделы набора, как описано выше, на основе подхода ключевого слова, определяемого @rici. Следующий алгоритм, написанный на python, делает это, и есть еще возможность оптимизации. Я нахожусь на нем. Как вы знаете, эта проблема NP-полная! Из-за оптимизации есть, может быть, какая-то странная нотация, например try/except. Тем не менее, переменные n и k должны определять через n, сколько разных элементов имеет множество, а k - количество различных классов, которым разрешено иметь множество. INFO: алгоритм генерирует ВСЕ разделы до количества разных классов не только этих классов !!!

def partitions(): 
     global n 
     global k 
     codeword = [1 for digitIndex in range(0, n)] 
     while True: 
       print codeword 
       startIndex = n - 1 
       while startIndex >= 0: 
         maxValue = max(codeword[0 : startIndex]) 
         if codeword[startIndex] > maxValue or maxValue > k or codeword[startIndex] >= k: 
           codeword[startIndex] = 1 
           startIndex -= 1 
         else: 
           codeword[startIndex] += 1 
           break 

n = 12 
k = 2 
try: 
     partitions() 
except: 
     pass 
Смежные вопросы