Búsqueda dicotómica de un elemento en una matriz ordenada

Steve17_17 -  
mamiemando Mensajes publicados 33174 Fecha de registro   Estado Moderador Última intervención   -

Hola,

Por favor, me gustaría ser aclarado con respecto a este código.

El objetivo es realizar una búsqueda dicotómica para verificar si un entero está presente en un arreglo de enteros supuesto ordenado en orden creciente.

Aquí está el programa en cuestión

#include <stdio.h> typedef enum (False, True) Boolean; int vec[6] = (5, 10, 17, 20, 25, 32); Boolean fun(int vec[], int inf, int sup, int elt) {     view(vec, inf, sup);     int med = (inf + sup) / 2;     if (inf < sup) {     if (elt > vec[med])            fun(vec, med + 1, sup,elt);     else            fun(vec, inf, med, elt);     } else {     if (vec[med] = *elt) return True;         else return False;     } } int main() {     if (fun(vec, 0,6,25) True) printf("True");     else printf("False");     return 0; }

¿Cómo escribir la cabecera de la función void view(intvec[ ], intinf, intsup) {...} que permita mostrar la parte del arreglo vec que está siendo procesada desde el índice inf hasta el índice sup ?

Aquí está lo que he podido hacer:

void view(intvec[], intinf, intsup) { int inf = 5; int sup = 32; for (int i = 5, i <= 32, i++) { printf("%d", i);     } }

También me gustaría proponer una versión iterativa de la función fun (¿cómo proceder?)

Moderación :

  • Gracias por usar un título más específico
  • Gracias por indicar el objetivo del programa
  • Gracias por indentar correctamente el código
  • Gracias por compartir los fragmentos de código como se explica en este tutorial y por indentar correctamente el código

El mensaje ha sido corregido en consecuencia

6 respuestas

  1. PierrotLeFou
     

    No aprendimos C en el mismo lugar. ¿Intentaste compilar tu código?

    ¿De dónde tomaste este código?

    En la función view, ¿por qué cambias el valor de inf y sup?

    Con un poco de imaginación, podría parecer una búsqueda dicotómica.

    Hay varias implementaciones iterativas de este algoritmo.

    Haz una búsqueda en la web.

    P.S. Una búsqueda dicotómica se realiza sobre un arreglo ordenado (este es el caso aquí)

    0
  2. PierrotLeFou
     

    Varios errores por tan poco código:
    No es la forma correcta de declarar un enum ni un typedef.
    Existe el tipo bool en <stdbool.h>
    Las definiciones de enum y de tablas se hacen con { } en lugar de ( )
    Estás usando *elt mientras que elt no ha sido definido como puntero (ver el enlace de Whismeril).
    En view(), el bucle va de 5 a 32 en lugar de infinito de abajo hacia arriba. Me hace creer que hay una incomprensión de pasar los parámetros.

    No mostrarás lo que crees mostrar.

    En fun(), las llamadas recursivas no devuelven nada.
    Y el algoritmo en sí no es correcto.

    edición: funciona pero obliga a descender a un intervalo de un solo elemento.

    0
  3. mamiemando Mensajes publicados 33174 Fecha de registro   Estado Moderador Última intervención   7 944
     

    Hola,

    Comentarios preliminares

    Gracias por compartir en el futuro tus fragmentos de código como se explica aquí y sobre todo por indentar correctamente, eso evitará errores de programación. También te recomiendo abrir y cerrar sistemáticamente pares de llaves después de cada if/else/for/while y volver a la línea después de cada { y cada }, eso hará que tu código sea más legible y probablemente evitará errores de programación en proyectos más largos.

    A continuación, también sería buena idea usar un título de discusión menos vago y explicar el objetivo de tu programa.

    Comentarios sobre el código

    La línea 3 está mal escrita:

    • como señaló Pierrot #3, el tipo bool se usa así:
    #include <stdbool.h> int main() { bool b1 = true; bool b2 = true; return 0; } 

    La línea 14 es "confusante" porque si entiendo bien tu programa, inf debe ser siempre menor o igual que sup y por tanto yo habría distinguido más bien:

    if (inf < sup) { // Seguir estrechando [inf, sup] } else if (inf == sup) { // Ver si el elemento está justo aquí } else { // Error, no deberíamos estar en este caso }

    La línea 15 me parece muy sospechosa.

    • Si quieres hacer una prueba (lo que a mi juicio es el fallo que buscas y que te trae aquí, por cierto), debes escribir :
    if (vec[med] == *elt)
    • Actualmente, estás haciendo una asignación de *elt en vec[med]. El resultado de esta operación es el valor asignado en vec[med]. Si este valor es distinto de cero entonces la prueba es verdadera, de lo contrario es falsa. Esto significa que cada vez que haces esta prueba, modificas el contenido de vec. Dudo que eso sea lo que quisieras escribir. Como escribir = en lugar de == es fácil de cometer, normalmente tu compilador te avisa. En los casos en que realmente quieras hacer la prueba + la asignación, el compilador esperará que insistas duplicando los paréntesis del if. En tu caso, esto volvería a escribirse como:
    if ((vec[med] = *elt))

    Cómo mostrar una parte de un arreglo

    Voy a reformularlo con mayor precisión: cómo mostrar una porción (slice) del arreglo tab de tipo int *, desde el índice i (incluido) hasta el índice j (excluido).

    #include <stdio.h> void print_tab( int * tab, size_t num_elements, size_t i, size_t j ) {     printf("[");     for (size_t k = i; k < j; k++) {         printf(" %d", tab[k]);     }         printf(" ]\n"); }      int main() {     int tab[] = {10, 20, 30, 40, 50, 60};     size_t num_elements = sizeof(tab) / sizeof(int);     print_tab(tab, num_elements, 0, 6); // [ 10 20 30 40 50 60 ]     print_tab(tab, num_elements, 2, 5); // [ 30 40 50 ]     print_tab(tab, num_elements, 2, 3); // [ 30 ]     print_tab(tab, num_elements, 2, 2); // [ ]     print_tab(tab, num_elements, 2, 1); // [ ]     return 0;       } 

    Version itérative

    En líneas generales, hay que reemplazar tu llamada recursiva y usar un bucle while del que deberás salir cuando se alcance un criterio de parada.

    La primera cosa a hacer es entender cuáles son esos criterios de parada, y cómo garantizar que en cada iteración del bucle, reduces estrictamente tu espacio de búsqueda.

    Como aquí tu objetivo es estrechar tus índices inf y sup para encontrar med, así que hay buenas probabilidades de que tu criterio de parada consista en verificar si inf == sup y asegurarte de que en cada iteración, ya sea inf se acerque a sup, o sup se acerque a inf.

    Bonne chance

    0
  4. PierrotLeFou
     

    @mamiemando, me pregunto si Steve17_17 ha entendido cómo calcular la longitud de un arreglo con la fórmula:
        num_elements = sizeof(tab) / sizeof(tab[0]);
    Esta variable no se usa en la función print_tab().
    Supongo que le dejaste a él la tarea de calcularlo por sí mismo si los límites i y j eran correctos.
    Matemáticamente: 0 <= i < j <= num_elements
    Una cosa que puede molestar al principiante que busca una implementación es el hecho de que hay dos variantes posibles:
    En primer lugar, inf es el índice del primer elemento y sup es el índice del último.
    En segundo lugar, inf es siempre el índice del primero, pero sup es el índice del siguiente al último.
    En esta segunda variante, sup - inf es la longitud del subarreglo a buscar.
    La prueba de estrechamiento y ajuste de inf o sup respecto a med es ligeramente diferente.
    La idea de estrechar alrededor del elemento buscado es correcta pero no necesariamente la más eficiente.
    Doy un ejemplo simple con el siguiente arreglo:
    [10, 20, 30, 40, 50, 60, 70]
    con la segunda variante, inf = 0 y sup = 7 y busco el valor 40.
    Entonces, med = (inf + sup) / 2 = (0 + 7) / 2 = 3
    Y precisamente 40 está en el índice 3. Lo encuentro en la primera recursión (o iteración)
    La idea es probar los límites y evaluar el valor de med inmediatamente después.
    Si inf > sup o inf >= sup según la variante, no se encontró.
    De lo contrario, se calcula de inmediato el valor de med y se prueba si el valor en esa posición es el buscado.
    Si es así, se sale con true o el índice donde se encuentra el valor buscado.
    De lo contrario, se busca en lo que precede o lo que sigue según si este valor viene antes o después del de la posición med.
    Primera variante: fun(tab, inf, med-1) o fun(tab, med+1, sup)
    Segunda variante: fun(tab, inf, med) o fun(tab, med+1, sup)

    0
    1. mamiemando Mensajes publicados 33174 Fecha de registro   Estado Moderador Última intervención   7 944
       

      Supongo que le dejaste a él la tarea de calcular por sí mismo si los límites i y j eran correctos.

      No, todo valor (positivo) de i y j es a priori correcto, como lo ilustra el cuerpo de la función main.

      Supongo que le dejaste a él la tarea de calcular por sí mismo si los límites i y j eran correctos.

      Digamos que solo respondo a la parte de la pregunta "cómo mostrar un slice de un arreglo". De hecho, dejé el ejercicio en sí de lado ya que es a steve a quien corresponde hacerlo. Por cierto, acabo de ver otro problema: las llamadas recursivas no van precedidas por return, lo que hace que el valor de la última llamada nunca suba por la cadena de llamadas. Al final es casi más sencillo escribir la versión iterativa, porque no se corre ese tipo de olvido.

      Matemáticamente: 0 <= i < j <= num_elements

      Puedes verificar que el programa es correcto para cualquier valor de i y j tales que:

      • 0 <= i < num_elements
      • 0 <= j < num_elements.

      Nota que los dos índices son estrictamente menores que el tamaño del arreglo (dado que partimos de 0), de lo contrario te desbordas una casilla. No hay restricción entre i y j, pero como en mi código, el paso es ascendente, normalmente tendrás ganas de tomar i <= j, o incluso i < j.

      Y respecto al final de tu mensaje, estoy de acuerdo con tus observaciones, hay que pensar en probar los límites durante la exploración. Así cada elemento del arreglo se lee como máximo una vez. Por lo tanto, si un elemento interviene en una frontera (inferior o superior), es que no es el número buscado.

      Buena suerte

      0
  5. Steve17_17 Mensajes publicados 27 Estado Miembro
     

    De acuerdo, muchas gracias, he tomado nota.

    0