Gyakorlat 6.
B.Sc course, University of Debrecen, Department of Data Science and Visualization, 2024
Kényszerkielégítéses feladatok
Általánosan
Mi a különbség egy általános fakeresési valamint egy kényszer-kielégítési probléma között?
- Általános keresési probléma
- Az állapot egy fekete doboz
- Az állapotot bármilyen adatstruktúra ábrázolhatja
- Csak az állapotátmenetek, heurisztika és célállapot legyen implementálva
- Kényszerkielégítési probléma
- Az állapotot Di tartományból származó Xi változókkal definiáljuk
- A célteszt kényszerek halmaza, mely mindegyike a változók egy részhalmazát és megfelelő értékeket tartalmazzák
Típusai
- Változórendezés
- A legkevesebb fennmaradó érték heurisztika (MRV)
- Fokszám heurisztik
- Értékrendezés: Legkevésbé–korlátozó–érték heurisztika
- Előrenéző ellenőrzés
- A kényszerek terjesztése
Élkonzisztencia</br>
Milyen adatokkal lehet megadni egy kényszer-kielégítési feladatot?
- változók: WA, NT, Q, NSW, V, SA, T
- tartományok: Di = {piros, zöld, kék}
- kényszerek: szomszédos tartomány nem lehet ugyanolyan színű: WA ≠ NT
Bináris kényszerkielégítési feladat
- Minden kényszer maximum két változót tartalmaz
- Kényszergráf: a csúcsok a változók, az élek a kényszereket jelölik
- a gráf szerkezetét felhasználva a keresés felgyorsítható
- Tazmánia független részprobléma
Gráf szinezési probléma
A gráf színezési probléma egy olyan probléma, amelyben egy gráfot kell színezni úgy, hogy minden csúcsot egy adott színnel kell jelölni, úgy hogy a szomszédos csúcsok nem lehetnek azonos színűek. A cél az, hogy minél kevesebb színt használjunk a gráf színezéséhez.
Például, ha van egy gráfunk, amelyben négy csúcs van és három színt használunk (piros, zöld és kék), akkor a gráf színezési problémája az lenne, hogy megtaláljuk azt a színezést, amelyben minden csúcsot egy adott színnel jelölünk úgy, hogy a szomszédos csúcsok nem lehetnek azonos színűek.
Ez egy NP-teljes probléma, ami azt jelenti, hogy nincs ismert polinomiális időben futó algoritmus a megoldására. Azonban vannak olyan heurisztikus algoritmusok és approximációs algoritmusok, amelyek közelítő megoldást adnak a problémára.
# Képezzük le gráfra Ausztráliát
graph = [[0, 1, 1, 0, 0, 0],
[1, 0, 1, 1, 0, 0],
[1, 1, 0, 1, 1, 1],
[0, 1, 1, 0, 1, 0],
[0, 0, 1, 1, 0, 1],
[0, 0, 1, 0, 1, 0]]
import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()
for i in range(len(graph)):
for j in range(i+1, len(graph)):
if graph[i][j]:
G.add_edge(i+1, j+1)
pos = nx.spring_layout(G)
nx.draw(G, pos)
nx.draw_networkx_labels(G, pos)
plt.show()
Visszalépéses keresés
- A kényszerkielégítési feladatra alkalmazott mélységi keresést, ahol egyszerre egy változó kap értéket és visszalép, ha már nincs megengedett hozzárendelési lehetőség visszalépéses keresésnek nevezzük.
- A visszalépéses keresés az alapvető nem informált módszere a kényszerkielégítési feladatoknak
def is_safe(graph, color, v, c):
"""
A is_safe függvény ellenőrzi, hogy egy adott szín biztonságos-e egy adott csúcson.
Ha a csúcsnak van olyan szomszédja, amelynek már ugyanaz a színe van mint a vizsgált színű csúcsnak akkor az nem biztonságos.
"""
for i in range(len(graph)):
if graph[v][i] and c == color[i]:
return False
return True
import numpy as np
def backtracking(graph, graf_colors, v, colors, h=None):
"""A graph_coloring_util függvény rekurzívan meghívja önmagát minden csúcsra és megpróbálja kiválasztani a színeket.
Ha egy adott szín nem biztonságos (azaz ha már használják egy szomszédos csúcson), akkor kipróbál egy másik színt.
Ha egyik szín sem biztonságos, akkor visszalép és megpróbálja újraszínezni az előző csúcsot."""
# Megvizsgáljuk hogy melyik elemnél vagyzunk
# ha 'v' == a gráf hosszával akkor készen vagyunk
if v == len(graph):
return True
# Próbáljuk végig a szineket
for c in range(colors):
# Ha kiszinezhető a 'v' csúcs a 'c' színnel
if h(graph, graf_colors, v, c):
# színezzük ki a 'v' csúcsot 'c' színnel
graf_colors[v] = c
# szinezzük ki a következő csúcsot
if backtracking(graph, graf_colors, v + 1, colors, h):
return True
# ha nem sikerül visszalépünk és az aktuálisan
# kiszinezett csúcsot '-1'-re azaz szín nélkülire állítjuk
graf_colors[v] = -1
return False
# Mennyi színnel színezzünk
colors = 3
# Legyen -1 a szintelen
non_color = -1
# Hozzunk létre egy listát ami tartalmazza az egyes csúcsok színeit
graf_colors = [non_color] * len(graph)
print(graf_colors)
backtracking(graph, graf_colors, 0, colors, is_safe)
if non_color not in graf_colors:
print("A gráf színezése: ", graf_colors)
else:
print("Nem találtam megoldást a megadott színekkel.")
Élkonzisztencia ellenőrzés
- Az él a kényszergráf irányított éleit jelenti.
- az X -ből Y -ba mutató él akkor konzisztens, ha X minden x értékéhez található egy xszel konzisztens y értéke Y -nak.
- Egy él konzisztenssé tehető az olyan értékek törlésével, amelyhez nem létezik a végpontnak megengedett értéke.
- Az élkonzisztencia ellenőrzés lehetővé teszi, hogy korábban észrevegyük az egyszerű előrenéző ellenőrzés által fel nem fedett inkonzisztenciát.
- Alkalmazható előfeldolgozó lépésként a keresés megkezdése előtt.
- A keresési folyamat minden egyes hozzárendelését követő terjesztési lépésként (az élkonzisztencia fenntartásának algoritmusa).
- Mindkét előző esetben addig kell ismételve alkalmazni a folyamatot, amíg nem marad inkonzisztencia.
- Ugyanis a törléssel a változóhoz mutató éleknél új inkonzisztencia jöhet létre.
- Ha az X változó egy értékét töröljük, akkor X szomszédait újra kell vizsgálni
def is_consistent(graph, colors):
"""A is_safe függvény ellenőrzi, hogy a gráf színezése megfelelő-e.
A függvény végig megy a gráf összes csúcsán és ellenőrzi,
hogy van-e két szomszédos csúcs azonos színnel.
Ha van, akkor a függvény hamis értékkel tér vissza, ha nincs akkor igaz értékkel."""
for i in range(len(graph)):
for j in range(i + 1, len(graph)):
if graph[i][j] and colors[j] == colors[i]:
return False
return True
def backtracking_c(graph, colors, v, graf_colors, h=None):
"""függvény egy rekurzív függvény, ami megpróbálja megtalálni a gráf színezését c színnel.
A függvény végig megy a gráf összes csúcsán és minden csúcsot megpróbál befesteni az c szín
valamelyikével. Ha a gráf összes csúcsát befestette és az élkonzisztencia teljesül"""
# Ha feldolgoztuk az összes csúcsot
if v == len(graph):
# Ha igaz akkor mindenhol tudtunk szinezni
if h(graph, graf_colors):
return True
# Ha hamis akkor nem oldható meg a probléma
else:
return False
# Rekurzívan bejárjuk a gráfot
for j in range(0, colors):
graf_colors[v] = j
if backtracking_c(graph, colors, v + 1, graf_colors, h):
return True
graf_colors[v] = -1
colors = 4
graf_colors = [-1] * len(graph)
backtracking_c(graph, colors, 0, graf_colors, is_consistent)
graf_colors