elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Introducción a Git (Primera Parte)


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación C/C++ (Moderadores: Eternal Idol, Littlehorse, K-YreX)
| | |-+  Problema k-paired (k-emparejados) en O(N)
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] 2 Ir Abajo Respuesta Imprimir
Autor Tema: Problema k-paired (k-emparejados) en O(N)  (Leído 6,354 veces)
dijsktra

Desconectado Desconectado

Mensajes: 110


Mr Edsger Dijsktra (Tribute to)


Ver Perfil
Problema k-paired (k-emparejados) en O(N)
« en: 17 Septiembre 2019, 14:44 pm »

Dado un vector de enteros V[0..N) , con todos sus componentes siguiendo un orden stricto creciente, (1,5,17,20) y un numero k >=0 , diseñar un algoritmo de complejidad lineal que resuelva el número de pares de componentes <i,j> , con 0<=i,j<N, tales que Vi-Vj=k.

En la siguientee lineas se da unos ejemplos como ilustracion

Código:
5 3   (Numero de componentes del vector y k)
1 4 5 6 8  (Componentes del vector)
 2  (Resultado)

4 2 (Numero de componentes del vector y k)
1 4 5 6 (Componentes del vector)
 1  (Resultado)


3 0 (Numero de componentes del vector y k)
1 2 3 (Componentes del vector)
3 Resultado)


Yo dispongo de la solución. La pongo en dos días. Pero ahí esta el reto. Cuanto más simple mejor!


En línea

Si la depuración es el proceso de eliminar fallos en el software, entonces programar debe ser el proceso de ponerlos dentro. (Edsger Dijsktra)
MAFUS


Desconectado Desconectado

Mensajes: 1.603



Ver Perfil
Re: Problema k-paired (k-emparejados) en O(N)
« Respuesta #1 en: 17 Septiembre 2019, 22:13 pm »

En éste no veo como puede dar 3 de resultado.

Citar
3 0 (Numero de componentes del vector y k)
1 2 3 (Componentes del vector)
3 Resultado)


En línea

K-YreX
Moderador
***
Desconectado Desconectado

Mensajes: 1.008



Ver Perfil
Re: Problema k-paired (k-emparejados) en O(N)
« Respuesta #2 en: 18 Septiembre 2019, 08:36 am »

En éste no veo como puede dar 3 de resultado.

Supongo que son todos los pares (i, j) donde i = j. Como son 3 elementos, pues 3 pares.
En línea

Código
  1. cout << "Todos tenemos un defecto, un error en nuestro código" << endl;
MAFUS


Desconectado Desconectado

Mensajes: 1.603



Ver Perfil
Re: Problema k-paired (k-emparejados) en O(N)
« Respuesta #3 en: 18 Septiembre 2019, 12:30 pm »

El enunciado dice que i-j=0, sin embargo no hay pares en {1, 2, 3} ,dónde la resta de dos elementos del conjunto de 0.
En línea

K-YreX
Moderador
***
Desconectado Desconectado

Mensajes: 1.008



Ver Perfil
Re: Problema k-paired (k-emparejados) en O(N)
« Respuesta #4 en: 18 Septiembre 2019, 12:40 pm »

El enunciado dice que i-j=0, sin embargo no hay pares en {1, 2, 3} ,dónde la resta de dos elementos del conjunto de 0.

Citar
Dado un vector de enteros V[0..N) , con todos sus componentes siguiendo un orden stricto creciente, (1,5,17,20) y un numero k >=0 , diseñar un algoritmo de complejidad lineal que resuelva el número de pares de componentes <i,j> , con 0 <= i, j < N, tales que Vi-Vj = k.
En ese supuesto, las soluciones serían:
  • (0,0) ya que V[0] - V[0] = k
  • (1,1) ya que V[1] - V[1] = k
  • (2,2) ya que V[2] - V[2] = k
Donde para todas las soluciones se cumplen todas las condiciones del enunciado. Es más se puede dar por supuesto que como el orden debe ser estrictamente creciente (no hay repetidos), siempre que k = 0, el número de pares que satisfacen las condiciones es N y por tanto la solución también.
En línea

Código
  1. cout << "Todos tenemos un defecto, un error en nuestro código" << endl;
dijsktra

Desconectado Desconectado

Mensajes: 110


Mr Edsger Dijsktra (Tribute to)


Ver Perfil
Re: Problema k-paired (k-emparejados) en O(N)
« Respuesta #5 en: 18 Septiembre 2019, 17:04 pm »

 ;-) ;-) ;-) ;-)

YreX-DwX, bravo!  Yo mismo no podría haberlo explicado mejor...

Ahora... alguien se anima a hacerlo en O(N)?
En O(N^2) no tiene mucho mérito...
En línea

Si la depuración es el proceso de eliminar fallos en el software, entonces programar debe ser el proceso de ponerlos dentro. (Edsger Dijsktra)
MAFUS


Desconectado Desconectado

Mensajes: 1.603



Ver Perfil
Re: Problema k-paired (k-emparejados) en O(N)
« Respuesta #6 en: 18 Septiembre 2019, 22:40 pm »

Pues sigo sin entenderlo.
En línea

Loretz

Desconectado Desconectado

Mensajes: 117


Ver Perfil
Re: Problema k-paired (k-emparejados) en O(N)
« Respuesta #7 en: 18 Septiembre 2019, 23:56 pm »

Edito...
Acomodo un poco que puede hacerse un pelín más claro y algo más rápido

Código
  1. #include <iostream>
  2. #include <algorithm>
  3. #include <vector>
  4. #include <cassert>
  5. #include <numeric>
  6. #include <chrono>
  7.  
  8. size_t n_pares(const std::vector<int>& v, int k)
  9. {
  10.    assert(std::is_sorted(v.begin(), v.end()) && "v debe estar ordenado.");
  11.    assert(v.size() && "v no puede venir vacio.");
  12.  
  13.    auto n{ 0 };
  14.    auto it = v.begin();
  15.  
  16.    for (auto i = v.begin(), j = v.end();
  17.        i != j && it != j;
  18.        ++i)
  19.    {
  20.        auto s = *i + k;
  21.        it = std::lower_bound(it, j, s);
  22.  
  23.        if (it != j && *it == s) {
  24.            ++n;
  25.            ++it;
  26.        }
  27.    }
  28.  
  29.    return n;
  30. }
  31.  
  32. int main()
  33. {
  34.    std::vector v1{ 1, 4, 5, 6, 8 };
  35.    std::vector v2{ 1, 4, 5, 6 };
  36.    std::vector v3{ 1, 2, 3 };
  37.  
  38.    std::cout << n_pares(v1, 3) << '\n';
  39.    std::cout << n_pares(v2, 2) << '\n';
  40.    std::cout << n_pares(v3, 0) << '\n';
  41.  
  42.    std::vector<int> v4(1000, 0);
  43.  
  44.    for (int i = 1; i < 6; ++i) {
  45.        v4.resize(v4.size() * 10);
  46.        std::iota(v4.begin(), v4.end(), 0);
  47.  
  48.        std::cout << "v4.size() == " << v4.size() << '\n';
  49.  
  50.        // comienza a contar... desde acá
  51.        auto start = std::chrono::high_resolution_clock::now();
  52.        std::cout << n_pares(v4, 25) << '\n';
  53.        // hasta acá.
  54.        auto finish = std::chrono::high_resolution_clock::now();
  55.        std::chrono::duration<double> elapsed = finish - start;
  56.        std::cout << "demora: " << elapsed.count() << " seg\n\n";
  57.    }
  58.  
  59.    std::cout << "No es perfectamente lineal, pero solo se abre un poco a partir de los 100 millones.\n";
  60. }
« Última modificación: 19 Septiembre 2019, 02:15 am por Loretz » En línea

dijsktra

Desconectado Desconectado

Mensajes: 110


Mr Edsger Dijsktra (Tribute to)


Ver Perfil
Re: Problema k-paired (k-emparejados) en O(N)
« Respuesta #8 en: 19 Septiembre 2019, 09:12 am »

Estupendo intento, Loretz, buena exhibición de recursos... No obstante creo que el punto flaco reside en la llamada que haces a lower_bound, lo que eleva la complejidad de tu algoritmo a O(Nlog(N)). Mejor que cuadrático O(N^2), al menos...

...
    for (auto i = v.begin(), j = v.end();
        i != j && it != j;
        ++i)
    {
        auto s = *i + k;
        it = std::lower_bound(it, j, s);

        if (it != j && *it == s) {
            ++n;
            ++it;
        }
    }
...


A ver que te parece   ésta, en O(N): Brevemente, para evitar llamadas a lower_bound, controlas con una variable extra m, que parte del vector recorrido YA sabes que no va a emparejar con el actual en curso V[n], por lo que no hace falta volver a recorrer...
Ah!, y lo mismo vale para vectores vacíos

Código
  1.  
  2. /*        
  3.  P : strict(V)
  4.  fun k_paired(V[0..N) of int, int k) dev r
  5.  Q : r =#i,j : 0 <= i , j<N : V[j]-V[i]=k
  6.  
  7.  I : Q[N/n] and 0 <= m <= n <= N and
  8.      n<N -> \forall i : 0<=i < m : V[n]-V[i] > k   **
  9.  
  10.      ** m for efficiency reasons, discarding
  11.         subtrahends V[i] far from V[n].
  12.      
  13.  
  14.  !B : n==N     B: n<N
  15.  
  16.   C(N,n,m)= 2N - (m+n) >= 0
  17.  
  18.  
  19.  
  20. Invariant snapshot:
  21. -------------------
  22.  
  23. k = 20  , r = 0
  24. 0                 m                             n           N
  25. +-----+-----+-----+-----+-...-+-----+-----+-----+-----+-...-+
  26. |  1  |  3  |  5  |  40 |     |     | 49  | 50  | 60  |     |
  27. +-----+-----+-----+-----+-...-+-----+-----+-----+-----+-...-+
  28.  
  29.  
  30. */
  31.  
  32.  
  33.  
  34. #include <iostream>
  35.  
  36. using namespace std;
  37. int k_paired(const int V[], const int N, const int k)
  38. {
  39.  int n,m,r;
  40.  for(n=m=r=0;n<N;)
  41.    if (V[n]-V[m] <= k)
  42.      r+=(V[n++]-V[m] == k);
  43.    else m++;
  44.  return r;
  45.  
  46. }
  47.  
  48. #define MAX 100000
  49.  
  50. int main(int argc, char **args)
  51. {
  52.  int N,k;
  53.  int V[MAX];
  54.  for(cin >> N ; N!=-1 ; cin>>N)
  55.    {
  56.      cin >> k;
  57.      for(int n=0;n<N;n++)
  58. cin >> V[n];
  59.      cout << k_paired(V,N,k) << endl;
  60.    }
  61.  return 0;
  62. }
  63.  

A ver si pudieras medir tiempos con tus clases C++...
« Última modificación: 19 Septiembre 2019, 09:16 am por dijsktra » En línea

Si la depuración es el proceso de eliminar fallos en el software, entonces programar debe ser el proceso de ponerlos dentro. (Edsger Dijsktra)
Loretz

Desconectado Desconectado

Mensajes: 117


Ver Perfil
Re: Problema k-paired (k-emparejados) en O(N)
« Respuesta #9 en: 19 Septiembre 2019, 12:44 pm »

Está bien,

pero este algoritmo "k_paired" se comporta realmente en forma lineal. Lo modifiqué mínimamente para hacer una comparación:

Código
  1. #include <iostream>
  2. #include <algorithm>
  3. #include <vector>
  4. #include <cassert>
  5. #include <numeric>
  6. #include <chrono>
  7.  
  8. int k_paired(const std::vector<int>& V, const int k)
  9. {
  10.    size_t n, m, r;
  11.  
  12.    for (n = m = r = 0; n < V.size();)
  13.    {
  14.        if (V[n] - V[m] <= k) {
  15.            r += (V[n++] - V[m] == k);
  16.        }
  17.  
  18.        else {
  19.            m++;
  20.        }
  21.    }
  22.  
  23.    return r;
  24. }
  25.  
  26. int main()
  27. {
  28.    std::vector<int> v4(1000, 0);
  29.  
  30.    for (int i = 1; i < 6; ++i) {
  31.        v4.resize(v4.size() * 10);
  32.        std::iota(v4.begin(), v4.end(), 0);
  33.  
  34.        std::cout << "v4.size() == " << v4.size() << '\n';
  35.  
  36.        // comienza a contar... desde acá
  37.        auto start = std::chrono::high_resolution_clock::now();
  38.        std::cout << k_paired(v4, 37) << '\n';
  39.        // hasta acá.
  40.        auto finish = std::chrono::high_resolution_clock::now();
  41.        std::chrono::duration<double> elapsed = finish - start;
  42.        std::cout << "demora: " << elapsed.count() << " seg\n\n";
  43.    }
  44. }

Con estos resultados:

Citar
k_paired               
------------           

v4.size() == 10000      
9975                   
demora: 0.0001789 seg   
                       
v4.size() == 100000     
99975                   
demora: 0.0004935 seg   
                       
v4.size() == 1000000   
999975                 
demora: 0.0037331 seg   
                       
v4.size() == 10000000   
9999975                 
demora: 0.03295 seg     
                       
v4.size() == 100000000 
99999975               
demora: 0.32598 seg     
                       

Se lo ve muy lineal, ciertamente; y sobre todo, 10 veces más rápido que usando lower_bound; nada mal.

Pero la versión con lower_bound (que llamé "n_pares") no es O(Nlog(N)), es en realidad prácticamente equivalente a k_paired, también avanza como un gusano, y creía que debía comportarse de la misma manera, con un desenvolvimiento lineal, aunque resultó 10 veces más lento. Un oprobio. Si te fijas en esta línea:
Código:
it = std::lower_bound(it, j, s);
it queda posicionado en la posición buscada o en una más allá del límite s, esperando en el mejor lugar para la próxima iteración. Y lower_bound es un binary search, logarítmico, que es mejor que avanzar secuencialmente, o mejor dicho, creí que debería serlo.

Casualmente justo ayer Andrei Alexandrescu dio una charla sobre estas cuestiones en la CppCon, y comentaba que después de fracasar con todas las soluciones inteligentes comenzó a probas las estúpidas; y, sorpresa...



Si esto no se pone demasiado pesado, comento que:

Yo también probé con una solución (aparentemente) muy inferior a la versión con lower_bound, donde lo reemplazo por un estúpido find, haciendo ahora sí una solución (prácticamente) O(N2), pero no, curiosamente:

Código
  1. size_t n_pares(const std::vector<int>& v, int k)
  2. {
  3.    assert(std::is_sorted(v.begin(), v.end()) && "v debe estar ordenado.");
  4.    assert(v.size() && "v no puede venir vacio.");
  5.  
  6.    auto n{ 0u };
  7.    auto it = v.begin();
  8.  
  9.    for (auto i = v.begin(), j = v.end(); i != j; ++i)
  10.    {
  11.        it = std::find(it, j, *i + k);
  12.  
  13.        if (it != j) {
  14.            ++n;
  15.            ++it;
  16.        }
  17.        else {
  18.            it = i + 1;
  19.        }
  20.    }
  21.  
  22.    return n;
  23. }

Ahora los resultados son hasta ligeramente mejores que los de los de k_paired. Joder.

Código:
k_paired                                    n_pares
------------                                ------------

v4.size() == 10000                          v4.size() == 10000
9975                                        9975
demora: 0.0001789 seg                       demora: 0.0001686 seg

v4.size() == 100000                         v4.size() == 100000
99975                                       99975
demora: 0.0004935 seg                       demora: 0.0004539 seg

v4.size() == 1000000                        v4.size() == 1000000
999975                                      999975
demora: 0.0037331 seg                       demora: 0.0026152 seg

v4.size() == 10000000                       v4.size() == 10000000
9999975                                     9999975
demora: 0.03295 seg                         demora: 0.0266995 seg

v4.size() == 100000000                      v4.size() == 100000000
99999975                                    99999975
demora: 0.32598 seg                         demora: 0.26099 seg

UN mundo muy ingrato.

Para los que se las arreglan con el inglés vale la pena también el histórico:
"CPU cache and why you care" de Scott Meyers: https://youtu.be/3G-LO9T3D1M



En línea

Páginas: [1] 2 Ir Arriba Respuesta Imprimir 

Ir a:  

WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines