Matrice Unisci senza duplicati


15

Recentemente ho visto questo codice Javascript su StackOverflow per unire due matrici e rimuovere duplicati:

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(); 

Mentre questo codice funziona, è orribilmente inefficiente ( O(n^2)). La tua sfida è creare un algoritmo con meno complessità.

I criteri vincenti sono la soluzione con la minima complessità , ma i legami saranno spezzati dalla lunghezza più breve dei personaggi.

Requisiti :

Comprimi tutto il codice in una funzione che soddisfi i seguenti requisiti di "correttezza:"

  • Input: due array
  • Uscita: un array
  • Unisce elementi di entrambi gli array: qualsiasi elemento in uno degli array di input deve essere nell'array di output.
  • L'array di output non dovrebbe avere duplicati.
  • L'ordine non ha importanza (a differenza dell'originale)
  • Ogni lingua conta
  • Non utilizzare le funzioni di array della libreria standard per rilevare l'univocità o unire set / array (anche se altre cose della libreria standard sono a posto). Consentitemi di fare la distinzione che la concatenazione di array va bene, ma le funzioni che già fanno tutto quanto sopra non lo sono.

Come dovremmo creare o aggiungere ad un array senza usare le funzioni di array?
Emil Vikström,

@ EmilVikström Vedi la mia modifica. Volevo dire che non puoi usare le funzioni di unicità dell'array. Scusa se non sono chiaro.
hkk,

Se uno degli array contiene duplicati, li rimuoviamo anche noi? Ad esempio, l'unione [1, 2, 2, 3]e la [2, 3, 4]restituzione dovrebbero [1, 2, 2, 3, 4]o [1, 2, 3, 4]?
OI

1
@OI Sì, sarebbe troppo facile.
hkk,

1
Posso chiedere: array di cosa ? Possiamo assumere semplicemente numeri interi o stringhe o dobbiamo anche consentire cose più complesse come oggetti multilivello?
jawns317,

Risposte:


8

Perl

27 personaggi

Hack Perl semplice

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

Sono sicuro che qualcuno potrebbe dirigerne uno .. e quindi (Grazie Dom Hastings)

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

1
"Non utilizzare le funzioni di array della libreria standard per rilevare l'univocità (anche se altre cose dalla libreria standard va bene)"
John Dvorak,

1
Come sto violando questa regola? Non sto usando funzioni uniche?
Zach Leighton,

Come funziona allora? Mi dispiace, non so leggere perl. Se legge le chiavi di una mappa hash - conta con OK come quella regola? Non voterò fino a quando non lo convincerò.
John Dvorak,

1
Combina gli array, esegue il loop su entrambi e aggiunge un hash incrementando il valore che è la chiave il valore corrente nel loop dell'array. Quindi prende le chiavi di quell'hash, l'ho usato in alcuni dei miei lavori .. Quindi [1,1,2,3,4,4] diventa {1 => 2, 2 => 1, 3 => 1 , 4 => 2}
Zach Leighton,

@ZachLeighton puoi accorciare il codice a 27 caratteri sub x{$_{$_}++for@_;keys%_}(nel caso si tratti di un pareggio!) E utilizzare come:z((1,2,3,4),(2,3,4,5,6))
Dom Hastings

10

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

Versione golfizzata:

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}

Versione da golf leggibile dall'uomo:

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
}

Potrei usare concatcosì e farlo in 86 caratteri:

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

Ma non sono sicuro che sia ancora O (N) basato su questo JsPerf: http://jsperf.com/unique-array-merging-concat-vs-looping poiché la versione concat è leggermente più veloce con array più piccoli ma più lenta con array più grandi (Chrome 31 OSX).

In pratica, fallo (il golf è pieno di cattive pratiche):

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]));

Non sono grande nella complessità informatica ma credo che lo sia O(N). Mi piacerebbe se qualcuno potesse chiarire.

Modifica: ecco una versione che accetta un numero qualsiasi di matrici e le unisce.

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]));

Questo è quasi esattamente quello che avrei pubblicato in un paio di secondi :-( Sì, è tempo lineare ammortizzato se le tabelle hash sono implementate con tempo costante ammortizzato per l'inserimento e la ricerca (che è comune in molte lingue, non lo so in modo specifico circa JS).
Emil Vikström,

@ EmilVikström Grazie per quello che credo JavaScript abbia, ma non ne ho le prove. Scuse per avere le dita veloci, rallentato con commenti: P
George Reith

Questo è un ottimo approccio. Tuttavia, potresti fornire anche una soluzione in stile "code-golf" oltre alla tua versione ben formattata? Visto che più persone hanno pensato a questo come l'approccio giusto, probabilmente ci sarà un pareggio O(N).
hkk,

@ cloudcoder2000 Ok, volevo stampare una versione completa in quanto la versione di golf del codice probabilmente sarebbe meno efficiente in pratica.
George Reith,

1
@ cloudcoder2000 Non sono completamente indipendenti, quindi il caso peggiore non lo è O(A*B)(non utilizza Nperché è confuso). Sarebbe che se ogni array di input (every A) avesse la stessa quantità di elementi ( B) di quello che è in realtà O(SUM(B) FOR ALL A), che può essere riscritto come O(N)quando si definisce Nil conteggio degli elementi di tutti gli input di array.
meiamsome

4

Python 2.7, 38 caratteri

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

Dovrebbe essere O (N) che assume una buona funzione hash.

L' setimplementazione di 8 caratteri di Wasi è migliore, se non pensi che violi le regole.


Bello! Le comprensioni in Python possono essere così eleganti e potenti.
OI,

3

PHP, 69/42 68/41 caratteri

Inclusa la dichiarazione di funzione è di 68 caratteri:

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

Non includendo la dichiarazione di funzione sono 41 caratteri:

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

3

Un modo in Ruby

Per rispettare le regole sopra descritte, utilizzerei una strategia simile alla soluzione JavaScript e utilizzerei un hash come intermediario.

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

In sostanza, questi sono i passaggi che sto attraversando nella riga sopra.

  1. Definire una variabile merged_arrche conterrà il risultato
  2. Inizializza un hash vuoto e senza nome come intermediario per inserire elementi unici
  3. Utilizzare Object#tapper popolare l'hash (indicato come hashnel tapblocco) e restituirlo per il successivo concatenamento del metodo
  4. Concatenare arr1e arr2in un singolo array non elaborato
  5. Per ogni elemento eldell'array concatenato, inserire il valore elin hash[el]se nessun valore hash[el]attualmente esistente. La memoization qui ( hash[el] ||= el) è ciò che garantisce l'unicità degli elementi.
  6. Recupera le chiavi (o i valori, poiché sono uguali) per l'hash ora popolato

Questo dovrebbe funzionare in O(n)tempo. Per favore fatemi sapere se ho fatto dichiarazioni inesatte o se posso migliorare la risposta di cui sopra sia per efficienza o leggibilità.

Possibili miglioramenti

L'uso della memoization non è probabilmente necessario dato che le chiavi dell'hash saranno uniche e che i valori sono irrilevanti, quindi questo è sufficiente:

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

Adoro davvero Object#tap, ma possiamo ottenere lo stesso risultato usando Enumerable#reduce:

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

Puoi persino usare Enumberable#map:

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

Come lo farei in pratica

Detto questo, se mi chiedessero di unire due array arr1e arr2tale che il risultato merged_arrabbia elementi unici e potessi usare qualsiasi metodo Ruby a mia disposizione, utilizzerei semplicemente l'operatore set union che è destinato a risolvere questo esatto problema:

merged_arr = arr1 | arr2

Una rapida occhiata alla fonte di Array#|, tuttavia, sembra confermare che l'uso di un hash come intermediario sembra essere la soluzione accettabile per eseguire una fusione unica tra 2 array.


"Non utilizzare le funzioni di array della libreria standard per rilevare l'univocità (anche se altre cose dalla libreria standard va bene)"
John Dvorak,

Come sto violando questa regola nel secondo esempio? La memorizzazione viene eseguita su un hash. Neanche questo è permesso?
OI,

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

Una funzione che prenderebbe n array potrebbe essere la seguente:

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);
}

Golfato, penso che dovrebbe funzionare (117 caratteri)

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)}

Aggiornamento Se si desidera mantenere il tipo originale, è possibile

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
}

o 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}

Questo può ancora mettere in dubbio, se si vuole distinguere 123 e '123', quindi, non funzionerebbe.


Grazie per la risposta. È incredibilmente breve, tuttavia questo rappresenta solo metà del problema. È inoltre necessario includere nella soluzione la parte di fusione effettiva (anche se è la stessa dell'esempio originale) e riunire tutto in un'unica funzione. Inoltre, potresti fornire la versione "golfed" oltre a questa (così com'è O(N))?
hkk,

In questo modo tutti i membri vengono convertiti in stringhe. ad esempio m([1,2,3,4,5],[2,3,4,5,6],[2,3,4,5,6,7])diventa["1", "2", "3", "4", "5", "6", "7"]
George Reith il

2

pitone, 46

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

Oppure, usando semplicemente l'operazione impostata

pitone, 8

set(a+b)

1
Mi dispiace non sia stato chiaro, anche usare le operazioni set è barare.
hkk,

Il tuo primo codice avrà duplicati se ci sono duplicati in a o se ci sono duplicati in b e quell'elemento non è in a.
Vedant Kandoi,

2

Perl

23 byte, se contiamo solo il blocco di codice all'interno della subroutine. Potrebbe essere 21, se la sovrascrittura dei valori globali è consentita (rimuoverà mydal codice). Restituisce elementi in ordine casuale, perché l'ordine non ha importanza. Per quanto riguarda la complessità, in media è O (N) (dipende dal numero di collisioni di hash, ma sono piuttosto rare - nel peggiore dei casi può essere O (N 2 ) (ma questo non dovrebbe accadere, perché Perl può rilevare hash patologici e cambia il seme della funzione hash quando rileva tale comportamento)).

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;

Output (mostrando anche casualità):

/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

Versione golfizzata:

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

Il che non solo sembra infinitamente migliore, ma in realtà compilerà (una linea troppo lunga nella sua forma golfata) con la forma leggibile dall'uomo:

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

Questo dovrebbe essere O(n)come copio ain ce quindi controllare ogni bcontro tutti c. L'ultimo passaggio è eliminare la spazzatura che cconterrà poiché non è inizializzata.


2

Mathematica 10 Chars

Union[a,b]

Esempio:

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

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

Mathematica2 43 caratteri

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

8
Penso che questo andrebbe nella categoria di utilizzo dei metodi standard di array di librerie.
hkk,

Ciao @ cloudcoder2000. Non è necessario chiamare una libreria specifica per utilizzare Union in Mathematica.
Murta,

5
Secondo me, usare una funzione integrata per fare esattamente quello che la domanda è fare è barare.
Konrad Borowski il

ok ok .. il secondo codice non usa Union.
Murta,

1
Immagino che Tally[Join[a, b]][[;; , 1]]sarebbe anche barare ;-) A proposito, potresti salvare i caratteri usando variabili a lettera singola.
Yves Klett,

1

Javascript 86

Versione golfizzata:

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

Versione leggibile:

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

1
Questo ignora i valori di falso ... m([1,0,0,0,0],[0,1,0])restituisce [1].
George Reith,

1
Cambia h[v]=vin h[v]=1.
George Reith,

@GeorgeReith ben notato! Siamo passati dall'86 all'84 :)
Bertrand il

Sono ancora 86, penso che ti sia confuso perché hai rimosso 2 caratteri dalla versione leggibile e non quella da golf.
George Reith,

1

JavaScript 60

Sto usando il generatore ES6.
Di seguito è testabile utilizzando Traceur REPL di Google .

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

0

Se stai cercando un'implementazione basata su JavaScript che si basi sugli oggetti sottostanti dietro il framework per essere efficiente, avrei appena usato Set. Di solito in un'implementazione, l'oggetto Set gestisce intrinsecamente oggetti univoci durante l'inserimento con una sorta di indicizzazione della ricerca binaria. So che in Java è una log(n)ricerca, usando la ricerca binaria basata sul fatto che nessun set può contenere un singolo oggetto più di una volta.


Anche se non ho idea se questo sia vero anche per Javascript, qualcosa di semplice come il seguente frammento potrebbe essere sufficiente per n*log(n)un'implementazione:

JavaScript , 61 byte

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

Provalo online!


Se utilizza lo snippet di cui sopra a = [1,2,3]e b = [1,2,3,4,5,6]quindi s=[1,2,3,4,5,6].

Se conosci la complessità della Set.add(Object)funzione in JavaScript fammi sapere, la complessità di questo è n + n * f(O)dov'è f(O)la complessità di s.add(O).


0

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

Funzione anonimo di tacita infissione.

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

Provalo online!

, concatenare gli argomenti; SU)

(... ) applica la seguente funzione tacita anonima su quella; O (1)

   ⍳⍨ selfie selfie (indici di prima occorrenza di ciascun elemento nell'intero array); SU)

  = confrontare elemento per elemento con; SU):

   ⍳∘≢ indici della lunghezza dell'array; SU)

(/⍨) usalo per filtrare; SU):

   l'argomento non modificato; O (1)

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


-2

JavaScript, 131 caratteri

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

4
Benvenuti in PPCG! Per favore dicci quale lingua è e formattalo come codice per una migliore leggibilità. (Funziona indentando le righe di codice con quattro spazi). Anche una spiegazione del tuo approccio sarebbe apprezzata.
Laikoni,

è solo un codice javascript.
deepak_pal,

@techdeepak Potresti aggiungere tali informazioni vitali al tuo post, formattarlo correttamente, aggiungere l'evidenziazione della sintassi e scrivere qualcosa in più sulla complessità dell'algoritmo, poiché si tratta dell'algoritmo più veloce . Allo stato attuale, questo post è di qualità piuttosto bassa.
Jonathan Frech,

-2

PHP di circa 28 caratteri [tralasciando le variabili di array di esempio e la variabile di risultato].

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

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


Dalla domanda: non utilizzare le funzioni di array della libreria standard per rilevare l'univocità o unire set / array . Inoltre, ciò non rimuove effettivamente i duplicati dall'array
Jo King,

Penso che tu abbia trascurato questa importante linea dalla domanda: " Non utilizzare le funzioni di array della libreria standard per rilevare l'univocità o unire set / array "
Peter Taylor,

Sì. È corretto. Grazie ragazzi per averlo sottolineato. Critiche accettate umilmente.
Endri,

@jo king. Hai assolutamente ragione su "Non usare le librerie standard ...". Il resto è sbagliato. Rimuove i duplicati. php.net/manual/en/function.array-merge.php . Ti consiglio di leggere completamente la documentazione di PHP. Sono sicuro al 100% che fa il lavoro. Devi solo fare attenzione a quale delle matrici consideri duplicati. Saluti.
Endri,

1
Ho letteralmente eseguito il codice nel tuo invio senza modifiche e l'output ha duplicati. Sembra che dovresti leggere la documentazione, vale a dire Se, tuttavia, le matrici contengono chiavi numeriche, il valore successivo non sovrascriverà il valore originale, ma verrà aggiunto
Jo King
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.