Z grafologi膮 wpis dzisiejszy nie ma nic wsp贸lnego, ani z algami, ani z rytmami te偶 nie. Jak ju偶 wiele razy wspomina艂em, wymy艣lanie tytu艂贸w do wpis贸w nie jest moj膮 najmocniejsz膮 stron膮.
Uwa偶ny Czytelnik mo偶e skojarzy膰 zagadk臋 o drabince liczb pierwszych (z maja zesz艂ego roku), do kt贸rej rozwi膮zania u偶y艂em pythonowej biblioteki networkx
. Opr贸cz standardowych operacji na grafach, biblioteka ta oferuje te偶 kilka algorytm贸w rysowania graf贸w. Dzi艣 chcia艂em pokaza膰 czym si臋 owe algorytmy r贸偶ni膮 oraz w jaki spos贸b wy艣wietli膰 wiele wykres贸w na jednym (co艣, z czym zawsze mam problem i musz臋 za ka偶dym razem gugla膰).
Zaczniemy od kodu:
from sympy import isprime import networkx as nx import matplotlib.pyplot as plt # oryginalny kod z rozwi膮zania zagadki z zesz艂ego roku # wersja dwucyfrowa digits = 2 primes = [n for n in range(10**(digits-1), 10**digits) if isprime(n)] G = nx.Graph() for p in primes: G.add_node(p) for nn in G.nodes: a_number = str(nn) for digit_pos in range(len(a_number)): for digit in range(10): candidate = a_number[:digit_pos] + str(digit) + a_number[digit_pos + 1:] if candidate != a_number and candidate[0] != '0' and isprime(int(candidate)): G.add_edge(nn, int(candidate)) # lista algorytm贸w layouts = [ (nx.kamada_kawai_layout, {'scale': 2}), # parametr scale=2 zmniejsza szans臋 na pokrywanie si臋 k贸艂ek (nx.spring_layout, {'scale': 2}), (nx.random_layout, {}), (nx.shell_layout, {}), (nx.circular_layout, {}), (nx.spectral_layout, {}), (nx.spiral_layout, {}) ] # lista podwykres贸w fig, axes = plt.subplots(3, 3) # zamieniamy dwywumiarow膮 list臋 podwykres贸w na jednowymiarow膮, # 偶eby by艂o 艂atwiej odwo艂ywa膰 si臋 do poszczeg贸lnych podwykres贸w axes = axes.flatten() for i, (layout, params) in enumerate(layouts): pos = layout(G, **params) nx.draw(G, pos, ax=axes[i], with_labels=True, node_shape='o', font_size=5, edge_color='lightgray', node_color='lightgray', width=0.5, node_size=80, linewidths=1) axes[i].set_title(layout.__name__.replace("_layout", "")) # usuwamy dwa ostatnie (puste) podwykresy axes[-1].remove() axes[-2].remove() plt.show()
Kod zawiera komentarze, kt贸re (mam nadziej臋) obja艣niaj膮 co si臋 dzieje w poszczeg贸lnych krokach. Zamiast wi臋c omawia膰 teraz skrypt krok po kroku przejd臋 od razu do wyniku, kt贸ry wygl膮da o, tak:
Gwoli wyja艣nienia: siedem graf贸w pokazanych na rysunku powy偶ej to jest - formalnie - jeden i ten sam graf, tylko narysowany na siedem r贸偶nych sposob贸w. Algorytm贸w rysowania grafu jest w networkx
wi臋cej (oko艂o 10 albo 11 o ile dobrze kojarz臋), jednak ten konkretny graf nie nadaje si臋 do pokazania wszystkich, dlatego ograniczy艂em si臋 do tych siedmiu.
Co mo偶na zaobserwowa膰 na podstawie powy偶szego obrazka?
Po pierwsze, wydaje si臋, 偶e algorytmy shell
i circular
produkuj膮 prawie identyczny uk艂ad (w zasadzie jakby obr贸ci膰 jeden z nich, da艂oby si臋 na艂o偶y膰 na drugi prawie co do piksela). W rzeczywisto艣ci algorytm circular
jest du偶o bardziej prymitywny - zawsze uk艂ada wierzcho艂ki grafu w pojedyncze ko艂o - natomiast shell
umo偶liwia zorganizowanie grafu w pier艣cienie, kt贸rych tu jednak nie mamy, wi臋c domy艣lnie zrobi艂 jedno ko艂o.
Po drugie, wida膰 wyra藕ne podobie艅stwo mi臋dzy algorytmami spring
oraz kamada_kawai
- i nie bez powodu, poniewa偶 obydwa te algorytmy opieraj膮 si臋 na minimalizowaniu "energii" w grafie, a wi臋c staraj膮 si臋, aby by艂o jak najmniej przecinaj膮cych si臋 kraw臋dzi, aby d艂ugo艣ci kraw臋dzi by艂y w miar臋 mo偶liwo艣ci r贸wne i tak dalej. Wewn臋trznie owa "energia" jest w ka偶dym z nich reprezentowana nieco inaczej: spring
operuje lokalnie, a wi臋c traktuje ka偶d膮 kraw臋d藕 jak "spr臋偶yn臋" d膮偶膮c膮 do pewnej r贸wnowagi, ponadto dodaje si艂臋 "odpychaj膮c膮" mi臋dzy wierzcho艂kami. kamada_kawai
natomiast jest bardziej zaawansowany i stara si臋 zminimalizowa膰 d艂ugo艣膰 kraw臋dzi nie tylko mi臋dzy najbli偶szymi wierzcho艂kami, ale r贸wnie偶 mi臋dzy tymi po艂膮czonymi przez po艣rednik贸w. Dlatego uk艂ady si臋 jednak nieco r贸偶ni膮. Wida膰 jednak, 偶e s膮siedztwa wierzcho艂k贸w s膮 w zasadzie identyczne w obu przypadkach. Na przyk艂ad trapez z艂o偶ony z wierzcho艂k贸w 67-37-97-47-17 pojawia si臋 i tu, i tam, w bardzo podobnym uk艂adzie.
Po trzecie, warto nadmieni膰, 偶e w wi臋kszo艣ci przypadk贸w da si臋 te algorytmy nieco "podrasowa膰" w celu unikni臋cia "zderze艅" wierzcho艂k贸w (parametr scale
- szczeg贸艂y patrz komentarze w skrypcie), jednak nie ze wszystkim. Spiral
prawie na pewno wyprodukuje nak艂adaj膮ce si臋 wierzcho艂ki w 艣rodku spirali, random
jest losowy, wi臋c nie mamy tu za bardzo kontroli nad uk艂adem wierzcho艂k贸w i dla wi臋kszych graf贸w na艂o偶enia s膮 w zasadzie gwarantowane, wreszcie spectral
produkuje klastry, w kt贸rych wierzcho艂ki s膮 u艂o偶one na tyle g臋sto, 偶e unikn膮膰 na艂o偶e艅 te偶 si臋 za bardzo nie da.
Na zako艅czenie bonusik, czyli te same algorytmy dla liczb trzycyfrowych:
... i dla czterocyfrowych:
Je偶eli chcesz do komentarza wstawi膰 kod, u偶yj sk艂adni:
[code]
tutaj wstaw sw贸j kod
[/code]
Je偶eli zrobisz liter贸wk臋 lub zmienisz zdanie, mo偶esz edytowa膰 komentarz po jego zatwierdzeniu.