[C] Insertion Sort

di il
9 risposte

[C] Insertion Sort

Salve a tutti, devo creare delle funzioni generiche di ordinamento. Il primo algoritmo che dovrei implementare è l'Insertion Sort. Ho creato un semplice programma per ordinare un array di double, al momento della stampa l'array risulta ordinato solo in parte. Per esempio:
Array da ordinare: {3.0,1.3,0.4,7.8,13.2,-1.1,6.0,-3.2,78};
Array dopo l'ordinamento: {-3.2,-1.1,0.4,1.3, 3.0,7.8,13.2,6.0,78};

Questo è il programma:
#include<stdio.h>
#include<stdlib.h>
#include<stddef.h>
#define N 9

int double_comparator(const void* a, const void* b)
{
    const double* aa = a;
    const double* bb = b;

    return (*aa > *bb) - (*aa < *bb);
}

void insertion_sort(void* base, size_t n, size_t size, int (*double_comparator)(const void *a, const void *b))
{
    size_t i;
    int j;
    double* dptr = malloc(sizeof(size));
    double* ptr_base = (double *)base;
    
    for(i=1; i<n; i++)
    {
        *dptr = ptr_base[i];
        j = i-1;
        
        /*Muove gli elementi di ptr_base[0...i-1], che sono maggiori di dptr, in una posizione prima di quella corrente*/
        while(j>=0 && (double_comparator(ptr_base, dptr)>0))
        {
            ptr_base[j+1] = ptr_base[j];
            j--;
        }
        ptr_base[j+1] = *dptr;
    }
    return;
}

int main()
{
    int i;
    double da[] = {3.0,1.3,0.4,7.8,13.2,-1.1,6.0,-3.2,78};

    printf("Array da ordinare:\t");

    for(i=0; i<N; i++)
    {
        printf("%f\t", da[i]);
    }

    printf("\n");
    printf("Arrat ordinato:\t");
    
    insertion_sort(da, N, sizeof(double), double_comparator);

    for(i=0; i<9; i++)
    {
        printf("%f\t", da[i]);
    }
}
Il compilatore non mi dà warning di nessun tipo (e la funzione di insertion sort deve essere necessariamente void). Qualcuno mi saprebbe aiutare?

9 Risposte

  • Re: [C] Insertion Sort

    Ciao, ti consiglio di inserire delle printf() nella funzione double_comparator():
    - una printf() all'inizio per stampare i valori dei due double da confrontare
    - una printf() alla fine (ovviamente prima del return) che stampa il valore di ritorno
    per l'ultima ti consiglio di salvare il risultato in una variabile int, in modo da stamparlo con printf() e poi restituirlo senza dover ricalcolare (non è una questione di velocità, ma per essere sicuro di stampare il valore che poi effettivamente restituisci)
  • Re: [C] Insertion Sort

    Ho modificato il codice dell'Insertion Sort in questo modo:
    void insertion_sort(void* base, size_t n, size_t size) /*Base punta al primo elemento dell'array da ordinare*/
    {
        size_t i;
        int j;
        int p = 0; /*Indice del minimo*/
        double* temp = malloc(sizeof(size));
        double* ptr_base = base;
    
        for(i=1; i<n; i++)
            if(ptr_base[p] > ptr_base[i]) p = i;
    
        printf("Indice valore minimo: %d\n", p);
    
        /*Mettiamo il minimo nella prima posizione ptr_base[0]*/
        
        *temp = ptr_base[0];
        ptr_base[0] = ptr_base[p];
        ptr_base[p] = *temp;
        
        /*Partiamo dalla posizione 2 perchè non c'è bisogno di confrontare con la posizione 0 inizialmente*/
        for(i=2; i<n; i++)
        {
            j = i;
            
            while(ptr_base[j] < ptr_base[j-1])
            {
                *temp = ptr_base[j];
                ptr_base[j] = ptr_base[j-1];
                ptr_base[j-1] = *temp;
                
                j--;
            }
        }
        return;
    }
    E funziona perfettamente. Ora però dovrei modificarla per fare in modo che ordini non solo double, ma anche array di stringhe e array misti come questi:
    static double da[] = {3.0,1.3,0.4,7.8,13.2,-1.1,6.0,-3.2,78};
    static const char* sa[] = {"The","quick","brown","fox","jumps","over","the","lazy","dog"};
    static item_t ca[] = {{9,"john"},{8,"jane"},{7,"mary"},{6,"anthony"},{5,"stevie"},{4,"bob"},{3,"ann"},{2,"claire"},{1,"alice"}};
    ma non so come dovrei convertire la base void che la funzione prende come argomento in modo che riesca ad ordinare tutti questi casi.
  • Re: [C] Insertion Sort

    Devi implementare correttamente la funzione

    comparator

    in modo da gestire il tipo di dato voluto.
  • Re: [C] Insertion Sort

    La tua funzione insertion_sort può seguire 2 evoluzioni:
    a) la più semplice, ne fai una copia per ciascun tipo di dato che vuoi gestire
    b) la rendi indipendente dal tipo di dato, come credo fosse il tuo desiderio iniziale.
    Per la prima soluzione non dovresti incontrare alcuna difficoltà e dipendentemente da quello che vuoi fare potrebbe essere una soluzione accettabile.
    Nel secondo caso, invece, devi fare un po' di lavoro; devi rendere parametrizzabili questi due aspetti:
    1) la funzione che esegue il confronto
    2) dare indicazioni su come eseguire l'eventuale swap, che dovrai probabilmente fare con memcpy(), indicando la dimensione del dato.
    Prova a dare un'occhiata al prototipo della funzione qsort() https://www.tutorialspoint.com/c_standard_library/c_function_qsort.htm e cerca di ragionarci sopra. Buon lavoro
  • Re: [C] Insertion Sort

    Allora, ho modificato il codice in questo modo:
    void insertion_sort(void* base, size_t n, size_t size, sort_comparator_t cmp)
    {
        size_t i;
        int j;
        int p = 0; /*Indice del minimo*/
        void* tmp = malloc(size);
        long* ptr_base = (long *)base;
    
        for(i=1; i<n; i++)
            if(cmp(&ptr_base[p], &ptr_base[i]) > 0) p = i;
    
        /*Mettiamo il minimo nella prima posizione ptr_base[0], lo usiamo come sentinella per non sforare l'array a sinistra*/
        /**tmp = ptr_base[0];
        ptr_base[0] = ptr_base[p];
        ptr_base[p] = *tmp;*/
    
        memmove(tmp, &ptr_base[0], size);
        memmove(&ptr_base[0], &ptr_base[p], size);
        memmove(&ptr_base[p], tmp, size);
        
        /*Partiamo dalla posizione 2 perchè non c'è bisogno di confrontare con la posizione 0 inizialmente*/
        for(i=2; i<n; i++)
        {
            j = i;
            
            /*Muove gli elementi di ptr_base[0...i-1], che sono maggiori di dptr, in una posizione prima di quella corrente*/
            while(cmp(&ptr_base[j], &ptr_base[j-1]) <= 0)
            {
                /**tmp = ptr_base[j];
                ptr_base[j] = ptr_base[j-1];
                ptr_base[j-1] = *tmp;*/
    
                memmove(tmp, &ptr_base[j], size);
                memmove(&ptr_base[j], &ptr_base[j-1], size);
                memmove(&ptr_base[j-1], tmp, size);
                
                j--;
            }
        }
        return;
    }
    E le funzioni comparator per i vari tipi di dato sono queste:
    int double_comparator(const void* a, const void* b)
    {
        const double* aa = a;
        const double* bb = b;
    
        return (*aa > *bb) - (*aa < *bb);
    }
    
    int string_comparator(const void* a, const void* b)
    {
        const char** aa = (const char**) a;
        const char** bb = (const char**) b;
    
        return strcmp(*aa, *bb);
    }
    
    int item_comparator(const void* a, const void* b)
    {
        const item_t* aa = a;
        const item_t* bb = b;
    
        return (aa->id > bb->id) - (aa->id < bb->id);
    }
    I double e le stringhe li ordina correttamente, ma ho ancora problemi ad ordinare la struttura item_t, non mi trova il valore minimo corretto e mi dà un segmentation fault ma non capisco perchè. Questo è il codice che sto utilizzando per testarla:
    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define N 9
    
    struct item_s
    {
        long id;
        char* name;
    };
    typedef struct item_s item_t;
    
    static item_t ca[] = {{9,"john"},{8,"jane"},{7,"mary"},{6,"anthony"},{5,"stevie"},{4,"bob"},{3,"ann"},{2,"claire"},{1,"alice"}};
    
    static int item_comparator(const void* a, const void* b);
    
    int item_comparator(const void* a, const void* b)
    {
        const item_t* aa = a;
        const item_t* bb = b;
    
        return (aa->id > bb->id) - (aa->id < bb->id);
    }
    
    void insertion_sort(void* base, size_t n, size_t size, int (*item_comparator)(const void *a, const void *b))
    {
        size_t i;
        int j;
        int p = 0; /*Indice del minimo*/
        void* tmp = malloc(size);
        long* ptr_base = (long *)base;
    
        for(i=1; i<n; i++)
            if(item_comparator(&ptr_base[p], &ptr_base[i]) > 0) p = i;
    
        /*Mettiamo il minimo nella prima posizione ptr_base[0], lo usiamo come sentinella per non sforare l'array a sinistra*/
        /**tmp = ptr_base[0];
        ptr_base[0] = ptr_base[p];
        ptr_base[p] = *tmp;*/
    
        memmove(tmp, &ptr_base[0], size);
        memmove(&ptr_base[0], &ptr_base[p], size);
        memmove(&ptr_base[p], tmp, size);
        
        /*Partiamo dalla posizione 2 perchè non c'è bisogno di confrontare con la posizione 0 inizialmente*/
        for(i=2; i<n; i++)
        {
            j = i;
            
            /*Muove gli elementi di ptr_base[0...i-1], che sono maggiori di dptr, in una posizione prima di quella corrente*/
            while(item_comparator(&ptr_base[j], &ptr_base[j-1]) <= 0)
            {
                /**tmp = ptr_base[j];
                ptr_base[j] = ptr_base[j-1];
                ptr_base[j-1] = *tmp;*/
    
                memmove(tmp, &ptr_base[j], size);
                memmove(&ptr_base[j], &ptr_base[j-1], size);
                memmove(&ptr_base[j-1], tmp, size);
                
                j--;
            }
        }
        return;
    }
    
    int main()
    {
        int i;
        
        item_t* ca_clone = NULL;
    
        ca_clone = malloc(N*sizeof(item_t));
        assert( ca_clone != NULL );
        memcpy(ca_clone, ca, N*sizeof(item_t));
    
        printf("Array di struct da ordinare:\n");
        for(i=0; i<N; i++)
        {
            printf("%ld %s\n", ca_clone[i].id, ca_clone[i].name);
        }
    
        insertion_sort(ca_clone, N, sizeof(item_t), item_comparator);
    
        printf("Ordinato:\n");
    
        for(i=0; i<N; i++)
        {
            printf("%ld %s\n", ca_clone[i].id, ca_clone[i].name);
        }
    }
  • Re: [C] Insertion Sort

    Attenzione al tipo di dato che stai trattando ...

    Deve essere

    item_t* ptr_base = (item_t *)base;
  • Re: [C] Insertion Sort

    Con quella sostituzione l'ordinamento dei dati item_t funziona, ma non quello dei double e delle stringhe :/
  • Re: [C] Insertion Sort

    E non potrebbe essere diversamente ...
  • Re: [C] Insertion Sort

    Siccome il singolo elemento che devi trattare non ha una dimensione nota in fase di compilazione, non puoi accedere come se fosse un array perchè presuppone che si conosca tale dimensione in fase di compilazione, per determinare gli opportuni offset.
    Se ptr_base è un puntatore a long ad esempio ptr_base[1] punterà al quinto byte e come puoi immaginare la tua funzione sarà adatta a gestire solo elementi di dimensione 4 byte.
    Quello che devi fare è usare la memcpy() fornendo la posizione in byte, dove l'elemento i si trova alla posizione base+(i*size) e è ha una dimensione di size.
    Prova a ragionarci sopra.
Devi accedere o registrarti per scrivere nel forum
9 risposte