Esame Di Programmazione

di il
23 risposte

23 Risposte - Pagina 2

  • Re: Esame Di Programmazione

    Alexv ha scritto:


    La difficoltà sta nel tenere traccia delle colonne e righe già bloccate quando si scende nella ricorsione.
    tutto dipende da come li vuoi passare
    semplificando al massimo potresti passare tutto come argomento

    potresti passare la matrice, un array di colonne attive e un array di righe attive
    ovvio che poi gli accessi alla matrice devi farli indirettamente attraverso gli array di colonna e di riga attive
    
    int M[MAX][MAX]; // la matrice
    int colonne[MAX]; // le colonne attive
    int righe[MAX]; // le righe attive
    int ordine=MAX; // la dimensione attuale della matrice in esame
    for (int i=0; i<MAX; i){
       righe[i]=i;
       colonne[i]=i; // al primo livello sono attive tutte le righe e tutte le colonne
    }
    
    per passare al livello successivo devi
    creare i nuovi array righe e colonne attive, di dimensione ridotta di uno

    in C puoi usare i VLA
    
    int ncolonne[ordine-1];
    int nrighe[ordine-1];
    
    in C++ usi allocazione dinamica

    scegli quale riga scarti e quale colonna scarti
    quella riga e quella colonna non le copi nei nuovi array


    a questo punto passi alla ricorsione la matrice, il nuovo array di righe e il nuovo array di colonne

    e naturalmente non puoi leggere la Iesima riga Jesima colonna con
    
    M[I][J];
    
    devi passare per la lettura degli array di riga e colonna valida
    
    M[nriga[i]][[ncollona[J]];
    
  • Re: Esame Di Programmazione

    Naturalmente esiste una maniera alternativa per risolvere la consegna
    che non fa uso di sottomatrici, e nemmeno sarebbe ricorsiva, probabilmente molto più veloce, ma comunque con un molto minore impegno di memoria

    si potrebbe descrivere con una parola sola:
    Gnomone


    mi correggo, maglio farla in maniera ricorsiva...
  • Re: Esame Di Programmazione

    A questo punto conviene usare un array globale booleano che tenga traccia di ogni riga e colonna, se è bloccato o no. Tanto se una tupla è bloccata, sarà bloccata anche nella chiamata sottostante. Se la blocca la chiamata sottostante, sarà essa ad occuparsi di sbloccarla, in modo da restituire al chiamante le condizioni di prima.
    Parlando ad occupazione di spazio, avrà dimensioni 2N, quindi trascurabili al crescere di N, rispetto alla matrice stessa.

    Si può anche ragionare, invece che di ricorsione, in termini di disposizioni D(2, N*N). Ovvero ogni riga e colonna può essere bloccata o sbloccata. Ad ogni nuova disposizione generata, si torna ad iterare sull'intera matrice.
    Ad esempio per una 2*2 hai:
    r0 r1 c0 c1
    Dove r sono le righe, c le colonne e ognuna assume valore bloccato o sbloccato.

    In ogni caso la complessità esplode (2^(N*N)). Servono comunque degli accorgimenti, tipo non ispezionare le sottomatrici trovate (e in questo il metodo ricorsivo forse è più facile). A meno che non ci siano metodi/teoremi che a noi sfuggono per fare più veloce.
  • Re: Esame Di Programmazione

    La soluzione che ho postato ci mette qualche secondo per una matrice 12x12, penso sia sufficiente a livello di esame universitario
  • Re: Esame Di Programmazione

    Mi sono cimentato anche io
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <time.h>
    #include <windows.h>
    
    #define N 13
    
    #define RED 12
    #define DEFAULT_COLOUR 7
    
    void set_position(const COORD xy)
    {
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), xy);
    }
    
    void set_colour(int colour)
    {
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colour);
    }
    
    void scambia_valori(unsigned int *a, unsigned int *b)
    {
        unsigned int temp = *a;
        *a = *b;
        *b = temp;
    }
    
    void ordina_array(unsigned int v[N], unsigned int u[N])
    {
        for(unsigned int i = 0; i < N - 1; ++i)
        {
            for(unsigned int j = 0; j < N - i - 1; ++j)
            {
                if(v[j] < v[j + 1])
                {
                    scambia_valori(v + j, v + (j + 1));
                    scambia_valori(u + j, u + (j + 1));
                }
            }
        }
    }
    
    bool inizializza_array(unsigned int u[N], const unsigned int dim)
    {
        for(unsigned int i = 0; i < dim; ++i)
        {
            u[i] = i;
        }
        return true;
    }
    
    bool combinazione_successiva(unsigned int u[N], const unsigned int n, const unsigned int k)
    {
        for(unsigned int i = 0; i < k; ++i)
        {
            if(u[k - i - 1] < n - i - 1)
            {
                ++u[k - i - 1];
                if(i && u[k - i] - u[k - i - 1] > 1)
                {
                    for(unsigned int j = k - i; j < k; ++j)
                    {
                        u[j] = u[j - 1] + 1;
                    }
                }
                return true;
            }
        }
        return false;
    }
    
    void riempi_matrice_casuale(int m[N][N], const int min, const int max)
    {
        for(unsigned int i = 0; i < N; ++i)
        {
            for(unsigned int j = 0; j < N; m[i][j++] = rand() % (max - min + 1) + min);
        }
    }
    
    void normalizza_matrice(bool m_[N][N], int m[N][N])
    {
        for(unsigned int i = 0; i < N; ++i)
        {
            for(unsigned int j = 0; j < N; ++j)
            {
                m_[i][j] = m[i][j] < 0;
            }
        }
    }
    
    void stampa_matrice(int m[N][N])
    {
        for(unsigned int i = 0; i < N; ++i)
        {
            for(unsigned int j = 0; j < N; printf("% d ", m[i][j++]));
            printf("\n");
        }
    }
    
    bool verifica_sottomatrice(bool m[N][N], unsigned int rig_2[N], unsigned int col_2[N], unsigned int u_rig[N], unsigned int u_col[N], const unsigned int k)
    {
        for(unsigned int i = 0; i < k; ++i)
        {
            for(unsigned int j = 0; j < k; ++j)
            {
                if(!m[rig_2[u_rig[i]]][col_2[u_col[j]]])
                {
                    return false;
                }
            }
        }
        return true;
    }
    
    void evidenzia_sottomatrice(int m[N][N], unsigned int rig_2[N], unsigned int col_2[N], unsigned int u_rig[N], unsigned int u_col[N], const unsigned int k)
    {
        set_colour(RED);
        for(unsigned int i = 0; i < k; ++i)
        {
            for(unsigned int j = 0; j < k; ++j)
            {
                set_position((COORD){3 * col_2[u_col[j]], rig_2[u_rig[i]]});
                printf("% d ", m[rig_2[u_rig[i]]][col_2[u_col[j]]]);
            }
        }
        set_position((COORD){0, N});
        set_colour(DEFAULT_COLOUR);
    }
    
    int main()
    {
        int m[N][N];
        srand(time(0));
        riempi_matrice_casuale(m, -5, 5);
        stampa_matrice(m);
    
        bool m_[N][N];
        normalizza_matrice(m_, m);
        unsigned int rig_1[N] = {0};
        unsigned int col_1[N] = {0};
        unsigned int rig_2[N];
        unsigned int col_2[N];
        unsigned int rig_3[N + 1] = {0};
        unsigned int col_3[N + 1] = {0};
        unsigned int u_rig[N];
        unsigned int u_col[N];
        for(unsigned int i = 0; i < N; ++i)
        {
            for(unsigned int j = 0; j < N; ++j)
            {
                rig_1[i] += m_[i][j];
                col_1[j] += m_[i][j];
            }
        }
        for(unsigned int k = 0; k < N; ++k)
        {
            rig_2[k] = col_2[k] = k;
            ++rig_3[rig_1[k]];
            ++col_3[col_1[k]];
        }
        ordina_array(rig_1, rig_2);
        ordina_array(col_1, col_2);
        for(unsigned int k = N; k; --k)
        {
            if(rig_3[k] >= k && col_3[k] >= k)
            {
                inizializza_array(u_rig, k);
                inizializza_array(u_col, k);
                do
                {
                    if(verifica_sottomatrice(m_, rig_2, col_2, u_rig, u_col, k))
                    {
                        evidenzia_sottomatrice(m, rig_2, col_2, u_rig, u_col, k);
                        return 0;
                    }
                }
                while(combinazione_successiva(u_rig, rig_3[k], k) || combinazione_successiva(u_col, col_3[k], k) && inizializza_array(u_rig, k));
            }
            rig_3[k - 1] += rig_3[k];
            col_3[k - 1] += col_3[k];
        }
    }
    L'ho testato poco e nulla, ma sembra funzionare.
    Con matrici di ordine 13 impiega qualche centesimo di secondo.

    Adesso non riesco, ma domani con calma cerco di spiegare il ragionamento che ho adottato e magari aggiorno il codice utilizzando dei nomi più significativi.


    P.S.
    Se riscontrate qualche bug fatemi sapere.
  • Re: Esame Di Programmazione

    Si può anche ragionare, invece che di ricorsione, in termini di disposizioni D(2, N*N). Ovvero ogni riga e colonna può essere bloccata o sbloccata. Ad ogni nuova disposizione generata, si torna ad iterare sull'intera matrice.
    Ad esempio per una 2*2 hai:
    r0 r1 c0 c1
    Dove r sono le righe, c le colonne e ognuna assume valore bloccato o sbloccato.

    In ogni caso la complessità esplode (2^(N*N)). Servono comunque degli accorgimenti, tipo non ispezionare le sottomatrici trovate (e in questo il metodo ricorsivo forse è più facile). A meno che non ci siano metodi/teoremi che a noi sfuggono per fare più veloce.
    in termini di disposizioni D(2, N*N)
    Questo e' gia' piu' intuitivo da capire.
    Praticamente si genera una stringa di bit (un valore binario di lunghezza 2N) dove i primi N rappresentano le righe e i secondi le colonne e il valore del bit indica se la riga o la colonna e' da testare o meno (bit=1 si, bit=0 no).
    La condizione su questo vettorone e' che ci siano almeno 2 bit a 1 nelle due meta'.
    Si passa (oppure si mette nell'area comune) questo "vettorone" alla funzione che esamina la matrice per cercare la condizione dell'esercizio, escludendo nel ciclo di riga e di colonna gli indici che nel vettorone sono settati a zero.
    Mi sembra convincente e piu' intuitivo di una soluzione ricorsiva, sicuramente meno elegante.
    Secondo me la complessita' e' minore di 2^2N perche' si prendono in considerazione solo i valori che hanno due bit a uno nella prima meta' e nella seconda.
    Forse la difficolta' sta nel generare il vettore, ma li' ci si puo' arrangiare in mille modi...
    L'importante e' non utilizzare la ricorsione , da evitare come il goto.
  • Re: Esame Di Programmazione

    Hai ragione HATFIELD, non tutte vanno esaminate. Nemmeno quelle che danno una sottomatrice 1*1. Però quelle stringhe vengono generate comunque.

    Perché dici che la ricorsione è il male? Non sempre riempie lo stack.
  • Re: Esame Di Programmazione

    Alexv ha scritto:


    Hai ragione HATFIELD, non tutte vanno esaminate. Nemmeno quelle che danno una sottomatrice 1*1. Però quelle stringhe vengono generate comunque.

    Perché dici che la ricorsione è il male? Non sempre riempie lo stack.
    Perche' la trovo poco intuitiva.
    Almeno per me che sono antico.
    Tra parantesi nella vita non e' che si incontrino tante situazioni in cui e' la soluzione ideale, forse esclusi i frattali, alberi e altre cose che adesso non mi vengono in mente, ma standardoil mi potra' facilmente smentire.
    Come soluzione e' elegante e sicuramente piace a Knuth, ma non agli ingegneri perche' non sempre e' efficiente.
    E poi qualsiasi algoritmo ricorsivo e' riconducibile ad uno non ricorsivo, mi ricordo che anni fa convertii il quicksort a non ricorsivo per utilizzarlo in basic.
  • Re: Esame Di Programmazione

    Nippolo ha scritto:


    Mi sono cimentato anche io
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <time.h>
    #include <windows.h>
    
    #define N 13
    
    #define RED 12
    #define DEFAULT_COLOUR 7
    
    void set_position(const COORD xy)
    {
        SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), xy);
    }
    
    void set_colour(int colour)
    {
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colour);
    }
    
    void scambia_valori(unsigned int *a, unsigned int *b)
    {
        unsigned int temp = *a;
        *a = *b;
        *b = temp;
    }
    
    void ordina_array(unsigned int v[N], unsigned int u[N])
    {
        for(unsigned int i = 0; i < N - 1; ++i)
        {
            for(unsigned int j = 0; j < N - i - 1; ++j)
            {
                if(v[j] < v[j + 1])
                {
                    scambia_valori(v + j, v + (j + 1));
                    scambia_valori(u + j, u + (j + 1));
                }
            }
        }
    }
    
    bool inizializza_array(unsigned int u[N], const unsigned int dim)
    {
        for(unsigned int i = 0; i < dim; ++i)
        {
            u[i] = i;
        }
        return true;
    }
    
    bool combinazione_successiva(unsigned int u[N], const unsigned int n, const unsigned int k)
    {
        for(unsigned int i = 0; i < k; ++i)
        {
            if(u[k - i - 1] < n - i - 1)
            {
                ++u[k - i - 1];
                if(i && u[k - i] - u[k - i - 1] > 1)
                {
                    for(unsigned int j = k - i; j < k; ++j)
                    {
                        u[j] = u[j - 1] + 1;
                    }
                }
                return true;
            }
        }
        return false;
    }
    
    void riempi_matrice_casuale(int m[N][N], const int min, const int max)
    {
        for(unsigned int i = 0; i < N; ++i)
        {
            for(unsigned int j = 0; j < N; m[i][j++] = rand() % (max - min + 1) + min);
        }
    }
    
    void normalizza_matrice(bool m_[N][N], int m[N][N])
    {
        for(unsigned int i = 0; i < N; ++i)
        {
            for(unsigned int j = 0; j < N; ++j)
            {
                m_[i][j] = m[i][j] < 0;
            }
        }
    }
    
    void stampa_matrice(int m[N][N])
    {
        for(unsigned int i = 0; i < N; ++i)
        {
            for(unsigned int j = 0; j < N; printf("% d ", m[i][j++]));
            printf("\n");
        }
    }
    
    bool verifica_sottomatrice(bool m[N][N], unsigned int rig_2[N], unsigned int col_2[N], unsigned int u_rig[N], unsigned int u_col[N], const unsigned int k)
    {
        for(unsigned int i = 0; i < k; ++i)
        {
            for(unsigned int j = 0; j < k; ++j)
            {
                if(!m[rig_2[u_rig[i]]][col_2[u_col[j]]])
                {
                    return false;
                }
            }
        }
        return true;
    }
    
    void evidenzia_sottomatrice(int m[N][N], unsigned int rig_2[N], unsigned int col_2[N], unsigned int u_rig[N], unsigned int u_col[N], const unsigned int k)
    {
        set_colour(RED);
        for(unsigned int i = 0; i < k; ++i)
        {
            for(unsigned int j = 0; j < k; ++j)
            {
                set_position((COORD){3 * col_2[u_col[j]], rig_2[u_rig[i]]});
                printf("% d ", m[rig_2[u_rig[i]]][col_2[u_col[j]]]);
            }
        }
        set_position((COORD){0, N});
        set_colour(DEFAULT_COLOUR);
    }
    
    int main()
    {
        int m[N][N];
        srand(time(0));
        riempi_matrice_casuale(m, -5, 5);
        stampa_matrice(m);
    
        bool m_[N][N];
        normalizza_matrice(m_, m);
        unsigned int rig_1[N] = {0};
        unsigned int col_1[N] = {0};
        unsigned int rig_2[N];
        unsigned int col_2[N];
        unsigned int rig_3[N + 1] = {0};
        unsigned int col_3[N + 1] = {0};
        unsigned int u_rig[N];
        unsigned int u_col[N];
        for(unsigned int i = 0; i < N; ++i)
        {
            for(unsigned int j = 0; j < N; ++j)
            {
                rig_1[i] += m_[i][j];
                col_1[j] += m_[i][j];
            }
        }
        for(unsigned int k = 0; k < N; ++k)
        {
            rig_2[k] = col_2[k] = k;
            ++rig_3[rig_1[k]];
            ++col_3[col_1[k]];
        }
        ordina_array(rig_1, rig_2);
        ordina_array(col_1, col_2);
        for(unsigned int k = N; k; --k)
        {
            if(rig_3[k] >= k && col_3[k] >= k)
            {
                inizializza_array(u_rig, k);
                inizializza_array(u_col, k);
                do
                {
                    if(verifica_sottomatrice(m_, rig_2, col_2, u_rig, u_col, k))
                    {
                        evidenzia_sottomatrice(m, rig_2, col_2, u_rig, u_col, k);
                        return 0;
                    }
                }
                while(combinazione_successiva(u_rig, rig_3[k], k) || combinazione_successiva(u_col, col_3[k], k) && inizializza_array(u_rig, k));
            }
            rig_3[k - 1] += rig_3[k];
            col_3[k - 1] += col_3[k];
        }
    }
    L'ho testato poco e nulla, ma sembra funzionare.
    Con matrici di ordine 13 impiega qualche centesimo di secondo.

    Adesso non riesco, ma domani con calma cerco di spiegare il ragionamento che ho adottato ...
    Il ragionamento di fondo è piuttosto semplice:

    - innanzitutto, affinché possa esistere una sottomatrice valida m di ordine k, la matrice completa M deve avere un numero n_rig di righe e un numero n_col di colonne, che presentino un numero di elementi validi (ossia di valori negativi in questo caso) maggiore o uguale a k, almeno pari a k;

    - in questo modo è possibile individuare un valore massimo di k da cui iniziare la ricerca delle sottomatrici. Se la ricerca per il valore attuale di k dà esito negativo, allora si diminuisce k di un'unità e si ripete la ricerca;

    - una volta definito il valore attuale di k, basta generare le sequenze date dalle combinazioni semplici degli n_rig elementi (costituiti dagli indici delle righe della matrice completa M con numero di elementi validi maggiore o uguale a k) di classe k, e quelle date dalle combinazioni semplici degli n_col elementi di classe k;

    - ogni possibile coppia tra combinazioni su riga e combinazioni su colonna definirà una sottomatrice facilmente verificabile.


    Questo per quanto riguarda l'algoritmo generale, relativamente al codice invece mi limiterò a spiegare cosa rappresentano rig_1, rig_2, rig_3 e u_rig:

    - rig_1 è un array di dimensione N (pari all'ordine della matrice completa M) i cui elementi rappresentano il numero di elementi validi nella corrispettiva riga di M.
    Discorso analogo vale per col_1 relativamente alle colonne;

    - rig_2 è un array di dimensione N, ricavato a partire da rig_1, i cui elementi rappresentano gli indici delle righe di M ordinati in senso decrescente relativamente al numero di elementi validi in esse contenuti.
    Discorso analogo vale per col_2 relativamente alle colonne;

    - rig_3 è un array di dimensione N+1, ricavato a partire da rig_1, i cui elementi rappresentano il numero di righe di M con numero di elementi validi pari all'indice in rig_3. Tale array viene utilizzato per ricavare i valori di k e n_rig menzionati in precedenza.
    Discorso analogo vale per col_3 relativamente alle colonne;

    - u_rig è un array intermedio utilizzato per generare le sequenze relative alle combinazioni semplici degli n_rig elementi di classe k.
    Discorso analogo vale per u_col relativamente alle colonne.


    Secondo voi quest'algoritmo in generale presenta ulteriori margini di ottimizzazione?


    P.S.
    @HATFIELD sarà contento che non sono "ricorso alla ricorsione"!
Devi accedere o registrarti per scrivere nel forum
23 risposte