Генерация шаров в коробках

Имея два отсортированных вектора a и b, найдите все векторы, которые являются суммами a и некоторой перестановки b и которые уникальны после сортировки.

Создать один из искомых векторов можно следующим образом:

  • Возьмите вектор a и перестановку вектора b.
  • Сложите их так, чтобы получилось c[i]=a[i]+b[i].
  • Сортировать c.

Мне интересно найти набор b-перестановок, которые дают полный набор уникальных c векторов.

Пример 0: a='ccdd' и b='xxyy'
Дает суммированные векторы: 'cycydxdx', 'cxcxdydy', 'cxcydxdy'.
Обратите внимание, что перестановки b: 'xyxy' и 'yxyx' равны, потому что в обоих случаях "поле c" и "коробка d" получают ровно одно 'x' и одно 'y'.

Я предполагаю, что это похоже на размещение M шаров в M ящиках (по одному в каждом), при этом некоторые группы шаров и ящиков идентичны.
Обновление: Учитывая строку a='aabbbcdddd' и b='xxyyzzttqq', ваша задача будет 10 мячи в 4 коробках. Есть 4 различных ящика размера 2, 3, 1 и 4. Шары попарно неразличимы.

Пример 1. Даны строки a='xyy' и b='kkd'.
Возможное решение: 'kkd', 'dkk'.
Причина: Мы видим, что все уникальные перестановки b равны 'kkd', 'kdk' и 'dkk'. Однако с нашими ограничениями две первые перестановки считаются равными, так как индексы, в которых различия сопоставляются с одним и тем же символом 'y' в строке a.

Пример 2. Даны строки a='xyy' и b='khd'.
Возможное решение: 'khd', 'dkh', 'hkd'.

Пример 3. Даны строки a='xxxx' и b='khhd'.
Возможное решение: 'khhd'.

Я могу решить проблему создания уникальных кандидатов b перестановок, используя алгоритм Нараяны Пандиты, как описано в Wikipedia/Permutation.
Вторая часть кажется сложнее. Мой лучший способ — попарно соединить две строки в списке, отсортировать его и использовать в качестве ключа в наборе поиска. ('xx'+'hd' соединение→'xh','xd' сортировка→'xd','xh').

Поскольку мои M часто бывают очень большими, а сходство в строках является обычным явлением, в настоящее время я генерирую гораздо больше b перестановок, чем на самом деле проходит через заданный фильтр. Я хотел бы иметь алгоритм, генерирующий правильные напрямую. Любое улучшение приветствуется.


person Thomas Ahle    schedule 18.06.2010    source источник
comment
У меня действительно проблемы с пониманием вашего ограничения и примеров. Не могли бы вы перефразировать его более формально? Формулировка также немного сложна для понимания (например, индексы, на которых различия отображаются на один и тот же символ).   -  person John Feminella    schedule 19.06.2010
comment
Тоже с этим туго. Может быть, вы считаете b[i] равным b[j], если a[i] == a[j]?! Итак, в примере 1 с i = 1 и j = 2, поскольку оба являются «y» в a, они считаются равными в b?   -  person Eiko    schedule 19.06.2010
comment
Я не уверен, что понимаю ваш вопрос. Сможете ли вы из первого предложения составить полное предложение?   -  person Justin L.    schedule 19.06.2010
comment
Хорошо, я снова прояснил первую часть. @Eiko: В примере один i=1 и j=2 и a[i]=a[j]. Таким образом, перестановки, которые отличаются только i из j, равны.   -  person Thomas Ahle    schedule 20.06.2010


Ответы (2)


Для создания k-комбинаций возможных повторяющихся элементов (мультимножества) может быть полезно следующее: Код Грея для комбинаций мультимножества (1995).

Для рекурсивного решения попробуйте следующее:

Подсчитайте, сколько раз появляется каждый символ. Скажем, они равны x1 x2 ... xm, что соответствует m различным символам.

Затем нужно найти все возможные упорядоченные пары (y1 y2 ... ym) такие, что

0 <= yi <= xi

и Сумма yi = k.

Здесь yi — количество раз появления символа i.

Идея состоит в том, чтобы исправить количество появлений символа 1 (y1). Затем рекурсивно сгенерируйте все комбинации k-y1 из оставшихся.

псевдокод:

List Generate (int [] x /* array index starting at 1*/, 
               int k /* size of set */) {

    list = List.Empty;

    if (Sum(x) < k) return list;

    for (int i = 0; i <= x[1], i++) {

        // Remove first element and generate subsets of size k-i.

        remaining = x.Remove(1);

        list_i = Generate(remaining, k-i);

        if (list_i.NotEmpty()) {

            list = list + list_i;    

        } else {

            return list;
        }

    }

    return list;
}

ДО РЕДАКТИРОВАНИЯ:

Если я правильно понял, вам нужно посмотреть на строку a, увидеть символы, которые встречаются ровно один раз. Скажем, таких символов k. Затем вам нужно сгенерировать все возможные перестановки b, которые содержат k элементов, и сопоставить эти символы в соответствующих позициях. Остальное вы можете игнорировать/заполнять по своему усмотрению.

Я помню, как публиковал код С# для этого здесь: >Как найти перестановку k заданной длины?

Я предполагаю, что xxyy даст только 1 уникальную строку, а те, которые появляются ровно один раз, являются «отличительными» точками.

Например, в случае a=xyy, b=add

отличительная черта х

Таким образом, вы выбираете перестановки «добавить» длины 1. Это дает вам a и d.

Таким образом, вам нужны add и dad (or dda).

Для a=xyyz b=good

отличительные точки x и z

Итак, вы генерируете перестановки b длины 2, дающие

go
og
oo
od
do
gd
dg

давая вам 7 уникальных перестановок.

Это помогает? Правильно ли я понимаю?

person Community    schedule 18.06.2010
comment
Нет, извините, мое новое объяснение лучше? - person Thomas Ahle; 19.06.2010
comment
Нет, решение не работает. Это ясно для случая, когда в a нет уникальных символов? - person Thomas Ahle; 20.06.2010
comment
@THomas: при отсутствии уникальных символов этот метод генерирует ровно один. Во всяком случае, это было до ваших правок. Возможно, вам следует привести более длинные/больше примеров (с тем, какой результат вы ожидаете), чтобы было понятнее. - person ; 20.06.2010
comment
Если вы видите пример 0, вы заметите, что a='xxyy', b='ccdd' дают три уникальных решения. - person Thomas Ahle; 20.06.2010
comment
Я думаю, что нашел способ решить проблему, но для этого требуется алгоритм, который дает все комбинации длины k строки, игнорируя дубликаты: combs('aabc',2)=['aa','ab','bc','ca']. Вы знаете хоть одного такого? - person Thomas Ahle; 20.06.2010
comment
@Thomas: я добавил ссылку на свой ответ, который, вероятно (не уверен, поскольку я не прочитал его полностью) можно использовать для реализации, которая использует очень мало памяти (что-то вроде next_combination). Рекурсивные решения должны быть возможны, но я ожидаю, что они будут использовать много памяти. - person ; 20.06.2010

Хорошо, извините, я никогда не мог четко объяснить проблему, но вот решение.

Нам нужны две функции combinations и runvector(v). combinations(s,k) генерирует уникальные комбинации мультимножества длины k. Для s='xxyy' это будет ['xx','xy','yy']. runvector(v) преобразует мультимножество, представленное в виде отсортированного вектора, в более простую структуру, вектор выполнения. runvector('cddeee')=[1,2,3].

Для решения задачи воспользуемся рекурсивными генераторами. Мы прогоняем все комбинации, которые подходят для поля 1, и обращение к остальным полям, запрещая значения, которые мы уже выбрали. Чтобы выполнить запрет, combinations будет поддерживать битовый массив вызовов.

В питоне подход выглядит так:

def fillrest(banned,out,rv,b,i):
    if i == len(rv):
        yield None
        return
    for comb in combinations(b,rv[i],banned):
        out[i] = comb
        for rest in fillrest(banned,out,rv,b,i+1):
            yield None

def balls(a,b):
    rv = runvector(a)
    banned = [False for _ in b]
    out = [None for _ in rv]
    for _ in fill(out,rv,0,b,banned):
        yield out[:]

>>> print list(balls('abbccc','xyyzzz'))
[['x', 'yy', 'zzz'],
 ['x', 'yz', 'yzz'],
 ['x', 'zz', 'yyz'],
 ['y', 'xy', 'zzz'],
 ['y', 'xz', 'yzz'],
 ['y', 'yz', 'xzz'],
 ['y', 'zz', 'xyz'],
 ['z', 'xy', 'yzz'],
 ['z', 'xz', 'yyz'],
 ['z', 'yy', 'xzz'],
 ['z', 'yz', 'xyz'],
 ['z', 'zz', 'xyy']]

Выходные данные имеют формат «коробки», но могут быть легко объединены обратно в простые строки: 'xyyzzzz', 'xyzyzz'...

person Thomas Ahle    schedule 20.06.2010
comment
Если вы закончили с вопросом, вы должны принять свой собственный ответ. - person Greg Kuperberg; 30.06.2010