-
Notifications
You must be signed in to change notification settings - Fork 0
/
MST.jl
2210 lines (1781 loc) · 70.4 KB
/
MST.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
### A Pluto.jl notebook ###
# v0.19.9
using Markdown
using InteractiveUtils
# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
quote
local iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end
local el = $(esc(element))
global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el)
el
end
end
# ╔═╡ 7532e879-36ae-4a3c-bdac-3b3524f802d6
using Graphs, Plots, PlutoUI, GraphPlot, Colors, DataFrames, Compose, DataStructures, SimpleWeightedGraphs
# ╔═╡ 2278b12a-62b0-45cc-abe2-50312fcfcea7
using Printf, TypedTables
# ╔═╡ c639f755-3d8d-4a25-a6f5-057016e79656
begin
using ImageShow, FileIO, ImageIO
spacing_image = download("https://image-hosting.zhangjc.tech/ghost/content/images/2019/06/1870653438.png")
end
# ╔═╡ 8eccfa40-1592-11ed-2c77-8f1d323c1330
md"""
# Minimum Spanning Tree (MST)
**Problema** : determinare come interconnettere diversi elementi fra loro minimizzando certi vincoli sulle connessioni.
Questo problema prende il nome di albero ricoprente di peso minimo (**MST**), o minimo albero ricoprente.
Più formalmente, dato un grafo non diretto, connesso e pesato $G(V,E,w)$, dove $V$ è l'insieme dei nodi, $E ⊆ V×V$ è l'insieme di archi e $w$ è la funzione peso degli archi $w:E \to \mathbb{R}^+$, si vuole trovare un *albero* $T ⊆ E$, che è uno *spanning tree* di $G$, di **costo minimo**.
Dove il *costo di un albero $T$* è dato dalla somma dei pesi dei suoi archi :
$C(T) = \sum_{e \in T} \ w(e)$
"""
# ╔═╡ 0f119f11-f4eb-4363-89be-21c66233ed96
md"""
#### Applicazioni
I minimi alberi ricoprenti hanno applicazioni dirette nella progettazione di reti, tra cui reti di computer, reti di telecomunicazioni, reti di trasporto, reti di approvvigionamento idrico, reti elettriche...
Un'altra importante applicazione è il **k-Clustering**, di cui vedremo un esempio più avanti.
"""
# ╔═╡ d2538f31-63dd-4c47-bdf9-c2a4eca85d6b
md"""
# Approccio generale : Greedy
Un algoritmo *greedy* è un paradigma algoritmico, dove l'algoritmo cerca una soluzione ammissibile da un punto di vista globale attraverso la scelta della soluzione più appetibile ( "greedy", definita in precedenza dal programmatore) ad ogni passo locale.
Di seguito introduciamo due algoritmi greedy per il calcolo del *minimum spanning tree*:
- **Algoritmo di Prim** : Parte da un qualunque nodo $s$ e fa una visita dove ogni volta si scelgono gli archi di peso minimo locali e uscenti dal nodo che si sta visitando.
- **Algoritmo di Kruskal** : L'algoritmo di Kruskal si basa sulla seguente semplice idea: ordina gli archi in ordine non decrescente di costo e successivamente li analizzia singolarmente, inserisce l'arco nella soluzione se non forma cicli con gli archi precedentemente selezionati.
"""
# ╔═╡ 26f298b1-3850-4406-b969-ef9a2826f43f
md"""
##### Imposta il tipo di grafo su cui lavoreremo :
$@bind tipo_grafo Select([
:bull,
:chvatal,
:cubical,
:desargues,
:diamond,
:dodecahedral,
:frucht,
:heawood,
:house,
:housex,
:icosahedral,
:karate,
:krackhardtkite,
:moebiuskantor,
:octahedral,
:pappus,
:petersen,
:sedgewickmaze,
:tetrahedral,
:truncatedcube,
:truncatedtetrahedron,
:truncatedtetrahedron_dir,
:tutte])
"""
# ╔═╡ 1924f2fc-2c2a-449b-a035-b0346b77a244
md"""
### Algoritmo di Prim
---
L'albero di spanning minimo viene costruito gradualmente aggiungendo gli archi uno alla volta. All'inizio lo spanning tree è costituito solo da un singolo vertice (scelto arbitrariamente). Quindi l'arco di peso minimo in uscita da questo vertice viene selezionato e aggiunto all'albero di spanning. Dopodiché l'albero di spanning è già costituito da due vertici. Successivamente seleziona e aggiunge l'arco con il peso minimo che ha un'estremità in un vertice già selezionato (cioè un vertice che fa già parte dell'albero di spanning) e l'altra estremità in un vertice non selezionato. E così via, cioè ogni volta che selezioniamo e aggiungiamo l'arco con il minimo peso che collega un vertice selezionato con un vertice non selezionato.
Il processo viene ripetuto fino a quando l'albero di spanning contiene tutti i vertici (o in modo equivalente fino a quando non abbiamo lati).
Per mantenere traccia degli dei pesi degli archi si può utilizzare una coda con
priorità in modo tale che:
$priorità(v) = \min_{(u,v):u ∈ S} \ peso(u,v)$ $u,v ∈ V$ e $S$ è l'insieme dei nodi visitati.
Più in dettaglio il funzionamento, dato un grafo $G$ :
- crea un grafo vuoto $T$ che sarà l'**MST** di $G$.
- crea un insieme vuoto per i **nodi visitati** $S$.
- crea una **coda con priorità** $Q$. Inizialmente le priorità dei nodi sono ∞.
- finche la coda $Q$ non risulta vuota:
- rimuove l'elemento $u$ con priorità minima da $Q$, con la funzione $deleteMin$, e lo inserisce in $S$
- per ogni vicino $v$ di $u$ :
- se $v$ ∉ $S$ e il peso $w$ dell'arco $(u,v)$ è minore della priorità di $v$:
- aggiorna la priorità di $v$ : priorità($v$) = $w$ con la funzione $decreaseKey$
- rende u *nuovo padre* di v in $T$
"""
# ╔═╡ 29e0ce06-92dc-4daa-b304-32bbc5018ccb
md"""
##### MST calcolato con Prim :
sistema nodi $(@bind locs CheckBox(default=true))
"""
# ╔═╡ 9706a014-4360-4947-84ef-7d95cc5d9a57
md"""
##### Simulazione passo per passo
---
"""
# ╔═╡ 0e78835d-2625-45ce-8322-75aaf4e1f948
md"""
| colore | significato |
|:-------------------------:|:------------:|
| $(colorant"plum3") |nodo corrente |
| $(colorant"lightskyblue1")| nodo visto |
| $(colorant"palegreen3") | nodo visitato|
| $(colorant"grey98") | nodo default |
"""
# ╔═╡ 56e74cdc-fbe0-4ae9-847d-fe0fcdd8c2d6
md"""
##### Complessità
---
L'algoritmo esegue $|V|$ iterazioni del ciclo *while*, una per ogni nodo $u$, dove :
| funzione | costo |
|:-----------:|:------------------:|
|*decreaseKey*| $log{\|V\|}⋅deg(u)$|
| *deleteMin* | $\log{\|V\|}$ |
costo di un ciclo:
$θ(\log{|V|}⋅deg(u) + \log|V|)$
abbiamo quindi che il costo totale dell'algoritmo è dato dalla sommatoria :
$\sum_{u \in V} \ θ(deg(u)⋅\log{|V|}) = \log{|V|} ⋅ \sum_{u \in V} \ deg(u) = \log{2|E|} = θ(|E|⋅\log{|V|})$
Le considerazioni su questi costi suppongono la coda con priorità implementata con heap binomiali o d-heap.
Se si implementa la coda con heap di fibonacci si avrà un costo ridotto a causa
della funzione decreaseKey che costa θ(1).
Costo utilizzando *heap di fibonacci* :
$O(|E|+|V|\log{|V|})$
"""
# ╔═╡ 64168c91-09a4-4dbc-b719-ffe6a2a692fc
# per tenere la cronologia dei passi
begin
mutable struct PrimStruct
G::AbstractSimpleWeightedGraph
T::AbstractSimpleWeightedGraph
nodi_visitati::Vector # ordinati
nodi_visti::Vector{Vector}
archi_scelti::Vector # ordinati
PrimStruct(G::AbstractSimpleWeightedGraph) = new(G, SimpleWeightedGraph(nv(G)), Int[], Vector[], Edge[])
end
function add_step_prim!(s::PrimStruct, nodo_corrente::Int, nodi_visti::Vector)
# salva gli archi scelti
if !isempty(s.nodi_visitati)
arco_scelto = Edge(-1,-1)
peso_min = Inf
for node in s.nodi_visitati
if has_edge(s.G, node, nodo_corrente)
if s.G.weights[node,nodo_corrente] < peso_min
arco_scelto = Edge(node, nodo_corrente)
peso_min = s.G.weights[node,nodo_corrente]
end
end
end
push!(s.archi_scelti, arco_scelto)
end
#salva i nodi
push!(s.nodi_visitati, nodo_corrente)
# salva i nodi aggiornati
push!(s.nodi_visti, nodi_visti)
setdiff!(s.nodi_visti[end], s.nodi_visitati)
end
PrimStruct
end
# ╔═╡ 712f751e-3d36-4e15-8ae9-c8bd365d833c
"""
Input:
- `G::AbstractSimpleWeightedGraph` il grafo
Output:
- `simulazione::PrimStruct` il risultato della visita di Prim
"""
function Prim(G::AbstractSimpleWeightedGraph)::PrimStruct
simulazione = PrimStruct(G)
T = SimpleGraph(nv(G))
Q = PriorityQueue(1:nv(G) .=> Inf)
S = Set()
is_visto = Dict(vertices(G) .=> false)
nodi_visti = []
while !isempty(Q)
u = dequeue!(Q) #deleteMin
push!(S, u)
is_visto[u] = true
for v ∈ neighbors(G, u)
if !is_visto[v]
is_visto[v] = true
push!(nodi_visti, v)
end
if !in(v,S) && G.weights[u,v] < Q[v]
Q[v] = G.weights[u,v] #decreaseKey
# rendi u nuovo padre di v in T
for w ∈ neighbors(T, v) # solo 1 vicino (il padre)
rem_edge!(T,v,w)
end
add_edge!(T, Edge(u,v))
end
end
add_step_prim!(simulazione, u, copy(nodi_visti))
end
simulazione.T = SimpleWeightedGraph(T)
#imposta i pesi
for e ∈ edges(simulazione.T)
simulazione.T.weights[e.src,e.dst] = simulazione.T.weights[e.dst, e.src] = G.weights[e.src, e.dst]
end
return simulazione
end
# ╔═╡ adeba58b-0872-4662-9052-b97bcb7ffcff
md"""
### Algoritmo di Kruskal
---
Dato un grafo $G$ ordina gli archi in ordine crescente di costo e successivamente li analizzia singolarmente, inserisce l'arco nella soluzione $T ⊆ E$ se *non forma cicli* con gli archi precedentemente selezionati. Notiamo che ad ogni passo, se abbiamo più archi con lo stesso costo, è indifferente quale viene scelto.
"""
# ╔═╡ d8cb3ce5-1295-4b5b-b32f-e2686c78d1e9
md"""
Più in dettaglio:
- Ordina gli archi in ordine non decrescente di peso → *sorted_edges*
- mantiene un vettore di insiemi, i cui insiemi sono le componenti connesse. Inizialmente ogni nodo appartiene ad una componente connessa diversa.
- per ogni arco $(u,v)$ in *sorted_edges*:
- se $u$ e $v$ non appartengono alla stessa componente, allora $(u,v)$ fa parte della soluzione, inserisce l'arco in $T$.
- viene quindi, fatta l'**unione** delle componenti connesse che contengono i nodi $u$ e $v$.
- Altrimenti l'arco $(u,v)$ sta creando un ciclo, quindi non fa parte della soluzione.
Un'unione diminuisce il numero di componenti connesse di 1 e inizialmente abbiamo
|V| componenti connesse. Al più quindi, si possono fare |V| - 1 unioni. Infatti
Kruskal esegue |V| - 1 unioni così che alla fine rimane un'unica componente
connessa.
"""
# ╔═╡ f9bad8d1-7b6d-497f-92ca-080b45274c8a
md"""
!!! note
Osserviamo che ad un grafo possono corrispondere più MST con lo stesso costo minimo
"""
# ╔═╡ 5d99e996-129f-4eb5-87a5-d1d47088b202
md"""
Confronto tra l'MST calcolato con *Kruskal* e con *Prim* :
"""
# ╔═╡ 93821fc3-02f9-418f-8188-c5527ff926ab
md"""
Se sono uguali possiamo provare ad impostare tutti i pesi degli archi uguali ad uno, in questo modo avremo buona probabilità che gli MST saranno diversi, ovviamente anche aumentando il numero di archi e nodi del grafo la probabilità aumenta.
- Imposta pesi a uno : $@bind pesi_a_uno CheckBox(default=false)
"""
# ╔═╡ 1e357bc3-bc31-4bef-a7f9-40a5fcd66e19
md"""
##### Simulazione passo per passo
---
"""
# ╔═╡ 752b6429-2dcf-4114-9c6d-0654119008c9
md"""
- L'arco più "spesso" è quello che Kruskal sta esaminando.
- Gli archi colorati sono gli archi dell'MST (quelli scelti da Kruskal)
- Ogni colore identifica in modo univoco una componente connessa.
"""
# ╔═╡ 45ddcc19-0249-4c99-85a2-3330faf2cdfb
# per tenere la cronologia dei passi
begin
mutable struct KruskalStruct
G::AbstractSimpleWeightedGraph
T::AbstractSimpleWeightedGraph
archi_ord::Vector
comp_connesse::Vector{Vector}
archi_scelti::Vector
KruskalStruct(G::AbstractSimpleWeightedGraph) = new(G, SimpleWeightedGraph(nv(G)),
sort(collect(edges(G)), by=weight),Vector[], Edge[])
end
function add_step_kruskal!(s::KruskalStruct, comp_connesse::Vector, arco_scelto::Edge)
# salva le componenti connesse
push!(s.comp_connesse, comp_connesse)
push!(s.archi_scelti, arco_scelto)
end
# solo per printare
function makeSet()
end
function find()
end
KruskalStruct
end
# ╔═╡ 13728ce1-f7e6-4008-bea5-a3d79ccab07d
"""
Input:
- `G::AbstractSimpleWeightedGraph` il grafo
Output:
- `simulazione::KruskalStruct` il risultato dell'esecuzione di Kruskal
"""
function Kruskal(G::AbstractSimpleWeightedGraph)::KruskalStruct
simulazione = KruskalStruct(G)
T = SimpleGraph(nv(G))
archi_ord = sort(collect(edges(G)), by=weight, alg=MergeSort)
comp_conn = Set.(1:nv(G))
arco_scelto = Edge(-1,-1)
for e in archi_ord
set_1 = Set()
set_2 = Set()
add_step_kruskal!(simulazione, [copy.(comp_conn)], arco_scelto)
for set in comp_conn
if length(intersect(set, Set([e.src, e.dst]))) == 1
if isempty(set_1)
set_1 = set
else
set_2 = copy(set)
#cancella set_2 da comp_conn
filter!(e->e≠set,comp_conn)
end
end
end
if !isempty(set_1) && !isempty(set_2)
union!(set_1,set_2)
add_edge!(T, e.src, e.dst)
arco_scelto = Edge(e.src, e.dst)
end
if size(comp_conn)[1] == 1
add_step_kruskal!(simulazione, [copy.(comp_conn)], arco_scelto)
end
end
simulazione.T = SimpleWeightedGraph(T)
#imposta i pesi
for e ∈ edges(simulazione.T)
simulazione.T.weights[e.src,e.dst] = simulazione.T.weights[e.dst, e.src] = G.weights[e.src, e.dst]
end
return simulazione
end
# ╔═╡ c07294c0-251e-4fe7-a06f-a1971009bd38
md"""
# k-Clustering
Dato un insieme $U$ di $n$ oggetti $p₁,...pₙ$,si vuole classificare questi oggetti in gruppi coerenti. Definiamo la *funzione distanza* come il valore numerico che specifica la "vicinanza" di 2 oggetti :
$d(pᵢ, pⱼ)$
dove pᵢ e pⱼ sono due oggetti.
**Problema** : Dividi gli oggetti in cluster (gruppi) in base alla distanza degli oggetti, in modo tale che punti in differenti cluster sono più lontani possibile.
Ad esempio possiamo dire che, più due oggetti sono simili e meno è la loro distanza, così che vengano raggruppati oggetti tra loro simili.
Piu formalmente, assumiamo che la funzione rispetti le proprietà naturali, ossia :
| |
|:-------------------------:|
|$d(pᵢ, pⱼ) = 0 ⟺ pᵢ = pⱼ$ |
|$d(pᵢ, pⱼ) ≥ 0$ |
|$d(pᵢ, pⱼ) = d(pⱼ, pᵢ)$ |
Definiamo lo **spacing** come la minima distanza tra una coppia di punti in differenti cluster.
###### Obbiettivo :
Dato un intero **k** vogliamo trovare un **k-clustering**, cioè **k** partizioni disgiunte di $U$, di *spacing massimale*.
"""
# ╔═╡ 4b5b0a42-5105-45b7-ac4b-22aa04b04056
md"""
Esempio di spacing tra due cluster
| |
|:-:|
|$(load(spacing_image)[1:219, 215:450])|
"""
# ╔═╡ ba7b2fcc-127f-4853-ac3a-b5fd983270ce
md"""
### Modello del problema
---
**Idea principale**: usare un grafo $G(V,E, d)$, dove $d:E→\mathbb{R}^+$, per rappresentare l'istanza, dove :
- $V = U = \{p₁,..., pₙ\}$
- $E = \{(u,v) : u, v ∈ V ⩓ u ≠ v \}$
- $peso(pᵢ,pⱼ) = d(pᵢ,pⱼ) , ∀ (pᵢ,pⱼ) ∈ E$
La soluzione cercata sarà una *k-partizione* di $V$.
"""
# ╔═╡ 0523463f-d64d-4a2d-a4ea-54558b8c42f3
md"""
### Relazione con Kruskal
---
Algoritmo per il *k-clustering*, dato $U$ l'insieme degli oggetti e $k$ un intero:
1. Forma il grafo descritto nel modello del problema, e ad ogni nodo corrisponde un cluster differente
2. Trova la coppia più vicina di oggetti $(p,p')$ tale che $p$ e $p'$ non sono nello stesso cluster.
3. Unisce $p$ e $p'$ nello stesso cluster
4. Riparte dal punto *2*
Questo viene rifatto per $|V|-k$ volte
Perchè |V|-k volte?
Si parte da |V| componenti, e si vuole arrivare ad avere k componenti. Ogni
iterazione unisce 2 componenti e quindi diminuisce il numero di componenti di 1.
Quindi abbiamo |V| - num_iterazioni = k → num_iterazioni = |V| - k.
Ci avete fatto caso? Quest'algoritmo è uguale all'algoritmo di **Kruskal** solo che si ferma quando si hanno k componenti !
!!! note
Un altro metodo per calcolare un k-clustering è quello di ricavare l'MST, dal grafo costruito nel punto 1, e cancellare i k-1 archi di peso maggiore.
"""
# ╔═╡ 08093235-d9da-4faa-b2ca-27284a25f1fb
md"""
### k-Clustering punti in un piano
Adesso vediamo una semplice applicazione di *k-Clustering* per un insieme di punti posti su un piano. Dove la funzione distanza associa ad ogni coppia di punti, la loro distanza Euclidea.
"""
# ╔═╡ 70d2917e-a0be-4bd9-b3cf-0515a06ec550
md"""
---
Se si vuole modificare il grafo :
| | |
|:-:|:-:|
|numero nodi |$@bind num_nodi Slider(2:20, show_value=true, default=10)|
"""
# ╔═╡ 20a8694c-718c-4eda-976d-21e4a84e52dc
begin
g_c = SimpleWeightedGraph(num_nodi)
locs_x_c, locs_y_c = random_layout(g_c)
nothing
end
# ╔═╡ 1bb898b1-eab8-4e5f-bec9-f235a216a0f9
md"""
posizione nodo $(@bind nodo_sel Select(1:num_nodi))
"""
# ╔═╡ 55b55dd5-8b48-4c0d-94bd-5ad97eeeb723
md"""
x : $@bind pos_x Slider(0:0.01:1, show_value=true, default=locs_x_c[nodo_sel])
"""
# ╔═╡ 149fc894-9b8b-4bb0-b6ef-f9664bb2ced9
md"""
y : $@bind pos_y Slider(0:0.01:1, show_value=true, default=locs_y_c[nodo_sel])
"""
# ╔═╡ cf19ce0b-fcf9-4def-893f-02ff6c8b203e
begin
locs_x_c[nodo_sel] = pos_x
locs_y_c[nodo_sel] = pos_y
# impostiamo le distanze
for u ∈ 1:nv(g_c)
for v ∈ 1:nv(g_c)
if u != v
distanza = sqrt((locs_x_c[u]-locs_x_c[v])^2+(locs_y_c[u]-locs_y_c[v])^2)
add_edge!(g_c, u, v, trunc(distanza*10,digits=2))
end
end
end
#pesi_c = weight.(collect(edges(g_c)))
# precalcolo iterazioni il numero di componenti cambia
cluster_s = Kruskal(g_c)
iter_comp = [1]
local n_comp = nv(g_c)
for iter in 1:ne(g_c)
temp = size(cluster_s.comp_connesse[iter][])[1]
if n_comp > temp
push!(iter_comp, iter)
n_comp = temp
end
end
@bind k Slider(iter_comp)
end
# ╔═╡ dc56ea28-9d6f-4e50-9f4d-484448a6b2a1
md"""
# Funzioni ausiliarie
"""
# ╔═╡ 9df7b24f-abc2-4e34-891c-6fcedd00db8e
function createWeightedGraph()
g = smallgraph(tipo_grafo)
locs_x, locs_y = spring_layout(g)
#assegna pesi
wg = SimpleWeightedGraph(g)
for e ∈ edges(g)
if !pesi_a_uno
wg.weights[e.src, e.dst] = wg.weights[e.dst, e.src] = round(rand(1:1:10))
else
wg.weights[e.src, e.dst] = wg.weights[e.dst, e.src] = 1
end
end
# array pesi
weights = weight.(collect(edges(wg)))
return wg, weights, locs_x, locs_y
end
# ╔═╡ a588cd4f-be81-4744-946d-2a6c2c631fbc
function showGraph(G::SimpleWeightedGraph, weights::Vector, locs_x=nothing, locs_y=nothing)
if locs_x == nothing && locs_y == nothing
locs_x, locs_y = spring_layout(G)
end
gplot(G, locs_x, locs_y, nodelabel=1:nv(G), edgelabel=weights, nodefillc="white", nodestrokec="grey", nodestrokelw=true, EDGELABELSIZE=6, NODELABELSIZE=5)
end
# ╔═╡ 965facde-6da3-41ff-a5b8-2a85a5d9bca2
begin
wg, pesi, locs_x, locs_y = createWeightedGraph()
showGraph(wg,Int.(pesi),locs_x,locs_y)
end
# ╔═╡ c8502f00-5f25-40c1-b5b4-8ae7e55ccaea
"""
plot_Prim
Funzione che mostra lo stato della `visita di Prim` al tempo `t`.
"""
function plot_Prim(
simulazione::PrimStruct,
t::Int,
locs_x=locs_x,
locs_y=locs_y,
nodelabel=nothing,
edgelabel= nothing,
)
grafo = simulazione.G
# colora i nodi visitati
nodi_visitati = simulazione.nodi_visitati[begin:t]
nodi_visti = simulazione.nodi_visti[t]
colorenodo = [colorant"palegreen3", colorant"grey98", colorant"lightskyblue1"]
colori_nodi = fill(2, nv(grafo))
colori_nodi[nodi_visitati] .= 1
colori_nodi[nodi_visti] .= 3
nodefillc = colorenodo[colori_nodi]
if !isempty(nodi_visitati)
nodefillc[nodi_visitati[t]] = colorant"plum3" # colora il nodo corrente
end
#colora gli archi scelti
archi_scelti = simulazione.archi_scelti[begin:t-1]
colorearco = [colorant"lightgrey", colorant"brown3"]
colori_archi = fill(1, ne(grafo))
for edge in archi_scelti
index = 1
for e in edges(grafo)
if Edge(edge.src, edge.dst) == e ||
Edge(edge.dst, edge.src) == e
colori_archi[index] = 2
end
index = index + 1
end
end
edgestrokec = colorearco[colori_archi]
gplot(
grafo, locs_x, locs_y,
nodefillc=nodefillc,
nodestrokec="black",
nodelabel=1:nv(grafo),
nodestrokelw=true,
edgelabel=Int.(pesi),
edgestrokec = edgestrokec,
EDGELABELSIZE = 6,
NODELABELSIZE=5
)
end
# ╔═╡ 28115885-5bdc-4d9c-9b79-01ee4e0cd6e6
begin
md"""
Simulazione Prim : $t$ $(@bind t₁ Slider(1:nv(wg), show_value=true))
"""
end
# ╔═╡ 594d8851-ccea-496f-b431-3200f092a172
plot_Prim(Prim(wg), t₁)
# ╔═╡ c0992ba6-cff2-4900-ade1-c12a5ec236e8
"""
plot_Kruskal
Funzione che mostra lo stato dell'esecuzione di Kruskal al tempo `t`.
"""
function plot_Kruskal(
simulazione::KruskalStruct,
t::Int,
locs_x=locs_x,
locs_y=locs_y,
edgestrokec=nothing, # imposta white solo per il clustering
edgelabelc=colorant"black" # solo per il clustering
)
grafo = simulazione.G
# colora le componenti
nodefillc = distinguishable_colors(nv(grafo), [RGB(0.9,0.9,0.9), RGB(0.1,0.1,0.1)], dropseed=true)
componenti = simulazione.comp_connesse[t]
for sets in componenti
for set in sets
color_set = 0
for nodo in set
if color_set == 0
color_set = nodefillc[nodo]
end
nodefillc[nodo] = color_set
end
end
end
if edgestrokec == nothing
#colora gli archi scelti nelle componenti
archi_scelti = simulazione.archi_scelti[begin:t]
edgestrokec = fill(colorant"lightgrey", ne(grafo))
for edge in archi_scelti[2:end]
index = 1
for e in edges(grafo)
if Edge(edge.src, edge.dst) == e ||
Edge(edge.dst, edge.src) == e
edgestrokec[index] = nodefillc[edge.src]
end
index = index + 1
end
end
end
#segna l'arco corrente
curr_edge_t = copy(t) #indice per l'arco corrente
# bound superiore
if curr_edge_t > ne(grafo)
curr_edge_t = curr_edge_t - (curr_edge_t - ne(grafo))
end
current_edge = simulazione.archi_ord[curr_edge_t]
edgelinewidth = fill(1.0, ne(grafo))
index = 1
for e in edges(grafo)
if Edge(current_edge.src, current_edge.dst) == e ||
Edge(current_edge.dst, current_edge.src) == e
edgelinewidth[index] = 3.0
end
index = index + 1
end
pesi = weight.(collect(edges(grafo)))
gplot(
grafo, locs_x, locs_y,
nodefillc=nodefillc,
nodestrokec="black",
nodelabel=1:nv(grafo),
nodestrokelw=true,
edgelabel= pesi,
edgestrokec = edgestrokec,
edgelinewidth = edgelinewidth,
EDGELABELSIZE = 6,
NODELABELSIZE=5,
edgelabelc=edgelabelc
)
end
# ╔═╡ ae0aecdf-f8fd-4276-9f04-14c2814e4dfc
begin
kappa = size(cluster_s.comp_connesse[k][])[1]
plot_Kruskal(cluster_s, k, locs_x_c, locs_y_c, colorant"white", colorant"white")
end
# ╔═╡ 2e5789b6-03e6-477b-95ef-729bc19aeff8
md"""
Puoi modificare il numero di cluster :
#### k : $kappa
"""
# ╔═╡ d9a20ee6-09fa-481a-a394-5d64dc5c2e0b
md"""
In questo modo abbiamo raggruppato i punti in $kappa cluster (Ogni cluster ha un suo colore associato).
"""
# ╔═╡ 0fea9759-343a-4d01-a6c0-b7f27417bd4d
begin
md"""
Simulazione Kruskal : $t$ $(@bind t₂ Slider(1:ne(wg)+1, show_value=true))
"""
end
# ╔═╡ b8e91235-fca7-44d6-8cbf-6c753b3e7a60
plot_Kruskal(Kruskal(wg), t₂)
# ╔═╡ d39858aa-932e-47fd-aa7e-443798336f77
begin
prim_struct = Prim(wg)
pesiT_prim = Int.(weight.(collect(edges(prim_struct.T))))
if !locs
showGraph(prim_struct.T, pesiT_prim)
else
showGraph(prim_struct.T, pesiT_prim, locs_x, locs_y)
end
end
# ╔═╡ a7488240-4fa2-44e9-879e-af5497c1d1f4
md"""
###### Costo soluzione : $(sum(pesiT_prim))
"""
# ╔═╡ b945af30-8729-4c8f-9a74-c9c0e07c4a31
begin
kruskalStruct = Kruskal(wg)
pesiT_kruskal = Int.(weight.(collect(edges(kruskalStruct.T))))
["Kruskal" => showGraph(kruskalStruct.T, pesiT_kruskal, locs_x, locs_y),"Prim"=> showGraph(prim_struct.T, pesiT_prim, locs_x, locs_y)]
end
# ╔═╡ 05c76e3f-b7e1-4ed3-a1cb-766cc56bea35
md"""
###### Costo soluzione Kruskal : $(sum(pesiT_kruskal))
"""
# ╔═╡ 2e17fe64-4dbc-4668-b3cd-3bae27e8b7c7
begin
uguali = "non uguali"
if (kruskalStruct.T == prim_struct.T)
uguali = "uguali"
end
nothing
end
# ╔═╡ 0f099b10-7bb0-441b-ace7-8f496097ee04
md"""
In questo caso gli MST calcolati risulano **$uguali**
"""
# ╔═╡ bf7f25bc-126c-4d9d-9c2c-d4eb4c471851
begin
costo_sort="O(|E|log|V|)"
costo_makeSet ="O(1)"
costo_union = "O(|V|log|V|)"
costo_find = "O(1)"
nothing
end
# ╔═╡ ff5609e6-7a6e-4755-9d5c-545ca39c5177
md"""
##### Complessità
---
Per quanto riguarda la complessità, utilizzando la struttura dati **UnionFind**, implementata con gli alberi **QuickFind** con *union by size*, per gestire le componenti connesse, abbiamo che l'algoritmo esegue queste funzioni con il rispettivo costo e numero di esecuzioni:
| funzione | costo | numero esecuzioni |
|:-----------:|:------------:|:-----------------:|
| *Sort* | $costo_sort | $1$ |
| *makeSet* |$costo_makeSet| $\|V\|$ |
| *union* | $costo_union | $*$ |
| *find* | $costo_find | $2\|E\|$ |
Il costo della union (by size) è dato da un'analisi ammortizzata (più precisa) sull'intera sequenza di |V|-1 union e quindi non di una singola operazione.
Il costo totale trascurando l'ordinamento è :
$O(|V|+ 2|E| + |V|\log{|V|}) = O(|E|+|V|\log{|V|})$
Questo costo è asintoticamente più piccolo del costo di ordinamento, quindi il costo finale dell'algoritmo di Kruskal è :
$O(|E|\log{|V|})$
"""
# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
Compose = "a81c6b42-2e10-5240-aca2-a61377ecd94b"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
GraphPlot = "a2cc645c-3eea-5389-862e-a155d0052231"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
ImageIO = "82e4d734-157c-48bb-816b-45c225c6df19"
ImageShow = "4e3cecfd-b093-5904-9786-8bbb286a6a31"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
SimpleWeightedGraphs = "47aef6b3-ad0c-573a-a1e2-d07658019622"
TypedTables = "9d95f2ec-7b3d-5a63-8d20-e2491e220bb9"
[compat]
Colors = "~0.12.8"
Compose = "~0.9.4"
DataFrames = "~1.3.4"
DataStructures = "~0.18.13"
FileIO = "~1.15.0"
GraphPlot = "~0.5.2"
Graphs = "~1.7.1"
ImageIO = "~0.6.6"
ImageShow = "~0.3.6"
Plots = "~1.31.5"
PlutoUI = "~0.7.39"
SimpleWeightedGraphs = "~1.2.1"
TypedTables = "~1.4.0"
"""
# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised
julia_version = "1.7.2"
manifest_format = "2.0"
[[deps.AbstractFFTs]]
deps = ["ChainRulesCore", "LinearAlgebra"]
git-tree-sha1 = "69f7020bd72f069c219b5e8c236c1fa90d2cb409"
uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
version = "1.2.1"
[[deps.AbstractPlutoDingetjes]]
deps = ["Pkg"]
git-tree-sha1 = "8eaf9f1b4921132a4cff3f36a1d9ba923b14a481"
uuid = "6e696c72-6542-2067-7265-42206c756150"
version = "1.1.4"
[[deps.Adapt]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "195c5505521008abea5aee4f96930717958eac6f"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.4.0"
[[deps.ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
[[deps.ArnoldiMethod]]
deps = ["LinearAlgebra", "Random", "StaticArrays"]
git-tree-sha1 = "62e51b39331de8911e4a7ff6f5aaf38a5f4cc0ae"
uuid = "ec485272-7323-5ecc-a04f-4719b315124d"
version = "0.2.0"
[[deps.Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[deps.Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.8+0"
[[deps.CEnum]]
git-tree-sha1 = "eb4cb44a499229b3b8426dcfb5dd85333951ff90"
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
version = "0.4.2"
[[deps.Cairo_jll]]
deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.1+1"
[[deps.ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "80ca332f6dcb2508adba68f22f551adb2d00a624"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.15.3"
[[deps.ChangesOfVariables]]
deps = ["ChainRulesCore", "LinearAlgebra", "Test"]
git-tree-sha1 = "38f7a08f19d8810338d4f5085211c7dfa5d5bdd8"
uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0"
version = "0.1.4"
[[deps.CodecZlib]]
deps = ["TranscodingStreams", "Zlib_jll"]
git-tree-sha1 = "ded953804d019afa9a3f98981d99b33e3db7b6da"
uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
version = "0.7.0"
[[deps.ColorSchemes]]
deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Random"]
git-tree-sha1 = "1fd869cc3875b57347f7027521f561cf46d1fcd8"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.19.0"
[[deps.ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.4"