Hinweise Aufgabenblatt 9

Werbung
1
Prof. Dr. Th. Letschert
FB MNI
25. Juni 2013
Hinweise Aufgabenblatt 9 - Verteilte Systeme
Aufgabe 1
Die natürlichen Zahlen mit den Operationen ggt und kgv bilden einen Verband mit der Relation | (ist Teiler von).
Ein adaptierter Wellen-Algorithmus für die GGT-Berechnung könnte so aussehen:
package aufgabe_1
import
import
import
import
import
akka.actor.Actor
akka.actor.ActorRef
akka.actor.ActorSystem
akka.actor.Props
scala.collection.immutable.ListSet
sealed abstract class Msg
case class Start(neighbors: List[ActorRef], init: Int) extends Msg
case class Token(msg : Int) extends Msg
class
var
var
var
GGTInitiator extends Actor {
neighbors : List[ActorRef] = List()
nr : Int = -1
rec = 0
def receive = {
case Start(n, s) => {
neighbors = n
nr = s
for (q <- neighbors) {
q ! Token(nr)
}
}
case Token(s) => {
rec = rec + 1
nr = GGT.ggt(nr, s)
if (rec == neighbors.length) {
println("decide: " + nr)
}
}
}
}
class
var
var
var
var
GGTFollower(id: Int) extends Actor {
neighbors : List[ActorRef] = List()
nr : Int = -1
rec = 0
father : Option[ActorRef] = None
def receive = {
case Start(n, s) => {
neighbors = n
nr = s
}
case Token(s) => {
nr = GGT.ggt(nr, s)
rec = rec + 1
if (!father.isDefined) {
2
father = Some(sender)
for (q <- neighbors) yield {
if (q != father.get) {
q ! Token(nr)
}
}
}
if (rec == neighbors.length) {
father.get ! Token(nr)
}
}
}
}
object GGT extends App {
def ggt(x: Int, y: Int) : Int = if (x==y) x else ggt(max(x,y) - min(x,y), min(x,y))
def max(x: Int, y: Int) : Int = if (x>y) x else y
def min(x: Int, y: Int) : Int = if (x>y) y else x
val system = ActorSystem("GGTSystem")
val actor: List[ActorRef] = Range(0,8)
.map(i => if (i == 0) system.actorOf(Props(new GGTInitiator), name = "actor_"+i)
else system.actorOf(Props(new GGTFollower(i)), name = "actor_"+i)) . toList
actor(1)
actor(2)
actor(3)
actor(4)
actor(5)
actor(6)
actor(7)
actor(0)
!
!
!
!
!
!
!
!
Start(List(actor(0), actor(2), actor(6)), 60)
Start(List(actor(3), actor(4)), 180)
Start(List(actor(2), actor(6)), 180)
Start(List(actor(2), actor(5), actor(7)), 90)
Start(List(actor(4)), 18)
Start(List(actor(7), actor(1)), 360)
Start(List(actor(4), actor(6)), 360)
Start(List(actor(1)), 120)
}
Aufgabe 2
Handelt es sich bei der Berechnung der Routing-Tabelle in einem Netz um eine Infimum-Berechnung? Erläutern Sie!
Zunächst muss definiert werden, um was es sich bei der “Berechnung der Routing-Tabelle” handelt: Eine Routingtabelle
sei einfach eine Tabelle, in der zu jeder Quelle und jedem Ziel vermerkt ist, über welchen Nachbarknoten der Quelle
das Ziel mit minimalen Kosten zu erreichen ist. Da diese Information sich aus einer Gesamtübersicht des Netzes ergibt,
identifizieren wir einfach die Routing Tabelle mit einer Gesamtdarstellung des Netzes.
Die Berechnung besteht dann einfach darin, die Informationen über das Netz zusammen zu tragen. Wir identifizieren das
Netz mit seinem Graph.
Sei G = V, E mit E ⊆ V × V ein Graph. Die Nachbarn eines Knotens v ∈ V seien die Knoten v 0 ∈ V mit (v, v 0 ) ∈ E.
Eine Tabelle t ∈ T ⊆ E sei eine Teilmenge der Kanten E. Die gesuchte Operation ist die Mengenvereinigung von
Tabellen. Diese Operation ist offensichtlich assoziativ, kommutativ und idempotent. Das Infimum (Supremum) ist den
Menge E.
Ein Routing–Algorithmus könnte also in einer Welle die Informationen über das Netz zusammentragen und dann die
Informationen geeignet aufbereiten, etwa in dem die Information per Broadcast an alle anderen Knoten (Router) gesendet
wird und diese dann jeweils mit einem Single Source Shortest Path Algorithmus (Dijkstra) die beste Route zu allen
anderen berechnen.
Ein Wellenalgorithmus kann die Informationen über die direkten Nachbarn sammeln:
package aufgabe_2
3
import
import
import
import
import
akka.actor.Actor
akka.actor.ActorRef
akka.actor.ActorSystem
akka.actor.Props
scala.collection.immutable.ListSet
sealed abstract class Msg
case class Start(neighbors: List[ActorRef], init: Set[Int]) extends Msg
case class LSMsg(links: Set[Pair[Int, Int]]) extends Msg
class
var
var
var
LSInitiator extends Actor {
neighbors : List[ActorRef] = List()
set : ListSet[Pair[Int, Int]] = ListSet()
rec = 0
def receive = {
case Start(n, s) => {
neighbors = n
s.foreach(n => set = set + Pair(0 , n))
for (q <- neighbors) {
q ! LSMsg(set)
}
}
case LSMsg(s) => {
rec = rec + 1
set = set ++ s
if (rec == neighbors.length) {
println("decide: " + set)
}
}
}
}
class
var
var
var
var
LSFollower(id: Int) extends Actor {
neighbors : List[ActorRef] = List()
set : ListSet[Pair[Int, Int]] = ListSet()
rec = 0
father : Option[ActorRef] = None
def receive = {
case Start(n, s) => {
neighbors = n
s.foreach(n => set = set + Pair(id , n))
}
case LSMsg(s) => {
set = set ++ s
rec = rec + 1
if (!father.isDefined) {
father = Some(sender)
for (q <- neighbors) yield {
if (q != father.get) {
q ! LSMsg(set)
}
}
}
if (rec == neighbors.length) {
father.get ! LSMsg(set)
}
}
}
}
4
object Routing extends App {
val system = ActorSystem("RoutingSystem")
val actor: List[ActorRef] = Range(0,8)
.map(i => if (i == 0) system.actorOf(Props(new LSInitiator), name = "actor_"+i)
else system.actorOf(Props(new LSFollower(i)), name = "actor_"+i)) . toList
actor(1)
actor(2)
actor(3)
actor(4)
actor(5)
actor(6)
actor(7)
actor(0)
!
!
!
!
!
!
!
!
Start(List(actor(0), actor(2), actor(6)), ListSet[Int](0, 2, 6))
Start(List(actor(3), actor(4)), ListSet[Int](3, 4))
Start(List(actor(2), actor(6)), ListSet[Int](2, 6))
Start(List(actor(2), actor(5), actor(7)), ListSet[Int](2, 5, 7))
Start(List(actor(4)), ListSet[Int](4))
Start(List(actor(7), actor(1)), ListSet[Int](7, 1))
Start(List(actor(4), actor(6)), ListSet[Int](4, 6))
Start(List(actor(1)), ListSet[Int](1))
}
Der Initiator kennt jetzt den Graph und kann daraus die Routingtabellen berechnen und verschicken, oder er verschickt
den Graph und die Knoten berechnen daraus ihre Routing–Tabelle.
Damit haben wir einen Link-State Routing-Algorithmus realisiert. Die alternative Methode nennt sich Distance Vector
Routing und operiert wie folgt:
Jeder Knoten x verwaltet
• Eine Tabelle cx mit den Kosten cx (n) der Verbindung von x zu einem Nachbarn n aus der Menge der Nachbarn
Nx .
• Eine Tabelle mit den Kosten Dx (v) des Weges zu einem beliebigen Ziel v – der eigene Distanz-Vektor.
• Die Distanz-Vektoren Dn (v) der Nachbarn n.
Die Aktionen sind:
• Jeder Knoten v sendet in periodischen Abständen seinen Distanz-Vektor Dv zu den Nachbarn.
• Jeder Knoten v, der einen Distanz-Vektor Dn von einem Nachbarn n empfängt, prüft ob er eine Veränderung
beinhaltet und wenn ja, dann berechnet er seinen Distanz-Vektor Dv neu
Dv (z) = min{Dn (z) + cv (n) | n ∈ Nv }
und versendet ihn an seine Nachbarn.
In dieser Form handelt es weder um eine Infimum-Berechnung noch um einen Wellen–Algorithmus. Man könnte das
Link-State Routing zu einem Wellen–Algorithmus umdeuten. Dazu müsste die gesamte Tabelle umher geschickt werden
und jeder Knoten die gleiche Berechnung ausführen.
Wir definieren Tabellen und eine entsprechende Operation + auf ihnen:
package aufgabe_2
import scala.collection.mutable.Map
case class Table(t : Map[Symbol, Map[Symbol, Pair[Symbol, Int]]] = Map()) {
def addRoute(start: Symbol, to: Symbol, via: Symbol, dist: Int) {
if (t.get(start).isDefined) {
t(start) += (to -> (via, dist))
} else {
t += (start -> Map(to -> (via, dist)))
}
}
5
def routeAdd(r1: Pair[Symbol, Int], r2: Pair[Symbol, Int]) : Pair[Symbol, Int] =
Pair(r1._1, r1._2 + r2._2)
def minRoute(p1: Pair[Symbol, Int], p2: Pair[Symbol, Int]) : Pair[Symbol, Int] =
if (p1._2 < p2._2) p1 else p2
def +(that: Table) : Table = {
val d1: Map[Symbol, Map[Symbol, Pair[Symbol, Int]]] = this.t
val d2: Map[Symbol, Map[Symbol, Pair[Symbol, Int]]] =
that match {case Table(tt) => tt}
val res : Map[Symbol, Map[Symbol, Pair[Symbol, Int]]] = Map()
var keys = d1.keySet ++ d2.keySet
d1.keySet.foreach{ k => keys = keys ++ d1(k).keySet }
d2.keySet.foreach{ k => keys = keys ++ d2(k).keySet }
val infinity = 100000
// Join tables
for (i <- keys) {
res(i) = Map()
for (j <- keys) {
res(i)(j) =
if (i==j)
(i, 0)
else if (d1.isDefinedAt(i) && d2.isDefinedAt(i))
minRoute(d1(i).getOrElse(j, (’_’, infinity)),
d2(i).getOrElse(j, (’_’, infinity)))
else if (d1.isDefinedAt(i) && !d2.isDefinedAt(i))
d1(i).getOrElse(j, (’_’, infinity))
else if (!d1.isDefinedAt(i) && d2.isDefinedAt(i))
d2(i).getOrElse(j, (’_’, infinity))
else (’_’, 10000)
}
}
// Combine routes (transitve closure using the Warshall-Floyd algorithm)
for (k <- keys) {
for (i <- keys) {
for (j <- keys) {
if (i != j && k != i) {
res(i)(j) = minRoute(res(i)(j), routeAdd(res(i)(k), res(k)(j)))
}
}
}
}
Table(res)
}
}
// example
object Warshall extends App {
val D_A : Map[Symbol, Map[Symbol, Pair[Symbol, Int]]] =
Map(’A’ -> Map(’A’ -> (’A’, 0), ’B’ -> (’B’, 7), ’E’ -> (’E’, 1)))
val D_B : Map[Symbol, Map[Symbol, Pair[Symbol, Int]]] =
6
Map(’B’ -> Map(’B’ -> (’B’, 0), ’A’ -> (’A’, 7), ’C’ -> (’C’, 1), ’E’ -> (’E’, 8)))
val D_C : Map[Symbol, Map[Symbol, Pair[Symbol, Int]]] =
Map(’C’ -> Map(’C’ -> (’C’, 0), ’B’ -> (’B’, 1), ’D’ -> (’D’, 2)))
val D_D : Map[Symbol, Map[Symbol, Pair[Symbol, Int]]] =
Map(’D’ -> Map(’D’ -> (’D’, 0), ’C’ -> (’C’, 2), ’E’ -> (’E’, 2)))
val D_E : Map[Symbol, Map[Symbol, Pair[Symbol, Int]]] =
Map(’E’ -> Map(’E’ -> (’E’, 0), ’A’ -> (’A’, 1), ’B’ -> (’B’, 8), ’D’ -> (’D’, 2)))
val
val
val
val
val
T_A
T_B
T_C
T_D
T_E
=
=
=
=
=
Table(D_A)
Table(D_B)
Table(D_C)
Table(D_D)
Table(D_E)
println(T_A + T_B + T_C + T_D + T_E)
}
Die Operation + auf Tabellen ist kommutativ, assoziativ und idempotent: Die Berechnung des Minimums hat diese Eigenschaften. Das Minimum wird über den transitiven Abschluss der kombinierten Erreichbarkeit von zwei Graphen berechnet. Der transitive Abschluss ist kommutativ, assoziativ und idempotent. Der Algorithmus berechnet den transitiven
Abschluss, weil er eine Implementierung des Warshall–Algorithmus (siehe Wikipedia) darstellt.
Aufgabe 3
Implementieren und modifizieren Sie den Algorithmus von Tarry derart, dass der implizit berechnete Spannbaum ausgegeben wird.
package aufgabe_3
import
import
import
import
akka.actor.Actor
akka.actor.ActorRef
akka.actor.ActorSystem
akka.actor.Props
sealed abstract class Msg
case class Start(neighbors: List[ActorRef]) extends Msg
case class Token(orig: Option[Node]) extends Msg
case class Node(nr: Int, children: List[Node])
class
var
var
var
TarryInitiator extends Actor {
neighbors : List[ActorRef] = List()
unused : List[ActorRef] = List()
tree
: Node = Node(0, List())
def receive = {
case Start(n) => {
neighbors = n
unused = n
val q = unused(0)
unused = unused.tail
q ! Token(None)
}
case Token(x) => {
if (x.isDefined) {
7
tree = Node(0, x.get :: tree.children)
}
if (unused.length == 0) {
println("Initiator decide ! tree = " + tree)
} else {
val q = unused(0)
unused = unused.tail
q !Token(None)
}
}
}
}
class
var
var
var
var
TarryFollower(nr: Int) extends Actor {
neighbors : List[ActorRef] = List()
father : Option[ActorRef] = None
tree
: Node = Node(nr, List())
unused : List[ActorRef] = List()
def m(i:Int) : Int = i+1
def receive = {
case Start(n) => {
neighbors = n
unused = n
}
case Token(x) => {
if (!father.isDefined) {
father = Some(sender)
} else {
if (x.isDefined) {
tree = Node(nr, x.get :: tree.children)
}
}
if (unused.length != 0) {
val q : Option[ActorRef] = unused.find(_ != father.get)
if (q.isDefined) {
unused = unused.filter(_ != q.get)
q.get ! Token(None)
} else {
unused = unused.filter(_ != father.get)
father.get ! Token(Some(tree))
}
}
}
}
}
object Tarry extends App {
val system = ActorSystem("TarrySystem")
val actor: List[ActorRef] = Range(0,5)
.map(i => if (i == 0) system.actorOf(Props(new TarryInitiator), name = "actor_"+i)
else system.actorOf(Props(new TarryFollower(i)), name = "actor_"+i)) . toList
actor(1)
actor(2)
actor(3)
actor(4)
actor(0)
!
!
!
!
!
Start(List(actor(0), actor(2), actor(4)))
Start(List(actor(1)))
Start(List(actor(0), actor(4)))
Start(List(actor(0), actor(1), actor(3)))
Start(List(actor(4), actor(1), actor(3)))
8
}
Aufgabe 4
Modifizieren und implementieren Sie den Algorithmus von Tarry zu einer Implementierung der Tiefensuche.
Leicht.
Aufgabe 5
Modifizieren Sie die Implementierung des Algorithmus von Tarry (oder den zur Tiefensuche) derart, dass mit seiner Hilfe
die Summe der Initialwerte aller Knoten berechnet werden kann.
package aufgabe_5
import
import
import
import
akka.actor.Actor
akka.actor.ActorRef
akka.actor.ActorSystem
akka.actor.Props
sealed abstract class Msg
case class Start(neighbors: List[ActorRef]) extends Msg
case class Token(orig: Option[Node]) extends Msg
case class Node(nr: Int, children: List[Node])
object Node {
def sum(n: Node) : Int =
n.nr + (0 /: n.children.map(sum))(_ + _)
}
class
var
var
var
TarryInitiator extends Actor {
neighbors : List[ActorRef] = List()
unused : List[ActorRef] = List()
tree
: Node = Node(0, List())
def receive = {
case Start(n) => {
neighbors = n
unused = n
val q = unused(0)
unused = unused.tail
q ! Token(None)
}
case Token(x) => {
//println("Actor Initiator received Token " + x + "
if (x.isDefined) {
tree = Node(0, x.get :: tree.children)
}
if (unused.length == 0) {
println("Initiator decide ! tree = " + tree)
println("SUM= " + Node.sum(tree))
} else {
val q = unused(0)
unused = unused.tail
q !Token(None)
}
}
from " + sender.path)
9
}
}
class
var
var
var
var
TarryFollower(nr: Int) extends Actor {
neighbors : List[ActorRef] = List()
father : Option[ActorRef] = None
tree
: Node = Node(nr, List())
unused : List[ActorRef] = List()
def m(i:Int) : Int = i+1
def receive = {
case Start(n) => {
neighbors = n
unused = n
}
case Token(x) => {
if (!father.isDefined) {
father = Some(sender)
} else {
if (x.isDefined) {
tree = Node(nr, x.get :: tree.children)
}
}
if (unused.length == 0) {
println("Follower " + nr + " decide ! unused = " + unused)
} else {
val q : Option[ActorRef] = unused.find(_ != father.get)
if (q.isDefined) {
unused = unused.filter(_ != q.get)
q.get ! Token(None)
} else {
unused = unused.filter(_ != father.get)
father.get ! Token(Some(tree))
}
}
}
}
}
object Tarry extends App {
val system = ActorSystem("TarrySystem")
val actor: List[ActorRef] = Range(0,5)
.map(i => if (i == 0) system.actorOf(Props(new TarryInitiator), name = "actor_"+i)
else system.actorOf(Props(new TarryFollower(i)), name = "actor_"+i)) . toList
actor(1)
actor(2)
actor(3)
actor(4)
actor(0)
}
!
!
!
!
!
Start(List(actor(0), actor(2), actor(4)))
Start(List(actor(1)))
Start(List(actor(0), actor(4)))
Start(List(actor(0), actor(1), actor(3)))
Start(List(actor(4), actor(1), actor(3)))
Herunterladen