momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Asupra unui graf 3-colorabil, planar, hamiltonian (V)

graf | limbajul R | orar şcolar
2023 jan

Anterior, pentru graful poliedral G (v. [2]) am obținut o reprezentare de tip 2-book, constând dintr-un „cotor” pe care am înscris ciclul hamiltonian și două pagini care conțin, fără intersecții interioare, celelalte muchii; am găsit și o reprezentare cam tot de tip "n-book", dar mai convenabilă: am scurtat cotorul, descompunându-l în două „subcotoare” suprapuse care conțin fiecare câte o parte a ciclului hamiltonian CH.

Dar pentru scheletele poliedrale se cuvine să dăm reprezentări plane convexe, evidențiind cum putem, fiecare față. Vom modela – în două moduri – o idee clasică (W. T. Tutte, "How to draw a graph" 1963): fixăm nodurile ciclului CH[1:5] în vârfurile unui pentagon convex și plasăm fiecare dintre vârfurile rămase, în baricentrul adiacenților săi.

Reprezentarea convexă clasică

Reconstituim din [2], matricea de adiacență Adj și ciclul hamiltonian CH (în care primele 5 vârfuri formează un ciclu de lungime 5, pe care îl vom alege ca „față” de bază); reindexăm (și denumim) liniile și coloanele din Adj, după valorile din CH:

library(tidyverse)
Adj <- read.table("../graph_1257.mat", sep=" ") %>% 
       as.matrix()  # matricea de adiacență a grafului hexecontaedral G (v. [2]-I)
CH <- c(92, 70, 13, 54, 71, 14, 55, 65, 18, 39, 79, 17, 40, 47, 5, 26, 46, 4, 25, 41, 
         3, 23, 28, 1, 21, 81, 24, 37, 84, 38, 53, 87, 52, 59, 88, 60, 12, 63, 58, 19, 
         35, 57, 8, 31, 56, 7, 30, 51, 6, 29, 36, 2, 22, 82, 27, 33, 83, 32, 43, 20,  
         34, 80, 89, 61, 45, 11, 62, 78, 91, 76, 44, 85, 42, 49, 10, 69, 50, 86, 48, 66,  
         9, 75, 68, 90, 67, 73, 16, 77, 74, 15, 64, 72)  # ciclu hamiltonian al grafului G
A <- Adj[CH, CH]  # liniile și coloanele vor fi accesate în ordinea dată de CH
dimnames(A) <- list(as.character(CH), as.character(CH))

Vom asocia vârfurilor, în ordinea lor din vectorul CH, punctele de afixe $z_i\in\mathbb{C}, i=1..92$. Vom fixa nodurile ciclului CH[1:5] în vârfurile unui pentagon convex; pentru celelalte noduri, exprimând faptul că fiecare este baricentrul adiacenților săi, trebuie să avem:

$$z_i=d_i^{-1}\sum_{j\,\sim\, i}z_j \qquad (i=6..92)$$

unde $\sim$ desemnează adiacența, iar $d_i$ este numărul de vârfuri $j$ pentru care $j\,\sim\, i$. Desigur $j\,\sim\,i$ echivalează cu A[i,j]==1, iar suma din membrul drept se descompune în două, ținând seama că $z_{1..5}$ sunt date (fixate); deci putem rescrie formula de mai sus astfel:

$$d_iz_i-\sum_{\substack{j\,\sim\,i\\j\gt 5}}z_j = \sum_{\substack{j\,\sim\,i\\j\le 5}}z_j \qquad (i=6..92)$$

Să observăm că putem condensa matriceal, acest set de 87 de ecuații – plecând de la matricea de adiacență; al doilea membru este vectorul (constant) K rezultat prin însumarea pe fiecare linie 6..92 a valorilor din primele 5 coloane ale matricei A, multiplicate respectiv cu afixele fixate $z_1..z_5$ – adică avem K = $\left(A[1:5,6:92]\right)^t\times \left(z_1,z_2,z_3,z_4,z_5\right)$.

După ce calculăm vectorul K, eliminăm din A primele 5 linii și coloane (blocul corespunzător valorilor CH[1:5]), schimbăm semnul valorilor rămase și plasăm pe diagonala principală valorile "$d_i$" (gradul vârfului CH[i] căruia în corespunde linia curentă). Notând cu L matricea rezultată astfel, vedem că produsul scalar dintre o linie a lui L și vectorul coloană al necunoscutelor $z=(z_6, z_7, ..., z_{92})^t$ reprezintă primul membru din câte una dintre cele 87 de ecuații de mai sus. De exemplu – pentru nodul CH[6] (cu numele "14") avem:

> A[6, 6:92]
14 55 65 18 39 79 17 40 47  ...  90 67 73 16 77 74 15 64 72 
 0  1  0  0  0  0  0  0  0  ...   0  1  0  0  0  0  0  0  0 

ceea ce (schimbând semnul și consemnând gradul vârfului "6") devine în matricea L:

> L[1, ]
14 55 65 18 39 79 17 40 47  ...  90 67 73 16 77 74 15 64 72 
 3 -1  0  0  0  0  0  0  0  ...   0 -1  0  0  0  0  0  0  0 

iar "$3z_6-z_{55}-z_{67}$" este primul membru al ecuației corespunzătoare pentru $i=6$ în sistemul de 87 de ecuații formulat mai sus.

Prin urmare, totul revine la rezolvarea ecuației matriceale Lz=K; Tutte a stabilit că determinant(L) dă numărul de arbori parțiali ai grafului, deci este nenul; rezultă z = L-1K. Folosim solve() pentru a inversa matricea și "%*%" sau crossprod() pentru a înmulți matricele; funcția draw_segment() a fost redată deja în [2].
Modelăm cele descrise mai sus astfel:

P5 <- c(-100+100i, 100+100i, 100-90i, -50-100i, -100-90i)  # cadru pentagonal convex
L <- -A
for(i in 1:nrow(L))
    L[i, i] <- sum(L[i, ] != 0)  # consemnează gradele vârfurilor
L <- L[-(1:5), -(1:5)]  # exclude primele 5 linii și coloane
K <- t(A[1:5, 6:92]) %*% P5
Z <- crossprod(solve(L), as.matrix(K))  # = L^(-1) × K
aZ <- c(P5, Z) %>% setNames(as.character(CH))  # reprezentarea baricentrică
old_par <- par(mar=c(0,0,0,0), family="mono", font=2, 
               bty="n", xaxt="n", yaxt="n")
plot(0, type = "n", xlim=c(-100, 100), ylim=c(-100, 100), asp=1) 
points(aZ, cex=0.4, pch=19, 
       col = ifelse(names(aZ) %in% as.character(81:92), "red", "blue"))
for(i in 1:91)
    for(j in (i+1):92) 
        if(A[i, j] > 0) 
            draw_segment(aZ[i], aZ[j], lwd = 0.6, col = "gray50")
text(x = Re(aZ[1:5]), y = Im(aZ[1:5]), label = names(aZ[1:5]), cex = 0.8)
par(old_par)  # reconstituie parametrii grafici iniţiali

Vârfurile rezultate sunt prea apropiate între ele în interiorul conturului de bază (și ar fi fost și mai înghesuite, dacă plecam de la un pentagon regulat), încât am evitat să le mai etichetăm; ne-am mulțumit doar să distingem prin culoare cele 12 vârfuri de grad 5 (fiecare fiind nodul comun a câte 5 „fețe” pentagonale convexe).

O reprezentarea convexă îmbunătățită…

Reprezentarea convexă redată mai sus se regăsește și în alte locuri (diferența principală fiind aceea că aici am explicitat, printr-un program concis, producerea ei); dar imaginea rezultată nu poate fi decât una „înghesuită”, câtă vreme ținem seama când calculăm baricentrele, doar de gradele vârfurilor (nu și de proprietăți structurale „mai fine”, ale grafului).

Pentru a „lărgi” cumva, pentagoanele interioare celui de bază – să ținem seama și de distanțele din graful G; amintim că distanța dintre două vârfuri este lungimea celui mai scurt drum existent în G, între cele două noduri.
Implicăm pachetul igraph, pentru a obține matricea distanțelor – pe care o reindexăm după ciclul hamiltonian CH – și pe baza acesteia, constituim matricea Dmin conținând distanțele (minime) față de ciclul de bază CH[1:5], ale celorlalte noduri ale grafului:

G <- igraph::graph_from_adjacency_matrix(Adj, mode="undirected")
D <- igraph::distances(G)  # matricea distanțelor (între oricare două noduri)
D <- D[CH, CH]  # liniile și coloanele vor fi accesate în ordinea dată de CH
Dmin <- sapply(1:nrow(D), function(v) min(D[v, 1:5])) %>% 
        setNames(as.character(CH))  # distanțele minime la fața CH[1:5]

În programul precedent, foloseam A cu valorile standard (1 și 0) și în L puneam pe diagonala principală gradele vârfurilor; acum ponderăm valorile, înlocuind "1" cu inversul celei mai mari dintre distanțele celor două noduri adiacente, la fața de bază CH[1:5] și însumăm (pe linii) aceste valori, constituind diagonala principală a lui L:

Q <- A
for(i in 1:91) 
    for(j in (i+1):92)
        if(Q[i, j] > 0 & !(i <= 5 & j <= 5))
            Q[j, i] <- Q[i, j] <-
                1 / max(Dmin[as.character(CH[i])], Dmin[as.character(CH[j])])
L <- -Q
for(i in 6:92)
    L[i, i] <- -sum(L[i, ])
L <- L[-(1:5), -(1:5)]

În rest, programul este identic celui anterior:

K <- t(A[1:5, 6:92]) %*% P5
Z <- crossprod(solve(L), as.matrix(K))
aZ <- c(P5, Z) %>% setNames(as.character(CH))
# etc.

doar că acum, având pentagoane mai mari, putem înscrie și etichetele vârfurilor:

Am marcat (prin "darkorange") și ciclul hamiltonian CH. Desigur, ar fi de căutat un sens pentru evidențierile colorate făcute…
Ar putea fi interesant de comparat cele 60 de pentagoane în privința contactelor cu CH: dintre cele 5 pentagoane cu vârful comun "92", două au câte 4 laturi "darkorange", două au câte 3 și unul (92 73 67 14 71) are numai două laturi în CH; în schimb, pentagoanele cu vârful comun "87" au toate, câte trei laturi "darkorange" (adică aflate în CH)…
Deasemenea, ar fi de urmărit ciclul "darkorange" pe cele două bucăți care au constituit „subcotoarele” reprezentării de tip "n-book" din [2].

vezi Cărţile mele (de programare)

docerpro | Prev | Next