Python: как сгенерировать все комбинации списков кортежей без повторения содержимого кортежа

Я работаю над загадкой:

Учитывая словарь с кортежами для ключей: dictionary = {(p,q):n}, мне нужно сгенерировать список новых словарей для каждой комбинации, чтобы ни p, ни q не повторялись в новом словаре. И во время создания этого списка словарей или после этого выберите один из словарей в качестве желаемого на основе расчета с использованием значений словаря.

пример того, что я имею в виду (но намного меньше):

dictionary = {(1,1): 1.0, (1,2): 2.0, (1,3): 2.5, (1,4): 5.0, (2,1): 3.5, (2,2): 6.0, (2,3): 4.0, (2,4): 1.0}

становится

listofdictionaries = [{(1,1): 1.0, (2,2): 6.0}, {(1,1): 1.0, (2,3): 4.0}, (1,1): 1.0, (2,4): 1.0}, {(1,2): 2.0, (2,1): 3.5}, {(1,2): 2.0, (2,3): 4.0}, и т. Д.

словарь вида: {(1,1): 1.0, (2,1): 3.5} недопустим, потому что q повторяется.

Теперь моя печальная история: я новичок в программировании ... но я пытался написать этот сценарий для анализа некоторых моих данных. Но я также думаю, что это интересная загадка алгоритма. Я написал что-то, что работает с очень маленькими словарями, но когда я ввожу большой, его запуск занимает слишком много времени (скопировано ниже). В моей попытке скрипта я фактически сгенерировал список комбинаций кортежей, которые я использую для обращения к моему главному словарю позже в скрипте. Скопирую ниже:

Ключи словарного кортежа были созданы с использованием двух списков: «ExpList1» и «ExpList2»

#first, I generate all the tuple combinations from my ExpDict dictionary
combos =(itertools.combinations(ExpDict,min(len(ExpList1),len(ExpList2))))

#then I generate a list of only the combinations that don't repeat p or q
uniquecombolist = []
for foo in combos:
    counter = 0
    listofp = []
    listofq = []
    for bar in foo:
        if bar[0] in listofp or bar[1] in listofq:
            counter=+1
            break
        else:
            listofp.append(bar[0])
            listofq.append(bar[1])
    if counter == 0:
        uniquecombolist.append(foo)

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

Я также попытался применить эту функцию, перебирая комбинации, выбирая уникальные p, q, а затем проверяя, меньше ли результирующее значение, чем предыдущее, и сохраняя его, если это так (это вместо создания этого списка «uniquecombolist», я в конечном итоге создается только окончательный список кортежей) - все еще занимает слишком много времени.

Я думаю, что решение заключается во внедрении p, q-no-repeat и последней функции выбора ВО ВРЕМЯ генерации комбинаций. Я просто не могу понять, как это сделать.

Спасибо за прочтение! Сара

РЕДАКТИРОВАТЬ:

Чтобы уточнить, я написал альтернативу своему коду, которая включает конечную функцию (в основном среднеквадратичное значение) для наборов пар.

`combos =(itertools.combinations(ExpDict,min(len(ExpList1),len(ExpList2))))


prevRMSD = float('inf')
for foo in combos:
    counter = 0
    distanceSUM = 0
    listofp = []
    listofq = []
    for bar in foo:
        if bar[0] in listofp or bar[1] in listofq:
            counter=+1
            break
        else:
            listofp.append(bar[0])
           listofq.append(bar[1])
        distanceSUM = distanceSUM + RMSDdict[bar]
    RMSD = math.sqrt (distanceSUM**2/len(foo))
    if counter == 0 and RMSD< prevRMSD:
        chosencombo = foo
        prevRMSD = RMSD`

Так что, если бы я мог включить расчет RMS во время генерации набора и оставить только самое маленькое, я думаю, это решит мою комбинаторную проблему.


person Sara    schedule 04.10.2017    source источник
comment
Вы хотите создать все возможные наборы пар, удовлетворяющие вашим критериям? Или возможный размер n наборов пар, где n - длина меньшего генерирующего списка?   -  person Jared Goguen    schedule 04.10.2017
comment
@JaredGoguen каждая пара - это отдельная запись в наборе. В наборе есть n пар, потому что p и q не могут повторяться, поэтому он должен быть ограничен размером меньшего списка генерации. Я хочу сгенерировать каждый возможный набор, учитывая два списка пар кортежей (или два словаря с ключами кортежа).   -  person Sara    schedule 04.10.2017
comment
Я попытался посмотреть код для itertools.combinations, но, честно говоря, просто не могу понять его, чтобы работать в моих собственных условиях для комбинаций и даже для последней функции, которую мне нужно применить. Я посмотрел на stackoverflow.com/ questions / 24907913 /, но, к сожалению, до сих пор не очень понимаю, как это работает. Как я уже сказал в своем посте, я новичок в этом (я написал еще один сценарий и никогда не проходил никаких курсов по информатике), так что, возможно, я откусываю больше, чем могу прожевать.   -  person Sara    schedule 04.10.2017


Ответы (2)


Если я понял вашу проблему, вас интересуют все возможные комбинации пар (p, q) с уникальными p и q с учетом заданного набора возможных значений для p и q. В своем ответе я предполагаю, что эти возможные значения находятся, соответственно, в list_p и list_q (я думаю, что это то, что у вас есть в ExpList1 и ExpList2, я прав?)

min_size = min(len(list_p), len(list_q))

combos_p = itertools.combinations(list_p, min_size)
combos_q = itertools.permutations(list_q, min_size)
prod = itertools.product(combos_p, combos_q)
uniquecombolist = [tuple(zip(i[0], i[1])) for i in prod]

Сообщите мне, если это то, что вы ищете. Кстати, добро пожаловать в SO, отличный вопрос!


Редактировать:

Если вас беспокоит, что ваш список может стать огромным, вы всегда можете использовать выражение генератора и применить к нему любую функцию, которую вы хотите, например,

min_size = min(len(list_p), len(list_q))

combos_p = itertools.combinations(list_p, min_size)
combos_q = itertools.permutations(list_q, min_size)
prod = itertools.product(combos_p, combos_q)
uniquecombo = (tuple(zip(y[0], y[1])) for y in prod) # this is now a generator expression, not a list -- observe the parentheses

def your_function(x):
    # do whatever you want with the values, here I'm just printing and returning
    print(x)
    return x

# now prints the minimum value
print(min(itertools.imap(your_function, uniquecombo)))

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

person hugos    schedule 04.10.2017
comment
Я думаю, что все равно столкнусь с проблемой, что список слишком велик, чтобы определить, какой набор пар кортежей дает наименьшее конечное значение (после ввода каждого набора в функцию). Я отредактирую сообщение выше, чтобы включить свою функцию. Я оставил его, чтобы этот пост оставался более общим, но я думаю, что он поможет читателям понять, что я пытаюсь сделать. - person Sara; 04.10.2017
comment
Если это действительно то, что вы хотите, я постараюсь немного лучше объяснить, что я сделал - person hugos; 04.10.2017
comment
Тем не менее, он элегантно решает первый раунд выбора набора! Спасибо :) Дайте мне знать, что вы думаете о моей редакции выше - person Sara; 04.10.2017
comment
Общность - это здорово, и это то, к чему вы должны стремиться при публикации в Stack Overflow. Я проверю твою правку. - person hugos; 04.10.2017
comment
Таким образом, кажется, что вы обеспокоены созданием слишком большого списка, прежде чем применять свою функцию. В этом случае вам не нужно сначала создавать список, вместо этого используйте выражение генератора. Я покажу тебе как - person hugos; 04.10.2017
comment
Привет, извините за задержку с ответом. Я был очень занят другой школьной работой ... этот сценарий - небольшой побочный проект / головоломка. Итак, я попробовал описанное выше сегодня вечером, и снова он работает, но также приводит к сбою моего компьютера, если я пытаюсь составить списки, содержащие более 5 или 6 записей. Я думаю, мне нужно сделать это совершенно по-другому, чтобы не нужно было искать и вычислять каждую комбинацию в моем списке. При использовании этого метода время выполнения всегда увеличивается на факториал с размером ввода (как указано выше Джаредом). Большое спасибо за вашу помощь, все равно было весело! - person Sara; 16.10.2017

В этом ответе предполагается, что вы пытаетесь создать наборы с помощью | S | elements, где S - меньший пул координат кортежа. Бассейн большего размера будет обозначен L.

Поскольку набор будет содержать | S | пары без повторяющихся элементов, каждый элемент из S должен встречаться ровно один раз. Отсюда сопоставьте перестановки L, где | S | элементы выбираются с помощью упорядоченных элементов S. Это сгенерирует все запрошенные наборы исчерпывающе и без повторения.

Обратите внимание, что P (| L |, | S |) равно | L |! / (| L | - | S |)!

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

Некоторый код для репликации этого перечисления может выглядеть так:

from itertools import permutations 

S, L = range(2), range(4) # or ExpList1, ExpList2
for p in permutations(L, len(S)):
    print(zip(S, p))

В целом ваш окончательный код может выглядеть примерно так:

S, L = ExpList1, ExpList2
pairset_maker = lambda p: zip(S, p)

if len(S) > len(L):
    S, L = L, S
    pairset_maker = lambda p: zip(p, S)

n = len(S)   
get_perm_value = lambda p: math.sqrt(sum(RMSDdict[t] for t in pairset_maker(p))**2/n)

min_pairset = min(itertools.permutations(L, n), key=get_perm_value)

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

person Jared Goguen    schedule 04.10.2017
comment
Да, я понял это, поэтому в идеале я хотел бы применить свою функцию во время генерации комбинаций ... В конце концов, я использую функцию, чтобы найти набор кортежей, который вычисляет наименьшее значение, поэтому, если Я мог бы включить эту функцию в код, генерирующий уникальные наборы, чтобы он проверял каждую новую комбинацию, чтобы увидеть, меньше ли она, чем предыдущая, а затем сохранял меньшую из двух, я думаю, это может сработать? Что вы думаете? - person Sara; 04.10.2017
comment
также я ищу комбинации, а не перестановки - person Sara; 04.10.2017
comment
Существует взаимно однозначное соответствие между комбинациями пар, как вы их описываете, и перестановками L, описанными в ответе выше. - person Jared Goguen; 04.10.2017
comment
Фрагмент кода не создает ожидаемых наборов пар? - person Jared Goguen; 04.10.2017
comment
Кроме того, объекты, генерируемые itertools и генераторами, что означает, что они ленивы и не производят элементы до тех пор, пока они не понадобятся (т.е. нет гигантского списка перестановок, плавающих в памяти). Итак, применение ваших функций к кортежу и сохранение только большего из них ничего не даст, я не думаю. - person Jared Goguen; 04.10.2017
comment
Спасибо за помощь Джареду. Я не мог вернуться и поработать над этим до сегодняшнего вечера. Я согласен с вашим комментарием выше о том, что у него может быть слишком много перестановок для перечисления ... независимо от того, как я это делаю. Мне нужно думать о совершенно другом способе делать то, что я хочу делать. - person Sara; 16.10.2017