Mauvaise nouvelle, quelqu'un

10

Dans l'épisode Futurama, le prisonnier de Benda, les membres de l'équipage échangent leurs corps les uns avec les autres, avec la capture qu'aucune paire de corps ne peut avoir leur esprit échangé plus d'une fois.

Défi

Écrivez un programme ou une fonction qui accepte une collection valide de permutations corps-esprit qui se sont déjà produites et génère un ensemble légal de permutations qui ramènera chaque esprit à son corps d'origine. Les identifiants de ces collections corps-esprit doivent être des chaînes qui ne contiennent pas de nouvelles lignes. Vous pouvez ajouter jusqu'à deux personnes (distinctement nommées) qui n'ont eu aucun échange préalable dans le groupe d'entrée. (Preuve que vous n'avez besoin que d'au plus 2 corps supplémentaires) Cependant, vous devez ajouter le nombre minimum de personnes nécessaires pour résoudre le problème.

L'entrée et la sortie peuvent prendre n'importe quelle forme claire, cependant, aucune information supplémentaire ne peut être stockée dans l'un ou l'autre. Vous pouvez supposer qu'il est toujours valide. C'est le golf de code, donc le gagnant est la soumission avec le moins d'octets.

Exemples

[('A','B'),('C','D')] -> [('A','C'),('B','D'),('A','D'),('B','C')]

['A','B'] -> ['C','D','A','C','B','D','A','D','B','C']

[('A','B'),('C','D'),('A','C'),('A','D')] -> [('B', 'E'), ('A', 'E'), ('C', 'B'), ('C', 'E')]

"A\nB\nC\nD\n" -> "A\nC\nB\nD\nA\nD\nB\nC\n"

Celui du spectacle:

[("Amy","Hubert"),("Bender","Amy"),("Hubert","Turanga"),("Amy","Wash Bucket"),("Wash Bucket","Nikolai"),("Phillip","John"),("Hermes","Turanga")]

La solution de l'émission, donnée ci-dessous, n'est pas valide:

[("Clyde","Phillip"),("Ethan","John"),("Clyde","John"),("Ethan",Phillip"),("Clyde","Hubert"),("Ethan","Wash Bucket"),("Clyde","Leela"),("Ethan","Nikolai"),("Clyde","Hermes"),("Ethan","Bender"),("Clyde","Amy"),("Ethan","Hubert"),("Clyde","Wash Bucket")]

Ceci n'est pas valide car Ethan et Clyde ne sont pas nécessaires en raison du peu de Fry Phillip, Zoidberg John et Hermes Hermes qui ont utilisé la machine. Une solution valable pour ce cas est fournie ci-dessous:

[("Philip","Hubert"),("John","Wash Bucket"),("Philip","Turanga"),("John","Nikolai"),("Philip","Hermes"),("John","Bender"),("Philip","Amy"),("John","Hubert"),("Philip","Wash Bucket")]

Notez qu'il existe clairement de nombreuses réponses possibles pour toute entrée valide. Tout est valide.

FryAmTheEggman
la source
Y a-t-il des noms dont nous pouvons supposer qu'ils ne seront pas utilisés?
feersum
@feersum Non, une partie du défi;)
FryAmTheEggman
1
@feersum Vous voulez dire que si vous avez pris la totalité de l'entrée sous forme de chaîne? Alors oui, cependant, vous pouvez supposer que les noms n'auront pas de nouvelle ligne entre eux. (éditant ceci maintenant)
FryAmTheEggman
1
Votre solution pour l'entrée de l'émission ne fonctionne pas. Amy et Bender sont échangés à la fin. Une solution correcte serait[('Nikolai', 'Phillip'), ('Nikolai', 'Hubert'), ('Nikolai', 'Turanga'), ('Nikolai', 'Bender'), ('Phillip', 'Amy'), ('John', 'Wash Bucket'), ('Nikolai', 'John'), ('Phillip', 'Wash Bucket'), ('Hubert', 'John'), ('Bender', 'Hermes')]
Jakube
1
@Jakube Désolé, il semble que j'ai fait une faute de frappe en entrant dans la situation pour l'émission. Je crois que c'est corrigé maintenant, et la solution est correcte.
FryAmTheEggman

Réponses:

3

Python 3: 328 caractères (lent), 470 caractères (rapide)

Probablement un peu trop long pour une réponse sérieuse.

Code lent et court:

from itertools import*
def d(u,s):i,j=map(u.index,s);u[i],u[j]=u[j],u[i]
def f(Q):
 n=set()
 for s in Q:n|=set(s)
 n=list(n)
 while 1:
  for t in permutations(i for i in combinations(n,2)if not set((i,i[::-1]))&set(Q)):
   u=n[:];j=0
   for s in Q:d(u,s)
   for s in t:
    j+=1;d(u,s)
    if n==u:return t[:j]
  n+=[''.join(n)]

d(u,s)applique un swap sà u. Dans la méthode principale f(Q), je génère d'abord la liste de toutes les personnes nutilisant les opérations set et convertissant le résultat en liste. La while 1boucle n'est bien sûr pas une boucle infinie. Dans ce document, j'essaie aussi de résoudre le problème en utilisant les personnes que j'ai n. Si cela ne réussit pas, j'ajoute une autre personne en combinant tous les noms n+=[''.join(n)]. Par conséquent, la while 1boucle est exécutée au plus 3 fois (voir la preuve dans la question).

La résolution du problème se fait par bruteforce. Je génère tous les swaps légaux et essaie toutes les permutations for t in permutations(i for i in combinations(n,2)if not set((i,i[::-1]))&set(Q)). Si chaque personne est dans son propre corps, je retourne la séquence des échanges.

Usage:

print(f([('A','B'),('C','D')]))
print(f([('A','B')]))
print(f([('A','B'),('C','D'),('A','C'),('A','D')]))

L'exemple de futurama prend beaucoup trop de temps. Il y a 9 personnes, il y a donc 36 swaps possibles et 28 d'entre eux sont légaux. Il y en a donc 26! permutations légales.

Code plus rapide

def w(u,s):i,j=map(u.index,s);u[i],u[j]=u[j],u[i]
def f(Q):
 n=set()
 for s in Q:n|=set(s)
 while 1:
  n=list(n);u=n[:];l=len(n)
  for s in Q:w(u,s)
  for d in range((l*l-l)//2-len(Q)+1):r(n,u,Q,[],d)
  n+=[''.join(n)]
def r(n,u,Q,t,d):
 m=0;v=u[:];l=len(u)
 for i in range(l):
  if n[i]!=v[i]:m+=1;w(v,(n[i],v[i]))
 if m<1:print(t);exit()
 for i in range(l*l):
  s=n[i//l],n[i%l]
  if m<=d and i//l<i%l and not set([s,s[::-1]])&set(Q+t):v=u[:];w(v,s);r(n,v,Q,t+[s],d-1)

La fonction f(Q)a une approche d'approfondissement itérative. Il essaie d'abord profondeur = 0, puis profondeur = 1, jusqu'à profondeur = (l * ll) // 2-len (Q), qui est le nombre maximal de mouvements légaux. Comme le code plus lent, il ajoute ensuite une autre personne et réessaye.

La fonction récursive r(n,u,Q,t,d)essaie de résoudre la position actuelle uavec des dswaps. nest la position résolue, Ql'entrée se déplace et tles mouvements déjà effectués. Il calcule d'abord une limite inférieure des swaps mnécessaires (en résolvant l'état avec des swaps légaux et illégaux). Si m== 0, toutes les personnes sont dans le bon corps, donc il imprime la solution t. Sinon, il essaie tous les swaps possibles s, if m<d(élagage), d>1(qui est déjà inclus dans m<d, i//l<i%l(ne pas autoriser les swaps comme ('A','A')ou ('A','B')et ('B','A')) et not set([s,s[::-1]])&set(Q+t)( sn'a pas encore été effectué).

Usage:

f([("Amy","Hubert"),("Bender","Amy"),("Hubert","Turanga"),("Amy","Wash Bucket"),("Wash Bucket","Nikolai"),("Philip","John"),("Hermes","Turanga")])

Il trouve les échanges optimaux pour le problème futurama en environ 17 secondes sur mon ordinateur portable en utilisant du pypy et environ 2 minutes sans pypy. Notez que les deux algorithmes génèrent des solutions différentes, lors de l'appel avec le même paramètre. C'est à cause de l'algorithme de hachage d'un python- set. nstocke la personne à chaque fois dans un ordre différent. Par conséquent, l'algorithme peut être plus rapide ou plus lent à chaque exécution.

Edit: le scénario de test futurama d'origine était faux, le scénario de test corrigé a une solution optimale de 9 au lieu de 10, et est donc plus rapide. 2 secondes avec pypy, 7 secondes sans.

Jakube
la source