2010-09-08 14 views
12

¿Conoce alguna forma eficiente en el tiempo de eliminar valores duplicados de una matriz de enteros muy grande utilizando Java? El tamaño de la matriz depende del usuario que haya iniciado sesión, pero siempre superará los 1500000 valores sin clasificar con algunos duplicados. Cada número entero contiene un número entre 100000 y 9999999.Eliminar duplicados de una matriz de enteros grandes utilizando Java

Intenté convertirlo en una lista, pero el montón en mi servidor no permite esta cantidad de datos (mi ISP lo ha restringido). Y un bucle regular para dentro de un bucle for toma más de 5 minutos para calcular.

El tamaño de la matriz sin los duplicados es el que almacenaré en mi base de datos.

¡La ayuda sería apreciada!

Respuesta

38

¿Quizás podría usar un juego de bits? No sé cuán eficiente es el BitSet de Java. Pero los valores posibles de 9999999 solo tomarían 9999999/8 = 1250000 bytes = poco más de 1Mb. A medida que recorre la matriz de valores, establezca el bit correspondiente en verdadero. Luego puede caminar sobre el conjunto de bits y generar el valor correspondiente siempre que encuentre un bit establecido en verdadero.

1Mb cabe en una caché de la CPU, por lo que esto podría ser bastante eficiente dependiendo de la implementación del conjunto de bits.

Esto también tiene el efecto secundario de ordenar los datos también.

Y ... este es un algoritmo O (n) ya que requiere un pase único sobre los datos de entrada, las operaciones establecidas son O (1) (para un conjunto basado en arreglos como este), y el pase de salida también es O (m) donde m es el número de valores únicos y, por definición, debe ser < = n.

+0

inteligente :), vale la pena intentarlo – Bozho

+0

+1 gran respuesta. – YoK

+5

Las respuestas inteligentes como estas son la razón por la que vengo a StackOverflow –

3

Haría un hashset donde almacenaría todos los valores contenidos en la lista, antes de comenzar a agregar elementos a la lista. Luego, simplemente verifique que el hashset no contenga el valor que desea agregar.

+0

"Intenté convertirlo en una lista, pero el montón en mi servidor no permite esta cantidad de datos", lo que probablemente también descarta los conjuntos. –

+1

En mi opinión, una lista es un desperdicio más de memoria que un hashset, para grandes conjuntos de datos. Pero podría estar equivocado. =/ –

+0

Eso depende en gran medida de la implementación de la lista. Creo que 'ArrayList' es más eficiente con la memoria que' HashSet', pero también podría estar equivocado :-) –

3
Set<Integer> set = new HashSet<Integer>(); 
Collections.addAll(set, array); 

que sólo necesitarán una serie de Integer[] en lugar de int[].

+1

"Intenté convertirlo en una lista, pero el montón en mi servidor no permite esta cantidad de datos" - eso probablemente también descarta a Sets. –

+0

Sí, eso es más al grano. @ user435140 tenga en cuenta que esto solo funcionará si su matriz contiene 'Integer''s, no primitive' int's. –

+0

@Bart K. buen punto – Bozho

2

Usted puede tratar de la clasificación de la primera matriz:

int arr[] = yourarray; 
Arrays.sort(arr); 
// then iterate arr and remove duplicates 
+0

eliminar duplicados ¿cómo? – Bozho

+0

@Bozho podría iterar la matriz y contar valores únicos. Aparentemente es lo único que tiene que hacer * ... El tamaño de la matriz sin los duplicados es el que almacenaré en mi base de datos ... * –

+1

Al ordenar primero, puede hacer un recorrido final de la matriz y solo conserva uno de cada valor único. Eso debería dar una complejidad de O (n log n) en oposición a O (n^2) para el bucle doble mencionado. –

0

Tal vez usted podría hacer un puñado de pasadas sobre los datos? Por ejemplo, si realizó diez pasadas sobre los datos y aplicó una de las sugerencias anteriores a un subconjunto más pequeño de los datos (por ejemplo, cuando value mod pass # == 0). Por lo tanto:

for (int i = 0 to 9) { 
    set = new Set() 
    for (each entry in the data set) { 
    if (entry % i == 0) { 
     set.add(entry) 
    } 
    } 
    output set 
} 

De esta manera se comercio fuera de tiempo para la memoria (aumentar el número de pases para menos memoria/más tiempo y viceversa).

1
int[] a; 
Arrays.sort(a); 
int j = 0; 
for (int i = 1; i < a.length; ++i) { 
    if (a[i] != a[j]) { 
    ++j; 
    a[j] = a[i]; 
    } 
} 
// now store the elements from 0 to j (inclusive - i think) 
+0

Si no es necesario ordenar el resultado, puede copiar los valores desde el "inicio" (que se incrementa cuando se copian) para reducir el número de copias. (uno por duplicado en lugar de uno por elemento) –

0

Si está seguro , que los enteros tienen valores pequeños razonables (por ejemplo, siempre más de cero y menos de 1000 o 10000), puedes probar un truco como este:

final int MAX = 100; 
    int[] arrayWithRepeats = {99, 0, 10, 99, 0, 11, 99}; 

    //we are counting here integers with the same value 
    int [] arrayOfValues = new int[MAX+1]; 
    int countOfUniqueIntegers = 0; 
    for(int i : arrayWithRepeats) { 
     if(arrayOfValues[i] == 0) { 
      countOfUniqueIntegers++; 
     } 
     arrayOfValues[i]++; 
    } 

    // you can use arrayOfValues (smaller) or convert it 
    // to table of unique values (more usable) 

    int[] arrayOfUniqueValues = new int[countOfUniqueIntegers]; 
    int index = 0; 
    for(int i = 0; i<arrayOfValues.length; i++) { 
     if(arrayOfValues[i] != 0) { 
      arrayOfUniqueValues[index] = i; 
      index++; 
     } 
    } 

    //and now arrayOfUniqueValues is even sorted 
    System.out.println(Arrays.toString(arrayOfUniqueValues)); 

de salida: [0, 10, 11, 99]

+0

Esto es esencialmente lo mismo que mi sugerencia de conjunto de bits, excepto que está utilizando 32 bits por entrada en lugar de 1, por lo que la memoria se convierte en un problema con bastante rapidez. Además, el OP dijo que los valores serán hasta 9999999. – dty

+0

Dado que "Todo número entero contiene un número entre 100000 y 9999999", esto no funcionará. – emory

+0

Tienes razón. Y la buena idea es cambiar arrayOfValues ​​form int [] a BitSet como la idea de Danny. –

1

Los verdaderamente desesperada podría escribir la matriz de disco y horquilla fuera sort | uniq | wc -l <infile.txt y capturar la salida. Esto sería necesario si la memoria aún estaba demasiado ajustada o si el espacio de dominio de los enteros aumentaba. No me gusta esto (¿está ejecutando unix?), Pero mi punto es que hay muchas formas de llevar a cabo la tarea.

Otra observación es que el valor mínimo es 100.000. Por lo tanto, podríamos restar 100,000 del valor máximo de 9,999,999, reduciendo el espacio del dominio y así ahorrar algo de memoria. Tal vez 100k/8 bits son cacahuetes en el esquema de las cosas, pero esencialmente es libre de hacerlo.

Cuestiones relacionadas