Faites le NP: trouvez la plus grande clique

22

Contexte

Au moment d'écrire ces lignes, le problème P vs NP n'est toujours pas résolu, mais vous avez peut-être entendu parler du nouveau document de Norbert Blum prétendant prouver que P! = NP, qui est déjà soupçonné d'être erroné (mais nous verrons).

Le problème discuté dans cet article est le problème de la clique . C'est du moins ce que j'ai lu dans un article de journal, alors corrigez-moi si je me trompe, mais en tout cas, j'aimerais que vous écriviez un programme qui résout la variante suivante:

La tâche

Supposons que nous ayons une grande école avec beaucoup d'étudiants. Chacun de ces élèves a des amis dans cette école. Une clique d'étudiants est un groupe composé uniquement d'étudiants qui sont amis entre eux .

Votre programme recevra des paires d'étudiants amis comme contribution. À partir de ces informations, le programme doit trouver la taille de la plus grande clique . Les étudiants sont identifiés par des identifiants entiers .

Si vous préférez les termes mathématiques, cela signifie que vous êtes alimenté par les bords d'un graphique non orienté, identifié par deux nœuds chacun.

Contribution

Votre entrée sera une liste non vide de paires entières positives, par exemple [[1,2],[2,5],[1,5]]. Vous pouvez prendre cette entrée sous n'importe quelle forme sensible, par exemple comme un tableau de tableaux, comme des lignes de texte contenant deux nombres chacune, etc ...

Sortie

La sortie attendue est un nombre unique n >= 2: la taille de la plus grande clique. Avec l'exemple saisi ci-dessus, le résultat serait 3, car tous les élèves ( 1, 2et 5) sont amis les uns avec les autres.

Cas de test

[[1,2]]
=> 2

[[1,2],[3,1],[3,4]]
=> 2

[[1,2],[2,5],[1,5]]
=> 3

[[2,5],[2,3],[4,17],[1,3],[7,13],[5,3],[4,3],[4,1],[1,5],[5,4]]
=> 4 (the largest clique is [1,3,4,5])

[[15,1073],[23,764],[23,1073],[12,47],[47,15],[1073,764]]
=> 3 (the largest clique is [23,764,1073])

[[1296,316],[1650,316],[1296,1650],[1296,52],[1650,711],[711,316],[1650,52],
 [52,711],[1296,711],[52,316],[52,1565],[1565,1296],[1565,316],[1650,1565],
 [1296,138],[1565,138],[1565,711],[138,1650],[711,138],[138,144],[144,1860],
 [1296,1860],[1860,52],[711,1639]]
=> 6 (the largest clique is [52,316,711,1296,1565,1650])

Vous pouvez utiliser cette implémentation de référence (stupide) (imprime une sortie supplémentaire avec -dindicateur) pour vérifier les résultats d'autres cas de test.

Les règles

  1. Votre programme n'a pas besoin d'un résultat défini sur une entrée non valide. Vous pouvez donc supposer que:
    • vous obtiendrez toujours au moins une paire d'identifiants
    • chaque paire se compose de deux identifiants différents
    • aucune paire n'apparaît deux fois (l'échange des emplacements des identifiants serait toujours la même paire)
  2. Votre algorithme n'est pas autorisé à définir une limite supérieure sur la taille d'entrée. Les limitations purement techniques et les limitations fixées par votre langage / environnement (comme la taille de la pile, le temps de calcul, etc.) sont bien sûr inévitables.
  3. Les failles standard sont interdites.
  4. C'est le , donc le code le plus court, mesuré en octets, gagne.
  5. Si votre algorithme a une complexité temporelle polynomiale, vous marquez -1immédiatement quelle que soit la taille de votre code, mais dans ce cas, vous souhaiterez peut-être soumettre votre solution ailleurs. ;)
Felix Palmen
la source
4
Je peux presque garantir qu'il y aura quelqu'un qui le fera (ou essaiera), il serait donc plus sûr de le retirer. Si vous voulez récompenser les gens de l'avoir fait, vous pouvez offrir une prime à la réponse la plus courte qui le fait en temps polynomial.
caird coinheringaahing
4
@cairdcoinheringaahing si quelqu'un le fait, -1c'est bien mérité ;)
Felix Palmen
13
@cairdcoinheringaahing Si quelqu'un a réussi à prouver que P = NP, c'est lui qui a le score le plus bas automatique sur un problème de golf de code qui est le moindre de nos soucis. Cela dit, la règle 5 ne contribue pas vraiment au défi, je suis donc d'accord pour la supprimer.
Mego
11
@Mego, il contribue simplement une blague et un petit bonus au 1M offert par CMI.
Felix Palmen
30
Eh bien, je ne le ferai pas, en faveur des quelques personnes ayant un certain sens de "l'humour scientifique". Veuillez ne pas commenter plus de suggestions à ce sujet, merci :)
Felix Palmen

Réponses:

6

Gelée ,  15 18  16 octets

+3 octets pour corriger les bugs dans ma méthode.
-2 octets grâce aux miles (notant que n × (n-1) ÷ 2 = nC2 )

ẎQL©c2⁼Lȧ®
ŒPÇ€Ṁ

Un lien monadique prenant la liste des amitiés (bords) et renvoyant un entier.

Essayez-le en ligne! forme le jeu de puissance des bords en mémoire et est donc inefficace à la fois dans l'espace et dans le temps (oui, c'est O (2 n ) )!

Comment?

ẎQL©c2⁼Lȧ® - Link 1, isClique?: list, edges  e.g. [[1,3],[2,3],[3,4],[4,1],[4,2],[2,1]]
Ẏ          - tighten                              [ 1,3 , 2,3 , 3,4 , 4,1 , 4,2 , 2,1 ]
 Q         - de-duplicate (gets unique ids)          [1,3,2,4]
  L        - length (get number of people involved)  4
   ©       - (copy to the register)
    c2     - combinations of 2 (z-choose-2)          6
       L   - length (of edges)                       6
      ⁼    - equal?                                  1
         ® - recall value from register              4
        ȧ  - logical and                             4
           - (Note: the number of edges of a clique of size n is n*(n-1) and we're
           -  guaranteed no repeated edges and that all edges are two distinct ids)

ŒPÇ€Ṁ - Link: list of lists, edges
ŒP    - power-set (all possible sets of edges (as lists))
  Ç€  - call last link (1) as a monad for €ach
    Ṁ - maximum
Jonathan Allan
la source
Wow, explication quand vous avez le temps s'il vous plaît
M. Xcoder
@EriktheOutgolfer Je suis d'accord. Je peux probablement ajouter du code pour récupérer ...
Jonathan Allan
Continuons cette discussion dans le chat .
Erik the Outgolfer
16 octets
miles
@miles - sympa, je viens de passer un peu de temps à essayer d'en obtenir 15, j'ai l'impression que ça devrait être possible!
Jonathan Allan
13

Mathematica, 34 octets

Tr[1^#&@@FindClique[#<->#2&@@@#]]&  

Fondamentalement, FindClique fait le travail et "trouve une plus grande clique dans le graphique g."
Tout le reste convertit la liste d'entrées en graphique

Contribution

[{{2, 5}, {2, 3}, {4, 17}, {1, 3}, {7, 13}, {5, 3}, {4, 3}, {4, 1}, {1, 5}, {5, 4}}]

Sortie

4

Contribution

[{{1296, 316}, {1650, 316}, {1296, 1650}, {1296, 52}, {1650, 711}, {711, 316}, {1650, 52}, {52, 711}, {1296, 711}, {52, 316}, {52, 1565}, {1565, 1296}, {1565, 316}, {1650, 1565}, {1296, 138}, {1565, 138}, {1565 , 711}, {138, 1650}, {711, 138}, {138, 144}, {144, 1860}, {1296, 1860}, {1860, 52}, {711, 1639}}]

Sortie

6

merci @Kelly Lowder pour -10 octets

J42161217
la source
23
Bien sûr, Mathematica a une fonction intégrée pour cela.
Erik the Outgolfer
1
Raser 10 octets avecTr[1^#&@@FindClique[#<->#2&@@@#]]&
Kelly Lowder
12
FindCliqueಠ ___ ಠ
M. Xcoder
6

Gelée , 20 octets

ŒPẎ€µQL’=ċЀ`ẠµÐfṪQL

Essayez-le en ligne!

Bien sûr, cela ne mérite pas le million: p

Cela aurait battu Pyth, sinon pour le µ(...)µet 2 octets Ðf.

Erik le Outgolfer
la source
Incroyable. Je peux aussi bien abandonner maintenant.
Mark Thomas
@FelixPalmen brute force: p
Erik the Outgolfer
@EriktheOutgolfer Je ne voulais pas parler de l'exécution du code;)
Felix Palmen
@FelixPalmen Je veux dire, l'approche par force brute n'a pas besoin de beaucoup de réflexion: p
Erik the Outgolfer
Donne une MemoryError avec le plus grand cas de test :( Bien sûr toujours valable, c'est une "limitation technique" - mais juste par curiosité, y a-t-il un moyen d'augmenter les ressources disponibles avec de la gelée?
Felix Palmen
3

J , 36 octets

[:>./](#(]*[=2!])#@~.@,)@#~2#:@i.@^#

Essayez-le en ligne!

Exécute dans le temps O (2 n ) où n est le nombre de paires.

Une solution plus rapide pour 65 octets est

3 :'$>{._2{~.@((+.&(e.&y)&<|.)@(-.,-.~)&>/#&,/:~@~.@,&.>/)~^:a:y'

Essayez-le en ligne!

Explication

[:>./](#(]*[=2!])#@~.@,)@#~2#:@i.@^#  Input: list of pairs
                                   #  Length
                           2      ^   2^n
                               i.@    Range [0, 2^n)
                            #:@       Binary
                         #~           Copy
      (                )@             For each
                      ,                 Flatten
                   ~.@                  Unique
                 #@                     Length
        (       )                       Dyad with RHS at previous and LHS as next
               ]                          Get RHS
             2!                           Binomial coefficient, choose 2
            =                             Equals
           [                              Get LHS
          *                               Times
         ]                                Get RHS
       #                                Length
[:>./                                 Reduce using maximum
miles
la source
2

Python 2 , 180 octets

G=input()
m=0
L=len
for i in range(2**L(G)):
 u=[];p=sum([G[j]for j in range(L(G))if 2**j&i],u)
 for j in p:u+=[j][j in u:]
 m=max(m,L(u)*all(p.count(j)==L(u)-1for j in u))
print m

Essayez-le en ligne!

-2 grâce à shooqie .
-1 merci à M. Xcoder .
-3 grâce à récursif .

Erik le Outgolfer
la source
Vous pouvez enregistrer deux octets en les affectant lenà une variable
shooqie
183 octets . (x not in y)signifie 0**(x in y).
M. Xcoder
@ Mr.Xcoder Je savais qu'il y avait un moyen de le raccourcir! Merci!
Erik the Outgolfer
Je n'ai jamais utilisé ça auparavant, juste une astuce qui m'a traversé l'esprit il y a quelques jours mais je n'ai pas encore trouvé d'utilité.
M. Xcoder
@ Mr.Xcoder Peu importe, si cela fonctionne, alors pourquoi pas? : D BTW, vous pouvez également remplacer 0**par -~-.
Erik the Outgolfer
1

Pyth, 28 octets

l{sSef<T.{SMQm.{ft{T.Cd2yS{s

Essayez-le en ligne

Explication

l{sSef<T.{SMQm.{ft{T.Cd2yS{s
                         S{sQ  Get the distinct nodes in the (implicit) input.
                        y      Take every subset.
             m      .Cd2       Get the pairs...
                ft{T           ... without the [x, x] pairs...
              .{               ... as sets.
     f<T                        Choose the ones...
        .{  Q                   ... which are subsets of the input...
          SM                    ... with edges in sorted order.
    e                           Take the last element (largest clique).
l{sS                            Get the number of distinct nodes.

la source
1

Python 3 , 162 159 octets

lambda x,f=lambda x:{i for s in x for i in s}:len(f(x))if all([(y,z)in x or(z,y)in x for y in f(x)for z in f(x)if y<z])else max(c(x.difference({y}))for y in x)

Essayez-le en ligne!

La fonction c prend des sommets sous la forme d'un ensemble de tuples triés ({(x, y), ...} où x est inférieur à y). Une fonction appelée "entrée" se trouve dans l'en-tête TIO pour tester avec des données au format liste de listes non triées . Si clique, renvoie la longueur. Si ce n'est pas clique, renvoie la taille maximale de clique des sommets, moins un sommet pour chaque sommet des sommets. Dépasse le temps du dernier cas de test dans TIO

Mise à jour: "ou (z, y) dans x" partie ajoutée pour supprimer la dépendance au tri "f = lambda x: {i pour s dans x pour i dans s}" au lieu de itertools.chain enveloppé dans l'ensemble.

-minus 3 octets grâce à @Jonathan Allen

Conner Johnston
la source
Mis à part - vous n'avez pas besoin de nommer c, vous pouvez donc supprimer c=(vous devez mettre c=\à la fin de l'en-tête et placer le lambdaen haut du bloc de code pour TIO)
Jonathan Allan
Vous pouvezs également vous débarrasser de et remplacer s(...)en {*...}permettant également la suppression de certains espaces.
Jonathan Allan
1
@JonathanAllan merci, tri corrigé
Conner Johnston
1

Gelée , 28 octets

œ^e³;U¤
Œcç/Ðfœ|Ṣ¥/€QµÐĿ-ịḢL

Essayez-le en ligne!

Solution plus rapide capable de résoudre le dernier cas de test en une seconde sur TIO.

miles
la source
Et quelle complexité cela a-t-il? Si elle est inférieure à O (2ⁿ), elle mérite 1 000 000 $.
Erik the Outgolfer
1
@EriktheOutgolfer, vous vous trompez, il existe des algorithmes qui ont un runtime O (1.1888ⁿ) .
rus9384
Ajoutant à cela, pour en valoir un million, le npeut n'apparaître que dans les bases :)
Felix Palmen
@FelixPalmen, ou ça ne peut pas. Quoi qu'il en soit, pour un million, une des deux déclarations doit être prouvée.
rus9384
1
Je crois que c'est O (1,414 ^ n). Vous pouvez voir de moins bonnes performances lorsque l'entrée est un graphique complet.
miles
1

Java + Guava 23,0, 35 + 294 = 329 octets

import com.google.common.collect.*;
a->{int l=0,o=1,c,z=a.size();for(;o>0&l<z;){o=0;c:for(Iterable<int[]>s:Sets.combinations(a,l*(l+1)/2)){Multiset<Integer>m=TreeMultiset.create();for(int[]x:s){m.add(x[0]);m.add(x[1]);}c=m.elementSet().size();for(int e:m.elementSet())if (m.count(e)!=c-1)continue c;l+=o=1;break;}}return z<3?2:l;}

Cet algorithme n'est pas graphique, mais génère à la place toutes les combinaisons de paires, d'une taille spécifique. Je charge toutes les combinaisons de paires dans un multiset et vérifie qu'elles ont toutes la taille attendue (le nombre d'entrées uniques - 1). S'ils le font, j'ai trouvé une clique et je vais en chercher une plus grande.

Depuis la bibliothèque Guava, j'utilise la nouvelle combinationsméthode et le type de collection d'outils Multiset.

Non golfé

import com.google.common.collect.*;
import java.util.function.*;

public class Main {

  public static void main(String[] args) {
    ToIntFunction<java.util.Set<int[]>> f
        = a -> {
          int l = 0, o = 1, c, z = a.size();
          for (; o > 0 & l < z;) {
            o = 0;
            c:
            for (Iterable<int[]> s : Sets.combinations(a, l * (l + 1) / 2)) {
              Multiset<Integer> m = TreeMultiset.create();
              for (int[] x : s) {
                m.add(x[0]);
                m.add(x[1]);
              }
              c = m.elementSet().size();
              for (int e : m.elementSet()) {
                if (m.count(e) != c - 1) {
                  continue c;
                }
              }
              l += o = 1;
              break;
            }
          }
          return z < 3 ? 2 : l;
        };
    int[][][] tests = {
      {{1, 2}},
      {{1, 2}, {3, 1}, {3, 4}},
      {{1, 2}, {2, 5}, {1, 5}},
      {{2, 5}, {2, 3}, {4, 17}, {1, 3}, {7, 13}, {5, 3}, {4, 3}, {4, 1}, {1, 5}, {5, 4}},
      {{15, 1073}, {23, 764}, {23, 1073}, {12, 47}, {47, 15}, {1073, 764}},
      {{1296, 316}, {1650, 316}, {1296, 1650}, {1296, 52}, {1650, 711}, {711, 316}, {1650, 52}, {52, 711}, {1296, 711}, {52, 316}, {52, 1565}, {1565, 1296}, {1565, 316}, {1650, 1565}, {1296, 138}, {1565, 138}, {1565, 711}, {138, 1650}, {711, 138}, {138, 144}, {144, 1860}, {1296, 1860}, {1860, 52}, {711, 1639}}
    };
    for (int[][] test : tests) {
      java.util.Set<int[]> s = new java.util.HashSet<int[]>();
      for (int[] t : test) {
        s.add(t);
      }
      System.out.println(f.applyAsInt(s));
    }
  }
}
Olivier Grégoire
la source
Je serais très surpris, voir Trouver le maximum de cliques dans des graphiques arbitraires - mais il me faudra du temps pour analyser ce code, je ne connais pas trop Java :)
Felix Palmen
@FelixPalmen J'ai aimé ce défi donc ma réponse restera quoi qu'il arrive, mais je suis tout à fait d'accord avec la suppression du "-1" si ce n'est pas une complexité polynomiale. Ensuite, je devrais probablement aller réviser quelques livres: P
Olivier Grégoire
"La combinaison de taille xest polynomiale " <- êtes-vous sûr? Je suppose que c'est la méthode utilisée . La valeur de retour est un AbstractSetavec un itérateur, et la forboucle suivante appellera cet itérateur x!fois si je ne me trompe pas ...
Felix Palmen
Correction: tant que x < n(avec nla taille complète de l'ensemble d'entrée), ce n'est n!/(x!(n-x)!)toujours pas polynomial :)
Felix Palmen
@FelixPalmen Vous avez très probablement raison. De plus, dites-vous que si je fais une combinationsméthode qui est X^n(ce qui est tout à fait possible), je peux l'obtenir? En attendant, je retire ma réclamation du "-1".
Olivier Grégoire
1

Python 2 , 102 octets

def f(g):
 x=g
 while x:y=x;x=map(set,{tuple(u|v)for u in x for v in x if u^v in g})
 return len(y[0])

Essayez-le en ligne!

miles
la source
0

6502 code machine (C64), 774 703 octets

(Je viens à ce faire, mon C64 peut tout faire ... hehe)

hexdump:

00 C0 A9 00 A2 08 9D 08 00 CA 10 FA A2 04 9D FB 00 CA 10 FA 20 54 C0 B0 20 AD 
C9 C2 AE CA C2 20 92 C1 B0 31 8D 31 C0 AD CB C2 AE CC C2 20 92 C1 B0 23 A2 FF 
20 FE C1 90 DB 20 6A C2 20 C1 C1 B0 05 20 6A C2 50 F6 A5 FB 8D D3 C2 20 43 C1 
A9 CD A0 C2 20 1E AB 60 A2 00 86 CC 8E 61 C0 20 E4 FF F0 FB A2 FF C9 0D F0 10 
E0 0B 10 0C 9D BD C2 20 D2 FF E8 8E 61 C0 D0 E5 C6 CC A9 20 20 D2 FF A9 0D 20 
D2 FF A9 00 9D BD C2 AA BD BD C2 F0 5C C9 30 30 0E C9 3A 10 0A 9D CD C2 E8 E0 
06 F0 4C D0 E9 C9 20 D0 46 A9 00 9D CD C2 E8 8E BC C0 20 EB C0 AD D3 C2 8D C9 
C2 AD D4 C2 8D CA C2 A2 FF A0 00 BD BD C2 F0 0F C9 30 30 21 C9 3A 10 1D 99 CD 
C2 C8 E8 D0 EC A9 00 99 CD C2 20 EB C0 AD D3 C2 8D CB C2 AD D4 C2 8D CC C2 18 
60 38 60 A2 FF E8 BD CD C2 D0 FA A0 06 88 CA 30 0A BD CD C2 29 0F 99 CD C2 10 
F2 A9 00 99 CD C2 88 10 F8 A9 00 8D D3 C2 8D D4 C2 A2 10 A0 7B 18 B9 53 C2 90 
02 09 10 4A 99 53 C2 C8 10 F2 6E D4 C2 6E D3 C2 CA D0 01 60 A0 04 B9 CE C2 C9 
08 30 05 E9 03 99 CE C2 88 10 F1 30 D2 A2 06 A9 00 9D CC C2 CA D0 FA A2 08 A0 
04 B9 CE C2 C9 05 30 05 69 02 99 CE C2 88 10 F1 A0 04 0E D3 C2 B9 CE C2 2A C9 
10 29 0F 99 CE C2 88 10 F2 CA D0 D9 C8 B9 CD C2 F0 FA 09 30 9D CD C2 E8 C8 C0 
06 F0 05 B9 CD C2 90 F0 A9 00 9D CD C2 60 85 0A A4 09 C0 00 F0 11 88 B9 D5 C2 
C5 0A D0 F4 8A D9 D5 C3 D0 EE 98 18 60 A4 09 E6 09 D0 01 60 A5 0A 99 D5 C2 8A 
99 D5 C3 98 99 D5 C4 18 60 A6 0B E4 09 30 01 60 BD D5 C5 C5 0B 30 09 A9 00 9D 
D5 C5 E6 0B D0 E9 A8 FE D5 C5 8A 29 01 D0 02 A0 00 BD D5 C4 59 D5 C4 9D D5 C4 
59 D5 C4 99 D5 C4 5D D5 C4 9D D5 C4 A9 00 85 0B 18 60 A8 A5 0C D0 08 A9 20 C5 
0D F0 21 A5 0C 8D 1E C2 8D 21 C2 A5 0D 09 60 8D 1F C2 49 E0 8D 22 C2 8C FF FF 
8E FF FF E6 0C D0 02 E6 0D 18 60 86 0E 84 0F A5 0D 09 60 8D 54 C2 49 E0 8D 5F 
C2 A6 0C CA E0 FF D0 10 AC 54 C2 88 C0 60 10 02 18 60 8C 54 C2 CE 5F C2 BD 00 
FF C5 0E F0 04 C5 0F D0 E0 BD 00 FF C5 0E F0 04 C5 0F D0 D5 38 60 A2 00 86 FC 
86 FD 86 FE BD D5 C4 A8 A6 FE E4 FC 10 11 BD D5 C7 AA 20 2B C2 90 14 E6 FE A6 
FE E4 FC D0 EF A6 FD BD D5 C4 A6 FC E6 FC 9D D5 C7 E6 FD A6 FD E4 09 D0 16 A6 
FB E4 FC 10 0F A2 00 BD D5 C7 9D D5 C6 E8 E4 FC D0 F5 86 FB 60 A0 00 84 FE F0 
B5

Démo en ligne

Utilisation: Commencez par sys49152, puis entrez les paires une par ligne comme par exemple

15 1073
23 764
23 1073
12 47
47 15
1073 764

L'arrière - plan n'est pas géré pendant la saisie (mais si vous l'utilisez vice, copiez et collez simplement votre saisie dans l'émulateur). Saisissez une ligne vide pour démarrer le calcul.

C'est trop grand pour publier une liste explicative de démontage ici, mais vous pouvez parcourir la source d'assemblage de style ca65 . L'algorithme est très inefficace, il génère toutes les permutations possibles des nœuds et avec chacun d'eux construit goulûment une clique en vérifiant tous les bords. Cela permet une efficacité d'espace de O (n) (un peu important sur une machine avec ce petit RAM), mais a une efficacité d'exécution horrible (*) . Les limites théoriques sont jusqu'à 256 nœuds et jusqu'à 8192 bords.

  • -71 octets: routine optimisée pour vérifier les bords et l'utilisation des zéros

Il existe une version plus grande ( 883 805 octets) avec de meilleures fonctionnalités:

  • rétroaction visuelle lors du calcul (chaque permutation des nœuds change la couleur de la bordure)
  • utilise le changement de banque pour stocker les bords dans la RAM "cachés" par les ROM pour économiser de l'espace
  • affiche la taille et les nœuds de la clique maximale trouvée

Démo en ligne

Parcourir la source


(*) Le dernier cas de test prend entre 12 et 20 heures (je dormais quand il a finalement fini). Les autres cas de test se terminent au pire en quelques minutes.

Capture d'écran du dernier cas de test

Felix Palmen
la source