Як можна ефективно створити всі бінарні послідовності з рівним числом 0 і 1?


10

Двійкова послідовність довжини просто впорядкована послідовність х 1 , ... , х п , так що кожен х J є або 0 або 1 . Для того, щоб генерувати всі такі бінарні послідовності, можна використовувати очевидну структуру двійкового дерева таким чином: корінь "порожній", але кожному лівому дочірньому відповідає додавання 0 до існуючого рядка, а кожен правий дитина до 1 . Тепер кожна двійкова послідовність - це просто шлях довжиною n + 1, починаючи від кореня і закінчуючи на листі.nx1,,xnxj0101n+1

Ось моє запитання:

Чи можемо ми зробити краще, якщо лише хочемо генерувати всі двійкові рядки довжиною які мають точно n нулів і n ?2nnn

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


n2n

Я не можу коментувати складність, але мій наївний алгоритм генерував би прогулянки по краях квадратної сітки від одного кута до діагоналі, використовуючи якусь схему зворотнього треку. Це означає, що 01 і 10 опиняються в тому ж самому положенні (на відміну від вашого дерева), але з зворотним ходом ми знаємо цю історію.
Гендрик Ян

Можливо, інша примітка, ось реалізація Java select -тератора . (nk)
Pål GD

Відповіді:


6

Очевидно, є двійкові рядки довжиною . Для переходу двійкового алгоритму необхідно відвідувати кожен вузол один раз, тобто він повинен робити кроки.4n2n

i=02n2i=22n+11=O(4n)

Розглянемо рекурсивний алгоритм, який обходить дерево, яке ви описали, але підраховує кількість одиниць і нулів на його шляху, тобто він лише пройде добру частину дерева.
Але скільки таких двійкових рядків з 0 і 1 є? Ми вибираємо 1's для своїх рядків довжиною і використовуємо формулу Стірлінга на кроці 2: nnn2n

(2nn)=(2n)!(n!)2=4nπn(1+O(1/n))

EDIT
Завдяки коментарям Пітера Шора ми також можемо проаналізувати кількість кроків, необхідних для другого алгоритму, який підраховує значення 1 і 0. Я цитую його коментар знизу:

Ми хочемо знайти всі бінарні послідовності з точно 0 та 1. Ми обходимо бінарне дерево, де кожен вузол є послідовністю 0 та 1's. Нам не потрібно відвідувати будь-який вузол з більш ніж 0 або більше 1. скільки вузлів нам потрібно відвідати? Є рядки з 0 і 1. Підсумовуючи це по всьому дає . Тепер нам потрібно відвідувати кожен із цих вузлів із постійною середньою вартістю за кожний вузол. Ми можемо це зробити, відвідавши першу кожну ліву дитину, а кожну - праворуч другу.nn2nnn(i+ji)iji,jni=0nj=0n(i+ji)=(2n+2n+1)1

Знову використовуючи формулу Стірлінга, ми отримуємо як час роботи нового алгоритму.

(2n+2n+1)1=4n+11n+1(1+O(1/n))1=O(4nn)

Ви повинні бути трохи обережнішими. Імовірно, після генерації кожного рядка ми обробляємо його в час. Тому просто обробка всіх збалансованих рядків вимагає часу . Якщо оптимізований алгоритм "нерозумного" покоління дійсно є , то нічого не можна отримати, перейшовши на розумніший алгоритм, крім можливостей для помилок. Ω(n)Ω(4nn)O(4n)
Yuval Filmus

@Yuval Filmus: Що саме ви маєте на увазі під "обробкою струн"? Якщо ви маєте на увазі час, витрачений на вихід, який, безумовно, , то ви повинні врахувати цей фактор також у часі виконання алгоритму "дурного", який тоді є . Θ(n)O(4nn)
треністор

2
Моя думка полягала в тому, що якщо ви переймаєтесь різницею між та , то як мінімум ви повинні вказати правильні час роботи; недостатньо, щоб виявити різницю потенціалів між двома алгоритмами. Крім того, ви повинні бути обережними, аналізуючи запропонований новий алгоритм, щоб побачити, що ці "незначні" невеликі фактори не роблять його повільніше, ніж тривіальний алгоритм. 4n4n/nO~(4n)
Yuval Filmus

2
Як ви будуєте лише "хорошу частину" дерева, не включаючи також "погані частини"? На шляху від кореня до них потрібно включити всі вузли дерева, у яких не більше лівих дітей чи правих дітей. Це працює, але вам потрібен додатковий аргумент, щоб показати, що він працює. Зокрема, вам потрібно використовувати формулу . nni=0nj=0n(i+ji)=(2n+2n+1)1
Пітер Шор

2
Ми хочемо знайти всі бінарні послідовності з точно 0 та 1. Ми обходимо бінарне дерево, де кожен вузол є послідовністю 0 та 1's. Нам не потрібно відвідувати будь-який вузол з більш ніж 0 або більше 1. скільки вузлів нам потрібно відвідати? Є рядки з 0 і 1. Підсумовуючи це по всьому дає . Тепер нам потрібно відвідувати кожен із цих вузлів із постійною середньою вартістю за кожний вузол. Ми можемо це зробити, відвідавши першу кожну ліву дитину, а кожну - праворуч другу.nn2nnn(i+ji)iji,jni=0nj=0n(i+ji)=(2n+2n+1)1
Пітер Шор

2

Можливо, я товстий, але в оригінальному запитанні було запропоновано спосіб генерувати всі "збалансовані" бінарні послідовності довжиною 2n, які були більш ефективними, ніж обхід дерева всіх бінарних послідовностей довжиною 2n і виведення лише тих, які були врівноважені. То навіщо взагалі використовувати дерево?

Ось псевдокод для рекурсивного алгоритму, який генерує всі такі послідовності (ключове слово "урожай" посилає послідовність на вихід):

function all-balanced(n) {
  all-specified( "", n, n );
};

function all-specified( currentString, zeroes, ones ) {

  if (zeroes == 0) {
    for i = 0 to ones {
      currentString += "1";
    };
    yield currentString;
    return;
  };

  if (ones == 0) {
    for i = 0 to zeroes {
      currentString += "0";
    };
    yield currentString;
    return;
  };

  all-specified( currentString+"0", zeroes-1, ones );
  all-specified( currentString+"1", zeroes, ones-1 );
  return;
};

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

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