diff --git a/Makefile b/Makefile index cd3b2da..d27fca7 100644 --- a/Makefile +++ b/Makefile @@ -13,12 +13,14 @@ leader_directed_ring_example: go run example/leader_directed_ring_sync.go 10 peterson go run example/leader_directed_ring_async.go 10 higham_przytycka go run example/leader_directed_ring_async.go 10 itai_rodeh + go run example/leader_directed_ring_async_hirschberg_sinclair.go 10 leader_undirected_ring_example: go run example/leader_undirected_ring_sync_hirschberg_sinclair.go 10 go run example/leader_undirected_ring_sync_franklin.go 10 go run example/leader_undirected_ring_sync_prob_as_far.go 10 go run example/leader_undirected_ring_async_hirschberg_sinclair.go 10 + go run example/leader_undirected_ring_async_hirschberg_sinclair_2.go 10 go run example/leader_undirected_ring_async_stages_with_feedback.go 10 go run example/leader_undirected_ring_async_franklin.go 10 go run example/leader_undirected_ring_async_probabilistic_franklin.go 10 3 diff --git a/README.md b/README.md index 26b4de3..2b51d70 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ A collection of algorithms for _Distributed Systems_ course (winter semesters 20 1. Dolev-Klawe-Rodeh algorithm B 1. Itai-Rodeh algorithm 1. Higham-Przytycka algorithm +1. Hirschberg-Sinclair algorithm #### Asynchronous undirected ring 1. Franklin algorithm diff --git a/example/leader_directed_ring_async_hirschberg_sinclair.go b/example/leader_directed_ring_async_hirschberg_sinclair.go new file mode 100644 index 0000000..df0ef8c --- /dev/null +++ b/example/leader_directed_ring_async_hirschberg_sinclair.go @@ -0,0 +1,13 @@ +package main + +import ( + "os" + "strconv" + + "github.com/krzysztof-turowski/distributed-framework/leader/directed_ring/async_hirschberg_sinclair" +) + +func main() { + n, _ := strconv.Atoi(os.Args[len(os.Args)-1]) + async_hirschberg_sinclair.Run(n) +} diff --git a/example/leader_undirected_ring_async_hirschberg_sinclair_2.go b/example/leader_undirected_ring_async_hirschberg_sinclair_2.go new file mode 100644 index 0000000..6f33e48 --- /dev/null +++ b/example/leader_undirected_ring_async_hirschberg_sinclair_2.go @@ -0,0 +1,13 @@ +package main + +import ( + "os" + "strconv" + + "github.com/krzysztof-turowski/distributed-framework/leader/undirected_ring/async_hirschberg_sinclair_2" +) + +func main() { + n, _ := strconv.Atoi(os.Args[len(os.Args)-1]) + async_hirschberg_sinclair_2.Run(n) +} diff --git a/leader/directed_ring/async_hirschberg_sinclair/async_hirschberg_sinclair.go b/leader/directed_ring/async_hirschberg_sinclair/async_hirschberg_sinclair.go new file mode 100644 index 0000000..1c1a814 --- /dev/null +++ b/leader/directed_ring/async_hirschberg_sinclair/async_hirschberg_sinclair.go @@ -0,0 +1,232 @@ +package async_hirschberg_sinclair + +import ( + "encoding/json" + "fmt" + "github.com/krzysztof-turowski/distributed-framework/lib" + "log" +) + +type ElectionStatus string +type MessageType string + +const ( + CandidateStatus ElectionStatus = "candidate" + LostStatus ElectionStatus = "lost" + WonStatus ElectionStatus = "won" +) +const ( + No MessageType = "no" + Ok MessageType = "ok" + From MessageType = "from" + End MessageType = "end" +) + +type message struct { + MessageType MessageType + Value int + Num uint64 + Maxnum uint64 +} + +type state struct { + MyStatus ElectionStatus +} + +func initialize(v lib.Node) bool { + s := state{MyStatus: CandidateStatus} + sEncoded, _ := json.Marshal(s) + v.SetState(sEncoded) + + data := message{From, v.GetIndex(), 0, 1} + initiateSendingDataClockwise(v, data) + return false +} + +func process(v lib.Node) bool { + maxnum := uint64(1) + var jsonReceived []byte + var chanIndex int + var receivedData message + //counter := 0 + final := false + var s state + for { + chanIndex, jsonReceived = v.ReceiveAnyMessage() + _ = json.Unmarshal(jsonReceived, &receivedData) + _ = json.Unmarshal(v.GetState(), &s) + + switch receivedData.MessageType { + case From: + receivedMessageIsFrom(v, receivedData, chanIndex) + _ = json.Unmarshal(v.GetState(), &s) + if s.MyStatus == WonStatus { + dataToSend := message{End, v.GetIndex(), 0, maxnum} + sendPassClockwise(v, &dataToSend, chanIndex) + } + + case No, Ok: + receivedMessageIsOkOrNo(v, receivedData, chanIndex) + _ = json.Unmarshal(v.GetState(), &s) + if receivedData.MessageType == No { + s.MyStatus = LostStatus + } + if receivedData.Value == v.GetIndex() { + if s.MyStatus == CandidateStatus { + maxnum *= 2 + dataToSend := initSendingData(From, v.GetIndex(), 0, maxnum) + initiateSendingDataClockwise(v, dataToSend) + } + } + case End: + if s.MyStatus == LostStatus { + sendPassClockwise(v, &receivedData, chanIndex) + } + final = true + + default: + //nothing to do + } + + sEncoded, _ := json.Marshal(s) + v.SetState(sEncoded) + + if final { + break + } + } + + if s.MyStatus == WonStatus { + log.Println("The Leader has index ", v.GetIndex()) + } + return true +} + +func run(v lib.Node) { + v.StartProcessing() + isFinished := initialize(v) + + isFinished = process(v) + v.FinishProcessing(isFinished) +} + +func check(vertices []lib.Node) { + var leader lib.Node + var s state + for _, v := range vertices { + _ = json.Unmarshal(v.GetState(), &s) + if s.MyStatus == WonStatus { + leader = v + break + } + } + if leader == nil { + panic("There is no Leader on the undirected ring") + } + for _, v := range vertices { + _ = json.Unmarshal(v.GetState(), &s) + if v != leader { + if s.MyStatus == WonStatus { + panic(fmt.Sprint( + "There is more then one Leader on the undirected ring")) + } + if s.MyStatus == CandidateStatus { + panic(fmt.Sprint("Node ", v.GetIndex(), " has state ", CandidateStatus)) + } + } else { + if s.MyStatus != WonStatus { + panic("There is more then one Leader on the undirected ring") + } + } + } +} + +func Run(n int) (int, int) { + vertices, synchronizer := lib.BuildSynchronizedRingWithOriginalTopology(n) + for _, v := range vertices { + log.Println("Node", v.GetIndex(), "about to run") + go run(v) + } + synchronizer.Synchronize(0) + check(vertices) + + return synchronizer.GetStats() +} + +func initiateSendingDataClockwise(v lib.Node, dataToSend message) { + jsonToSend, _ := json.Marshal(dataToSend) + v.SendMessage(1, jsonToSend) +} + +func abs(n int) int { + if n < 0 { + return -n + } + return n +} + +func getChanToPrev(chanIndex int) int { + return chanIndex +} + +func getChanToNext(chanIndex int) int { + return abs(chanIndex - 1) +} + +func sendReplyCounterclockwise(v lib.Node, dataToSend *message, chanIndex int) { + channel := getChanToPrev(chanIndex) + jsonToSend, _ := json.Marshal(dataToSend) + v.SendMessage(channel, jsonToSend) +} + +func sendPassClockwise(v lib.Node, dataToSend *message, chanIndex int) { + channel := getChanToNext(chanIndex) + jsonToSend, _ := json.Marshal(dataToSend) + v.SendMessage(channel, jsonToSend) +} + +func receivedMessageIsFrom(v lib.Node, receivedData message, chanIndex int) { + if receivedData.Value < v.GetIndex() { + dataToSend := initSendingData(No, receivedData.Value, 0, 0) + sendReplyCounterclockwise(v, &dataToSend, chanIndex) + return + } + var s state + _ = json.Unmarshal(v.GetState(), &s) + if receivedData.Value > v.GetIndex() { + s.MyStatus = LostStatus + sEncoded, _ := json.Marshal(s) + v.SetState(sEncoded) + if receivedData.Num+1 < receivedData.Maxnum { + dataToSend := receivedData + dataToSend.Num += 1 + sendPassClockwise(v, &dataToSend, chanIndex) + } else { + dataToSend := initSendingData(Ok, receivedData.Value, 0, 0) + sendReplyCounterclockwise(v, &dataToSend, chanIndex) + } + return + } + + //value == v.GetIndex() + s.MyStatus = WonStatus + sEncoded, _ := json.Marshal(s) + v.SetState(sEncoded) +} + +func receivedMessageIsOkOrNo(v lib.Node, receivedData message, chanIndex int) { + if receivedData.Value != v.GetIndex() { + sendPassClockwise(v, &receivedData, chanIndex) + } else { + //nice to see you back, my message! + } +} + +func initSendingData(messageType MessageType, value int, num uint64, maxnum uint64) message { + return message{ + MessageType: messageType, + Value: value, + Num: num, + Maxnum: maxnum, + } +} diff --git a/leader/undirected_ring/async_hirschberg_sinclair_2/async_hirschberg_sinclair_2.go b/leader/undirected_ring/async_hirschberg_sinclair_2/async_hirschberg_sinclair_2.go new file mode 100644 index 0000000..200cb29 --- /dev/null +++ b/leader/undirected_ring/async_hirschberg_sinclair_2/async_hirschberg_sinclair_2.go @@ -0,0 +1,251 @@ +package async_hirschberg_sinclair_2 + +import ( + "encoding/json" + "fmt" + "log" + "sync" + + "github.com/krzysztof-turowski/distributed-framework/lib" +) + +type ElectionStatus string +type MessageType string + +const ( + CandidateStatus ElectionStatus = "candidate" + LostStatus ElectionStatus = "lost" + WonStatus ElectionStatus = "won" +) +const ( + No MessageType = "no" + Ok MessageType = "ok" + From MessageType = "from" + End MessageType = "end" +) + +type message struct { + MessageType MessageType + Value int + Num uint64 + Maxnum uint64 +} + +type state struct { + MyStatus ElectionStatus +} + +func initialize(v lib.Node) bool { + s := state{MyStatus: CandidateStatus} + sEncoded, _ := json.Marshal(s) + v.SetState(sEncoded) + + data := message{From, v.GetIndex(), 0, 1} + sendBoth(v, data) + return true +} + +func process(v lib.Node) bool { + maxnum := uint64(1) + var jsonReceived []byte + var chanIndex int + var heardL bool + var heardR bool + var receivedData message + counter := 0 + final := false + var s state + for { + chanIndex, jsonReceived = v.ReceiveAnyMessage() + _ = json.Unmarshal(jsonReceived, &receivedData) + _ = json.Unmarshal(v.GetState(), &s) + + switch receivedData.MessageType { + case From: + receivedMessageIsFrom(v, receivedData, chanIndex) + _ = json.Unmarshal(v.GetState(), &s) + if s.MyStatus == WonStatus { + counter++ + if counter == 2 { + dataToSend := message{End, v.GetIndex(), 0, maxnum} + sendPass(v, &dataToSend, chanIndex) + } + } + + case No, Ok: + receivedMessageIsOkOrNo(v, receivedData, chanIndex) + _ = json.Unmarshal(v.GetState(), &s) + if receivedData.MessageType == No { + s.MyStatus = LostStatus + } + if receivedData.Value == v.GetIndex() { + if chanIndex == 0 { + heardL = true + } else { + heardR = true + } + if heardL && heardR && s.MyStatus == CandidateStatus { + heardL = false + heardR = false + maxnum *= 2 + dataToSend := initSendingData(From, v.GetIndex(), 0, maxnum) + sendBoth(v, dataToSend) + } + } + case End: + if s.MyStatus == LostStatus { + sendPass(v, &receivedData, chanIndex) + } + final = true + + default: + //nothing to do + } + + sEncoded, _ := json.Marshal(s) + v.SetState(sEncoded) + + if final { + break + } + } + + if s.MyStatus == WonStatus { + log.Println("The Leader has index ", v.GetIndex()) + } + return true +} + +func run(v lib.Node, wg *sync.WaitGroup) { + defer wg.Done() + v.StartProcessing() + isFinished := initialize(v) + + isFinished = process(v) + v.FinishProcessing(isFinished) + +} + +func check(vertices []lib.Node) { + var leader lib.Node + var s state + for _, v := range vertices { + _ = json.Unmarshal(v.GetState(), &s) + if s.MyStatus == WonStatus { + leader = v + break + } + } + if leader == nil { + panic("There is no Leader on the undirected ring") + } + for _, v := range vertices { + _ = json.Unmarshal(v.GetState(), &s) + if v != leader { + if s.MyStatus == WonStatus { + panic(fmt.Sprint( + "There is more then one Leader on the undirected ring")) + } + if s.MyStatus == CandidateStatus { + panic(fmt.Sprint("Node ", v.GetIndex(), " has state ", CandidateStatus)) + } + } else { + if s.MyStatus != WonStatus { + panic("There is more then one Leader on the undirected ring") + } + } + } +} + +func Run(n int) (int, int) { + vertices, runner := lib.BuildRing(n) + var wg sync.WaitGroup + wg.Add(n) + for _, v := range vertices { + log.Println("Node", v.GetIndex(), "about to run") + go run(v, &wg) + } + runner.Run(true) + check(vertices) + wg.Wait() + return runner.GetStats() +} + +func sendBoth(v lib.Node, dataToSend message) { + jsonToSend, _ := json.Marshal(dataToSend) + v.SendMessage(0, jsonToSend) + v.SendMessage(1, jsonToSend) +} + +func abs(n int) int { + if n < 0 { + return -n + } + return n +} + +func getChanToPrev(chanIndex int) int { + return chanIndex +} + +func getChanToNext(chanIndex int) int { + return abs(chanIndex - 1) +} + +func sendEcho(v lib.Node, dataToSend *message, chanIndex int) { + channel := getChanToPrev(chanIndex) + jsonToSend, _ := json.Marshal(dataToSend) + v.SendMessage(channel, jsonToSend) +} + +func sendPass(v lib.Node, dataToSend *message, chanIndex int) { + channel := getChanToNext(chanIndex) + jsonToSend, _ := json.Marshal(dataToSend) + v.SendMessage(channel, jsonToSend) +} + +func receivedMessageIsFrom(v lib.Node, receivedData message, chanIndex int) { + if receivedData.Value < v.GetIndex() { + dataToSend := initSendingData(No, receivedData.Value, 0, 0) + sendEcho(v, &dataToSend, chanIndex) + return + } + var s state + _ = json.Unmarshal(v.GetState(), &s) + if receivedData.Value > v.GetIndex() { + s.MyStatus = LostStatus + sEncoded, _ := json.Marshal(s) + v.SetState(sEncoded) + if receivedData.Num+1 < receivedData.Maxnum { + dataToSend := receivedData + dataToSend.Num += 1 + sendPass(v, &dataToSend, chanIndex) + } else { + dataToSend := initSendingData(Ok, receivedData.Value, 0, 0) + sendEcho(v, &dataToSend, chanIndex) + } + return + } + + //value == v.GetIndex() + s.MyStatus = WonStatus + sEncoded, _ := json.Marshal(s) + v.SetState(sEncoded) +} + +func receivedMessageIsOkOrNo(v lib.Node, receivedData message, chanIndex int) { + if receivedData.Value != v.GetIndex() { + sendPass(v, &receivedData, chanIndex) + } else { + //nice to see you back, my message! + } +} + +func initSendingData(messageType MessageType, value int, num uint64, maxnum uint64) message { + return message{ + MessageType: messageType, + Value: value, + Num: num, + Maxnum: maxnum, + } +} diff --git a/lib/lib_graph.go b/lib/lib_graph.go index 32ed29d..be11924 100644 --- a/lib/lib_graph.go +++ b/lib/lib_graph.go @@ -76,6 +76,24 @@ func BuildRing(n int) ([]Node, Runner) { return vertices, runner } +func BuildRingWithOriginalTopology(n int) ([]Node, Runner) { + vertices, runner := BuildEmptyGraph(n, GetRandomGenerator()) + chans := getSynchronousChannels(2 * n) + for i := 0; i < n; i++ { + addTwoWayConnection( + vertices[i].(*oneWayNode), vertices[(i+1)%n].(*oneWayNode), + chans[2*i], chans[(2*i+1)%(2*n)]) + log.Println("Channel", vertices[i].GetIndex(), "->", vertices[(i+1)%n].GetIndex(), "set up") + log.Println("Channel", vertices[(i+1)%n].GetIndex(), "->", vertices[i].GetIndex(), "set up") + } + return vertices, runner +} + +func BuildSynchronizedRingWithOriginalTopology(n int) ([]Node, Synchronizer) { + vertices, runner := BuildRingWithOriginalTopology(n) + return vertices, asSynchronizer(runner) +} + func BuildSynchronizedRing(n int) ([]Node, Synchronizer) { vertices, runner := BuildRing(n) return vertices, asSynchronizer(runner) diff --git a/test/leader_directed_ring_test.go b/test/leader_directed_ring_test.go index fc31099..a70834d 100644 --- a/test/leader_directed_ring_test.go +++ b/test/leader_directed_ring_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/krzysztof-turowski/distributed-framework/leader/directed_ring/async_higham_przytycka" + "github.com/krzysztof-turowski/distributed-framework/leader/directed_ring/async_hirschberg_sinclair" "github.com/krzysztof-turowski/distributed-framework/leader/directed_ring/async_itah_rodeh" "github.com/krzysztof-turowski/distributed-framework/leader/directed_ring/sync_chang_roberts" "github.com/krzysztof-turowski/distributed-framework/leader/directed_ring/sync_dolev_klawe_rodeh" @@ -13,6 +14,11 @@ import ( "github.com/krzysztof-turowski/distributed-framework/leader/directed_ring/sync_peterson" ) +func TestDirectedRingAsyncHirschbergSinclair(t *testing.T) { + checkLogOutput() + async_hirschberg_sinclair.Run(1000) +} + func TestDirectedRingChangRoberts(t *testing.T) { checkLogOutput() sync_chang_roberts.Run(1000) diff --git a/test/leader_undirected_ring_test.go b/test/leader_undirected_ring_test.go index 605989f..8fee776 100644 --- a/test/leader_undirected_ring_test.go +++ b/test/leader_undirected_ring_test.go @@ -7,6 +7,7 @@ import ( "github.com/krzysztof-turowski/distributed-framework/leader/undirected_ring/async_franklin" "github.com/krzysztof-turowski/distributed-framework/leader/undirected_ring/async_hirschberg_sinclair" + "github.com/krzysztof-turowski/distributed-framework/leader/undirected_ring/async_hirschberg_sinclair_2" "github.com/krzysztof-turowski/distributed-framework/leader/undirected_ring/async_probabilistic_franklin" "github.com/krzysztof-turowski/distributed-framework/leader/undirected_ring/async_stages_with_feedback" "github.com/krzysztof-turowski/distributed-framework/leader/undirected_ring/sync_franklin" @@ -15,6 +16,12 @@ import ( "github.com/krzysztof-turowski/distributed-framework/lib" ) +func TestUndirectedRingAsyncHirschbergSinclair2(t *testing.T) { + checkLogOutput() + for n := 1; n < 100; n++ { + async_hirschberg_sinclair_2.Run(n) + } +} func TestUndirectedRingAsyncHirschbergSinclair(t *testing.T) { checkLogOutput() for n := 2; n <= 100; n++ { diff --git a/text/1184415/async_hirschberg_sinclair_2.pdf b/text/1184415/async_hirschberg_sinclair_2.pdf new file mode 100644 index 0000000..36504ca Binary files /dev/null and b/text/1184415/async_hirschberg_sinclair_2.pdf differ diff --git a/text/1184415/async_hirschberg_sinclair_2.tex b/text/1184415/async_hirschberg_sinclair_2.tex new file mode 100644 index 0000000..62544f1 --- /dev/null +++ b/text/1184415/async_hirschberg_sinclair_2.tex @@ -0,0 +1,223 @@ +\documentclass{article} + +\usepackage[english]{babel} +\usepackage{filecontents} +\usepackage[letterpaper,top=2cm,bottom=2cm,left=3cm,right=3cm,marginparwidth=1.75cm]{geometry} + +\usepackage{amsmath} +\usepackage{csquotes} +\usepackage{graphicx} +\usepackage{amsthm} +\usepackage{listings} +\usepackage{color} +\documentclass{minimal} +\usepackage{mathtools} +\DeclarePairedDelimiter\ceil{\lceil}{\rceil} +\DeclarePairedDelimiter\floor{\lfloor}{\rfloor} +\usepackage{amssymb} + + +\lstset{frame=tb, + language=Java, + aboveskip=3mm, + belowskip=3mm, + showstringspaces=false, + columns=flexible, + basicstyle={\small\ttfamily}, + numbers=none, + breaklines=true, + breakatwhitespace=true, + tabsize=3 +} + + +\usepackage[colorlinks=true, allcolors=blue]{hyperref} +\usepackage{natbib} +\usepackage{cleveref} +\newtheorem{lemma}{Lemma} +\newtheorem{theorem}{Theorem} +\newtheorem{corollary}[theorem]{Corollary} + +\begin{document} +\section*{Problem} +From n nodes we want to choose one that will be the leader of the rest. \\ +Conditions: +\begin{enumerate} + \item Each node don't know the number of members. + \item Unordered ring. + \item Synchronized rounds. + \item "left" processor may not mean the same to all processors, so their lists of neighbours can be not in logical order. +\end{enumerate} + +\section*{Overview} +This algorithm, provided by Hirschberg and Sinclair [1], is an efficient way two solw Leader problem, requiring +\(\mathcal{O}(n \log n)\) messages. +Previous successes belong to: +\begin{itemize} + \item LeLann [2] algorithm, wich requires $O(n^2)$ messages. + \item Chang and Roberts [3]: average - O(n log n), but worst case still \(\mathcal{O}(n^2)\) +\end{itemize} + + +\section*{Algorithm: Undirected Ring} +A naive algorithm, which achieves +\(\mathcal{O}(n^2)\), involves simply passing our +$Id$ in different directions. However, this quadratic time complexity can be reduced to \(\mathcal{O}(n)\) rounds. Our algorithm takes steps of length +$2^i$, during which the number of active processes is reduced. Those that fall within the range $2^i$ from a more possible candidate for the role of leader will become inactive, resulting in a significant reduction in the number of messages sent.\\ +"Must have" to be in head: +\begin{enumerate} + \item \(sendpass\) - sending message in same direction as recieved was going. + \item \(sendecho\) - returns message back to sender of recieved message. +\end{enumerate} + +Lets take a look on pseudocode from [1]. +\begin{lstlisting} +The Algorithm + +/// if after coming back process's messages it still has +/// "candidate" status, it means, that it is the biggest in +/// range 2^i around him +run_candidate: + status <--"candidate" + maxnum <-- 1 + WHILE status = "candidate" + sendboth ("from", myvalue, 0, maxnum) + await both replies (but react to other messages) + IF either reply is "no" THEN status <-- "lost" + maxnum <-- 2*maxnum + + +/// the function thanks to wich candidates eleminate number of possible canditates +/// and have abbility to get info, that they have won +receive_message ("from", value, num, maxnum): + IF value < myvalue + sendecho ("no", value) + IF value > myvalue + status <-- "lost" + num <- num + 1 + IF num < maxnum + sendpass ("from", value, num, maxnum) + ELSE + sendeeho ("ok", value) + IF value = myvalue + status <- "won" + + +///besides accepting his own message, which came back to him, +///the processor can play the role of a transit, inactive node +receive_message ("no", value) or ("ok", value) + IF value != myvalue + senpass the message + ELSE + this is a reply the processor was awaiting + +///one from me, just for simle and correct ending +///of this election, eleminating all possible unread messages +receive_message ("end", value): + inform next processor of end + stop process +\end{lstlisting} +The given pseudocode effectively reflects the algorithm's flow, as much, as it reduces the number of messages. + + +\section*{Correctness} +There is not much to say: +\begin{enumerate} + \item if process is "lost", then it is lesser than some other process, so it is OK, that it become inactive + \item if process is still "candidate", then it is bigger, than met by him. + \item if "from" message comes back to original sender - than it passed all the processes with success, so original sender is greater than each one of the rest. + \item when \(maxnum >= n\) then the algo would end, because somebody would cover all the processes by "from" message. + \item if process is "won", then it received message after it visited all the processes +\end{enumerate} + + +\section*{Proof of complexity} +\subsection*{The worst case} + Note that each active processor by the start of a round $i$(starting with 0) can initiate at most $4*2^i$ messages to be sent. \\ + Another observation gives us on start of round $i$: + \begin{equation} + \textbf{number of "candidate" processes} \leq \ceil*{\frac{n}{2^{i-1} + 1}} + \end{equation} + It follows from the fact, that if we separate all nodes on blocks of length $2^{i-1} + 1$, then after round $i-1$ only one of nodes(processes) in block could stay active.\\ + + \bigskip + \noindent + It all provides us to the fact that total number of sent messages is bounded from above by: + \begin{equation} + 4\cdot(\sum_0^{\ceil*{\log n}} 2^i \cdot \ceil*{\frac{n}{2^{i-1} + 1}}) + \quad\leq\quad 4\cdot(\sum_0^{\ceil{\log n}} 2n) \quad \approx \quad 8\, n \,\log n + \end{equation} + \subsection*{The "better can't imagine" case} + In case we would have instance of size $2^k$ which look like this: + \begin{equation} + \cdots p_{n-1} < p_0 > p_1 > p_2 \cdots + \end{equation} + After first round the is possibility, that could happen, that every one but the biggest process ($p_0$), would get "lost" status, what provide us to comlexity: + \begin{equation} + 4\cdot n + \sum_0^{\ceil*{\log n}} 2^i \cdot 1 \,\,= \,\, + 4\cdot n + \sum_0^{k} 2^i + \,\, <= \,\, + 4\cdot n + 2 \cdot n\,\, = \,\,\mathcal{O}(n) + \end{equation} +\bigskip + +\section*{Algorithm: Directed Ring} +To fit Hirschberg-Sinclair algorithm to directed ring we need to provide the ability to receive a response to initiated messages, because of which we will adhere to the method of "twisting" the cycle. \\ +We are using structure of undirected ring with unshuffled topology simulating directed ring by initiating sending messages only clockwise.\\ + +\begin{lstlisting} +The Algorithm + +The Pseudocode is the same as in "Algorithm: Undirected Ring" with only two differences. + +1. sendboth ("from", myvalue, 0, maxnum) is replaced with: +sendClockwise ("from", myvalue, 0, maxnum) +2. await both replies (but react to other messages) is replaced with: +await reply (but react to other messages) + +\end{lstlisting} + +\section*{Correctness} +The proof of correctness is the same as in "Algorithm: Undirected Ring". + + +\section*{Proof of complexity} +\subsection*{The worst case} + Note that each active processor by the start of a round $i$(starting with 0) can initiate process in which at most $2*2^i$ messages would be about to sent. \\ + Another observation gives us on start of round $i > 2$: + \begin{equation} + \textbf{number of "candidate" processes} \leq \ceil*{\frac{n}{2^{i-2} + 1}} + \end{equation} + It follows from the fact, that if we separate all nodes on blocks of length $2^{i-2} + 1$, then after round $i-1$ only one of nodes(processes) in block could stay active.\\ + + + \bigskip + \noindent + It all provides us to the fact that total number of sent messages is bounded from above by: + \begin{equation} + 2\cdot(\sum_0^{\ceil*{\log n}} 2^i \cdot \ceil*{\frac{n}{2^{i-2} + 1}}) + \quad \leq \quad + 4\cdot2\cdot(\sum_0^{\ceil*{\log n}} 2^i \cdot \ceil*{\frac{n}{2 \cdot(2^{i-2} + 1)}}) + \quad\leq\quad + 8\cdot(\sum_0^{\ceil*{\log n}} 2^i \cdot \ceil*{\frac{n}{2^{i-1} + 1}}) + \quad\leq\quad + 16\, n \,\log n + \end{equation} + Where the last transition comes from (2). + \subsection*{The best case} + Example and proof are exactly the same as in "Algorithm: Undirected Ring".\\ + So the number of sent messages is bounded from below by \mathcal{O}(n). +\bigskip + + + +\begin{thebibliography}{9} +\bibitem{article} +Hirschberg, D.S., Sinclair, J.B.: Decentralized extrema-finding in circular configurations of processes. Commun. ACM 23, 627–628 (1980) +\bibitem{article} +LeLann, G. Distributed systems--Towards a formal approach. Inform. Proc. 77, North-Holland Pub. Co., 1977, Amsterdam, pp. 155-160. +\bibitem{article} +Chang, E., and Roberts, R. An improved algorithm for decentralized extrema-fmding in circularly configuraiions of processes. Comm. ACM 22, 5 (May 1979), 281-283. +\end{thebibliography} + +\end{document} \ No newline at end of file