Dada una matriz simétrica real , ¿existe un algoritmo que calcule la suma sobre todo 1 \ leq i <j <k \ leq n con complejidad de tiempo mejor que O (n ^ 3) ?
algorithms
time-complexity
usuario89217
fuente
fuente
Respuestas:
Existe un enfoque bastante práctico que funciona en tiempo , donde es el número de bits en la palabra del procesador. La idea principal es iterar sobre los elementos de la matriz uno por uno en orden creciente (romper los lazos arbitrariamente) y "encenderlos". Considere el momento en que el elemento más grande de algunos triples está encendido. Por simplicidad, supongamos que dicho elemento es . Es natural agregar el valor del triple a la respuesta ahora, cuando se enciende el último elemento. Entonces, tenemos que contar el número de posibles 's de modo que yO(n3/w) w aij,aik,ajk aij k aik ajk ya están encendidos (ese sería el número de triples, aquí es el elemento más grande, por lo que se encendieron por completo en este momento). Aquí podemos acelerar la implementación ingenua de utilizando la optimización de bits.aij O(n)
Para obtener detalles, puede consultar la siguiente implementación en C ++ 11 que debería funcionar para , (no está muy optimizado; sin embargo, aún supera la suma ingenua para por un amplio margen al menos en mi máquina).n⩽5000 |aij|⩽109 n=5000
Si considera el uso de trampas de optimizaciones de bits, puede usar el método de cuatro rusos para obtener el mismo resultado aquí, produciendo un algoritmo , que debería ser menos práctico (porque es bastante grande en la mayoría del hardware moderno) pero es teóricamente mejor. De hecho, elijamos y mantengamos cada fila de la matriz como una matriz de enteros de a , donde el -ésimo número en la matriz corresponde a los bits de la fila que van desde inclusive hasta exclusivo enO(n3/logn) w b≈log2n ⌈nb⌉ 0 2b−1 i ib min(n,(i+1)b) 0 -indexación. Podemos precalcular los productos escalares de cada dos de estos bloques en el tiempo . Actualizar una posición en la matriz es rápido porque estamos cambiando solo un número entero. Para encontrar el producto escalar de las filas y simplemente repita los arreglos correspondientes a esas filas, busque productos escalares de los bloques correspondientes en la tabla y resuma los productos obtenidos.O(22bb) i j
El párrafo anterior supone que las operaciones con números enteros toman tiempo. Es una suposición bastante común , porque generalmente no cambia la velocidad comparativa de los algoritmos (por ejemplo, si no usamos esa suposición, el método de fuerza bruta realmente funciona en tiempo (aquí medimos el tiempo en operaciones de bit) si toma valores enteros con valores absolutos al menos hasta para alguna constante (y de lo contrario podemos resolver el problema con multiplicaciones matriciales de todos modos); sin embargo, el método de los cuatro rusos sugerido anteriormente usa⩽n O(1) O(n3logn) aij nε ε>0 O(nε) O(n3/logn) operaciones con números de tamaño en ese caso; por lo que realiza operaciones de bit , que aún es mejor que la fuerza bruta a pesar del cambio del modelo).O(logn) O(n3)
Sin embargo, la pregunta sobre la existencia del enfoque sigue siendo interesante.O(n3−ε)
Las técnicas (optimizaciones de bits y método de cuatro rusos) presentadas en esta respuesta no son en absoluto originales y se presentan aquí para completar la exposición. Sin embargo, encontrar una manera de aplicarlos no fue trivial.
fuente
mat[i]
mat[j]
mat
cuál parece ser importante. Entiendo cómo se podría definir, pero me pregunto si(mat[i] & mat[j]).count()
funcionaría como se desea con cualquier contenedor STL.mat
- Supongo que debemos usarstd::vector<boost::dynamic_bitset<int64_t>>
.mat
: sí, tenía en mente un bitset estándar, peroboost::dynamic_bitset
en este caso es aún mejor, porque su tamaño no tiene que ser constante en tiempo de compilación. Editará la respuesta para agregar este detalle y aclarar el enfoque de los cuatro rusos.