Est-il possible de trouver si une séquence existe en temps polynomial dans le problème suivant?


27

Je réfléchis au problème suivant depuis un certain temps et je n'ai pas trouvé de solution polynomiale pour cela. Uniquement source brute. J'ai essayé de réduire également un problème NP-Complete sans succès.

Voici le problème :


Vous avez un ensemble trié de paires d'entiers positifs. {(A1,B1),(A2,B2),,(An,Bn)}

(Ai,Bi)<(Aj,Bj)Ai<Aj(Ai=AjBi<Bj) (Ai,Bi)=(Aj,Bj)Ai=AjBi=Bj

L'opération suivante peut être appliquée à une paire: Swap(pair). Il échange les éléments de la paire, donc deviendra(10,50)(50,10)

Lorsqu'une paire de l'ensemble est échangée, l'ensemble est automatiquement trié à nouveau (la paire échangée n'est pas à sa place et elle sera déplacée à sa place dans l'ensemble).

Le problème consiste à voir s'il existe une séquence qui, à partir d'une paire, permute l'ensemble entier, avec la condition suivante:

Après l'échange d'une paire, la paire suivante à échanger doit être soit le successeur soit la paire de prédécesseurs de l'ensemble.


Ce serait formidable de trouver une solution temporelle polynomiale à ce problème, ou une réduction d'un problème NP-Complete.

Remarque:
C'est déjà un problème de décision. Je ne veux pas savoir quelle est la séquence: seulement si une séquence existe.

Exemple de tri de l'ensemble après échange d'une paire

(6, 5)
(1,2)
(3,4)
(7,8)

Si j'échange la première paire, cela devient: , et après avoir trié l'ensemble (en plaçant la paire triée dans sa nouvelle position), nous avons:(5,6)

(1,2)
(3,4)
(5,6)
(7,8)

Ensuite, je dois échanger la paire (prédécesseur) ou (successeur), et répéter le processus jusqu'à ce que toutes les paires soient échangées (si possible).(3,4)(7,8)

Important:
vous ne pouvez pas échanger une paire déjà échangée.
S'il y a une séquence d'opérations de «permutation», alors toutes les paires doivent être renommées une fois et une seule fois.

Exemple où il n'est pas possible d'échanger toutes les paires

( 1 , 4 ) ( 3 , 2 ) ( 5(0,0)
(1,4)
(3,2)
(5,5)


1
La liste est-elle triée après avoir renommé le fichier et avant de choisir le fichier suivant à renommer? Pouvez-vous réécrire la condition de tri comme suit: ssi ( A < A ) ou ( A = A et B < B ) ou ( A = A Et B = B et C < C (A,B,C)<(A,B,C)A<AA=AB<BA=AB=BC<C)?
mjqxxxx

3
Les problèmes d'affectation ne sont pas les bienvenus sur cstheory.stackexchange.com en général.
Tsuyoshi Ito

3
Hmm, je ne suis pas sûr. Habituellement, la logique ici est que ce n'est pas une bonne pratique de répondre aux questions typiques des devoirs, car cela ruinerait le but des devoirs pour quelqu'un à l'avenir. Mais dans ce cas, le problème ne ressemble pas à un problème typique .
Tsuyoshi Ito

2
peut-être que si vous donnez une motivation différente de "c'était un devoir", les gens pourraient s'y intéresser et ce ne sera pas fermé. Quelle pourrait être une application possible de cela?
Marcos Villagra

2
sur la reformulation du problème, vous pouvez oublier les fichiers et le voir de cette façon. Vous avez un ensemble de paires d'entiers positifs , et les règles sont les mêmes que vous le dites. Initialement est trié dans la première colonne, puis vous commencez à renommer les points. A={(x1,y1),,(xn,yn)}
Marcos Villagra

Réponses:


16

... J'ai cherché quelques modèles pour construire une réduction à partir d'un problème de PNJ, mais je n'ai pas trouvé un moyen de représenter un "flux" avec un "fork" ...

Donc (après quelques travaux) c'est un algorithme polynomial ...

ALGORITHME

La liste de départ peut être vue comme un tableau de " trous " consécutifs . Pour chaque paire initiale ( a j , b j ) , mettez "l' élément " b j au numéro de trou a j . Chaque paire peut être considérée comme un bord dirigé de la position a j à la position b j . Un mouvement consiste à ramasser un élément b j à la position a j et à le déplacer vers sa position de destination b jN2(aj,bj)bjajajbjbjajbj(le trou de destination devient une cheville inamovible ). Nous supprimons l'arête et choisissons le mouvement suivant qui commencera à partir de l'un des deux éléments accessibles les plus proches depuis la position b j (seuls les trous entre b j et b k sont autorisés). Il faut trouver une séquence de N coups consécutifs.bkbjbjbkN

  • Pour chacun considérons b j (à la position du tableau a j ) comme élément de départ s t a r t .(aj,bj)bjajstart

    • Pour chacun considère a k comme l'élément final e n d (le bord de la position a k à la position b k sera le bord final).(ak,bk),akajakendakbk

      • générer une séquence de mouvements à partir de utilisant les critères suivants jusqu'à ce que vous atteigniez l'élément e n d (et qu'une solution ait été trouvée), ou une condition d'arrêtstartend

Lorsque vous effectuez un mouvement, vous fixez une cheville à la position et le tableau est divisé en deux partitions L (gauche) et R (droite) et la seule façon de passer de L à R (ou de R à L ) est d'utiliser un bord qui saute à travers la cheville. EnsemblebjLRLRRL

  • = nombre d'arêtes de gauche à droite (ne pas compter l'arête finale)edgesLR
  • = nombre d'arêtes de droite à gauche (sans compter l'arête finale)edgesRL
  • = e d g e s L R - e d g e s R LflowedgesLRedgesRL

Cas:

A) si puis l'une des deux partitions deviendra inaccessible, arrêtez|flow|>1

Supposons maintenant que , c'est-à-dire e n d Rend>bjendR

B) si alors il y a un bord supplémentaire de gauche à droite, vous devez aller à gauche (choisir l'élément le plus proche de L ), sinon vous n'atteindrez jamais e n dflow=1Lend

C) si alors il y a un bord supplémentaire de droite à gauche et quel que soit le nœud que vous choisissez, vous n'atteindrez jamais e n d , arrêtezflow=1end

D) si vous devez aller à droite (choisir l'élément le plus proche de R ), sinon vous n'atteindrez jamais e n dflow=0Rend

Si ( e n d L ), B, C, D sont inversés.end<bjendL

REMARQUE: lorsque vous vous déplacez vers la gauche ou la droite, vous devez considérer comme une cheville. Par exemple, si vous devez aller à droite, mais l'élément le plus proche sur R est e n d alors le mouvement est impossible (et vous devez procéder avec une autre paire ( s t a r t , e n d ) )endRend(start,end)

Appliquez la même résonance à chaque mouvement.

COMPLEXITÉ

Les flux sur chaque trou peuvent être précalculés en O (N) et réutilisés à chaque balayage.

Les boucles sont:

for start = 1 to N
  for end = 1 to N
    for move = 1 to N
      make a move (fix a peg and update flows)
      check if another move can be done using flow     

Aucun choix n'est fait lors du calcul, donc la complexité de l'algorithme est O(N3)

CODE

Il s'agit d'une implémentation Java fonctionnelle de l'algorithme:

public class StrangeSort {
    static int PEG = 0xffffff, HOLE = 0x0;
    static int M = 0, N = 0, choices = 0, aux = 0, end;
    static int problem[][], moves[], edgeflow[], field[];    
    boolean is_hole(int x) { return x == HOLE; }
    boolean is_peg(int x) { return x == PEG; }
    boolean is_ele(int x) { return ! is_peg(x) && ! is_hole(x); };
    int []cp(int src[]) { // copy an array
        int res[] = new int[src.length];
        System.arraycopy(src, 0, res, 0, res.length);
        return res;
    }    
    /* find the first element on the left (dir=-1) right (dir=1) */
    int find(int pos, int dir, int nm) {
        pos += dir;
        while (pos >= 1 && pos <= M ) {
            int x = field[pos];
            if ( is_peg(x) || (pos == end && nm < N-1) ) return 0;
            if ( is_ele(x) ) return pos;
            pos += dir;
        }
        return 0;
    }
    void build_edges() {
        edgeflow = new int[M+1];
        for (int i = 1; i<=M; i++) {
            int start = i;
            int b = field[start];
            if (! is_ele(b)) continue;
            if (i == end) continue;
            int dir = (b > start)? 1 : -1;
            start += dir;
            while (start != b) { edgeflow[start] += dir; start += dir; }
        }
    }
    boolean rec_solve(int start, int nm) {
        boolean f;
        int j;
        int b = field[start];
        moves[nm++] = b;
        if (nm == N) return true;
        //System.out.println("Processing: " + start + "->" + field[start]);        
        field[start] = HOLE;
        field[b] = PEG;
        int dir = (b > start)? 1 : -1;
        int i = start + dir;
        while (i != b) { edgeflow[i] -= dir; i += dir; } // clear edge                
        int flow = edgeflow[b];
        if (Math.abs(flow) > 2) return false;
        if (end > b) {
            switch (flow) {
            case 1 :                    
                j = find(b,-1,nm);
                if (j <= 0) return false;
                return rec_solve(j,nm);
            case -1 :
                return false;
            case 0 :          
                j = find(b,1,nm);
                if (j <= 0) return false;
                return rec_solve(j,nm);
            }        
        } else {
            switch (flow) {
            case -1 :                    
                j = find(b,1,nm);
                if (j <= 0) return false;
                return rec_solve(j,nm);
            case 1 :
                return false;
            case 0 :          
                j = find(b,-1,nm);
                if (j <= 0) return false;
                return rec_solve(j,nm);
            }            
        }
        return false;
    }
    boolean solve(int demo[][]) {
        N = demo.length;
        for (int i = 0; i < N; i++)
            M = Math.max(M, Math.max(demo[i][0], demo[i][1]));
        moves = new int[N];
        edgeflow = new int[M+1];
        field = new int[M+1];
        problem = demo;        
        for (int i = 0; i < problem.length; i++) {
            int a = problem[i][0];
            int b = problem[i][1];
            if ( a < 1 || b < 1 || a > M || b > M || ! is_hole(field[a]) || ! is_hole(field[b])) {
                System.out.println("Bad input pair (" + a + "," + b + ")");
                return false;
            }
            field[a] = b;
        }
        for (int i = 1; i <= M; i++) {
            end = i;
            build_edges();
            if (!is_ele(field[i])) continue;
            for (int j = 1; j <= M; j++) {
                if (!is_ele(field[j])) continue;
                if (i==j) continue;
                int tmp_edgeflow[] = cp(edgeflow);
                int tmp_field[] = cp(field);
                choices = 0;
                //System.out.println("START: " + j + " " + " END: " + i);
                if (rec_solve(j, 0)) {
                    return true;
                }
                edgeflow = tmp_edgeflow;
                field = tmp_field;
            }
        }
        return false;
    }
    void init(int demo[][]) {

    }
    public static void main(String args[]) {
        /**** THE INPUT ********/        

        int demo[][] =  {{4,2},{5,7},{6,3},{10,12},{11,1},{13,8},{14,9}};

        /***********************/        
        String r = "";
        StrangeSort sorter = new StrangeSort();       
        if (sorter.solve(demo)) {
            for (int i = 0; i < N; i++) { // print it in clear text
                int b =  moves[i];
                for (int j = 0; j < demo.length; j++)
                    if (demo[j][1] == b)
                        r += ((i>0)? " -> " : "") + "(" + demo[j][0] + "," + demo[j][1] + ")";
            }             
            r = "SOLUTION: "+r;
        }
        else
            r = "NO SOLUTIONS";
        System.out.println(r);
    }    
}

Il s'agit d'une approche intéressante. En général, chaque fois que vous utilisez une arête , il doit y avoir un nombre égal (ou différent par un) d'arêtes inutilisées traversant b dans chaque direction; et si les nombres diffèrent d'un, vous savez quel bord vous devez prendre ensuite. Lorsque les nombres sont égaux, vous avez un choix, que vous devez résoudre en testant les deux options. Cela semble être une stratégie de recherche suffisamment efficace, mais comment savez-vous que c'est un polynôme dans le pire des cas? C'est-à-dire, comment savez-vous que vous ne rencontrerez que des choix O ( log n ) où le nombre de bords de croisement inutilisés dans chaque direction est égal? (a,b)bO(logn)
mjqxxxx

@mjqxxxx ... J'ai réécrit toute la réponse pour qu'elle corresponde à l'algorithme Java ...
Marzio De Biasi

@mjqxxxx ... ok, enfin je l'ai eu ... :-)
Marzio De Biasi

2
Cela me semble correct et très élégant. Une fois que vous utilisez un bord , vous ne pouvez plus "marcher" sur b ; les seules transitions restantes à travers b sont les "sauts" inutilisés (bords dirigés) qui le traversent, que vous devez tous utiliser. Si l'arête finale ( a n , b n ) est spécifiée, vous devez vous retrouver du même côté de b que a n(a,b)bb(an,bn)ban. Il n'y a alors qu'une seule direction possible pour marcher après chaque bord, puisqu'un nombre impair (pair) de sauts vous laissera du côté opposé (le même) vers lequel vous avez initialement marché. Ainsi, tester chaque choix de bords de début et de fin peut être effectué en temps polynomial.
mjqxxxx

1
Ceci est un bel algorithme. Il ne m'est jamais venu à l'esprit de fixer le dernier mouvement en premier. Points mineurs: (1) Comme l'a écrit mjqxxxx, la fin doit être a_k. Sinon, la condition «end> b_j» est incorrecte. (2) Soit la définition de «flux» doit être annulée, soit les cas B et C doivent être inversés.
Tsuyoshi Ito

10

Ce n'est pas une solution, mais une reformulation qui évite de mentionner explicitement les opérations de swapping et de tri. Commencez par trier la liste combinée complète des noms de fichiers et leurs versions échangées, et identifiez chaque nom de fichier avec son index dans cette liste. Ensuite, deux fichiers sont voisins si tous les anciens noms de fichiers entre eux ont déjà été détruits et si aucun des nouveaux noms de fichiers entre eux n'a encore été créé. Le problème reformulé est le suivant:

Etant donné un ensemble de arêtes orientées disjoints ( a , b ) avec un , b { 1 , 2 , ... , 2 n } , est - il un ordre ( a 1 , b 1 ) , ( a 2 , b 2 ) , . . . , ( a n , b n ) de ces arêtes telles quen(a,b)a,b{1,2,,2n}(a1,b1),(a2,b2),...,(an,bn)

  • si est compris entre b i et a i + 1 , alors j i , etajbiai+1ji
  • si est compris entre b i et a i + 1 , alors j i + 1 ?bjbiai+1ji+1

2
+1. Il s'agit d'une manière beaucoup plus simple d'énoncer le problème équivalent. Une seule précision: les bords (a, b) sont dirigés (en ce sens que le bord (a, b) et le bord (b, a) ont des significations différentes).
Tsuyoshi Ito

@Tsuyoshi: merci; J'ai édité pour dire «dirigé».
mjqxxxx

Si je comprends bien, l'expression " est entre a et c " signifie a b c . Donc je suppose que cela vaut la peine de changer l'ancienne notation par la dernière. bacabc
Oleksandr Bondarenko

@Oleksandr: Ici, «b est entre a et c» signifie «soit a <b <c ou c <b <a».
Tsuyoshi Ito
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.