web-dev-qa-db-fra.com

Algorithme for find deux nombres répétés in a tableau, sans trier

Il existe un tableau de taille n (les nombres sont compris entre 0 et n - 3) et seuls 2 nombres sont répétés. Les éléments sont placés au hasard dans le tableau.

Par exemple. dans {2, 3, 6, 1, 5, 4, 0, 3, 5} n = 9 et les nombres répétés sont 3 et 5.

Quelle est la meilleure façon de trouver les nombres répétés?

P.S. [Vous ne devriez pas utiliser le tri]

26
Aman Jain

Il existe une solution O(n) si vous connaissez le domaine de saisie possible. Par exemple, si votre tableau d'entrée contient des nombres compris entre 0 et 100, considérez le code suivant. 

bool flags[100];
for(int i = 0; i < 100; i++)
    flags[i] = false;

for(int i = 0; i < input_size; i++)
    if(flags[input_array[i]])
         return input_array[i];
    else       
        flags[input_array[i]] = true;

Bien sûr, il y a la mémoire supplémentaire, mais c'est le plus rapide. 

27
Sesh

OK, semble que je ne peux tout simplement pas me reposer :) 

Solution la plus simple

int A[N] = {...};

int signed_1(n) { return n%2<1 ? +n : -n;  } // 0,-1,+2,-3,+4,-5,+6,-7,...
int signed_2(n) { return n%4<2 ? +n : -n;  } // 0,+1,-2,-3,+4,+5,-6,-7,...

long S1 = 0;  // or int64, or long long, or some user-defined class
long S2 = 0;  // so that it has enough bits to contain sum without overflow

for (int i=0; i<N-2; ++i)
{
   S1 += signed_1(A[i]) - signed_1(i);
   S2 += signed_2(A[i]) - signed_2(i);
} 

for (int i=N-2; i<N; ++i)
{
   S1 += signed_1(A[i]);
   S2 += signed_2(A[i]);
} 

S1 = abs(S1);
S2 = abs(S2);

assert(S1 != S2);  // this algorithm fails in this case

p = (S1+S2)/2;
q = abs(S1-S2)/2;

Une somme (S1 ou S2) contient p et q avec le même signe, l'autre somme - avec des signes opposés, tous les autres membres sont éliminés.
S1 et S2 doivent avoir assez de bits pour recevoir des sommes, l’algorithme ne signifie pas débordement à cause de abs ().

si abs (S1) == abs (S2), l'algorithme échoue, bien que cette valeur reste la différence entre p et q (c'est-à-dire abs (p - q) == abs (S1)).

Solution précédente

Je doute que quelqu'un rencontre un tel problème sur le terrain;)
.__ et je suppose que je connais les attentes de l'enseignant:

Prenons le tableau {0,1,2, ..., n-2, n-1},
Le donné peut être produit en remplaçant les deux derniers éléments n-2 et n-1 par inconnus p et q (ordre inférieur) 

alors, la somme des éléments sera (n-1) n/2+ p + q - (n-2) - (n-1)
la somme des carrés (n-1) n (2n-1)/6+ p ^ 2 + q ^ 2 - (n-2) ^ 2 - (n-1) ^ 2 

Reste simple maths:

  (1)  p+q = S1  
  (2)  p^2+q^2 = S2

Vous ne pourrez certainement pas le résoudre car les cours de mathématiques enseignent la résolution d’équations carrées. 

Tout d’abord, tout calculer modulo 2 ^ 32, c’est-à-dire permettre le débordement.
Ensuite, vérifiez les paires {p, q}: {0, S1}, {1, S1-1} ... contre l'expression (2) pour trouver les candidats (il peut y en avoir plus de 2 en raison de modulo et de la quadrature )
Et enfin, vérifiez les candidats trouvés s’ils sont réellement présents dans le tableau deux fois.

21
eugensk00

Vous savez que votre tableau contient tous les nombres de 0 à n-3 et les deux nombres qui se répètent (p & q). Pour plus de simplicité, ignorons le cas 0 pour le moment.

Vous pouvez calculer la somme et le produit sur le tableau, ce qui donne:

1 + 2 + ... + n-3 + p + q = p + q + (n-3)(n-2)/2

Donc, si vous soustrayez (n-3) (n-2)/2 de la somme de tout le tableau, vous obtenez

sum(Array) - (n-3)(n-2)/2 = x = p + q

Maintenant, faites la même chose pour le produit:

1 * 2 * ... * n - 3 * p * q = (n - 3)! * p * q

prod(Array) / (n - 3)! = y = p * q

Vous avez maintenant ces termes:

x = p + q

y = p * q

=> y(p + q) = x(p * q)

Si vous transformez ce terme, vous devriez pouvoir calculer p et q

12
sdfx

Insérez chaque élément dans un ensemble/hashtable, en vérifiant d’abord s'il en contient déjà.

7
tjdonaldson

Vous pourrez peut-être tirer parti du fait que somme (tableau) = (n-2) * (n-3)/2 + deux nombres manquants. 

Edit: Comme d’autres l’ont noté, en combinaison avec la somme des carrés, vous pouvez l’utiliser, j’étais un peu lent à le comprendre.

7
Eclipse

Vérifiez ce vieux mais bon article sur le sujet:

6
CMS

Quelques réponses à la question: Algorithme pour déterminer si le tableau contient n… n + m? contient en tant que sous-problème des solutions que vous pouvez adopter en fonction de vos besoins.

Par exemple, voici une partie pertinente de ma réponse :

bool has_duplicates(int* a, int m, int n)
{
  /** O(m) in time, O(1) in space (for 'typeof(m) == typeof(*a) == int')

      Whether a[] array has duplicates.

      precondition: all values are in [n, n+m) range.

      feature: It marks visited items using a sign bit.
  */
  assert((INT_MIN - (INT_MIN - 1)) == 1); // check n == INT_MIN
  for (int *p = a; p != &a[m]; ++p) {
    *p -= (n - 1); // [n, n+m) -> [1, m+1)
    assert(*p > 0);
  }

  // determine: are there duplicates
  bool has_dups = false;
  for (int i = 0; i < m; ++i) {
    const int j = abs(a[i]) - 1;
    assert(j >= 0);
    assert(j < m);
    if (a[j] > 0)
      a[j] *= -1; // mark
    else { // already seen
      has_dups = true;
      break;
    }
  }

  // restore the array
  for (int *p = a; p != &a[m]; ++p) {
    if (*p < 0) 
      *p *= -1; // unmark
    // [1, m+1) -> [n, n+m)
    *p += (n - 1);        
  }

  return has_dups; 
}

Le programme laisse le tableau inchangé (le tableau doit être accessible en écriture mais ses valeurs sont restaurées à la sortie).

Cela fonctionne pour les tailles de tableau jusqu'à INT_MAX (sur les systèmes 64 bits, il s'agit de 9223372036854775807).

3
jfs
supposons qu'un tableau est 

 a [0], a [1], a [2] ..... a [n-1] 

 sumA = a [0] + a [1 ] + .... + a [n-1] 
 sumASquare = a [0] * a [0] + a [1] * a [1] + a [2] * a [2] + .. .. + a [n] * a [n] 

 sumFirstN = (N * (N + 1))/2 où N = n-3 so 
 sumFirstN = (n-3) (n -2)/2 

 De même 

 SumFirstNSquare = N * (N + 1) * (2 * N + 1)/6 = (n-3) (n-2) (2n -5)/6 

 Supposons que les éléments répétés soient = X et Y 

 Donc X + Y = sumA - sumFirstN; 
 X * X + Y * Y = sumASquare - sumFirstNSquare ;

 Ainsi, en résolvant ce quadratique, nous pouvons obtenir les valeurs de X et Y .
 Complexité temporelle = O (n) 
 Complexité spatiale = O (1) 
2
GG.

Je sais que la question est très ancienne, mais je l'ai soudainement posée et je pense avoir une réponse intéressante à cette question ..__ Nous savons qu'il s'agit d'un casse-tête et d'une solution triviale (par exemple, HashMap, Sort, etc.), peu importe leur qualité serait ennuyeux.

Comme les nombres sont des nombres entiers, ils ont une taille de bits constante (c'est-à-dire 32). Supposons que nous travaillons actuellement avec des entiers 4 bits. Nous cherchonsAetBqui sont les numéros en double.

Nous avons besoin de 4 seaux, chacun pour un bit. Chaque compartiment contient des nombres dont le bit spécifique est 1. Par exemple, le compartiment 1 obtient 2, 3, 4, 7, ...:

Bucket 0 : Sum ( x where: x & 2 power 0 == 0 )
...
Bucket i : Sum ( x where: x & 2 power i == 0 )

Nous savons quelle serait la somme de chaque panier s'il n'y avait pas de doublon. Je considère cela comme une connaissance préalable.

Une fois que les compartiments ci-dessus sont générés, nombre d'entre eux auront des valeurs plus élevées que prévu. En construisant le nombre à partir de seaux nous aurons (A OR B pour votre information).

Nous pouvons calculer (A XOR B) comme suit:

A XOR B = Array[i] XOR Array[i-1] XOR ... 0, XOR n-3 XOR n-2  ... XOR 0

Revenons maintenant aux catégories, nous savons exactement quelles catégories ont nos numéros et lesquelles n’en ont qu’un (de la partie XOR).

Pour les compartiments n'ayant qu'un seul numéro, nous pouvons extraire le nombre num = (somme - somme attendue du compartiment). Cependant, nous ne devrions être bons que si nous pouvons trouver l'un des numéros en double, donc si nous avons au moins un bit dans A XOR B, nous avons la réponse.

Mais que se passe-t-il si A XOR B est égal à zéro? Ce cas n’est possible que si les deux numéros en double sont identiques, ce qui correspond à la réponse de A OR B.

2
naiem

répondez à 18 .. vous prenez un tableau de 9 et les éléments commencent à 0..so max ele sera 6 dans votre tableau. Prendre la somme des éléments de 0 à 6 et la somme des éléments du tableau. calcule leur différence (disons d). C'est p + q. Maintenant, prenez XOR des éléments de 0 à 6 (disons x1). Maintenant, prenons XOR des éléments du tableau (disons x2). x2 est XOR de tous les éléments de 0 à 6 sauf deux éléments répétés car ils s'annulent l'un l'autre. maintenant pour i = 0 à 6, pour chaque ele de tableau, disons que p est cet ele a [i] afin que vous puissiez calculer q en soustrayant cet ele du d. faites XOR de p et q et XOR avec x2 et vérifiez si x1 == x2. de même, pour tous les éléments, vous obtiendrez les éléments pour lesquels cette condition sera vraie et vous le ferez dans O (n). Continuez à coder!

1
Minal

Puisqu'une plage est spécifiée, vous pouvez effectuer un tri de base. Cela trierait votre tableau dans O (n). La recherche de doublons dans un tableau trié est alors O (n)

1
Ravi

Vous pouvez utiliser une boucle imbriquée simple  

 int[] numArray = new int[] { 1, 2, 3, 4, 5, 7, 8, 3, 7 };

        for (int i = 0; i < numArray.Length; i++)
        {
            for (int j = i + 1; j < numArray.Length; j++)
            {
                if (numArray[i] == numArray[j])
                {
                   //DO SOMETHING
                }
            }

*OR vous pouvez filtrer le tableau et utiliser la fonction récursive si vous souhaitez obtenir le nombre d'occurrences *  

int[] array = { 1, 2, 3, 4, 5, 4, 4, 1, 8, 9, 23, 4, 6, 8, 9, 1,4 };
int[] myNewArray = null;
int a = 1;

 void GetDuplicates(int[] array)
    for (int i = 0; i < array.Length; i++)
            {
                for (int j = i + 1; j < array.Length; j++)
                {
                    if (array[i] == array[j])
                    {
                          a += 1;
                    }
                }
                Console.WriteLine(" {0} occurred {1} time/s", array[i], a);

                IEnumerable<int> num = from n in array where n != array[i] select n;
                 myNewArray = null;
                 a = 1;
                 myNewArray = num.ToArray() ;

                 break;

            }
             GetDuplicates(myNewArray);
1
Tarek Fouda

vérifier cela ... O (n) temps et O(1) complexité de l'espace 

 for(i=0;i< n;i++)
 xor=xor^arr[i]
 for(i=1;i<=n-3;i++)
 xor=xor^i;

Donc, dans l'exemple donné, vous obtiendrez le xor de 3 et 5 

xor=xor & -xor  //Isolate the last digit

for(i = 0; i < n; i++)
{
if(arr[i] & xor)
  x = x ^ arr[i]; 
else
  y = y ^ arr[i]; 
}
for(i = 1; i <= n-3; i++)
{
if(i & xor)
  x = x ^ i; 
else
  y = y ^ i; 

}

x et y sont tes réponses 

1
Sree Ram

Trier la matrice semble être la meilleure solution. Un simple tri rendrait alors la recherche triviale et prendrait beaucoup moins de temps/espace.

Sinon, si vous connaissez le domaine des nombres, créez un tableau contenant autant de compartiments et incrémentez-les au fur et à mesure de votre progression dans le tableau. quelque chose comme ça:

int count [10];

for (int i = 0; i < arraylen; i++) {
    count[array[i]]++;
}

Ensuite, il suffit de rechercher dans votre tableau les nombres supérieurs à 1. Il s’agit des éléments avec des doublons. Ne nécessite qu'un seul passage sur le tableau d'origine et un autre sur le tableau count.

1
Steve Rowe

Voici l'implémentation en Python de la réponse de @ eugensk00 (une de ses révisions) qui n'utilise pas l'arithmétique modulaire. C'est un algorithme single-pass, O(log(n)) dans l'espace. Si des entiers à largeur fixe (par exemple 32 bits) sont utilisés, il ne nécessite que deux nombres à largeur fixe (par exemple pour 32 bits: un nombre 64 bits et un nombre 128 bits). Il peut gérer des séquences entières arbitraires de grande taille (il lit un entier à la fois, donc une séquence entière ne nécessite pas d'être en mémoire).

def two_repeated(iterable):
    s1, s2 = 0, 0
    for i, j in enumerate(iterable):
        s1 += j - i     # number_of_digits(s1) ~ 2 * number_of_digits(i)
        s2 += j*j - i*i # number_of_digits(s2) ~ 4 * number_of_digits(i) 
    s1 += (i - 1) + i
    s2 += (i - 1)**2 + i**2

    p = (s1 - int((2*s2 - s1**2)**.5)) // 2 
    # `Decimal().sqrt()` could replace `int()**.5` for really large integers
    # or any function to compute integer square root
    return p, s1 - p

Exemple:

>>> two_repeated([2, 3, 6, 1, 5, 4, 0, 3, 5])
(3, 5)

Une version plus détaillée du code ci-dessus suit avec une explication:

def two_repeated_seq(arr):
    """Return the only two duplicates from `arr`.

    >>> two_repeated_seq([2, 3, 6, 1, 5, 4, 0, 3, 5])
    (3, 5)
    """
    n = len(arr)
    assert all(0 <= i < n - 2 for i in arr) # all in range [0, n-2)
    assert len(set(arr)) == (n - 2) # number of unique items

    s1 = (n-2) + (n-1)       # s1 and s2 have ~ 2*(k+1) and 4*(k+1) digits  
    s2 = (n-2)**2 + (n-1)**2 # where k is a number of digits in `max(arr)`
    for i, j in enumerate(arr):
        s1 += j - i     
        s2 += j*j - i*i

    """
    s1 = (n-2) + (n-1) + sum(arr) - sum(range(n))
       = sum(arr) - sum(range(n-2))
       = sum(range(n-2)) + p + q - sum(range(n-2))
       = p + q
    """
    assert s1 == (sum(arr) - sum(range(n-2)))

    """
    s2 = (n-2)**2 + (n-1)**2 + sum(i*i for i in arr) - sum(i*i for i in range(n))
       = sum(i*i for i in arr) - sum(i*i for i in range(n-2))
       = p*p + q*q
    """
    assert s2 == (sum(i*i for i in arr) - sum(i*i for i in range(n-2)))

    """
    s1 = p+q
    -> s1**2 = (p+q)**2
    -> s1**2 = p*p + 2*p*q + q*q
    -> s1**2 - (p*p + q*q) = 2*p*q
    s2 = p*p + q*q
    -> p*q = (s1**2 - s2)/2

    Let C = p*q = (s1**2 - s2)/2 and B = p+q = s1 then from Viete theorem follows
    that p and q are roots of x**2 - B*x + C = 0
    -> p = (B + sqrtD) / 2
    -> q = (B - sqrtD) / 2
    where sqrtD = sqrt(B**2 - 4*C)

    -> p = (s1 + sqrt(2*s2 - s1**2))/2
    """
    sqrtD = (2*s2 - s1**2)**.5
    assert int(sqrtD)**2 == (2*s2 - s1**2) # perfect square
    sqrtD = int(sqrtD)
    assert (s1 - sqrtD) % 2 == 0 # even
    p = (s1 - sqrtD) // 2
    q = s1 - p
    assert q == ((s1 + sqrtD) // 2)
    assert sqrtD == (q - p)
    return p, q

REMARQUE: le calcul de la racine carrée entière d'un nombre (~ N ** 4) rend l'algorithme ci-dessus non linéaire.

1
jfs

En c:

    int arr[] = {2, 3, 6, 1, 5, 4, 0, 3, 5};

    int num = 0, i;

    for (i=0; i < 8; i++)
         num = num ^ arr[i] ^i;

Depuis x^x=0, les nombres répétés un nombre impair de fois sont neutralisés. Appelons les numéros uniques a et b. Il nous reste a^b. Nous connaissons a^b != 0, depuis a != b. Choisissez un bit de a^b et utilisez-le comme masque ie.choose x comme puissance 2, de sorte que x & (a^b) soit différent de zéro.

Maintenant divisez la liste en deux sous-listes - une sous-liste contient tous les nombres y avec y&x == 0, et le reste va dans l’autre sous-liste En passant, nous avons choisi x, nous savons que les paires de a et b sont dans des compartiments différents. Nous pouvons donc maintenant appliquer la même méthode utilisée ci-dessus à chaque compartiment de manière indépendante et découvrir ce que sont a et b.

0

Que dis-tu de ça:

for (i=0; i<n-1; i++) {
  for (j=i+1; j<n; j++) {
    if (a[i] == a[j]) {
        printf("%d appears more than once\n",a[i]);
        break;
    }
  }
}

Bien sûr, ce n’est pas le plus rapide, mais c’est simple, facile à comprendre et ne nécessite pas de mémoire supplémentaire. Si n est un petit nombre comme 9 ou 100, alors il se peut que ce soit le "meilleur". (Par exemple, "Meilleur" pourrait signifier différentes choses: plus rapide à exécuter, plus petite empreinte mémoire, plus maintenable, moins coûteux à développer, etc.

0
vulcan

J'ai écrit un petit programme qui détecte le nombre d'éléments non répétés. Lisez-le moi, votre opinion est la suivante: pour le moment, je suppose qu'un nombre pair d'éléments est pair mais peut facilement être étendu pour des nombres impairs également.

Donc, mon idée est d’abord de trier les nombres, puis d’appliquer mon algorithme. Un tri rapide peut être utilisé pour trier ces éléments.

Prenons un tableau d'entrée comme ci-dessous

int arr[] = {1,1,2,10,3,3,4,5,5,6,6};

les chiffres 2,10 et 4 ne sont pas répétés, mais ils sont triés. Sinon, utilisez le tri rapide pour le trier.

Permet d'appliquer mon programme sur ce

using namespace std;

main()
{
    //int arr[] = {2, 9, 6, 1, 1, 4, 2, 3, 5};
    int arr[] = {1,1,2,10,3,3,4,5,5,6,6};

    int i = 0;

    vector<int> vec;

    int var = arr[0];
    for(i = 1 ; i < sizeof(arr)/sizeof(arr[0]); i += 2)
    {
            var = var ^ arr[i];

            if(var != 0 )
            {
                //put in vector
                var = arr[i-1];
                vec.Push_back(var);
                i = i-1;
            }
            var = arr[i+1];
    }

    for(int i = 0 ; i < vec.size() ; i++)
        printf("value not repeated = %d\n",vec[i]);

}

Cela donne la sortie:

value not repeated= 2

value not repeated= 10

value not repeated= 4

C'est simple et très simple, utilisez simplement XOR man.

0
Yusuf Khan

Voici un algorithme qui utilise les statistiques d'ordre et s'exécute dans O(n).

Vous pouvez résoudre ce problème en appelant à plusieurs reprises SELECT avec la médiane en tant que paramètre.

Vous vous appuyez également sur le fait que, après un appel à SELECT, , Les éléments inférieurs ou égaux à la médiane sont déplacés à gauche de la médiane.

  • Appelez SELECT sur A avec la médiane comme paramètre.
  • Si la valeur médiane est floor(n/2), les valeurs répétées correspondent à la médiane. Donc, vous continuez avec la moitié droite du tableau.
  • Sinon, si ce n'est pas le cas, une valeur répétée est laissée à la médiane. Donc, vous continuez avec la moitié gauche du tableau.
  • Vous continuez ainsi récursivement.

Par exemple:

  • Lorsque A={2, 3, 6, 1, 5, 4, 0, 3, 5}n=9, la médiane doit être la valeur 4.
  • Après le premier appel à SELECT
  • A={3, 2, 0, 1, <3>, 4, 5, 6, 5} La valeur médiane étant inférieure à 4, nous continuons avec la moitié gauche.
  • A={3, 2, 0, 1, 3}
  • Après le deuxième appel à SELECT
  • A={1, 0, <2>, 3, 3} alors la médiane devrait être 2 et nous allons donc continuer avec la moitié droite.
  • A={3, 3}, trouvé.

Cet algorithme fonctionne dans O(n+n/2+n/4+...)=O(n).

0
Avi Cohen
for(i=1;i<=n;i++) {
  if(!(arr[i] ^ arr[i+1]))
        printf("Found Repeated number %5d",arr[i]);
}
0
srinunaik

Pour chaque nombre: vérifiez s'il existe dans le reste du tableau.

0
mookid8000

Sans trier, vous gardez une trace des chiffres que vous avez déjà visités.

dans psuedocode, cela serait essentiellement (ce qui fait que je ne vous donne pas simplement la réponse):

for each number in the list
   if number not already in unique numbers list
      add it to the unique numbers list
   else
      return that number as it is a duplicate
   end if
end for each
0
mezoid

Qu'en est-il de l'utilisation du https://en.wikipedia.org/wiki/HyperLogLog

Redis fait http://redis.io/topics/data-types-intro#hyperloglogs

Un HyperLogLog est une structure de données probabiliste utilisée pour compter des choses uniques (on parle techniquement d'estimer la cardinalité d'un ensemble). Généralement, le comptage d’éléments uniques nécessite d’utiliser une quantité de mémoire proportionnelle au nombre d’éléments à compter, car vous devez vous rappeler des éléments que vous avez déjà vus dans le passé pour éviter de les compter plusieurs fois. Cependant, il existe un ensemble d'algorithmes qui échangent de la mémoire contre de la précision: vous vous retrouvez avec une mesure estimée avec une erreur standard, dans le cas de l'implémentation Redis, inférieure à 1%. La magie de cet algorithme est qu'il n'est plus nécessaire d'utiliser une quantité de mémoire proportionnelle au nombre d'éléments comptés, mais que vous pouvez utiliser une quantité de mémoire constante! 12k octets dans le pire des cas, ou beaucoup moins si votre HyperLogLog (nous les appellerons simplement HLL à partir de maintenant) a vu très peu d'éléments.

0
brutuscat