∥
∥ 中研院 ∥ 人社中心 ∥ 繁體中文 ∥ English
在圖論(graph theory)與地圖學(cartography)中,「四色定理(Four Color Theorem)」與「五色定理(Five Color Theorem)」是兩項經典的重要命題。四色定理指出,任意一張平面地圖,只需使用四種顏色,就能保證所有相鄰區域都不會使用相同顏色,是1852年由數學家賈斯禮(Francis Guthrie)首次提出四色定理的猜想,雖然從地圖設計的實務上確實能僅用四種顏色就把地圖著色,且相鄰不同色,但一直與無法用數學方式證明它,直到 1976 年由 Appel 與 Haken 借助電腦演算法輔助(暴力窮舉法)成功證明,成為數學史上首例以電腦完成的證明之一。
在四色定理尚未被正式證明前,數學家 Heawood 於 1890 年提出了較為保守但易於證明的五色定理,主張任意平面圖最多使用五種顏色也可避免相鄰區域顏色重複。五色定理雖非最精簡解法,卻在地圖設計實務上提供了一項重要保證,特別適用於複雜地圖或圖層自動著色演算法中。
目前在QGIS 3.x版中內建有拓撲著色(Topological coloring)功能,但因演算法設計需求而採用最多五色完成著色任務,即使理論上四色已經足夠,以下為簡要操作說明:
2.指定一個多邊形向量圖層(例如行政區劃),以及最少使用顏色數量,演算法就會自動分類(在屬性資料表新增一個color_id欄位,並給予不同的分類代號)。
3.程式將建立一個新圖層,新增一個名為 color_id
的欄位,但顏色仍與原圖層相同;接著,編輯這個新圖層的圖層屬性,進入「圖徵(Symbology)」分頁進行細部設定:
color_id
。4.同樣操作也可以運用到臺灣縣市行政區域圖。
5.受限於演算法因素,拓撲著色(Topological coloring)功能,針對比較複雜的多邊形(存在 MultiPolygon幾何、飛地等情況),最終分類(顏色)數量可能會大於4,成為五色或六色地圖。
6.如果可以接受少量的相鄰多邊形有相同顏色情況,可以利用ChatGPT可以產生Python 程式,用來自動為多邊形圖層加上「四色理論」分類欄位,限制只用 4 色完成配色:
color_id
整數欄位(值為 1~4),盡可能確保相鄰的多邊形不會有相同顏色。import networkx as nx
from qgis.core import (
QgsProject, QgsField, QgsSpatialIndex, QgsFeature, QgsGeometry, QgsPointXY
)
from PyQt5.QtCore import QVariant
layer = iface.activeLayer()
if not layer or layer.geometryType() != 2:
raise Exception("請選取一個多邊形圖層")
# 新增 color_id 欄位
layer.startEditing()
if 'color_id' not in [f.name() for f in layer.fields()]:
layer.dataProvider().addAttributes([QgsField('color_id', QVariant.Int)])
layer.updateFields()
# 建立要素快取與空間索引
features = {f.id(): f for f in layer.getFeatures()}
index = QgsSpatialIndex()
for feat in features.values():
index.insertFeature(feat)
# 建立鄰接圖
G = nx.Graph()
for fid, feat in features.items():
G.add_node(fid)
neighbor_ids = index.intersects(feat.geometry().boundingBox())
for nid in neighbor_ids:
if nid != fid and feat.geometry().touches(features[nid].geometry()):
G.add_edge(fid, nid)
# 計算圖層中心點
layer_center = layer.extent().center()
center_geom = QgsGeometry.fromPointXY(QgsPointXY(layer_center))
# 根據與中心距離排序
def dist_to_center(feat):
return feat.geometry().centroid().distance(center_geom)
sorted_nodes = sorted(G.nodes, key=lambda fid: dist_to_center(features[fid]))
# 自行進行貪婪著色(最多使用 4 色)
coloring = {}
for fid in sorted_nodes:
neighbor_colors = {coloring[n] for n in G.neighbors(fid) if n in coloring}
for color in range(1, 5): # 限定四色
if color not in neighbor_colors:
coloring[fid] = color
break
# 寫入 color_id
field_idx = layer.fields().indexFromName('color_id')
for fid, color in coloring.items():
layer.changeAttributeValue(fid, field_idx, color)
layer.commitChanges()
print(f"完成!已從圖層中心開始排序並套用四色著色,共使用顏色:{max(coloring.values())}")
進階閱讀: