vectores pseint programacion matriz matrices davicotico caracol algoritmo algorithm math

algorithm - pseint - ¿Cuál es el algoritmo más rápido para determinar si algún número en una matriz ordenada es múltiplo de `x`?



matriz caracol en java davicotico (6)

Dado un entero positivo x y una matriz entera positiva ordenada A

¿Hay algún algoritmo más rápido que O(N) para determinar si algún elemento en A es un múltiplo de x ? No hay elementos negativos en A

Unir ingenuamente A vez es mi única idea hasta ahora, no sé si hay alguna forma de aprovechar el hecho de que A está ordenada para acelerarlo.


Creo que la respuesta a la pregunta general es no. Supongamos que la siguiente estructura para A : el elemento i ''th de A ai está dada por i*x +1 por lo que no hay elementos en A , que son múltiplos de x. Sin embargo, nunca ahorra tiempo al usar cualquiera de los "trucos" descritos anteriormente ... básicamente tiene que tomar una decisión basada en su conocimiento de A y x . Básicamente, puede elegir entre O (N) y O (K), donde K es el número de múltiplos posibles de x en A , puede lograr O (K) utilizando el hashing, de manera que i*x in A convierte en un tiempo constante Operación (en promedio ...).


Dos cosas para probar: primero encuentre el primer elemento en la matriz mayor que x. No hay necesidad de buscar la mitad inferior. Una búsqueda binaria puede hacer esto. Eso reducirá el tiempo en algunos casos, pero si el primer elemento es mayor que x, entonces obviamente no hay necesidad. Luego, habiendo identificado la sección con posibles múltiplos de x, haga una búsqueda binaria si está ejecutando un solo hilo, o si puede ejecutar varios hilos, divida la mitad superior en segmentos, y haga que cada uno busque un hilo por separado. Creo que esta puede ser la forma más eficiente, pero sujeto a las siguientes advertencias.

  1. Si el primer elemento mayor que x es bastante bajo en la matriz, pasará más tiempo configurando la búsqueda binaria que en el escaneo lineal.

  2. Hay un costo para hacer búsquedas binarias también. Si la matriz no es muy grande, será menos eficiente con eso que las búsquedas lineales. No estoy hablando del orden del algoritmo. Estoy considerando solo el tiempo de ejecución.

  3. Si puede hacer varios hilos, hay un costo bastante alto involucrado en la configuración de esos también. A menos que su matriz sea obscenamente larga, puede que no obtenga ningún beneficio al hacerlo también. Sin embargo, si tiene varios millones de elementos, puede beneficiarse al dividirlo en partes más pequeñas. Yo diría que sería raro que este escenario sea de utilidad.


El comentario de HT @ tobias_k.

Puede resolverlo en ~ O(n/x) (la actualización podría ser O(N*log(N)/X^2)) . Usted hace una búsqueda binaria para todos los múltiplos de x simultáneamente. Cuando subdivides cada espacio de búsqueda en cada iteración y cuando el espacio de búsqueda no puede contener un múltiplo de x, abortas esa rama. Entonces, en lugar de buscar binarios en cada valor, busca binarios todos los valores pero solo para aquellas ramas que aún contienen un múltiplo válido dentro de su rango. Lo mejor de esto es que evita totalmente la búsqueda del mismo espacio dos veces, lo que hace que el peor de los casos sea x = 1 u O(n/1) O(n) . En el mejor de los casos sabrá que el rango no puede contener un múltiplo y abortar en O (1).

Ya que tiene garantizado un caso peor de O (n) en el que básicamente pierde cada búsqueda de caché maldita (tenga en cuenta que en términos del mundo real esto podría terminar siendo más importante que la complejidad del tiempo, por lo tanto, pruebe esas cosas). Obtendría una complejidad de tiempo teórica que podría ser mejor que O (n) pero nunca peor que eso (guardar los saltos alrededor de una matriz perderá cachés porque así es como las computadoras realmente funcionan en el mundo real).

Como se predijo, el aumento de velocidad depende mucho del valor de k (x).

Esto comienza a ir más rápido que el bucle sin procesar en k = ~ 128. (factor de división)

Truncar las ramas logra que el mundo real supere el bucle en bruto. Supongo que la cuenta n no va a importar mucho, ya que parece escalar más o menos igual, pero tal vez directamente comprobando que es mejor.

Nota: por la naturaleza de este código omitirá los dobles, que es la diferencia en los conteos.

public class MultipleSearch { public static void main(String[] args) { Random random = new Random(); int[] array = new int[500000000]; for (int i = 0, m = array.length; i < m; i++) { array[i] = Math.abs(random.nextInt()); } Arrays.sort(array); for (int k = 1; k < 16777216; k *= 2) { long time; time = System.currentTimeMillis(); binaryFactorLocator(array, k); System.out.println("Factors found multi: " + (System.currentTimeMillis() - time) + " @" + k); time = System.currentTimeMillis(); loopFactorLocator(array, k); System.out.println("Factors found loop: " + (System.currentTimeMillis() - time) + " @" + k); } } public static void loopFactorLocator(int[] array, int k) { int count = 0; for (int i = 0, m = array.length; i < m; i++) { if (array[i] % k == 0) { count++; //System.out.println("loop: " + array[i] + " value at index " + i + " is a proven multiple of " + k); } } System.out.println(count + " items found."); } public static void binaryFactorLocator(int[] array, int k) { int count = binaryFactorLocator(0, array, k, 0, array.length); System.out.println(count + " items found."); } public static int binaryFactorLocator(int count, int[] array, int k, int start, int end) { if (start >= end) { //contains zero elements. (End is exclusive) return count; } int startValue = array[start]; //first value int endValue = array[end - 1]; //last value; if (startValue / k == endValue / k) { //if both values are contained within the same factor loop. if (startValue % k == 0) { //check lower value for being perfect factor. //System.out.println("multi-binary: " + startValue + " value at index " + start + " is a proven multiple of " + k); return count + 1; } return count; //There can be no other factors within this branch. } int midpoint = (start + end) / 2; //subdivide count = binaryFactorLocator(count, array, k, start, midpoint); //recurse. count = binaryFactorLocator(count, array, k, midpoint, end); //recurse. return count; } }

Esta implementación debe ser bastante sólida, ya que trunca el bucle dentro de los elementos start / k == end / k, debe omitir el doble (a veces, puede dividirse entre dos valores duplicados). Obviamente, la recursividad como esta probablemente no será óptima y tal vez debería ser reescrita con una pila de pila de llamadas menos.

474682772 items found. Factors found multi: 21368 @1 500000000 items found. Factors found loop: 5653 @1 236879556 items found. Factors found multi: 21573 @2 250000111 items found. Factors found loop: 7782 @2 118113043 items found. Factors found multi: 19785 @4 125000120 items found. Factors found loop: 5445 @4 58890737 items found. Factors found multi: 16539 @8 62500081 items found. Factors found loop: 5277 @8 29399912 items found. Factors found multi: 12812 @16 31250060 items found. Factors found loop: 5117 @16 14695209 items found. Factors found multi: 8799 @32 15625029 items found. Factors found loop: 4935 @32 7347206 items found. Factors found multi: 5886 @64 7812362 items found. Factors found loop: 4815 @64 3673884 items found. Factors found multi: 3441 @128 3906093 items found. Factors found loop: 4479 @128 1836857 items found. Factors found multi: 2100 @256 1953038 items found. Factors found loop: 4592 @256 918444 items found. Factors found multi: 1335 @512 976522 items found. Factors found loop: 4361 @512 459141 items found. Factors found multi: 959 @1024 488190 items found. Factors found loop: 4447 @1024 229495 items found. Factors found multi: 531 @2048 243961 items found. Factors found loop: 4114 @2048 114715 items found. Factors found multi: 295 @4096 121964 items found. Factors found loop: 3894 @4096 57341 items found. Factors found multi: 195 @8192 61023 items found. Factors found loop: 4061 @8192 28554 items found. Factors found multi: 106 @16384 30380 items found. Factors found loop: 3757 @16384 14282 items found. Factors found multi: 65 @32768 15207 items found. Factors found loop: 3597 @32768 7131 items found. Factors found multi: 35 @65536 7575 items found. Factors found loop: 3288 @65536 3678 items found. Factors found multi: 17 @131072 3883 items found. Factors found loop: 3281 @131072 1796 items found. Factors found multi: 13 @262144 1900 items found. Factors found loop: 3243 @262144 873 items found. Factors found multi: 6 @524288 921 items found. Factors found loop: 2970 @524288 430 items found. Factors found multi: 3 @1048576 456 items found. Factors found loop: 2871 @1048576 227 items found. Factors found multi: 2 @2097152 238 items found. Factors found loop: 2748 @2097152 114 items found. Factors found multi: 1 @4194304 120 items found. Factors found loop: 2598 @4194304 48 items found. Factors found multi: 0 @8388608 51 items found. Factors found loop: 2368 @8388608


En un intento anterior, intenté una búsqueda dicotómica simple pero, como se señaló, eso no lleva a ninguna parte.

Aquí está mi mejor intento. Dudo que valga la pena la molestia, e incluso podría ser más lento para los casos del mundo real, pero aquí va.

Si tiene una matriz A [0..n] de enteros positivos ordenados y desea verificar si hay un múltiplo de un entero positivo X en A [i..j], donde 0≤i

If i>j then A[i..j] ist empty and thus contains no multiple of X Else if A[i] % X = 0 or A[j] % X = 0 then A[i..j] contains a multiple of X Else if A[i] / X = A[j] / X then A[i..j] contains no multiple of X Else A[i..j] contains a multiple of X iff A[i+1..(i+j)/2] or A[(i+j)/2+1..j-1] contains a multiple of X

Mi conjetura es que la complejidad sería O (n / X) ish, por lo tanto, no es realmente una mejora en el gran esquema de las cosas.

Editado para agregar: Si sus datos son realmente peculiares, esto podría ayudar. En muchos casos, podría realmente doler:

  • existe la sobrecarga de administrar la pila de retorno (la primera llamada recursiva no es terminal)
  • porque saltamos hacia adelante y hacia atrás a través de los datos en lugar de atravesarlos, destrozamos la localidad de caché
  • Hacemos la predicción de rama más difícil para el procesador.

Esto parece depender mucho del tamaño de x y del número de elementos dentro de A , y particularmente del número de múltiplos candidatos de x dentro de A

La búsqueda binaria de un número específico dentro de A lleva tiempo O (log (n)) (siendo n el número de elementos dentro de A ), por lo que si hay k posibles múltiplos de x entre el primer y el último elemento de A , tomará O(k * log(N)) para comprobarlos todos. Si ese número es más pequeño que n , puede usar este algoritmo, de lo contrario, simplemente haga una búsqueda lineal.

(Además, es probable que haya algunas pequeñas optimizaciones para el algoritmo anterior. Por ejemplo, una vez que marcó x*i (y no lo encontró), puede usar la posición donde x*i debería haber sido como el límite inferior al buscar x*(i+1) lugar del primer elemento de la matriz.)


Resta x de A iterativamente. Detente si cualquier resultado es igual a 0.

Con respecto a las restas, lo haces 1 + {max(A) DIV x} en el peor de los escenarios de esta manera, ya que debes restar x del elemento máximo de A después de que todos los demás hayan fallado la comprobación, y una vez más (de ahí el 1) al elemento máximo en sí, que también falla la comprobación, como 7 DIV 3 = 2, por lo que en tres iteraciones:

  1. 7 - 3 = 4
  2. 4 - 3 = 1
  3. 1 - 3 = -2,! = 0 por lo tanto no es divisible

Esto todavía califica como O(n) , pero termina siendo rápido, haciendo una resta de enteros en una matriz.