Controllo ricorsivo di due insiemi

di il
8 risposte

Controllo ricorsivo di due insiemi

Salve,

Sto portando a compimento un progetto d'esame. Mi servirebbe di capire bene una cosa. L'esercizio d'esame ci invita a creare una funzione che possa controllare ricorsivamente che due insiemi di numeri siano uguali. Da qui ho optato per creare una struttura che contenesse due vettori ( questo mi serve per altre funzioni), il problema è che il controllo sembra non vada come dovrebbe, vi espongo il mio ragionamento

Struttura "insieme":

typedef struct insieme {
 double *vettore; /* Array di tipo vettore */
 int dimensione; /* Variabile integer che definisce la dimensione del vettore */
} insieme;

Intestazione delle funzione:

int insiemi_uguali(insieme vettore, insieme vettore2)

dichiarazione variabile esito ( la utilizzo un po' come se fosse un valore booleano. Sarà lei a darci l'esito positivo o negativo alla domanda " i due insiemi sono uguali"?)

int esito; /* Output: 1 se i due insiemi sono uguali, 0 altrimenti */

Caso base, i due insiemi hanno dimensione 0. Se i due insiemi fossero di partenza vuoti allora sarebbero gia uguali, in caso contrario nella funzione di controllo successiva partiamo dalla dimensione massima dei due vettori e li confrontiamo, se i numeri "ennessimi" sono uguali decrementiamo la dimensione fino allo 0.

if(vettore.dimensione == 0 && vettore2.dimensione == 0)
     {
        esito = 1; /* i due insiemi sono uguali */
     }

Sarebbe stupido controllare due insiemi che hanno dimensione diversa. Anche se la dimensione dovesse differire di una sola unità i due insiemi non potrebbero mai essere uguali, di conseguenza:

/* Caso base, due insiemi non hanno la stessa dimensione */
    else if(vettore.dimensione > vettore2.dimensione || vettore.dimensione < vettore2.dimensione ) /*vettore.dimensione != vettore2.dimensione*/
      {
       esito = 0; /* dimensione diversa = i due insiemi non possono essere uguali = 0 */
      }

Se abbiamo la stessa dimensione, e questa è maggiore di 0, allora possiamo eseguire un controllo partendo dagli ultimi numeri dei due vettori (dimensione ennessima) ed eseguire un controllo.

NOTA BENE: I DUE VETTORI SONO GIÀ ORDINATI, DI CONSEGUENZA SE I VETTORI DOVESSERO ESSERE UGUALI SI AVREBBE UN PARALLELISMO INCONDIZIONATO.

  
    else if (vettore.vettore == vettore2.vettore)/*(da aggiungere una funzione che ordina i numeri nel main, prima della chiamata a funzione)*/		
     {
      vettore.dimensione --;
      vettore2.dimensione --;
      esito = insiemi_uguali(vettore,vettore2); 
     }
    
ora, non capisco perchè e dove sbaglio. Mi sembra di aver ragionato correttamente, ma se il programma non funziona ovviamente qualcosa l' ho sbagliato. Se qualche buon anima potesse aiutarmi gliene sarei grato. Vi lascio il codice completo senza commenti!

Lascio il codice anche su pastebin, magari si riesce a leggere meglio! ==> https://pastebin.com/Q6g8DKA

/* Funzione libreria 1: stabilire se i due insiemi sono uguali  */
int insiemi_uguali(insieme vettore, insieme vettore2)
{ /*start function*/
    int esito; /* Output: 1 se i due insiemi sono uguali, 0 altrimenti */
 
/* Caso base, due insiemi hanno dimensione 0, o l'hanno raggiunta tramite la ricorsione */
    if(vettore.dimensione == 0 && vettore2.dimensione == 0)
     {
        esito = 1; /* i due insiemi sono uguali */
     }

/* Caso base, due insiemi non hanno la stessa dimensione */
    else if(vettore.dimensione > vettore2.dimensione || vettore.dimensione < vettore2.dimensione ) /*vettore.dimensione != vettore2.dimensione*/
      {
       esito = 0; /* dimensione diversa = i due insiemi non possono essere uguali = 0 */
      }

    /* se i due elementi dei due vettori sono uguali */
    else if (vettore.vettore == vettore2.vettore)/*(da aggiungere una funzione che ordina i numeri nel main, prima della chiamata a funzione)*/		
     {
      vettore.dimensione --;
      vettore2.dimensione --;
      esito = insiemi_uguali(vettore,vettore2); 
     }
     else
     {
     printf("ERRORE");
    
    /*se non è stato trovato, si decrementa la dimensione e si richiama la funzione*/
    return(esito);
} /*end function*/ 

8 Risposte

  • Re: Controllo ricorsivo di due insiemi

    Zenek ha scritto:


    Sto portando a compimento un progetto d'esame. Mi servirebbe di capire bene una cosa. L'esercizio d'esame ci invita a creare una funzione che possa controllare ricorsivamente che due insiemi di numeri siano uguali.

    Toh, non sei il primo! Spero che tu, prima di postare, abbia letto l'altro thread.

    
    typedef struct insieme {
     double *vettore; /* Array di tipo vettore */
     int dimensione; /* Variabile integer che definisce la dimensione del vettore */
    } insieme;
    
    I commenti sono sballati, non sottovalutarne l'importanza. Usa cosa del tipo
    //vettore di double, contiene i dati
    //numero di elementi del vettore
    sarebbe molto meglio. Tra l'altro un insegnante che legge "array di tipo vettore" si potrebbe chiedere quanti punti toglierti

    Intestazione delle funzione:
    
    int insiemi_uguali(insieme vettore, insieme vettore2)
    
    Immagino che sia una carenza degli insegnanti: magari sono bravissimi come insegnanti ma hanno poca esperienza sul campo e poco tempo, pertanto tralasciano molti suggerimenti importanti. I nomi delle variabili DEVONO essere significativi.
    I nomi delle strutture e dei tipi DEVONO essere distinguibili da quelli delle variabili e delle costanti. Esistono "manuali di stile", cerca e studia ORA!

    dichiarazione variabile esito ( la utilizzo un po' come se fosse un valore booleano. Sarà lei a darci l'esito positivo o negativo alla domanda " i due insiemi sono uguali"?)
    
    int esito; /* Output: 1 se i due insiemi sono uguali, 0 altrimenti */
    
    Perché non un boolean? Non che non si faccia anche così ma, magari, il prof potrebbe chiederlo.

    Caso base, i due insiemi hanno dimensione 0. Se i due insiemi fossero di partenza vuoti allora sarebbero gia uguali, in caso contrario nella funzione di controllo successiva partiamo dalla dimensione massima dei due vettori e li confrontiamo, se i numeri "ennessimi" sono uguali decrementiamo la dimensione fino allo 0.
    
    if(vettore.dimensione == 0 && vettore2.dimensione == 0)
         {
            esito = 1; /* i due insiemi sono uguali */
         }
    
    Una domandina: perché uno si chiama "vettore" e uno "vettore2"? E "vettore1" dove è finito? Ripeto: attento ai nomi.

    NOTA BENE: I DUE VETTORI SONO GIÀ ORDINATI, DI CONSEGUENZA SE I VETTORI DOVESSERO ESSERE UGUALI SI AVREBBE UN PARALLELISMO INCONDIZIONATO.
    A parte l'uso eccessivo delle maiuscole, che accidente significa "parallelismo incondizionato" in questo contesto? Se niente, non scriverlo...

    Stai passando i due vettori come "valori": in una funzione ricorsiva è estremamente pericoloso perché la dimensione dello stack è limitata. Si fa solo in casi particolari e quando si è CERTI che non ci saranno problemi (mai).

    Ultima nota, per ora, usa meglio lo spazio: la leggibilità è una caratteristica importante del codice.
    E un'ultima domanda: come si confrontano due vettori?
  • Re: Controllo ricorsivo di due insiemi

    ... e 3

    In due topic recenti è postata la soluzione sia di questo problema che della versione a intersezione
  • Re: Controllo ricorsivo di due insiemi

    Un paio di osservazioni:

    1) BRUTTA COSA confrontare double per UGUAGLIANZA. Per problemi di arrotondamento due numeri che SULLA CARTA dovrebbero essere uguali POTREBBERO NON ESSERE UGUALI. Ad esempio uno potrebbe essere 5.000000000000001 e l'altro 4.9999999999999, ma quando li stampi li vedi entrambi scritti come 5.00, MA NON SONO UGUALI.
    MOLTO MEGLIO usare degli interi

    2) le funzioni RICORSIVE NON HANNO "side-effects", quindi, ad esempio, NON HAI CONTATORI. Questo implica che l'espressione
    
    vettore.dimensione --;
    
    NON E' AMMESSA

    3) se modifichi la dimensione del vettore, PERDI L"INFORMAZIONE sulla dimensione dello stesso. Quindi, ad esempio, non puoi passare la struttura "insieme" ad altre funzioni. La struttura "insieme" DESCRIVE l'insieme, quindi, una volta inizializzata NONDEVE ESSERE PIU' TOCCATA a meno che tu modifichi l'insieme stesso

    4) due insiemi sono UGUALI INDIPENDENTEMENTE DALL"ORDINE dei suoi elementi. Quindi, ad esempio [1,2,3] E' UGUALE a [3,2,1] E ANCHE A [3,1,2], [2,1,3], ecc.

    5) l'idea di controllare se due insiemi hanno lo stesso numero di elementi e' buona: due insiemi con un diverso numero di element NON POSSONO ESSERE UGUALI. MA due insiemi con LO STESSO NUMERO DI ELEMENTI NON SONO necessariamente UGUALI. Infatti [1,2,3] e [4,3,2] hanno entrambi 3 elementi, ma non sono minimamente ugiali.
    QUINDI, una volta che ti sei assicurato che i due insiemi hanno lo stesso numero di elementi, devi controllare se gli elementi di un insieme sono presenti nell'altro.

    6) la prima funzione RICORSIVA e' una che controlla se un elemento e' presente in un insieme, la seconda e' una che scegli egli elementi di un insieme, la terza che controlla se TUTTI gli elementi di un insieme sono presenti nell'ALTRO insieme.

    Come si risolve tutto questo?
    La PRIMA COSA e' avere una struttura dati che si presta ad essere devinita in modo ricorsive.
    La struttura dati per ECCELLENZA per questo tipo di operazioni e' la lista SEMPLICE.

    La definizione RICORSIVA di una LISTA semplice e' la seguente:

    1) [] (la lista vuota) e' una LISTA
    2) [a| REST]: una lista e' composta da un elemento e da una lista ottenuta rimuovendo il primo elemento

    Quindi, se tu hai la LISTA [1,2,3], questa puo' essere smontata nella coppia (1, [2,3]).
    La lista [2,3] puo' essere smontata nella coppia (2, [3]).
    La lista [3] puo' essere smontata nella coppia (3, [])


    La SECONDA COSA e' SAPERE/STUDIARE il PRINCIPIO DI INDUZIONE:

    La TERZA COSA e' trovare un modo intelligente per applicare il PRINCIPIO DI INDUZIONE ai tre algoritmi indicati in 6)

    Tieni presente che NON DEVI NECESSARIAMENTE implementare una lista. Anche un vettore puo' essere pensato come una lista, usi in modo INTELLIGENTE la tua struttura dati "insieme" piu' un INDICE che usi per indicare dove INIZIA la tua lista. All'inizio la tua lista inizia con il PRIMO elemento del vettore, ma POTREBBE iniziare anche con il secondo, il terzo, ecc ... (questo e' quello che chiamerebbero spoiler )


    LASCIA PERDERE IL CODICE: fino a che non lo sai fare con carat e matita, non riuscirai MAI a scrivere del codice funzionante.
  • Re: Controllo ricorsivo di due insiemi

    migliorabile ha scritto:


    Un paio di osservazioni:
    6 fa almeno 3 paia .
    1) BRUTTA COSA confrontare double per UGUAGLIANZA. Per problemi di arrotondamento due numeri che SULLA CARTA dovrebbero essere uguali POTREBBERO NON ESSERE UGUALI. Ad esempio uno potrebbe essere 5.000000000000001 e l'altro 4.9999999999999, ma quando li stampi li vedi entrambi scritti come 5.00, MA NON SONO UGUALI.
    Come al solito pontifichi a vanvera. Confondi un "double" con la sua rappresentazione decimale. I "double" sono tipi elementari e si confrontano allo stesso modo degli interi. E il paio di casi particolari non sono pertinenti.
    2) le funzioni RICORSIVE NON HANNO "side-effects", quindi, ad esempio, NON HAI CONTATORI.
    Ma che brutta spiegazione... potresti fare (molto) meglio.
    Questo implica che l'espressione
    
    vettore.dimensione --;
    
    NON E' AMMESSA
    Sbagli. E sbagli anche nel punto successivo: come facevo notare io la struttura viene passata per valore quindi (a parte i problemi dello stack) quella parte del codice ha senso e "funziona".
    Non è comunque il modo giusto di fare il confronto.
    4) due insiemi sono UGUALI INDIPENDENTEMENTE DALL"ORDINE dei suoi elementi. Quindi, ad esempio [1,2,3] E' UGUALE a [3,2,1] E ANCHE A [3,1,2], [2,1,3], ecc.
    Ma questo lo sapeva già: lo ha scritto chiaramente! Non lo hai letto???

    Bah...
  • Re: Controllo ricorsivo di due insiemi

    migliorabile ha scritto:



    1) BRUTTA COSA confrontare double per UGUAGLIANZA. Per problemi di arrotondamento due numeri che SULLA CARTA dovrebbero essere uguali POTREBBERO NON ESSERE UGUALI. Ad esempio uno potrebbe essere 5.000000000000001 e l'altro 4.9999999999999, ma quando li stampi li vedi entrambi scritti come 5.00, MA NON SONO UGUALI.
    MOLTO MEGLIO usare degli interi

    Ok le altre osservazioni, ma non confondergli le idee su questo punto, che devono fare un esame e poi rischiano di essere segati
    L'esercizio chiedeva cast a double da valori immessi da tastiera: è giusto che i due valori vengano dichiarati uguali a seguito degli arrotondamenti. Poi nella vita reale scopriranno le limitazioni dei double, che in C++ dovranno usare la libreria gmplib, etc. etc.
  • Re: Controllo ricorsivo di due insiemi

    @nicolap, lo so che ti sto sulle

    ,
    ma per dimostrare la tua

    competenza, invece di contestare quello che scrivo, puoi SEMPLICEMENTE fornire una SPIEGAZIONE MIGLIORE
  • Re: Controllo ricorsivo di due insiemi

    F1
  • Re: Controllo ricorsivo di due insiemi

    Grazie a tutti ragazzi alla fine son riuscito !

    Non mi va di fare necropost, ma almeno un grazie era dovuto !!!
Devi accedere o registrarti per scrivere nel forum
8 risposte