39 votes

Comment dire si un tableau est une permutation en O(n) ?

Entrée : A en lecture seule un tableau de N éléments contenant des valeurs entières de 1 à N (certaines valeurs entières peuvent apparaître plus d'une fois !). Et une zone de mémoire d'un Correction de la taille (10, 100, 1000 etc.) no en fonction de N).

Comment dire en O(n) si le tableau représente une permutation ?

-- Ce que j'ai réalisé jusqu'à présent (une réponse a prouvé que c'était no bon):--

~~

  1. J'utilise la zone de mémoire limitée pour stocker la somme et le produit du tableau.
  2. Je compare la somme avec N*(N+1)/2 et le produit avec N !

Je sais que si la condition (2) est vraie, je pourrait ont une permutation. Je me demande s'il existe un moyen de prouver que la condition (2) est suffisante pour savoir si j'ai une permutation. Jusqu'à présent, je n'ai pas réussi à le faire ...

~~

16voto

Jason S Points 58434

Je suis très légèrement sceptique quant à l'existence d'une solution. Votre problème semble être très proche d'un problème posé il y a plusieurs années dans la littérature mathématique, avec un résumé donné ici ("The Duplicate Detection Problem", S. Kamal Abdali, 2003) qui utilise la détection de cycle, l'idée étant la suivante :

S'il y a un doublon, il existe un numéro j entre 1 et N tel que ce qui suit conduirait à une boucle infinie :

x := j;
do
{
   x := a[x];
}
while (x != j);

car une permutation consiste en un ou plusieurs sous-ensembles S d'éléments distincts s 0 , s 1 , ... s k-1 où s j \= a[s j-1 ] pour tous les j entre 1 et k-1, et s 0 \= a[s k-1 ], donc tous les éléments sont impliqués dans les cycles -- l'un des doublons ne ferait pas partie d'un tel sous-ensemble.

Par exemple, si le tableau = [2, 1, 4, 6, 8 , 7, 9, 3, 8]

alors l'élément en gras en position 5 est un doublon car tous les autres éléments forment des cycles : { 2 -> 1, 4 -> 6 -> 7 -> 9 -> 8 -> 3}. Alors que les tableaux [2, 1, 4, 6, 5, 7, 9, 3, 8] et [2, 1, 4, 6, 3, 7, 9, 5, 8] sont des permutations valides (avec les cycles { 2 -> 1, 4 -> 6 -> 7 -> 9 -> 8 -> 3, 5 } et { 2 -> 1, 4 -> 6 -> 7 -> 9 -> 8 -> 5 -> 3 } respectivement).

Abdali explique comment trouver les doublons. En gros, l'algorithme suivant (utilisant L'algorithme de recherche de cycle de Floyd ) fonctionne si vous tombez sur l'un des doublons en question :

function is_duplicate(a, N, j)
{
     /* assume we've already scanned the array to make sure all elements
        are integers between 1 and N */
     x1 := j;
     x2 := j;
     do
     {             
         x1 := a[x1];
         x2 := a[x2];
         x2 := a[x2];
     } while (x1 != x2);

     /* stops when it finds a cycle; x2 has gone around it twice, 
        x1 has gone around it once.
        If j is part of that cycle, both will be equal to j. */
     return (x1 != j);
}

La difficulté est que je ne suis pas sûr que votre problème tel qu'il est énoncé corresponde à celui de son article, et je ne suis pas non plus sûr que la méthode qu'il décrit fonctionne en O(N) ou utilise une quantité fixe d'espace. Un contre-exemple potentiel est le tableau suivant :

[3, 4, 5, 6, 7, 8, 9, 10, ... N-10, N-9, N-8, N-7, N-2, N-5, N-3, N-5, N-1, N, 1, 2]

qui est en fait la permutation d'identité décalée de 2, avec les éléments [N-6, N-4 et N-2] remplacés par [N-2, N-5, N-5]. La somme est correcte (mais pas le produit correct, mais je rejette l'idée de prendre le produit comme méthode de détection possible, car l'espace nécessaire pour calculer N ! avec une précision arithmétique arbitraire est de O(N), ce qui viole l'esprit de l'exigence d'un "espace mémoire fixe"), et si vous essayez de trouver des cycles, vous obtiendrez les cycles { 3 -> 5 -> 7 -> 9 -> .... N-7 -> N-5 -> N-1 } et { 4 -> 6 -> 8 -> ... N-10 -> N-8 -> N-2 -> N -> 2}. Le problème est qu'il pourrait y avoir jusqu'à N cycles (la permutation d'identité a N cycles), chacun prenant jusqu'à O(N) pour trouver un duplicata, et vous devez garder la trace d'une manière ou d'une autre des cycles qui ont été tracés et de ceux qui ne l'ont pas été. Je suis sceptique quant à la possibilité de faire cela dans une quantité fixe d'espace. Mais c'est peut-être le cas.

C'est un problème suffisamment important pour qu'il vaille la peine de le poser sur le site de la Commission. mathoverflow.net (malgré le fait que la plupart du temps mathoverflow.net est cité sur stackoverflow c'est pour des problèmes qui sont trop faciles)


éditer : Je l'ai fait. demander sur mathoverflow il y a une discussion intéressante là-dessus.

10voto

Strilanc Points 7161

Ceci est impossible à réaliser en espace O(1), du moins avec un algorithme à balayage unique.

Preuve

Supposons que vous ayez traité N/2 des N éléments. Si l'on suppose que la séquence est une permutation, alors, étant donné l'état de l'algorithme, vous devriez être capable de déterminer l'ensemble des N/2 éléments restants. Si vous ne pouvez pas déterminer les éléments restants, alors l'algorithme peut être trompé en répétant certains des anciens éléments.

Il y a N choix de N/2 ensembles restants possibles. Chacun d'entre eux doit être représenté par un état interne distinct de l'algorithme, car sinon vous ne pourriez pas déterminer les éléments restants. Cependant, il faut un espace logarithmique pour stocker X états, il faut donc un espace BigTheta(log(N choisir N/2)) pour stocker N choisir N/2 états. Cette valeur croît avec N, et par conséquent l'état interne de l'algorithme ne peut pas s'inscrivent dans un espace O(1).

Une preuve plus formelle

Vous voulez créer un programme P qui, étant donné les N/2 éléments finaux et l'état interne de l'algorithme linéaire temps-espace-constant après qu'il ait traité N/2 éléments, détermine si la séquence entière est une permutation de 1..N. Il n'y a pas de limite de temps ou d'espace sur ce programme secondaire.

En supposant que P existe, nous pouvons créer un programme Q, ne prenant que l'état interne de l'algorithme temps linéaire-espace des constantes, qui détermine les N/2 éléments finaux nécessaires de la séquence (si c'était une permutation). Q fonctionne en passant à P tous les éléments finaux N/2 possibles et en retournant l'ensemble pour lequel P retourne vrai.

Cependant, puisque Q a N choix N/2 sorties possibles, il doit avoir au moins N choix N/2 entrées possibles. Cela signifie que l'état interne de l'algorithme original doit stocker au moins N choix N/2 états, ce qui nécessite BigTheta(log N choix N/2), qui est supérieur à la taille constante.

Par conséquent, l'algorithme original, qui a des limites de temps et d'espace, ne peut pas non plus fonctionner correctement s'il a un état interne de taille constante.

(Je pense que cette idée peut être généralisée, mais la réflexion n'est pas prouvée).

Conséquences

BigTheta(log(N choisir N/2)) est égal à BigTheta(N). Par conséquent, utiliser simplement un tableau de booléens et cocher les valeurs au fur et à mesure que vous les rencontrez est (probablement) optimal en termes d'espace et de temps puisque cela prend un temps linéaire.

5voto

Jules Points 3603

Je doute que vous puissiez le prouver ;)

  (1, 2, 4, 4, 4, 5, 7, 9, 9)

Je pense que, plus généralement, ce problème n'est pas soluble en traitant les nombres dans l'ordre. Supposons que vous traitiez les éléments dans l'ordre et que vous soyez à mi-chemin du tableau. Maintenant, l'état de votre programme doit en quelque sorte refléter les nombres que vous avez rencontrés jusqu'à présent. Cela nécessite au moins O(n) bits à stocker.

3voto

McBeth Points 783

Cela ne fonctionnera pas, car la complexité est donnée en fonction de N plutôt que de M, ce qui implique que N >> M.

C'est ce que j'ai essayé de faire, mais pour qu'un filtre bloom soit utile, il faut un grand M, auquel cas il vaut mieux utiliser un simple basculement de bit pour quelque chose comme des nombres entiers.

http://en.wikipedia.org/wiki/Bloom_filter

Pour chaque élément du tableau Exécutez les k fonctions de hachage Vérifier l'inclusion dans le filtre bloom S'il y figure, il y a une probabilité que vous ayez déjà vu cet élément auparavant. S'il n'y est pas, ajoutez-le

Lorsque vous avez terminé, vous pouvez tout aussi bien le comparer aux résultats d'un tableau 1..N dans l'ordre, car cela ne vous coûtera qu'un autre N.

Maintenant, si je n'ai pas mis assez de mises en garde. Ce n'est pas 100%, ou même proche puisque vous avez spécifié la complexité en N, ce qui implique que N >> M, donc fondamentalement cela ne fonctionnera pas comme vous l'avez spécifié.

Par ailleurs, le taux de faux positifs pour un élément individuel devrait être de e = 2^(-m/(n*sqrt(2)))

En y jouant, vous aurez une idée de la taille que devrait avoir M pour être acceptable.

1voto

Sparky Points 4660

Je ne sais pas comment le faire en O(N), ni même si cela peut être fait en O(N). Je sais que cela peut être fait en O(N log N) si vous utilisez un tri et une comparaison (appropriés).

Cela dit, il existe de nombreuses techniques O(N) permettant de montrer que l'une n'est PAS une permutation de l'autre.

  1. Vérifiez la longueur. Si elle est inégale, il ne s'agit manifestement pas d'une permutation.
  2. Créez une empreinte XOR. Si la valeur de tous les éléments combinés par XOR ne correspond pas, il ne peut s'agir d'une permutation. Une correspondance serait toutefois peu concluante.
  3. Trouvez la somme de tous les éléments. Bien que le résultat puisse déborder, cela ne devrait pas poser de problème lors de la comparaison de cette "empreinte". Cependant, si vous faites une somme de contrôle qui implique une multiplication, le débordement sera un problème.

J'espère que cela vous aidera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X