Створення комбінацій із набору пар без повторення елементів


28

У мене є набір пар. Кожна пара має вигляд (x, y) такий, що x, y належать цілим числам з діапазону [0,n).

Отже, якщо n дорівнює 4, то у мене є такі пари:

(0,1) (0,2) (0,3)
(1,2) (1,3) 
(2,3) 

У мене вже є пари. Тепер я повинен побудувати комбінацію за допомогою n/2пар, щоб жодне з цілих чисел не повторювалося (іншими словами, кожне ціле число з’являється принаймні один раз у кінцевій комбінації). Далі наведено приклади правильної та неправильної комбінації для кращого розуміння

 1. (0,1)(1,2) [Invalid as 3 does not occur anywhere]
 2. (0,2)(1,3) [Correct]
 3. (1,3)(0,2) [Same as 2]

Хтось може запропонувати мені спосіб генерувати всі можливі комбінації, як тільки у мене є пари.


Можливо, використовуючи 2d масив для представлення ваших пар. Дійсні комбінації відповідають вибору n клітинок масиву таким чином, що кожен рядок і стовпець містить рівно 1 вибрану комірку.
Джо

4
Ви кажете, що вхід - це безліч усіх пар? Якщо так, то слід просто сказати, що введення просто . n
rgrig

2
Чи завжди ровно? Якщо ні, висловлювання "жодне з цілих чисел не повторюється" і "кожне ціле число з'являється принаймні один раз у кінцевій комбінації" суперечливі. n
Дмитро Кордубан

1
та ж проблема, що і @rgrig: це вхід всіх не упорядкованих пар чи це довільний набір можливих пар? Якщо це всі пари, то ви можете просто сказати, що вхід , не потрібно надавати список. n
Каве

1
Ви зацікавлені в створенні всіх ідеальних відповідностей графіка на точок, визначених вашим початковим набором пар. Крім того, здається, що ви вважаєте, що цей графік є повним графіком у цих точках. Ваше питання було б зрозумілішим, якби ви це згадали. Є такі співпадіння. ( n - 1 ) ! ! : = 1 × 3 × 5 × × ( n - 1 )n(n1)!!:=1×3×5××(n1)
Марк ван Левенен

Відповіді:


14

Одним із прямих способів є рекурсивна процедура, яка робить наступне для кожного виклику. Вхід до процедури - це список вже вибраних пар і список усіх пар.

  1. Обчисліть найменше число, яке ще не охоплено списком введення. Для першого виклику це, звичайно, буде 0, оскільки не було обрано жодної пари.
  2. Якщо всі цифри вкриті, у вас є правильна комбінація, роздрукуйте її та поверніть попередній крок. В іншому випадку найменше число, яке не буде виявлено, - це ціль, на яку ми будемо прагнути.
  3. Шукайте через пари, які шукають спосіб покрити цільове число. Якщо його немає, то просто поверніться до попереднього рівня рекурсії.
  4. Якщо є спосіб прикрити цільове число, виберіть перший спосіб і рекурсивно зателефонуйте всю процедуру ще раз, при цьому щойно вибрану пару додайте до списку обраних пар.
  5. Коли це повернеться, шукайте наступний спосіб прикрити цільове число парою, не перекриваючи раніше вибрану пару. Якщо ви знайдете його, виберіть його і знову рекурсивно зателефонуйте на наступну процедуру.
  6. Продовжуйте кроки 4 та 5, поки не буде більше способів покрити цільове число. Пройдіть весь список пар. Коли більше немає правильного вибору, поверніться до попереднього рівня рекурсії.

Спосіб візуалізації цього алгоритму полягає у дереві, шляхи якого є послідовностями пар, що не перетинаються. Перший рівень дерева містить усі пари, які містять 0. Для наведеного вище прикладу дерево є

           Корінь
             |
     ----------------
     | | |
   (0,1) (0,2) (0,3)
     | | |
   (2,3) (1,3) (1,2)

У цьому прикладі всі шляхи через дерево насправді дають правильні колекції, але, наприклад, якщо ми залишили пару (1,2), то найправіший шлях мав би лише один вузол і відповідав би пошуку на етапі 3, якщо це не вдалося.

Алгоритми пошуку цього типу можуть бути розроблені для багатьох подібних проблем перерахування всіх об'єктів певного типу.


Було висловлено припущення, що, можливо, ОП означає, що всі пари знаходяться на вході, а не просто їхній набір, як йдеться в запитанні. У такому випадку алгоритм набагато простіше, тому що більше не потрібно перевіряти, які пари дозволені. Не потрібно навіть генерувати набір усіх пар; наступний псевдокод зробить те, про що попросив ОП. Тут - номер введення, "список" починається як порожній список, а "накритий" - це масив довжини ініціалізований до 0. Це може бути дещо ефективніше, але це не моя найближча мета.nnn

sub cover {
  i = 0;
  while ( (i < n) && (covered[i] == 1 )) {
   i++;
  }
  if ( i == n ) { print list; return;}
  covered[i] = 1;
  for ( j = 0; j < n; j++ ) {
    if ( covered[j] == 0 ) {
      covered[j] = 1;
      push list, [i,j];
      cover();
      pop list;
      covered[j] = 0;
    }
  }
  covered[i] = 0;
}

Це має працювати, але це, мабуть, не найефективніший спосіб зробити це.
Джо

2
Зрештою, справа полягає в тому, щоб якось перерахувати стежки цього дерева. Якщо кількість пар у вхідному списку набагато менша за кількість можливих пар, такий алгоритм буде ідеально ефективним, особливо якщо використовуються деякі хеш-таблиці, які допоможуть запам'ятати, які числа вже охоплені на кожному кроці, так що це можна запитати в постійному часі.
Карл Маммерт

Якщо в списку використовуються вказівники, тоді варто звернути увагу на танцювальні посилання Кнут . Коли ви повертаєтесь, виконайте рекурсивний виклик і вам доведеться відновити попередній стан списку.
uli

10

Ви можете вирішити це ітеративно. Припустимо, у вас є всі рішення для діапазону . Тоді ви можете легко побудувати рішення з . Розмір надзвичайно швидко зростає з , тому може бути добре написати генератор, а не зберігати всі набори в пам'яті, див. Приклад Python нижче. [ 0 , n ) S n + 2 S n nSn[0,n)Sn+2Snn

def pairs(n):
    if (n%2==1 or n<2):
        print("no solution")
        return
    if (n==2):
        yield(  [[0,1]]  )
    else:
        Sn_2 = pairs(n-2) 
        for s in Sn_2:
            yield( s + [[n-2,n-1]] )
            for i in range(n/2-1):
                sn = list(s)
                sn.remove(s[i])
                yield( sn + [ [s[i][0], n-2] , [s[i][1], n-1] ] )
                yield( sn + [ [s[i][1], n-2] , [s[i][0], n-1] ] )

Ви можете перерахувати всі пари, зателефонувавши

for x in pairs(6):
   print(x)

6

Оновлення : моя раніше відповідь стосувалася двосторонніх графіків, про які ОП НЕ запитував. Зараз я залишаю це як відповідна інформація. але більш релевантна інформація стосується ідеальних відповідностей у нероздільних графах.

У цьому плані є приємне опитування Проппа, яке окреслює прогрес (до 1999 року). Деякі ідеї цієї статті та пов’язані з цим посилання можуть виявитися корисними. TL; DR - це хитро :)

--- Початок старої відповіді

Зауважте, що те, що ви просите, - це перерахувати всі можливі ідеальні відповідники на двосторонньому графіку. Існує багато різних алгоритмів для цього, зокрема один із останніх - з ISAAC 2001 .

Основна ідея полягає в тому, щоб знайти одне ідеальне узгодження за допомогою мережевих потоків, а потім неодноразово змінювати це за допомогою змінних циклів (для отримання додаткової інформації див. Розділ підручника з алгоритмів щодо мережевих потоків).


Двосторонній графік складається з двох наборів із заданими позначками [0, n), і є край (i, j), якщо і лише тоді, коли (i! = J)
Джо

nKn

2
постійні обчислюють відповідь. але ОП хоче їх перерахувати
Суреш

всі вони є ізоморфними через структуру графа, тому роздуми про застосування перестановок можуть бути гарною ідеєю (але проблема полягає в тому, що це створить дублікати).
Каве

4

Кожна пара, яку ви вибрали, виключає два ряди, з яких ви більше не можете вибирати. Ця ідея може бути використана для налаштування рекурсивного алгоритму (у Scala):

def combine(pairs : Seq[(Int,Int)]) : Seq[Seq[(Int, Int)]] = pairs match {
  case Seq() => Seq()
  case Seq(p) => Seq(Seq(p))
  case _ => {
    val combinations = pairs map { case (a,b) => {
      val others = combine(pairs filter { case (c,d) =>
        a != c && a != d && b != c && b != d
      })

      others map { s => ((a,b) +: s) }
    }}

    combinations.flatten map { _.sorted } distinct
  }
}

Це, безумовно, можна виразити більш ефективним способом. Зокрема, ідея не потрібно розглядати цілі рядки для комбінацій не використовується викликом до filter.


Чи не будуть це також поверненнями комбінацій, які не включають кожне число, але які неможливо розширити, оскільки в початковій послідовності немає пар, які можуть їх розширити? Якщо так, ці комбінації потрібно відфільтрувати.
Карл Маммерт

n2N

(0,1)n=4

Так. Але, як я вже говорив, моя відповідь стосується сценарію, який пропонує ОП, тобто не довільних вкладів.
Рафаель

Коли я читав оригінальне запитання, мова йде про довільний набір пар, ОП ніколи не говорить про те, що всі пари можливі. Але я погоджуюся, що ОП може бути більш зрозумілим щодо цього.
Карл Маммерт

4

Хоча до цього питання вже є багато чудових відповідей, я думаю, було б непогано вказати на основну, загальну хитрість, що стоїть за ними.

Набагато простіше генерувати унікальні комбінації, якщо ви можете мати загальне впорядкування елементів, які потрібно поєднувати . Таким чином, унікальність гарантується, якщо ми дозволимо лише відсортовані комбінації. Генерувати відсортовані комбінації також не важко - просто виконайте пошук звичайного перерахунку грубої сили, але на кожному кроці вибирайте лише елементи більшого розміру, ніж ті, які вже були підібрані на кожному кроці.

Додатковим ускладненням у цій конкретній проблемі є бажання отримати лише комбінації довжини n / 2 (максимальна довжина). Це не важко зробити, якщо ми зважимось на хорошу стратегію сортування. Наприклад, як вказувалося у відповіді Карла Маммета, якщо ми розглянемо лексикографічний сорт, (зверху вниз, ліво-праворуч на діаграмі у питанні), ми отримуємо стратегію завжди приймати наступний елемент, щоб його перша цифра була найменше досі невикористане число.

Ми також можемо розширити цю стратегію, якщо хочемо створити послідовності інших довжин. Просто пам’ятайте, що кожного разу, коли ми вибираємо наступний елемент, перший номер якого не найменший з наявних, ми виключаємо появу одного або декількох рядків елементів у відсортованій послідовності, тому максимальна довжина премутації відповідно зменшується.


3

Я не впевнений, що ви це запитуєте, але, як я розумію, у вас є всі упорядковані пари і хочете порахувати список усіх пар, які накрийте множину де - парне число. Ми можемо думати про це як ребрових покриттів з , повного графа на вершинах. [n]={1,,n}[n]nKnn(n2)[n]={1,,n}[n]nKnn

Більше того, питання, мабуть, передбачає, що кожне число в відображається лише один раз у списку. У цьому випадку ми дивимось лише на покриття, які ідеально відповідають . Кількість збігається в графі одно постійний його матриці суміжності . Тому нам потрібно обчислити .P e r m ( K n )[n]Perm(Kn)

Відомо, що постійним є , але це взагалі так. Для є такі списки. #P-complete n !Knn!2n2

Найпростіший спосіб генерувати все це - виправити одну ідеальну відповідність, а потім застосувати перестановку але це створить багато дублікатів.[n]

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.