Esercizio del 28/10/21⚓︎
Letti in input due insiemi di elementi \(I_1\) e \(I_2\), di cardinalità rispettivamente \(n\) e \(m\), scrivere un algoritmo in grado di:
- individuare gli elementi comuni ai due insiemi;
- unire i due insiemi in un unico insieme;
- calcolare la differenza tra i due insiemi;
- stampare a video tutti i risultati.
Considerare i controlli sui dati di input. Realizzare come prima cosa la decomposizione funzionale.
Iniziare immediatamente con lo pseudo-codice potrebbe essere controproducente, dunque analizziamo prima le macro-operazioni.
Come prima cosa è necessario ricevere gli insiemi in input, dunque
Successivamente sono presenti le operazioni sugli insiemi, quindi:
e, infine:
Si tratta di una sequenza di azioni. Quelle appena elencate non sono altro che delle sotto-operazioni dell'operazione principale.
Tralasciando ancora lo pseudo-codice, leggere leggere insieme [...]
fa
immediatamente pensare a LeggereDaTastiera()
, ma per fare questo è necessaria
la cardinalità dell'insieme, dopodiché i valori dell'insieme e infine verificare
i vincoli sui dati. Quindi le operazioni distinte da effettuare sono quattro.
Analizzando ora stampare i risultati
, bisogna stampare gli insiemi Unione,
Intersezione e Differenza, vedremo dopo come.
Per quanto riguarda le operazioni sugli insiemi, il calcolo dell'intersezione è stato visto precedentemente. Il calcolo dell'unione è molto simile, bisogna prima inserire tutti gli elementi del primo insieme e poi controllare quali elementi del primo sono presenti nel secondo e se non è presente, aggiungerlo. Anche la differenza è simile in quanto si effettua una ricerca.
L'algoritmo va ora risolto per ogni operazione separata, ogni operazione ha un suo algoritmo separato delle altre.
Ora che son state analizzate brevemente le varie operazioni, possiamo creare un albero di lavoro:
Operazioni su insiemi
Leggere insieme I1
leggere cardinalità n di I1
verificare i vincoli di n
leggere i valori di I1
verificare i vincoli di I1
Leggere insieme I2
leggere cardinalità m di I2
verificare i vincoli di m
leggere i valori di I2
verificare i vincoli di I2
Calcolare intersezione
ricercare elemento in insieme
Calcolare unione
ricercare elemento in insieme
Calcolare Differenza
ricercare elemento in insieme
Stampare i risultati
stampare l'insieme intersezione
stampare l'insieme unione
stampare l'insieme differenza
flowchart LR;
%% main node
ops(Operazioni su insiemi);
%%% secondary nodes
rI1("leggere l'insieme I₁"); rI2("leggere l'insieme I₂");
cc(calcolare l'insieme);
pp(stampare i risultati); ppi(stampare l'insieme);
%%%% tertiary nodes
rcI1(leggere la cardinalità n); rcI2(leggere la cardinalità m);
vvn(verificare i vincoli di n); vvm(verificare i vincoli di m);
rvI1("leggere i valori di I₁"); rvI2("leggere i valori di I₂");
vvI1("verificare i vincoli di I₁"); vvI2("verificare i vincoli di I₂");
cint(intersezione); cuni(unione); cdif(differenza);
pint(intersezione); puni(unione); pdif(differenza);
%% from main
ops ---> rI1 & rI2 & cc & pp;
%%% from secondary
rI1 --> rcI1 --> vvn;
rI1 --> rvI1 --> vvI1;
rI2 --> rcI2 --> vvm;
rI2 --> rvI2 --> vvI2;
cc --> cint & cuni & cdif;
pp ---> ppi --> pint & puni & pdif;
Operazioni sugli insiemi⚓︎
Rappresenta il problema nella sua interezza.
- INPUT:
- \(I_1\) – primo insieme di \(n\) elementi, insieme
- \(I_2\) – secondo insieme di \(m\) elementi, insieme
- \(n\) – cardinalità di \(I_1\), con \(n \in \N^\ast\)
- \(m\) – cardinalità di \(I_2\), con \(m \in \N^\ast\)
- OUTPUT:
intersezione
– insieme di elementi comuni a \(I_1\) e \(I_2\), insiemeunione
– insieme di elementi di \(I_1\) e \(I_2\) distinti, insiemedifferenza
– insieme di elementi di \(I_1\) non comuni a \(I_2\), insieme
Bozza | |
---|---|
Questa è un'idea di come l'algoritmo debba funzionare. Poiché diventi un vero algoritmo andrebbe scritto in pseudo-codice.
ALGORITMO:
La lettura degli insiemi, come le altre operazioni saranno delle funzioni:
L'algoritmo non si discosta molto dall'albero di lavoro.
Leggere cardinalità⚓︎
Poiché la cardinalità degli insiemi è un dato continuamente utilizzato, è necessario richiederlo in input.
- INPUT:
- vuoto – non è presente input
- OUTPUT:
cardinalità
– numero di elementi di un insieme, naturale
ALGORITMO:
Text Only | |
---|---|
Leggere insieme⚓︎
- INPUT:
cardinalità
– numero di elementi dell'insieme \(I\), appartiene a \(\N^\ast\)- OUTPUT:
- \(I\) – insieme di elementi, insieme
ALGORITMO:
Text Only | |
---|---|
È necessario dare la possibilità di controllare la cardinalità e, finché questa non sia corretta, l'algoritmo non produce risultato.
Alcune di queste operazioni possono essere riscritte, ad esempio:
Per quanto riguarda la verifica dei vincoli, sarebbe meglio che questi vengano uniti alla lettura dei valori dell'insieme:
Text Only | |
---|---|
La precedente soluzione è funzionale, però non risulta essere comoda per l'utente poiché nel caso in cui anche un singolo valore sia errato, è necessario reinserire anche gli altri.
Calcolare⚓︎
Intersezione⚓︎
- INPUT:
- \(I_1\) – primo insieme di cui effettuare l'intersezione, insieme
- \(I_2\) – secondo insieme di cui effettuare l'intersezione, insieme
- \(n\) – cardinalità di \(I_1\), appartiene a \(\N^\ast\)
- \(m\) – cardinalità di \(I_2\), appartiene a \(\N^\ast\)
- OUTPUT:
intersezione
– insieme risultante dell'intersezione tra \(I_1\) e \(I_2\), insieme- LAVORO:
- \(p\) – posizione degli elementi di \(I_1\), appartiene a \(\N^\ast\)
- \(q\) – posizione degli elementi di
intersezione
, appartiene a \(\N^\ast\) trovato
– indica se un elemento è presente in un insieme, booleano
ALGORITMO:
Differenza⚓︎
- INPUT:
- \(I_1\) – primo insieme di cui effettuare l'intersezione, insieme
- \(I_2\) – secondo insieme di cui effettuare l'intersezione, insieme
- \(n\) – cardinalità di \(I_1\), appartiene a \(\N^\ast\)
- \(m\) – cardinalità di \(I_2\), appartiene a \(\N^\ast\)
- OUTPUT:
differenza
– insieme risultante dell'intersezione tra \(I_1\) e \(I_2\), insieme- LAVORO:
- \(p\) – posizione degli elementi di \(I_1\), appartiene a \(\N^\ast\)
- \(q\) – posizione degli elementi di
differenza
, appartiene a \(\N^\ast\) trovato
– indica se un elemento è presente in un insieme, booleano
ALGORITMO:
Text Only | |
---|---|
Unione⚓︎
- INPUT:
- \(I_1\) – primo insieme di cui effettuare l'intersezione, insieme
- \(I_2\) – secondo insieme di cui effettuare l'intersezione, insieme
- \(n\) – cardinalità di \(I_1\), appartiene a \(\N^\ast\)
- \(m\) – cardinalità di \(I_2\), appartiene a \(\N^\ast\)
- OUTPUT:
unione
– insieme risultante dell'intersezione tra \(I_1\) e \(I_2\), insieme- LAVORO:
- \(p\) – posizione degli elementi di \(I_1\), appartiene a \(\N^\ast\)
- \(q\) – posizione degli elementi di
unione
, appartiene a \(\N^\ast\) trovato
– indica se un elemento è presente in un insieme, booleano
ALGORITMO:
Text Only | |
---|---|
Stampare risultati⚓︎
- INPUT:
intersezione
– insieme di elementi comuni a \(I_1\) e \(I_2\), insiemedifferenza
– insieme di elementi di \(I_1\) non comuni a \(I_2\), insiemeunione
– insieme di elementi di \(I_1\) e \(I_2\) distinti, insieme-
cardinalitàIntersezione
– numero di elementi diintersezione
, appartiene- a \(\N^\ast\)
-
cardinalitàDifferenza
– numero di elementi didifferenza
, appartiene a- \(\N^\ast\)
cardinalitàUnione
– numero di elementi diunione
, appartiene a \(\N^\ast\)
- OUTPUT:
- nullo – non è previsto output
ALGORITMO:
Questa soluzione è certamente funzionale ma risulta essere estremamente brutta. L'alternativa più piacevole è la seguente:
Algoritmo 2 | |
---|---|
In questo modo introduciamo una nuova funzione, StampareInsieme()
, la quale
va specificata con le appropriate liste di input, output, etc. Quindi:
- INPUT:
- \(I\) – insieme di elementi da stampare, insieme
cardinalità
– numero di elementi dell'insieme da stampare, appartiene a \(\N^\ast\)- OUTPUT:
- nullo – non è previsto output
- LAVORO:
- \(p\) – posizione degli elementi dell'insieme \(I\), appartiene a \(\N^\ast\)
ALGORITMO:
Text Only | |
---|---|
In questo modo, anziché avere tre cicli MENTRE
diversi, utilizziamo una
funzione, che risulta essere una soluzione più elegante.
Calcolo della cardinalità⚓︎
Per ovviare al problema di chiedere continuamente come dato di input la cardinalità di ogni insieme, sarebbe possibile calcolarla.
- INPUT:
- \(I\) – insieme di elementi di cui calcolare la cardinalità, insieme
- OUTPUT:
- \(C\) – cardinalità dell'insieme \(I\), appartiene a \(\N^\ast\)
ALGORITMO:
Ma cos'è VALORE_FINALE
? Si tratta di un elemento di controllo che non
contribuisce ad aumentare la cardinalità dell'insieme ma permette all'algoritmo
di terminare. Un insieme potrebbe dunque essere il seguente:
In questo modo è possibile calcolare la cardinalità di un insieme, avendo la certezza di fermare l'iterazione al momento corretto.
È importante notare che, se la cardinalità viene utilizzata molte volte, risulta più efficiente1 o conservare il valore della stessa o chiederla in input.
-
Più efficiente dal punto di vista computazionale. ↩