Daha çox

Növbəti obyektlərin təkrarlanmaması üçün obyektin rəngini hesablayın


16 rəngdən birini obyektlərin xəritələşdirilməsinə (məsələn, ölkələr, əyalətlər, poçt kodları və s.) Təyin etməyə çalışıram ki, eyni rəngdə bitişik iki obyekt yoxdur.

İnternetdə "kənar rəngləmə" və "dörd rəng teoremi" ilə əlaqəli tonlarla məqalə görürəm, amma bu teoremləri PostgreSQL / PostGIS-də (və ya bunun üçün hər hansı bir praktik tətbiqdə) bir alqoritm kimi tətbiq etməyin bir yolu tapmıram. .

Bunun həll olunmuş bir problem olduğuna 100% əminəm, amma Google cavab açıqlamadığına görə, bu suala düzgün sual vermək üçün çox cahil olduğumu düşünürəm.

Kimsə məni düzgün istiqamətə yönəldə bilərmi?


Girişdə təsvir olunan 6 rəngli alqoritmdən istifadə edərək uğurlar qazandım Planar Qrafın Beş Boyanması üçün İki Xətti Zaman Alqoritmi. Daha az rəngdən istifadə edərək qrafiki rəngləndirmək qətiliklə mümkün olsa da, 5 və ya 6 istifadə etməkdən daha yaxşı görünə bilməz.

ALGORİTM 6-RƏNG.

Verilmişdir n-vertex planar qrafik G bitişik siyahı şəklində bu alqoritm 6 rənglənməni təyin edir G.

Addım 1. dərəcə siyahıları yaradın.

Hər biri üçün j harada 0 , bütün təpələrinin ikiqat əlaqəli bir siyahısını təşkil edin G dərəcə j.

Addım 2. Ən kiçik dərəcə olan zirvələri etiketləyin.

Üçün i = n, n-1, n-2,…, 1, boş olmayan ilk təpəni təyin edin j-kiçik dərəcə siyahısı j təpə kimi vmən.

Sil vmən j dərəcə siyahısından.

Hər təpə üçün v ' bitişikdi vmən in G və bir dərəcə siyahısında qalır j ', sil v ' etibarən j ' dərəcə siyahısı və əlavə edin v ' içində j '- 1 dərəcə siyahısı.

Addım 3. Rəngli zirvələr.

Üçün i = 1,2,…, n, vertex təyin edin vmən bitişik təpələrdə meydana çıxmayan ən kiçik rəng dəyəri (bir ilə altı arasında bir tam rəqəm olmalıdır) vmən artıq rənglənmişdir.


Əminəm ki, bu funksiya inanılmaz dərəcədə səmərəsizdir (çox zəif kodlandığını demirəm - "birdəfəlik" tez və çirkli bir tətbiq olaraq yazılmışdı), amma nəticəsi mənim üçün olduqca yaxşı işləyən biri:

FUNKSİYANI YARATIN VƏ DƏYİŞİN public.calc_colors (tbl mətn, benzersiz_fild mətni, qonşu_stili mətni, axtarış_distance real DEFAULT 0) SETOF qeydini $ BODY $ DECLARE first_vertex mətni OLARAQ QAYDADI; r KEYD; next_color INTEGER; max_used_color INTEGER; mövcud_ rənglər INTEGER; next_vertex mətni; create_contig mətni; color_string MƏTN; BAŞLAYIN - qonşuları tapın IF qonşu_style = 'no_corners' ONDA create_contig = 'SEÇİN P1 SİZİN KOMİTSİYA DAMLA ÜZƏRİNDƏ TEMP CƏDVƏLİ YARADIN.' || quote_ident (unique_field) || ':: mətn AS c1, p1.geom kimi geom, p2.' || quote_ident (unique_field) || ':: mətn AS c2 FROM' || tbl || 'p1 CROSS QOŞUL' || tbl || 'p2 WHERE (ST_Relate (p1.geom, p2.geom, "2 ********") VƏ ST_Relate (p1.geom, p2.geom, "**** 1 ****")) VƏ p1. ' || quote_ident (unique_field) || '! = p2.' || quote_ident (unique_field); ELSIF neighbour_style = 'within' THEN create_contig = 'SEÇİLMƏSİNDƏ SƏHİF DAMLA ÜÇÜN TEMP CƏDVƏLİ YARADIN contig_temp.' || quote_ident (unique_field) || ':: mətn AS c1, p1.geom kimi geom, p2.' || quote_ident (unique_field) || ':: mətn AS c2 FROM' || tbl || 'p1 CROSS QOŞUL' || tbl || 'p2 WHERE (ST_Intersects (p1.geom, p2.geom) Və ya ST_DWithin (st_envelope (p1.geom), st_envelope (p2.geom),' || search_distance || ')) AND p1.' || quote_ident (unique_field) || '! = p2.' || quote_ident (unique_field); ELSE create_contig = 'SEÇİM SƏHİFƏSİNDƏ TƏMİN OLUNMASI ÜZRƏ TEMP CƏDVƏLİ contig_temp yaradın.' || quote_ident (unique_field) || ':: mətn AS c1, p1.geom kimi geom, p2.' || quote_ident (unique_field) || ':: mətn AS c2 FROM' || tbl || 'p1 CROSS QOŞUL' || tbl || 'p2 WHERE ST_Intersects (p1.geom, p2.geom) VƏ p1.' || quote_ident (unique_field) || '! = p2.' || quote_ident (unique_field); SONDUR; EXECUTE create_contig; İndeksi sidx_contig_temp contig_temp İSTİFADƏLƏNMƏSİNDƏ (geom) OLUN; C1 SEÇİLƏN KOMİTSİYƏ DAMLA ÜÇÜN TEMP CƏDVƏLİ YARADIN, c1, contig_temp GROUP BY c1 SİFARİŞ BY count (*) DESC, c1; Vertex_degree_temp (C1) ÜÇÜN INDEX dv_tmp_idx YARATIN; - ən yüksək qonşuların olduğu vertex olan ilk rəngli vertex, SEÇ c1 INTO first_vertex FROM vertex_degree_temp LIMIT 1; TEMP CƏDVƏLİ YARADIN vertex_colors_temp SEÇMƏK OLARAQ DAMLA ÜÇÜN c_1, 1 :: int color_idx; INDEX vc_tmp_idx vertex_degree_temp (C1) ÜZRƏ YARADIN; max_used_color = 1; LOOP - qonşularda ən çox fərqli rəngə sahib olan təpə olan növbəti vertexi tapın - bağlar üçün ən çox qonşu olan vertex seçin SEÇ d.c1 INTO next_vertex FROM (SELECT DISTINCT d.c1, d.neighbour_count, c. color_idx FROM vertex_degree_temp d, contig_temp n LEFT JOIN vertex_colors_temp c ON (c.c1 = n.c2) HARADA d.c1 YOXDUR (vertex_colors_temp-dən C1 SEÇİN) və d.c1 = n.c1) d GROUP BY d. .neighbour_count SİFARİŞ SAYI (d.color_idx) DESC, qonşu_sayt DESC LIMIT 1; Next_vertex boş olduqda ÇIXDI; - vertex üçün ən az istifadə olunan rəngi tapın DROP CƏDVƏLİ OLDUĞUNDA, istifadə_colors_temp; TEMP CƏDVƏLİNİ istifadə edin_colors_temp SEÇİM OLARAQ * SEÇİN (SEÇİN generate_series (1, max_used_color) a) seq WHERE a NOT (SELECT c.color_idx FROM contig_temp n, vertex_colors_temp c WHERE (c.c1 = n.c2) və nc1 SİFARİŞ color_idx); SEÇİN say (*) FROM istifadə_colors_temp INTO available_colors; IF available_colors = 0 THEN next_color = max_used_color + 1; max_used_color = next_color; ELSE - ən kiçik sayda, lakin cari həndəsədən ən uzaq məsafədə olan rəngi seçin SEÇ d.color_idx FROM (SELECT avail.color_idx, ST_Distance (st_envelope (c.geom), st_envelope (avail.geom)) dist FROM contig_temp c, (SELECT u .c1, u.color_idx, c.geom FROM vertex_colors_temp u LEFT JOIN contig_temp c ON u.c1 = c.c1 color_idx IN WHERE (SELECT a FROM used_colors_temp)) HARADA c.c1 = next_vertex) d GROUP BY d.or BY dəq (dist) DESC LIMIT 1 INTO next_color; SONDUR; - INSERT INTO vertex_colors_temp (c1, color_idx) VALUES (next_vertex, next_color) əlavə edin; max_used_color = BÖYÜK (max_used_color, next_color); SON DÖNGÜ; SORĞU SEÇİMİ * vertex_colors_temp'DƏN QAYDA; SON; $ BODY $ DİL plpgsql DƏYİŞLİ MALİYYƏ 100 SIRA 1000;

Mövcud masaya qoşulmaqla istifadə olunur, məsələn:

L.id, colors.color_idx, l.geom FROM public.localities l SOL JOIN public.calc_colors ('public.localities' :: text, 'id' :: text, 'within' :: text, 2000 :: real ) rənglər (id mətni, color_idx tam ədədi) ON rənglərdə.id::bigint = l.id

(Yenə də dəhşətli sintaksis üçün üzr istəyirik ... bu, atma bir tətbiq idi!)

Bu funksiya, funksiyaya ötürülən ilk arqument kimi göstərilən cədvəldəki hər bir xüsusiyyət üçün rəng nömrəsini qaytaracaqdır. İkinci arqument hər bir xüsusiyyəti unikal şəkildə müəyyənləşdirən bir sütundur (rəngləri orijinal cədvələ qoşmaq üçün istifadə olunur). Sehir 3-cü və 4-cü arqumentlərdədir - bunlar rənglərin necə təyin olunduğunu idarə edir. Üçüncü arqument "qonşu_stili" ya ola bilər:

  • boş, yəni rənglərin heç bir şəkildə təyin ediləcəyi deməkdir kəsişən xüsusiyyətləri eyni rəng nömrəsini bölüşür
  • ya da 'no_corners', yəni xeyr kəsişən və ya toxunan xüsusiyyətləri eyni rəngi paylaşacaq
  • və ya 'daxilində', bu vəziyyətdə məsafəyə dözümlülüyü təyin etmək üçün 4-cü parametr istifadə olunur. Xüsusiyyətləri yoxdur bu məsafədə bir-birinin eyni rəngi paylaşacaqdır (xüsusiyyətlər demək olar ki, bir-birinə toxunarsa və bunların eyni rəngi paylaşmasını istəmirsinizsə faydalı ola bilər).

Alqoritm, müəyyən bir rəng nömrəsi olan xüsusiyyətlərin ümumi sayının təxminən bərabər olması və rənglərin giriş cədvəlinin coğrafi hüdudlarına təxminən bərabər şəkildə yayılması üçün rənglər təyin etməyə çalışacaqdır.

Bu skriptin yaxşılaşdırılması üçün təkliflər çox təqdir ediləcəkdir!


Burada başqalarına (və ya gələcək mənlikimə) kömək edəcək ümidlə öz sualımı cavablandırmaq. Sualı göndərdiyimdən bəri keçən həftələrdə PostgreSQL-də alqoritmik şəkildə edilə bilən bu problemə zərif bir həll tapa bilmədim. Bunun əvəzinə tapşırığı tərkib hissələrə ayırdım və az-çox kobudcasına məcbur etdim.

Bu keçid məqsədləri üçün nümunə olaraq poçt kodlarını istifadə edəcəyəm, lakin bu yanaşma ilçeler, şəhərlər, əyalətlər və ölkələr üçün də yaxşı işlədi. Yenidən istifadə edilə bilməyəcək çox sistemə aid bir kod göndərməkdənsə, yalnız yanaşmaımı izah edəcəyəm.

1) Hər qonşu poçtun şəxsiyyət vəsiqələrini ehtiva edən müvəqqəti əlaqəli cədvəl yaratdım. (məs. 12345, 12346, 12347 ve 12348-ə bitişikdir). Bitişikliyi təyin etmək üçün ST_Intersects () istifadə etdim. (Şəhərlər haqqında aşağıdakı qeydlərə baxın). İşləri sürətləndirmək üçün, müəyyən bir məsafədə bitişik poçtların axtarışını məhdudlaşdırmaq üçün sorğunu əvvəlcədən süzə bilərdim, amma bu birdəfəlik qaçış üçün hər şeyi tolerant qəhvə fasiləsində işləyə biləcəyimi gördüm.

2) Daha sonra hər ZIP işləyən bir) hovuzdan təsadüfi bir rəng götürərək və b) yuxarıdakı # 1 vasitəsilə bitişik poçtlardan birinin bu rəngi istifadə edib-etmədiyini araşdıraraq bir PHP skriptini yaratdım. Rəng istifadə olunsaydı, təzə, istifadə olunmamış bir rəng tapana qədər hovuzdan təsadüfi rəngləri gəzdim.

Qeydlər:

  • Rəng dəyərlərim üçün ilk partiyada 0-15, incə bir ikinci cəhddə 0-7 arasında bir int aralığını istifadə etdim. Bu, təsadüfi bir dəyəri seçməyi çox asanlaşdırır (məsələn, rand (0,7)) və xəttin düzəldilməsində daha çox rahatlıq təmin edir. Mənim vəziyyətimdə, bu int dəyərini CartoCSS-dəki şərti qaydalarla həqiqi rəng dəyərlərini tərtib etmək üçün istifadə edirəm.
  • Yalnız bir poçt kodu və 7-dən çox qonşusu olan üç bölgəni tapdım (beləliklə 8 rəng dəstimdəki mövcud rəng hovuzunu tükəndirdim (bunların necə mümkün olduğunu düşünürsənsə, hər tərəfdən çox qonşu olan fövqəladə hündür formalardı) # 2-dəki ssenarinin sonsuz döngələnməsinin qarşısını almaq üçün seçdiyim və təkrarlanan rənglə yaşadığım son təsadüfi dəyəri vurdum. Bu problem açıq-aydın 16 rəngli hovuzda baş vermədi. 50 ABŞ-ın olduğunu oxudum cəhd etmədiyimə baxmayaraq ən azı 4 rənglə edilə bilər.
  • Şəhərlər biraz daha hiyləgərdirlər, çünki əksəriyyəti başqasına qarşı dayandırmır. Beləliklə, ST_Intersects () işləməyəcəkdir. Bunun əvəzinə, yaxınlığı müəyyən bir qısa məsafədə (çox uzun və çox qonşu olacaqsınız) kimi təyin etdim. Bu yanaşma riyazi cəhətdən təmiz bir həll yaratmasa da, praktikada xəritəni çəkərkən gözü aldatmaq üçün kifayət qədər yaxşı işləyir.
  • Yuxarıdakı # 2-dəki ssenari ilə müvafiq olaraq ZIP və şəhərləri işləmək üçün iş vaxtı mənim qurğumda təxminən bir saat idi. Php.ini-də max_execution_time dəyərinin həqiqətən uzun bir işə imkan verəcək şəkildə qurulduğundan əmin olun.

Ümid edirəm bu sizə kömək edəcəkdir. Hər hansı bir sualınız və ya açıqlama ilə mənə müraciət etməkdən çəkinməyin.


Videoya baxın: 2nd Law of thermodynamics - Principles of Refrigeration (Oktyabr 2021).