Fusion de baies sans doublons


15

J'ai récemment vu ce code Javascript sur StackOverflow pour fusionner deux tableaux et supprimer les doublons:

Array.prototype.unique = function() {
    var a = this.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }
    return a;
};

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = array1.concat(array2).unique(); 

Bien que ce code fonctionne, il est horriblement inefficace ( O(n^2)). Votre défi est de créer un algorithme avec moins de complexité.

Le critère gagnant est la solution la moins complexe , mais les liens seront rompus par la plus courte longueur de caractères.

Prérequis :

Empaquetez tout votre code ensemble dans une fonction qui répond aux exigences suivantes de «correction»:

  • Entrée: deux tableaux
  • Sortie: un tableau
  • Fusionne les éléments des deux tableaux ensemble - Tout élément dans l'un ou l'autre tableau d'entrée doit être dans le tableau en sortie.
  • Le tableau en sortie ne doit pas avoir de doublons.
  • L'ordre n'a pas d'importance (contrairement à l'original)
  • N'importe quelle langue compte
  • N'utilisez pas les fonctions de tableau de la bibliothèque standard pour détecter l'unicité ou fusionner des ensembles / tableaux (bien que d'autres choses de la bibliothèque standard soient correctes). Permettez-moi de faire la distinction que la concaténation de tableaux est correcte, mais les fonctions qui font déjà tout ce qui précède ne le sont pas.

Comment sommes-nous censés créer ou ajouter à un tableau sans utiliser les fonctions de tableau?
Emil Vikström

@ EmilVikström Voir ma modification. Je voulais dire que vous ne pouvez pas utiliser les fonctions d'unicité de tableau. Désolé d'être flou.
hkk

Si l'un des tableaux contient des doublons, les supprimons-nous également? Par exemple, la fusion [1, 2, 2, 3]et le [2, 3, 4]retour devraient -ils [1, 2, 2, 3, 4]ou [1, 2, 3, 4]?
OI

1
@OI Oui, ce serait trop facile.
hkk

1
Puis-je demander: des tableaux de quoi ? Pouvons-nous supposer simplement des entiers ou des chaînes, ou devons-nous également autoriser des choses plus complexes comme les objets à plusieurs niveaux?
jawns317

Réponses:


8

Perl

27 personnages

Simple Perl Hack

my @vals = ();
push @vals, @arr1, @arr2;
my %out;
map { $out{$_}++ } @vals;
my @unique = keys %out;

Je suis sûr que quelqu'un pourrait aller seul. Et donc (Merci Dom Hastings)

sub x{$_{$_}++for@_;keys%_}

1
"N'utilisez pas les fonctions de tableau de la bibliothèque standard pour détecter l'unicité (bien que d'autres choses forment la bibliothèque standard est acceptable)"
John Dvorak

1
Comment je viole cette règle? Je n'utilise pas de fonctions uniques?
Zach Leighton

Comment ça marche alors? Désolé, je ne peux pas lire perl. S'il lit les clés d'une carte de hachage - cela compte-t-il comme OK avec cette règle? Je ne voterai pas tant que ce ne sera pas convaincu.
John Dvorak

1
Il combine les tableaux, boucle sur les deux et ajoute à un hachage incrémentant la valeur dont la clé est la valeur actuelle dans la boucle du tableau. Ensuite, il faut les clés de ce hachage, je l'ai utilisé dans une partie de mon travail .. Donc [1,1,2,3,4,4] devient {1 => 2, 2 => 1, 3 => 1 , 4 => 2}
Zach Leighton

@ZachLeighton, vous pouvez raccourcir le code à 27 caractères avec sub x{$_{$_}++for@_;keys%_}(au cas où il s'agirait d'une égalité!) Et l'utiliser comme:z((1,2,3,4),(2,3,4,5,6))
Dom Hastings

10

JavaScript O (N) 131 124 116 92 (86?)

Version golfée:

function m(i,x){h={};n=[];for(a=2;a--;i=x)i.map(function(b){h[b]=h[b]||n.push(b)});return n}

Version golfée lisible par l'homme:

function m(i,x) {
   h = {}
   n = []
   for (a = 2; a--; i=x)
      i.map(function(b){
        h[b] = h[b] || n.push(b)
      })
   return n
}

Je pourrais utiliser concatcomme ça et le faire en 86 caractères:

function m(i,x){h={};n=[];i.concat(x).map(function(b){h[b]=h[b]||n.push(b)});return n}

Mais je ne sais pas s'il est toujours O (N) basé sur ce JsPerf: http://jsperf.com/unique-array-merging-concat-vs-looping car la version concat est légèrement plus rapide avec des tableaux plus petits mais plus lente avec des baies plus grandes (Chrome 31 OSX).

En pratique, faites cela (le golf regorge de mauvaises pratiques):

function merge(a1, a2) {
   var hash = {};
   var arr = [];
   for (var i = 0; i < a1.length; i++) {
      if (hash[a1[i]] !== true) {
        hash[a1[i]] = true;
        arr[arr.length] = a1[i];
      }
   }
   for (var i = 0; i < a2.length; i++) {
      if (hash[a2[i]] !== true) {
        hash[a2[i]] = true;
        arr[arr.length] = a2[i];
      }
   }
   return arr;
}
console.log(merge([1,2,3,4,5],[1,2,3,4,5,6]));

Je ne suis pas doué pour la complexité informatique, mais je pense que c'est O(N) . J'adorerais si quelqu'un pouvait clarifier.

Edit: Voici une version qui prend n'importe quel nombre de tableaux et les fusionne.

function merge() {
   var args = arguments;
   var hash = {};
   var arr = [];
   for (var i = 0; i < args.length; i++) {
      for (var j = 0; j < args[i].length; j++) {
        if (hash[args[i][j]] !== true) {
          arr[arr.length] = args[i][j];
          hash[args[i][j]] = true;
        }
      }
    }
   return arr;
}
console.log(merge([1,2,3,4,5],[1,2,3,4,5,6],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7,8]));

C'est presque exactement ce que j'allais publier dans quelques secondes :-( Oui, c'est du temps linéaire amorti si des tables de hachage sont implémentées avec un temps constant amorti pour l'insertion et la recherche (ce qui est courant dans de nombreuses langues, je ne sais pas spécifiquement à propos de JS).
Emil Vikström

@ EmilVikström Merci pour cela, je crois que JavaScript le fait, mais je n'ai aucune preuve à ce sujet. Excuses pour avoir des doigts rapides, ralentissez-vous avec des commentaires: P
George Reith

C'est une excellente approche. Cependant, pourriez-vous également fournir une solution de style "code-golf" en plus de votre version bien formatée? Étant donné que plusieurs personnes ont considéré cela comme la bonne approche, il y aura probablement une égalité O(N).
hkk

@ cloudcoder2000 Ok, je voulais imprimer une version complète car la version code-golf est probablement moins efficace en pratique.
George Reith

1
@ cloudcoder2000 Ils ne sont pas totalement indépendants, donc le pire des cas ne l'est pas O(A*B)( ne pas utiliser Ncar c'est déroutant). Ce serait que si chaque tableau d'entrée (tous A) avait la même quantité d'éléments ( B) qu'elle l'est réellement O(SUM(B) FOR ALL A), ce qui peut être réécrit comme O(N)lors de la définition Ndu nombre d'éléments de toutes les entrées du tableau.
meiamsome

4

Python 2.7, 38 caractères

F=lambda x,y:{c:1 for c in x+y}.keys()

Doit être O (N) en supposant une bonne fonction de hachage.

L' setimplémentation de 8 caractères de Wasi est meilleure, si vous ne pensez pas qu'elle viole les règles.


Agréable! Les compréhensions en Python peuvent être si élégantes et puissantes.
OI

3

PHP, 69/42 68/41 caractères

La déclaration de fonction comprend 68 caractères:

function m($a,$b){return array_keys(array_flip($a)+array_flip($b));}

La déclaration de fonction ne comprend pas 41 caractères:

array_keys(array_flip($a)+array_flip($b))

3

Aller simple en Ruby

Pour respecter les règles décrites ci-dessus, j'utiliserais une stratégie similaire à la solution JavaScript et utiliserais un hachage comme intermédiaire.

merged_arr = {}.tap { |hash| (arr1 + arr2).each { |el| hash[el] ||= el } }.keys

Ce sont essentiellement les étapes que je traverse dans la ligne ci-dessus.

  1. Définissez une variable merged_arrqui contiendra le résultat
  2. Initialisez un hachage vide et sans nom comme intermédiaire pour mettre des éléments uniques dans
  3. Utilisez Object#tappour remplir le hachage (référencé comme hashdans le tapbloc) et le renvoyer pour le chaînage de méthode ultérieur
  4. Concaténation arr1et arr2en une seule matrice non traitée
  5. Pour chaque élément eldans le tableau chaînés, mettre la valeur elen hash[el]si aucune valeur hash[el]existe actuellement. La mémorisation ici ( hash[el] ||= el) est ce qui garantit l'unicité des éléments.
  6. Récupérez les clés (ou les valeurs, car elles sont identiques) pour le hachage maintenant rempli

Cela devrait fonctionner dans O(n) temps. Veuillez me faire savoir si j'ai fait des déclarations inexactes ou si je peux améliorer la réponse ci-dessus pour des raisons d'efficacité ou de lisibilité.

Améliorations possibles

L'utilisation de la mémorisation n'est probablement pas nécessaire étant donné que les clés du hachage seront uniques et que les valeurs ne sont pas pertinentes, donc cela suffit:

merged_arr = {}.tap { |hash| (arr1 + arr2).each { |el| hash[el] = 1 } }.keys

J'adore vraiment Object#tap, mais nous pouvons obtenir le même résultat en utilisant Enumerable#reduce:

merged_arr = (arr1 + arr2).reduce({}) { |arr, val| arr[val] = 1; arr }.keys

Vous pourriez même utiliser Enumberable#map:

merged_arr = Hash[(arr1 + arr2).map { |val| [val, 1] }].keys

Comment je le ferais dans la pratique

Cela dit, si on me demandait de fusionner deux tableaux arr1et arr2que le résultat merged_arrait des éléments uniques et puisse utiliser n'importe quelle méthode Ruby à ma disposition, j'utiliserais simplement l'opérateur union défini qui est destiné à résoudre ce problème exact:

merged_arr = arr1 | arr2

Un coup d'œil rapide à la source de Array#|, cependant, semble confirmer que l'utilisation d'un hachage comme intermédiaire semble être la solution acceptable pour effectuer une fusion unique entre 2 tableaux.


"N'utilisez pas les fonctions de tableau de la bibliothèque standard pour détecter l'unicité (bien que d'autres choses forment la bibliothèque standard est acceptable)"
John Dvorak

Comment suis-je en train de violer cette règle dans le deuxième exemple? La mémorisation est effectuée sur un hachage. N'est-ce pas permis non plus?
OI

2
Array.prototype.unique = function()
{
  var o = {},i = this.length
  while(i--)o[this[i]]=true
  return Object.keys(o)
}

Une fonction qui prendrait n tableaux pourrait être la suivante:

function m()
{
  var o={},a=arguments,c=a.length,i;
  while(c--){i=a[c].length;while(i--)o[a[c][i]] = true} 
  return Object.keys(o);
}

Golfé, je pense que cela devrait fonctionner (117 caractères)

function m(){var o={},a=arguments,c=a.length,i;while(c--){i=a[c].length;while(i--)o[a[c][i]]=1}return Object.keys(o)}

Mise à jour Si vous souhaitez conserver le type d'origine, vous pouvez

function m()
{
  var o={},a=arguments,c=a.length,f=[],g=[];
  while(c--)g.concat(a[c])
  c = g.length      
  while(c--){if(!o[g[c]]){o[g[c]]=1;f.push(g[c])}}
  return f
}

ou golf 149:

function m(){var o={},a=arguments,c=a.length,f=[],g=[];while(c--)g.concat(a[c]);c= g.length;while(c--){if(!o[g[c]]){o[g[c]]=1;f.push(g[c])}}return f}

Cela peut encore jeter des doutes, si vous voulez faire la distinction 123et '123'cela ne fonctionnera pas.


Merci d'avoir répondu. Il est incroyablement court, mais cela ne fait que la moitié du problème. Vous devez également inclure dans la solution la partie réelle de la fusion (même si elle est la même que dans l'exemple d'origine) et la regrouper dans une seule fonction. Pourriez-vous également fournir une version "golfée" en plus de cela (tel quel O(N))?
hkk

Cela transforme tous les membres en chaînes. par exemple m([1,2,3,4,5],[2,3,4,5,6],[2,3,4,5,6,7])devient["1", "2", "3", "4", "5", "6", "7"]
George Reith

2

python, 46

def A(a,b):print[i for i in b if i not in a]+a

Ou, en utilisant simplement l'opération set

python, 8

set(a+b)

1
Désolé, ce n'était pas clair, utiliser les opérations de set est aussi de la triche.
hkk

Votre 1er code aura des doublons s'il y a des doublons dans a ou s'il y a des doublons dans b et que cet élément n'est pas dans a.
Vedant Kandoi

2

Perl

23 octets, si l'on ne compte que le bloc de code à l'intérieur du sous-programme. Peut être 21, si l'écrasement des valeurs globales est autorisé (il serait supprimé mydu code). Il renvoie les éléments dans un ordre aléatoire, car l'ordre n'a pas d'importance. En ce qui concerne la complexité, en moyenne c'est O (N) (dépend du nombre de collisions de hachage, mais elles sont plutôt rares - dans le pire des cas, cela peut être O (N 2 ) (mais cela ne devrait pas se produire, car Perl peut détecter les hachages pathologiques , et modifie la valeur de la fonction de hachage lorsqu'il détecte un tel comportement)).

use 5.010;
sub unique{
    my%a=map{$_,1}@_;keys%a
}
my @a1 = (1, 2, 3, 4);
my @a2 = (3, 4, 5, 6);
say join " ", unique @a1, @a2;

Sortie (montrant également le caractère aléatoire):

/tmp $ perl unique.pl 
2 3 4 6 1 5
/tmp $ perl unique.pl 
5 4 6 2 1 3

2

Fortran: 282 252 233 213

Version golfée:

function f(a,b,m,n) result(d);integer::m,n,a(m),b(n),c(m+n);integer,allocatable::d(:);j=m+1;c(1:m)=a(1:m);do i=1,n;if(.not.any(b(i)==c(1:m)))then;c(j)=b(i);j=j+1;endif;enddo;allocate(d(j-1));d=c(1:j-1);endfunction

Ce qui non seulement semble infiniment meilleur, mais se compilera en fait (une ligne trop longue dans sa forme jouée) avec la forme lisible par l'homme:

function f(a,b,m,n) result(d)
  integer::m,n,a(m),b(n),c(m+n)
  integer,allocatable::d(:)
  j=m+1;c(1:m)=a(1:m)
  do i=1,n
     if(.not.any(b(i)==c(1:m)))then
        c(j)=b(i);j=j+1
     endif
  enddo
  allocate(d(j-1))
  d=c(1:j-1)
end function

Cela devrait être O(n)comme je copie adans cpuis vérifie chacun bcontre tout c. La dernière étape consiste à éliminer les déchets qui ccontiendront car ils ne sont pas initialisés.


2

Mathematica 10 caractères

Union[a,b]

Exemple:

a={1,2,3,4,5};
b={1,2,3,4,5,6};
Union[a,b]

{1, 2, 3, 4, 5, 6}

Mathematica2 43 caractères

Sort@Join[a, b] //. {a___, b_, b_, c___} :> {a, b, c}

8
Je pense que cela irait dans la catégorie de l'utilisation des méthodes de tableau de bibliothèque standard.
hkk

Salut @ cloudcoder2000. Pas besoin d'appeler une bibliothèque spécifique pour utiliser Union dans Mathematica.
Murta

5
À mon avis, utiliser une fonction intégrée pour faire exactement ce que la question demande est de tricher.
Konrad Borowski

ok ok .. le deuxième code n'utilise pas Union.
Murta

1
Je suppose que ce Tally[Join[a, b]][[;; , 1]]serait aussi de la triche ;-) BTW, vous pouvez enregistrer des caractères en utilisant des variables à une seule lettre.
Yves Klett

1

Javascript 86

Version golfée:

function m(a,b){var h={};return a.concat(b).filter(function(v){return h[v]?0:h[v]=1})}

Version lisible:

function merge(a, b) {
  var hash = {};
  return a.concat(b).filter(function (val) {
    return hash[val] ? 0 : hash[val] = 1;
  });
}

1
Cela ignore les valeurs de Falsey ... m([1,0,0,0,0],[0,1,0])revient [1].
George Reith du

1
Remplacez h[v]=vpar h[v]=1.
George Reith

Bien repéré @GeorgeReith! Nous sommes passés de 86 à 84 :)
Bertrand

Il est toujours de 86, je pense que vous vous êtes trompé parce que vous avez supprimé 2 caractères de la version lisible, pas celui joué.
George Reith du

1

JavaScript 60

J'utilise le générateur ES6.
Les éléments suivants peuvent être testés à l'aide du Traceur REPL de Google .

m=(i,j)=>{h={};return[for(x of i.concat(j))if(!h[x])h[x]=x]}

0

Si vous recherchez une implémentation basée sur JavaScript qui s'appuie sur les objets sous-jacents derrière le framework pour être efficace, j'aurais juste utilisé Set. Habituellement, dans une implémentation, l'objet Set gère intrinsèquement des objets uniques lors de l'insertion avec une sorte d'indexation de recherche binaire. Je sais qu'en Java c'est unlog(n) recherche, utilisant une recherche binaire basée sur le fait qu'aucun ensemble ne peut contenir un seul objet plus d'une fois.


Bien que je ne sache pas si cela est également vrai pour Javascript, quelque chose d'aussi simple que l'extrait suivant peut suffire pour une n*log(n)implémentation:

JavaScript , 61 octets

var s = new Set(a);      // Complexity O(a.length)
b.forEach(function(e) {  // Complexity O(b.length) * O(s.add())
  s.add(e);
}); 

Essayez-le en ligne!


Si l'extrait ci-dessus utilise a = [1,2,3]etb = [1,2,3,4,5,6] puis s=[1,2,3,4,5,6].

Si vous connaissez la complexité de la Set.add(Object)fonction en JavaScript, faites le moi savoir, la complexité de ceci est n + n * f(O)f(O)est la complexité de s.add(O).


0

APL (Dyalog Unicode) , O (N), 28 octets

Fonction infixe tacite anonyme.

(⊢(/⍨)⍳∘≢=⍳⍨),

Essayez-le en ligne!

, concaténer les arguments; SUR)

() Appliquer la fonction tacite anonyme suivante à ce sujet; O (1)

   ⍳⍨ indices selfie (indices de première occurrence de chaque élément dans l'ensemble du tableau); SUR)

  = comparer élément par élément à; SUR):

   ⍳∘≢ indices de la longueur du tableau; SUR)

(/⍨) utilisez-le pour filtrer; SUR):

   l'argument non modifié; O (1)

O (N + 1 + N + N + N + N + 1) = O (N)


-2

JavaScript, 131 caractères

var array1 = ["Vijendra","Singh"];   
var array2 = ["Singh", "Shakya"];     
result = Array.from(new Set([...array1, ...array2]))

4
Bienvenue chez PPCG! Veuillez nous indiquer de quelle langue il s'agit et formatez-le en tant que code pour une meilleure lisibilité. (Cela fonctionne en mettant en retrait les lignes de code avec quatre espaces). Une explication de votre approche serait également appréciée.
Laikoni

c'est juste un code javascript.
deepak_pal

@techdeepak Vous pouvez ajouter de telles informations vitales à votre message, les formater correctement, ajouter une coloration syntaxique et écrire un peu plus sur la complexité de votre algorithme, car il s'agit de l'algorithme le plus rapide . En l'état, ce poste est de qualité assez médiocre.
Jonathan Frech

-2

PHP environ 28 caractères [en omettant les exemples de variables de tableau et la variable de résultat].

$ array1 = array (1, 2, 3); $ array2 = tableau (3, 4, 5);

$ result = array_merge ($ array1, $ array2);


À partir de la question: N'utilisez pas les fonctions de tableau de la bibliothèque standard pour détecter l'unicité ou fusionner des ensembles / tableaux . De plus, cela ne supprime pas les doublons du tableau
Jo King

Je pense que vous avez ignoré cette importante ligne de la question: " N'utilisez pas les fonctions de tableau de la bibliothèque standard pour détecter l'unicité ou fusionner des ensembles / tableaux "
Peter Taylor

Oui. C'est correct. Merci les gars d'avoir souligné cela. Critiques humblement acceptées.
Endri

@jo king. Vous avez absolument raison de ne pas utiliser les bibliothèques standard ... Le reste est faux. Il supprime les doublons. php.net/manual/en/function.array-merge.php . Je vous recommande de lire entièrement la documentation de PHP. Je suis sûr à 100% qu'il fait l'affaire. Vous devez juste faire attention à l'un des tableaux que vous considérez comme des doublons. À votre santé.
Endri

1
J'ai littéralement exécuté le code dans votre soumission sans changement et la sortie a des doublons. Il semble que vous devriez lire la documentation, à savoir si, cependant, les tableaux contiennent des clés numériques, la valeur ultérieure ne remplacera pas la valeur d'origine, mais sera ajoutée
Jo King
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.