From 3d13d0a26866ac539a8e437f15586ab90a3a6667 Mon Sep 17 00:00:00 2001 From: Martin Mares <mj@ucw.cz> Date: Thu, 7 Mar 2019 19:07:38 +0100 Subject: [PATCH] Splay: Translated some parts from the Labyrinth book --- 02-splay/Makefile | 4 + 02-splay/log-avg.asy | 36 ++++++ 02-splay/splay-delete.asy | 68 +++++++++++ 02-splay/splay-pot.asy | 96 +++++++++++++++ 02-splay/splay-rotate.asy | 188 +++++++++++++++++++++++++++++ 02-splay/splay-steps.asy | 65 ++++++++++ 02-splay/splay.py | 183 ++++++++++++++++++++++++++++ 02-splay/splay.tex | 243 ++++++++++++++++++++++++++++++++++++++ pics/ads.asy | 184 +++++++++++++++++++++++++++++ pics/pdftex | 3 + pics/trees.asy | 192 ++++++++++++++++++++++++++++++ tex/adsmac.tex | 12 +- 12 files changed, 1266 insertions(+), 8 deletions(-) create mode 100644 02-splay/Makefile create mode 100644 02-splay/log-avg.asy create mode 100644 02-splay/splay-delete.asy create mode 100644 02-splay/splay-pot.asy create mode 100644 02-splay/splay-rotate.asy create mode 100644 02-splay/splay-steps.asy create mode 100755 02-splay/splay.py create mode 100644 02-splay/splay.tex create mode 100644 pics/ads.asy create mode 100755 pics/pdftex create mode 100644 pics/trees.asy diff --git a/02-splay/Makefile b/02-splay/Makefile new file mode 100644 index 0000000..076aebb --- /dev/null +++ b/02-splay/Makefile @@ -0,0 +1,4 @@ +TOP=.. +PICS=splay-rotate splay-steps splay-pot splay-delete log-avg + +include ../Makerules diff --git a/02-splay/log-avg.asy b/02-splay/log-avg.asy new file mode 100644 index 0000000..6006476 --- /dev/null +++ b/02-splay/log-avg.asy @@ -0,0 +1,36 @@ +import ads; +import graph; + +real aspect = 0.5; +size(100mm, 100mm*aspect, IgnoreAspect); + +real f(real x) { return log(x) / log(2); } + +xaxis("$x$", xmin=0, xmax=20); +yaxis("$y$", ymin=0, ymax=5); +draw(graph(f, 1, 20)); + +real a = 1.5; +real b = 18; +real c = (a+b)/2; +pair aa = (a, f(a)); +pair bb = (b, f(b)); +pair cc = (aa + bb)/2; +pair dd = (c, f(c)); + +void pt(pair x) +{ + fill(shift(x) * scale(0.1, 0.1*aspect) * unitcircle); +} + +pt(aa); +pt(bb); +pt(cc); +pt(dd); +draw(aa -- bb, halfthick); +draw((c,0) -- (c,f(c)+1), dotted); + +label("$A=(\alpha,f(\alpha))$", aa, SE); +label("$B=(\beta,f(\beta))$", bb, 2*dir(-55)); +label("$S'=\left({\alpha+\beta\over 2},f\!\left({\alpha+\beta\over 2}\right)\right)$", dd, 3*dir(140)); +label("$S=\left({\alpha+\beta\over 2},{f(\alpha)+f(\beta)\over 2}\right)$", cc, 2*dir(-35)); diff --git a/02-splay/splay-delete.asy b/02-splay/splay-delete.asy new file mode 100644 index 0000000..36b5380 --- /dev/null +++ b/02-splay/splay-delete.asy @@ -0,0 +1,68 @@ +import ads; +import trees; + +void t(int parent_id, real rangle, int rsize, int key) +{ + tnode v = tn_add(parent_id, rangle); + v.draw_vertex = new void (tnode w) { + vertex(w.id, mode=(key == 2 ? v_lightgray : v_white)); + label("\eightrm " + (string) key, w.pos); + }; +} + +void d(string op, pair pos) +{ + label("$\hbox{\sevenrm " + op + "}\atop\longrightarrow$", pos); +} + +tree_node_size = 0.15; +tn_edge_len = 0.7; + +tn_init((0, 0)); +t(-1,0,7,5); // 0 +t(0,-20,5,0); // 1 +t(1,10,4,2); // 2 +t(2,-20,1,1); // 3 +t(2,20,2,4); // 4 +t(4,-10,1,3); // 5 +t(0,20,1,6); // 6 +tn_draw(); + +label("$\longrightarrow$", (1,-1)); + +tn_init((2.5,0)); +t(-1,0,7,2); // 0 +t(0,-40,2,0); // 1 +t(1,10,1,1); // 2 +t(0,40,4,5); // 3 +t(3,-20,2,4); // 4 +t(4,-10,1,3); // 5 +t(3,20,1,6); // 6 +tn_draw(); + +label("$\longrightarrow$", (4,-1)); + +tn_init((4.8,-tn_edge_len)); +t(-1,0,2,0); // 0 +t(0,10,1,1); // 1 +tn_draw(); + +tn_init((6,-tn_edge_len)); +t(-1,0,4,5); // 0 +t(0,-20,2,4); // 1 +t(1,-10,1,3); // 2 +t(0,20,1,6); // 3 +tn_draw(); + +label("$\longrightarrow$", (7,-1)); + +tn_init((8.5,0)); +t(-1,0,2,1); // 0 +t(0,-40,1,0); // 1 +t(0,40,4,5); // 2 +t(2,-20,2,4); // 3 +t(3,-10,1,3); // 4 +t(2,20,1,6); // 5 +tnodes[2].parent = null; +draw(tn_pos[0] -- tn_pos[2], thick); +tn_draw(); diff --git a/02-splay/splay-pot.asy b/02-splay/splay-pot.asy new file mode 100644 index 0000000..cce84d7 --- /dev/null +++ b/02-splay/splay-pot.asy @@ -0,0 +1,96 @@ +import ads; +import trees; + +pair s = (2.5, 0); +tree_node_size = 0.15; +tn_edge_len = 0.7; +int goal; + +void t(int parent_id, real rangle, int rsize, int key) +{ + tnode v = tn_add(parent_id, rangle); + v.draw_vertex = new void (tnode w) { + vertex(w.id, mode=(key == goal ? v_lightgray : v_white)); + label("\eightrm " + (string) key, w.pos); + }; +} + +void phi(real p) +{ + label("\eightrm " + (string) p, tn_ref + (0,0.6)); +} + +tn_init(); +goal = 9; +t(-1,0,10,0); // 0 +t(0,10,9,1); // 1 +t(1,10,8,2); // 2 +t(2,10,7,3); // 3 +t(3,10,6,4); // 4 +t(4,10,5,5); // 5 +t(5,10,4,6); // 6 +t(6,10,3,7); // 7 +t(7,10,2,8); // 8 +t(8,10,1,9); // 9 +tn_draw(); +phi(21.791); + +tn_init(s); +goal = 0; +t(-1,0,10,9); // 0 +t(0,-20,9,0); // 1 +t(1,10,8,2); // 2 +t(2,-20,1,1); // 3 +t(2,20,6,4); // 4 +t(4,-20,1,3); // 5 +t(4,20,4,6); // 6 +t(6,-20,1,5); // 7 +t(6,20,2,8); // 8 +t(8,-10,1,7); // 9 +tn_draw(); +phi(15.077); + +tn_init(1.8*s); +goal = 7; +t(-1,0,10,0); // 0 +t(0,20,9,9); // 1 +t(1,-20,8,2); // 2 +t(2,-20,1,1); // 3 +t(2,20,6,4); // 4 +t(4,-20,1,3); // 5 +t(4,20,4,6); // 6 +t(6,-20,1,5); // 7 +t(6,20,2,8); // 8 +t(8,-20,1,7); // 9 +tn_draw(); +phi(15.077); + +tn_init(3*s); +goal = 3; +t(-1,0,10,7); // 0 +t(0,-40,7,0); // 1 +t(1,10,6,4); // 2 +t(2,-30,3,2); // 3 +t(3,-15,1,1); // 4 +t(3,15,1,3); // 5 +t(2,30,2,6); // 6 +t(6,-10,1,5); // 7 +t(0,40,2,9); // 8 +t(8,-10,1,8); // 9 +tn_draw(); +phi(12.299); + +tn_init(4*s); +goal = -1; +t(-1,0,10,3); // 0 +t(0,-40,3,0); // 1 +t(1,10,2,2); // 2 +t(2,-10,1,1); // 3 +t(0,40,6,7); // 4 +t(4,-30,3,4); // 5 +t(5,10,2,6); // 6 +t(6,-10,1,5); // 7 +t(4,30,2,9); // 8 +t(8,-10,1,8); // 9 +tn_draw(); +phi(12.077); diff --git a/02-splay/splay-rotate.asy b/02-splay/splay-rotate.asy new file mode 100644 index 0000000..d5f4777 --- /dev/null +++ b/02-splay/splay-rotate.asy @@ -0,0 +1,188 @@ +import ads; +import trees; + +pair e = bst_edge; +pair le = rotate(-45) * e; +pair re = rotate(45) * e; +pair le1 = rotate(-60) * e * 1.2; +pair re1 = rotate(60) * e * 1.2; +pair le2 = rotate(-45) * e * 0.7; +pair re2 = rotate(45) * e * 0.7; + +real ux = 0; +real vx = 5.5; +real ly = 0; +real lly = -3.2; +real lry = -6.8; +real arrlen = 0.5; +real arrx = (ux+vx)/2; +real arry = -0.5; + +/*** L: předtím ***/ + +pair lu[]; +lu[0] = (ux, ly); +lu[1] = lu[0] + le; +lu[2] = lu[0] + re; +lu[3] = lu[1] + le2; +lu[4] = lu[1] + re2; +lu[5] = lu[0] + bst_mini; + +tree_init(lu); +tree_edge(0, 1, thick); +tree_edge(0, 2); +tree_edge(0, 5); +tree_edge(1, 3); +tree_edge(1, 4); +tree_node(0, "y"); +tree_node(1, "x"); +tree_sub(2, "C"); +tree_sub(3, "A"); +tree_sub(4, "B"); + +/*** L: potom ***/ + +pair lv[]; +lv[0] = (vx, ly); +lv[1] = lv[0] + re; +lv[2] = lv[0] + le; +lv[3] = lv[1] + le2; +lv[4] = lv[1] + re2; +lv[5] = lv[0] + bst_mini; + +tree_init(lv); +tree_edge(0, 1, thick); +tree_edge(0, 2); +tree_edge(0, 5); +tree_edge(1, 3); +tree_edge(1, 4); +tree_node(0, "x"); +tree_node(1, "y"); +tree_sub(2, "A"); +tree_sub(3, "B"); +tree_sub(4, "C"); + +/*** LL: předtím ***/ + +pair llu[]; +llu[0] = (ux, lly); // z +llu[1] = llu[0] + le1; // y +llu[2] = llu[1] + le; // x +llu[3] = llu[0] + re1; // D +llu[4] = llu[1] + re; // C +llu[5] = llu[2] + le2; // A +llu[6] = llu[2] + re2; // B +llu[7] = llu[0] + bst_mini; + +tree_init(llu); +tree_edge(0, 1, thick); +tree_edge(1, 2, thick); +tree_edge(0, 3); +tree_edge(0, 7); +tree_edge(1, 4); +tree_edge(2, 5); +tree_edge(2, 6); +tree_node(0, "z"); +tree_node(1, "y"); +tree_node(2, "x"); +tree_sub(3, "D"); +tree_sub(4, "C"); +tree_sub(5, "A"); +tree_sub(6, "B"); + +/*** LL: poté ***/ + +pair llv[]; +llv[0] = (vx, lly); // x +llv[1] = llv[0] + re1; // y +llv[2] = llv[1] + re; // z +llv[3] = llv[0] + le1; // A +llv[4] = llv[1] + le; // B +llv[5] = llv[2] + le2; // C +llv[6] = llv[2] + re2; // D +llv[7] = llv[0] + bst_mini; + +tree_init(llv); +tree_edge(0, 1, thick); +tree_edge(1, 2, thick); +tree_edge(0, 7); +tree_edge(0, 3); +tree_edge(1, 4); +tree_edge(2, 5); +tree_edge(2, 6); +tree_node(0, "x"); +tree_node(1, "y"); +tree_node(2, "z"); +tree_sub(3, "A"); +tree_sub(4, "B"); +tree_sub(5, "C"); +tree_sub(6, "D"); + +/*** LP: předtím ***/ + +pair lru[]; +lru[0] = (ux, lry); // y +lru[1] = lru[0] + le1; // w +lru[2] = lru[1] + re; // x +lru[3] = lru[0] + re1; // D +lru[4] = lru[1] + le; // A +lru[5] = lru[2] + le2; // B +lru[6] = lru[2] + re2; // C +lru[7] = lru[0] + bst_mini; + +tree_init(lru); +tree_edge(0, 1, thick); +tree_edge(1, 2, thick); +tree_edge(0, 3); +tree_edge(0, 7); +tree_edge(1, 4); +tree_edge(2, 5); +tree_edge(2, 6); +tree_node(0, "y"); +tree_node(1, "w"); +tree_node(2, "x"); +tree_sub(3, "D"); +tree_sub(4, "A"); +tree_sub(5, "B"); +tree_sub(6, "C"); + +/*** LP: poté ***/ + +pair lrv[]; +lrv[0] = (vx+0.2, lry); // x +lrv[1] = lrv[0] + le1; // w +lrv[2] = lrv[0] + re1; // y +lrv[3] = lrv[1] + le2; // A +lrv[4] = lrv[1] + re2; // B +lrv[5] = lrv[2] + le2; // C +lrv[6] = lrv[2] + re2; // D +lrv[7] = lrv[0] + bst_mini; + +tree_init(lrv); +tree_edge(0, 1, thick); +tree_edge(0, 2, thick); +tree_edge(0, 7); +tree_edge(1, 3); +tree_edge(1, 4); +tree_edge(2, 5); +tree_edge(2, 6); +tree_node(0, "x"); +tree_node(1, "w"); +tree_node(2, "y"); +tree_sub(3, "A"); +tree_sub(4, "B"); +tree_sub(5, "C"); +tree_sub(6, "D"); + +/*** Šipky ***/ + +void arr(real y, string lab) +{ + y += arry; + draw((arrx-arrlen, y) -- (arrx+arrlen, y), Arrow(size=6)); + label("\bf " + lab, (arrx, y), 1.5N); +} + +arr(ly, "zig"); +arr(lly, "zig-zig"); +arr(lry, "zig-zag"); diff --git a/02-splay/splay-steps.asy b/02-splay/splay-steps.asy new file mode 100644 index 0000000..b869afa --- /dev/null +++ b/02-splay/splay-steps.asy @@ -0,0 +1,65 @@ +import ads; +import trees; + +void t(int parent_id, real rangle, int rsize, int key) +{ + tnode v = tn_add(parent_id, rangle); + v.draw_vertex = new void (tnode w) { + vertex(w.id, mode=(key == 5 ? v_lightgray : v_white)); + label("\eightrm " + (string) key, w.pos); + }; +} + +void d(string op, pair pos) +{ + label("$\hbox{\sevenrm " + op + "}\atop\longrightarrow$", pos); +} + +tree_node_size = 0.17; +tn_edge_len = 0.72; + +tn_init(); +t(-1,0,7,0); // 0 +t(0,10,6,1); // 1 +t(1,10,5,2); // 2 +t(2,10,4,3); // 3 +t(3,10,3,4); // 4 +t(4,10,2,5); // 5 +t(5,10,1,6); // 6 +tn_draw(); + +d("zig-zig", (1.25, -1)); + +tn_init((2.5, 0)); +t(-1,0,7,0); // 0 +t(0,10,6,1); // 1 +t(1,10,5,2); // 2 +t(2,10,4,5); // 3 +t(3,-20,2,4); // 4 +t(4,-10,1,3); // 5 +t(3,20,1,6); // 6 +tn_draw(); + +d("zig-zig", (3.75, -1)); + +tn_init((5, 0)); +t(-1,0,7,0); // 0 +t(0,10,6,5); // 1 +t(1,-20,4,2); // 2 +t(2,-20,1,1); // 3 +t(2,20,2,4); // 4 +t(4,-10,1,3); // 5 +t(1,20,1,6); // 6 +tn_draw(); + +d("zig", (6.25, -1)); + +tn_init((7.5, 0)); +t(-1,0,7,5); // 0 +t(0,-20,5,0); // 1 +t(1,10,4,2); // 2 +t(2,-20,1,1); // 3 +t(2,20,2,4); // 4 +t(4,-10,1,3); // 5 +t(0,20,1,6); // 6 +tn_draw(); diff --git a/02-splay/splay.py b/02-splay/splay.py new file mode 100755 index 0000000..5542420 --- /dev/null +++ b/02-splay/splay.py @@ -0,0 +1,183 @@ +#!/usr/bin/python3 + +import math +import sys + +class Node: + + def __init__(self, key, l=None, r=None, p=None): + self.key = key + self.l = l + self.r = r + self.p = p + if l != None: + l.p = self + if r != None: + r.p = self + + def find(x, k): + if x == None or x.key == k: + return x + if k < x.key: + return Node.find(x.l, k) + else: + return Node.find(x.r, k) + + def insert(x, k, p=None): + if x == None: + return Node(k, None, None, p) + elif k < x.key: + x.l = Node.insert(x.l, k, x) + else: + x.r = Node.insert(x.r, k, x) + return x + + def rr(y): + x = y.l + b = x.r + x.r = y + y.l = b + p = y.p + if p != None: + if p.l == y: + p.l = x + else: + p.r = x + x.p = p + y.p = x + if b != None: + b.p = y + + def rl(x): + y = x.r + b = y.l + y.l = x + x.r = b + p = x.p + if p != None: + if p.r == x: + p.r = y + else: + p.l = y + y.p = p + x.p = y + if b != None: + b.p = x + + def splay_step(x): + p = x.p + pp = p.p + if pp == None: + if x == p.l: + p.rr() + else: + p.rl() + else: + if p == pp.l: + if x == p.l: + pp.rr() + p.rr() + else: + p.rl() + pp.rr() + else: + if x == p.l: + p.rr() + pp.rl() + else: + pp.rl() + p.rl() + + def splay(x): + if x == None or x.p == None: + return x + x.splay_step() + return x.splay() + + def dump(x, depth=1, parent=None): + if x == None: + return + if x.p != parent: + print("### BAD PARENT LINK ###") + Node.dump(x.r, depth+1, x) + print(" " * depth + str(x.key) + " [" + str(x.size) + "]") + Node.dump(x.l, depth+1, x) + + def sizes(x): + s = 1 + if x.l != None: + x.l.sizes() + s += x.l.size + if x.r != None: + x.r.sizes() + s += x.r.size + x.size = s + x.rank = math.log(s, 2) + + def phi(x): + if x == None: + return 0 + else: + return x.rank + Node.phi(x.l) + Node.phi(x.r) + + def export(x, id=0, parent=-1, ang=0): + if x == None: + return id + print("t({0:d},{1:d},{2:d},{3:d}); // {4:d}".format(parent, ang, x.size, x.key, id)) + pid = id + id += 1 + id = Node.export(x.l, id, pid, -10) + id = Node.export(x.r, id, pid, 10) + return id + + def show(x): + x.sizes() + x.dump() + print() + print("tn_init();") + x.export() + print("tn_draw();") + print("phi({0:.3f});".format(x.phi())) + print("", "-" * 60) + +mode = "" +if len(sys.argv) > 1: + mode = sys.argv[1] + +if mode == "steps": + r = None + for i in range(0, 7): + r = Node.insert(r, i) + five = r.find(5) + r.show() + five.splay_step() + r.show() + five.splay_step() + r.show() + five.splay_step() + five.show() +elif mode == "delete": + r = None + r = Node.insert(r, 5) + r = Node.insert(r, 0) + r = Node.insert(r, 6) + r = Node.insert(r, 2) + r = Node.insert(r, 1) + r = Node.insert(r, 4) + r = Node.insert(r, 3) + r.show() + r = r.find(2).splay() + r.show() +else: + r = None + for i in range(0, 10): + r = Node.insert(r, i) + r.show() + r = r.find(9).splay() + r.show() + r = r.find(0).splay() + r.show() + r = r.find(7).splay() + r.show() + r = r.find(3).splay() + r.show() diff --git a/02-splay/splay.tex b/02-splay/splay.tex new file mode 100644 index 0000000..9299b43 --- /dev/null +++ b/02-splay/splay.tex @@ -0,0 +1,243 @@ +\ifx\chapter\undefined +\input adsmac.tex +\singlechapter{2} +\fi + +\chapter[splay]{Splay trees} + +In this chapter, we will present self-adjusting binary search trees called the Splay trees. +They were discovered in 1983 by Daniel Sleator and Robert Tarjan. They are based on a~very +simple idea: whenever we access a~node, we bring it to the root by a~sequence of rotations. +Surprisingly, this is enough to guarantee amortized $\O(\log n)$ cost of all operations. +In cases when the items are accessed with uneven probabilities, the Splay trees will turn out +to be even superior to ordinary balanced trees. + +\section{Splaying} + +Let us consider an~arbitrary binary tree. We define the operation $\opdf{Splay}(x)$, +which brings the node~$x$ to the root using rotations. This can be obviously done by repeatedly +rotating the edge above~$x$ until $x$~becomes the root, but this does not lead to good amortized +complexity (see exercise~\exref{splaynaive}). + +The trick is to prefer double rotations as shown in figure~\figref{splayrot}. +If~$x$ is a~left child of a~left child (or symetrically a~right child of a~right child), +we perform the \em{zig-zig} step. If~$x$ is a~right child of a~left child (or vice versa), +we do the \em{zig-zag} step. When $x$ finally becomes a~child of the root, we perform +a~single rotation --- the \em{zig} step. + +\figure[splayrot]{splay-rotate.pdf}{}{Three types of splay steps} + +We can see the process of splaying in figure~\figref{splaysteps}: First we perform +a~zig-zig, then again a~zig-zig, and finally a~single zig. You can see that splaying +tends to reform long paths to branched trees. This gives us hope that randomly appearing +degenerate subtrees will not stay for long. + +\figure[splaysteps]{splay-steps.pdf}{}{Steps of splaying the node~5} + +Our amortized analysis will be based on a~cleverly chosen potential. It might look +magical, as if Sleator and Tarjan pulled it out of a~magician's hat. But once we define +the potential, the rest of the analysis becomes straightforward. + +\nota{ +\tightlist{o} +\:$T(v)$ denotes the subtree rooted at the node~$v$. +\:The \em{size} $s(v)$ is the cardinality of the subtree $T(v)$. +\:The \em{rank} $r(v)$ is the binary logarithm of~$s(v)$. +\:The \em{potential} of the splay tree is the sum of ranks of all nodes. +\endlist +} + +\figure[splaypot]{splay-pot.pdf}{}{Evolution of potential during splays of nodes 9, 0, 7, 3} + +Figure~\figref{splaypot} suggests that higher potentials correspond to less +balanced trees. By repeated splaying, the path gradually becomes a~branching tree. +The potential keeps decreasing; it decreases faster during expensive splays. + +We are going to prove that this holds in general. We will measure the cost of splaying +in the number of rotations performed (so a~zig-zig or zig-zag counts as two rotations). +Real time complexity is obviously linear in this cost. + +\theorem{ +Amortized cost of $\alg{Splay}(x)$ is at most $3\cdot(r'(x) - r(x)) + 1$, +where $r(x)$ is the rank of the node~$x$ before the operation and $r'(x)$ the +rank after it. +} + +\proof +The amortized cost of the full \alg{Splay} is a~sum of amortized costs of the individual steps. +Let $r_1(x),\ldots,r_t(x)$ denote the rank of~$x$ after each step and $r_0(x)$ the rank +before the first step. + +We are going to prove several lemmas, which guarantee that the amortized cost of the $i$-th step is bounded +by $3r_i(x) - 3r_{i-1}(x)$. The sole exception is the final zig step, whose cost can be greater by~1. +The total amortized cost is therefore +$$ +A \le \sum_{i=1}^t \bigl(3r_i(x) - 3r_{i-1}(x) \bigr) + 1. +$$ +This is a~telescopic sum: each rank except $r_0$ and~$r_t$ is participates once positively +and once negatively. Therefore the right-hand side is equal to $3r_t(x) - 3r_0(x) + 1$ +as claimed by the theorem. +\qed + +\corr{ +As all ranks are logarithmic, the amortized cost of \alg{Splay} is $\O(\log n)$. +} + +Before we get to analysis of the individual steps, we prove a~simple lemma bounding +a~mean of logarithms. + +\lemman{mean of logarithms}{ +For any two positive real numbers $\alpha,\beta$ we have: +$$ + \log{\alpha+\beta\over 2} \ge {\log \alpha + \log \beta \over 2}. +$$ +\unparskip +} + +\proof +The inequality holds not only for logarithm, but for an~arbitrary \em{concave} function~$f$. +These are the functions whose graphs lie above every line segment connecting two points on the graph. +The natural logarithm is concave, because its second derivative is negative. +The binary logarithm is a~constant multiple of the natural logarithm, so it must +be concave, too. + +Let us consider graph of a~concave function~$f$ in figure \figref{logavg}. +We mark points $A=(\alpha,f(\alpha))$ and $B=(\beta,f(\beta))$. We find the +midpoint~$S$ of the segment~$AB$. Its coordinates are the averages of coordinates +of the endpoints $A$ and~$B$: +$$ S = \left({\alpha+\beta\over 2},\, {f(\alpha) + f(\beta)\over 2}\right). $$ +By concavity, the point~$S$ must lie below the graph, so especially below the point +$$ S' = \left({\alpha+\beta\over 2},\, f\!\left( {\alpha+\beta\over 2} \right) \right). $$ +Comparison of~$y$-coordinates of the points $S$ and~$S'$ yileds the claimed inequality. +\qed + +\figure[logavg]{log-avg.pdf}{}{The mean inequality for a~concave function~$f$} + +\corr{ +As $\log{\alpha+\beta\over 2} = \log(\alpha+\beta) - 1$, +we also have $\log \alpha + \log \beta \le 2\log(\alpha+\beta) - 2$. +} + +\note{ +The lemma also follows by taking logarithms of both sides of the +Arithmetic Mean -- Geometric Mean inequality +$ \sqrt{\alpha\beta} \le (\alpha + \beta) / 2 $. +} + +Now we calculate amortized costs of all three types of splay steps. +In each case, $x$~is the vertex being splayed, $r(x)$ is its rank +before the step, and $r'(x)$ its rank after the step. We will use +this the same convention for $s(\cdot)$ and $r(\cdot)$. + +\lemman{zig-zag}{ +The amortized cost of a~zig-zag step is at most $3r'(x) - 3r(x)$. +} + +\proof +Let us follow figure~\figref{splayrot} and consider, how the potential changes +during the step. The only nodes whose ranks can change are $w$, $x$, and~$y$. +Thus the potential increases by $(r'(w)-r(w)) + (r'(x)-r(x)) + (r'(y)-r(y))$. +The real cost of the operation is~2, so the amortized cost becomes: +$$ + A = 2 + r'(w) + r'(x) + r'(y) - r(w) - r(x) - r(y). +$$ +We want to prove that $A \le 3r'(x) - 3r(x)$. We therefore need to bound +all other ranks using $r(x)$ and $r'(x)$. + +We invoke our averaging lemma on the sum $r'(w) + r'(y)$: +$$\eqalign{ + r'(w) + r'(y) + &= \log m'(w) + \log m'(y) \cr + &\le 2\log (m'(w) + m'(y)) - 2. +}$$ +The subtrees $T'(w)$ and $T'(y)$ are disjoint and they lie below~$x$, +so we have $\log(m'(w) + m'(y)) \le \log m'(x) = r'(x)$. Thus: +$$ + r'(w) + r'(y) \le 2r'(x) - 2. +$$ +Substituting this to the inequality for~$A$ yields: +$$ + A \le 3r'(x) - r(w) - r(x) - r(y). +$$ +The other ranks can be bounded trivially: +$$\vbox{\halign{\hfil $#$&${}#$\hfil\quad&because $#$\hfil\cr + r(w) &\ge r(x) &T(w) \supseteq T(x), \cr + r(y) &\ge r(x) &T(y) \supseteq T(x). \cr +}}$$ +Here goes the claim of the lemma. +\qed + +\lemman{zig-zig}{ +The amortized cost of a~zig-zig step is at most $3r'(x) - 3r(x)$. +} + +\proof +We will follow the same idea as for the zig-zag step. Again, the real cost is~2. +Ranks can change only at vertices $x$, $y$, and~$z$, so the amortized cost becomes: +$$ + A = 2 + r'(x) + r'(y) + r'(z) - r(x) - r(y) - r(z). +$$ +We want to get rid of all terms except $r(x)$ a~$r'(x)$. +To achieve this, we would like to invoke the averaging lemma on a~pair of subtrees, +which are disjoint and their union contains almost all nodes. One such pair is +$T(x)$ and $T'(z)$: +$$\eqalign{ + r(x) + r'(z) + &= \log m(x) + \log m'(z) \cr + &\le 2\log (m(x)+m'(z)) - 2 \cr + &\le 2\log m'(x) - 2 = 2r'(x) - 2. \cr +}$$ +This is equivalent to the inequality $r'(z) \le 2r'(x) - r(x) - 2$. Thus: +$$ + A \le 3r'(x) + r'(y) - 2r(x) - r(y) - r(z). +$$ +All other terms can be bounded trivially: +$$\vbox{\halign{\hfil $#$&${}#$\hfil\quad&because $#$\hfil\cr + r(z) &= r'(x) &T(z) = T'(x), \cr + r(y) &\ge r(x) &T(y) \supseteq T(x), \cr + r'(y) &\le r'(x) &T'(y) \subseteq T'(x). \cr +}}$$ +The claim $A \le 3r'(x) - 3r(x)$ follows. +\qed + +\lemman{zig}{ +The amortized cost of a~zig is at most $3r'(x) - 3r(x) + 1$. +} + +\proof +The real cost is~1, ranks can change only at vertices $x$ and~$y$, +so the amortized cost becomes: +$$ + A = 1 + r'(x) + r'(y) - r(x) - r(y). +$$ +By inclusion of subtrees, we have $r'(y) \le r'(x)$ a~$r(y) \ge r(x)$, hence: +$$ + A \le 1 + 2r'(x) - 2r(x). +$$ +It is also clear from inclusion that $r'(x) - r(x)$ must be non-negative, +so we also have $A \le 1 + 3r'(x) - 3r(x)$ as we wanted. +\qed + +This completes the proof of the previous theorem. + +\theorem{ +A~sequence of $m$~\alg{Splay}s on an~$n$-node binary tree runs in time +$\O((n+m)\log n)$. +} + +\proof +{\bf TODO!} +\qed + +\exercises + +\ex[splaynaive]{Prove that a~naive splay strategy using only single rotations cannot have +better amortized complexity than $\Omega(n)$. Consider what happens when splaying a~path.} + +\endexercises + +\section{Search tree operations} + +\section{Weighted analysis} + +\endchapter diff --git a/pics/ads.asy b/pics/ads.asy new file mode 100644 index 0000000..fe9b8f9 --- /dev/null +++ b/pics/ads.asy @@ -0,0 +1,184 @@ +/* + * Makra pro Asymptote společná pro všechny obrázky + */ + +void ads_init_pic() { + unitsize(1cm); +} + +ads_init_pic(); + +/*** Základní typy čar a velikosti ***/ + +pen thin = black + roundcap + 0.2mm; +pen hair = black + roundcap + 0.1mm; +pen quarterthick = thin + 0.2mm; +pen halfthick = thin + 0.4mm; +pen almostthick = thin + 0.5mm; +pen thick = thin + 0.6mm; +defaultpen(thin); + +pen shortdashed = linetype(new real[] { 1, 4 }); +pen middashed = linetype(new real[] { 4, 4 }); +pen finedotted = thin + 0.3mm + linetype(new real[] { 0, 2.5 }); +pen agray = gray(0.6); +pen area_gray = gray(0.75); + +// Šířka čáry v uživatelských souřadnicích +real line_width_user(pen p) +{ + pair lw = inverse(currentpicture.scaling()) * (0, linewidth(p)); + return lw.y; +} + +texpreamble("\font\eightrm=cmr8\font\ninerm=cmr9\font\seventt=cmtt8 at 7pt"); + +/*** Kreslení grafů ***/ + +real vertex_size = 0.06; +arrowbar e_arrow = Arrow(size=7); +arrowbar e_biarrow = Arrows(size=7); +arrowbar e_smallarrow = Arrow(size=5); +arrowbar e_smallbiarrow = Arrows(size=5); +arrowbar e_miniarrow = Arrow(size=3); + +int v_invisible = -1; +int v_black = 0; +int v_white = 1; +int v_gray = 2; +int v_dashed = 3; +int v_lightgray = 4; +int v_bold = 5; +int v_shadow = 6; + +void draw_in_mode(path p, int mode=0) +{ + if (mode == v_black) + filldraw(p); + else if (mode == v_white) + filldraw(p, white, black); + else if (mode == v_gray) + filldraw(p, agray, black); + else if (mode == v_dashed) + filldraw(p, white, black + shortdashed); + else if (mode == v_lightgray) + filldraw(p, area_gray, black); + else if (mode == v_bold) + filldraw(p, white, black + thick); + else if (mode == v_shadow) + filldraw(p, white, agray); +} + +void vertex(pair p, int mode=0) +{ + if (p.x > 100 || p.y > 100) { + return; + } + draw_in_mode(circle(p, vertex_size), mode); +} + +pair current_graph[]; + +void graph(pair g[], int mode=0) +{ + current_graph = g; + for (pair p: g) { + vertex(p, mode); + } +} + +void labeled_graph(pair g[]) +{ + vertex_size = 0.17; + graph(g, 1); + for (int i=0; i<g.length; ++i) { + if (g[i].x < 100 && g[i].y < 100) { + label("\eightrm " + (string) i, g[i]); + } + } +} + +void vertex(int v, int mode=0) +{ + vertex(current_graph[v], mode); +} + +void edge(pair v, pair w, pen p = thin) +{ + pair d = unit(w-v); + draw((v+d*vertex_size) -- (w-d*vertex_size), p + squarecap); +} + +void edge(int v, int w, pen p = thin) +{ + edge(current_graph[v], current_graph[w], p); +} + +void edged(pair v, pair w, pair dv, pair dw, pen p = thin) +{ + draw(v + vertex_size*dv {dv} .. {dw} w - vertex_size*dw, p + squarecap); +} + +void edged(int v, int w, pair dv, pair dw, pen p = thin) +{ + edged(current_graph[v], current_graph[w], dv, dw, p); +} + +void arc(pair v, pair w, pen p = thin, arrowbar a = e_arrow) +{ + pair d = unit(w-v); + draw((v+d*vertex_size) -- (w-d*vertex_size), p + squarecap, a); +} + +void arc(int v, int w, pen p = thin, arrowbar a = e_arrow) +{ + arc(current_graph[v], current_graph[w], p, a); +} + +void arcd(pair v, pair w, pair dv, pair dw, pen p = thin, arrowbar a = e_arrow) +{ + draw(v + vertex_size*dv {dv} .. {dw} w - vertex_size*dw, p + squarecap, a); +} + +void arcd(int v, int w, pair dv, pair dw, pen p = thin, arrowbar a = e_arrow) +{ + arcd(current_graph[v], current_graph[w], dv, dw, p, a); +} + +void selfloop(pair v, int d, pen p = thin) +{ + draw(v + vertex_size*dir(d+45) {dir(d+45)} .. v + 3*vertex_size*dir(d) .. {-dir(d-45)} v + vertex_size*dir(d-45), p + squarecap, e_arrow); +} + +void selfloop(int v, int d, pen p = thin) +{ + selfloop(current_graph[v], d, p); +} + +/*** Geometrie ***/ + +void fpoint(pair p) +{ + fill(circle(p, 0.08)); +} + +void epoint(pair p) +{ + filldraw(circle(p, 0.08), white, black); +} + +/*** Dílčí obrázky ***/ + +picture ads_sub_pic() +{ + picture p = new picture; + currentpicture = p; + ads_init_pic(); + return p; +} + +frame centered(picture p) +{ + frame f = p.fit(); + return align(f, (0,0)); +} diff --git a/pics/pdftex b/pics/pdftex new file mode 100755 index 0000000..18c7b2b --- /dev/null +++ b/pics/pdftex @@ -0,0 +1,3 @@ +#!/bin/bash +# pdftex wrapper to make Asymptote use csplain +exec /usr/bin/pdftex -output-format pdf -fmt pdfcsplain "$@" diff --git a/pics/trees.asy b/pics/trees.asy new file mode 100644 index 0000000..d04b958 --- /dev/null +++ b/pics/trees.asy @@ -0,0 +1,192 @@ +/* Společné funkce pro kreslení stromů všeho druhu */ + +import ads; + +pair bst_edge = (0, -1); +pair bst_mini = (0, 0.5); + +real tree_node_size = 0.2; +real tree_sub_size = 0.23; +real tree_sub_aspect = 1; +pair tree_label_offset = (0,0); + +void tree_init(pair g[]) +{ + current_graph = g; + vertex_size = tree_node_size; +} + +void tree_node(int i, string name = "", int mode=1) +{ + vertex(i, mode); + label("\strut $" + name + "$", current_graph[i] + tree_label_offset); +} + +void tree_num(int i, int x, int mode=1) +{ + vertex(i, mode); + label("$" + (string) x + "$", current_graph[i] + tree_label_offset); +} + +void tree_num_inverse(int i, int x) +{ + vertex(i, 0); + label("$" + (string) x + "$", current_graph[i] + tree_label_offset, white); +} + +void tree_ext(int i, real size=0.5, int mode=1) +{ + pair p = current_graph[i]; + path v = shift(p - size*(vertex_size, vertex_size)) * scale(size*2*vertex_size) * unitsquare; + draw_in_mode(v, mode); +} + +void tree_sub(int i, string name = "") +{ + pair p = current_graph[i]; + real vsize = tree_sub_size * tree_sub_aspect; + draw(p -- p + (-1.5*tree_sub_size, -3*vsize) -- p + (1.5*tree_sub_size, -3*vsize) -- cycle); + label("\ninerm " + name, p + (0, -2*vsize)); +} + +void tree_elliptic_node(int i, string name = "", real elong=1, int mode=1) +{ + pair p = current_graph[i]; + real h = vertex_size; + real w = elong*vertex_size; + draw_in_mode( + arc(p-(w,0), h, 90, 270) -- + arc(p+(w,0), h, -90, 90) -- + cycle, + mode + ); + label("\strut $" + name + "$", p - (0, 0.02) + tree_label_offset); +} + +void tree_edge(int i, int j, pen p = thin) +{ + draw(current_graph[i] -- current_graph[j], p); +} + +/* AVL stromy */ + +void lbracket(real x, real ytop, real ybot, string name, real lpos = 0.5) +{ + pair t = (x, ytop); + pair b = (x, ybot); + draw(t -- t-(0.1,0) -- b-(0.1,0) -- b); + label("$" + name + "$", point(t--b, lpos), 2*W); +} + +void rbracket(real x, real ytop, real ybot, string name, real lpos = 0.5) +{ + pair t = (x, ytop); + pair b = (x, ybot); + draw(t -- t+(0.1,0) -- b+(0.1,0) -- b); + label("$" + name + "$", point(t--b, lpos), 2*E); +} + +real br_top_vertex(int i) +{ + return current_graph[i].y + 1.2*vertex_size; +} + +real br_bot_sub(int i) +{ + return current_graph[i].y - 3.5*tree_sub_size; +} + +void tree_sub_ht(int i, string name = "", string ht) +{ + tree_sub(i, name); + label("$" + ht + "$", current_graph[i] + 3*tree_sub_size*tree_sub_aspect*S, S); +} + +/* (a,b)-stromy */ + +void ab_edge(int i, int j, real delta = 0, pen p = thin) +{ + draw(current_graph[i] + (delta,0) -- current_graph[j], p); +} + +/* LLRB stromy */ + +pen rb_red = cmyk(0, 0.9, 0.7, 0.2) + colorless(halfthick); +pen rb_black = gray(0) + halfthick; +pen rb_any = agray + halfthick; + +/* Úplné binarní stromy */ + +pair [] complete_binary_tree(real width, real spread, int depth) +{ + pair u[]; + u[0] = (0,0); + u[1] = (0,0); + int r = 1; + int w = 2; + for (int i=1; i<depth; ++i) { + real d = width / 2**i; + for (int j=0; j<2**(i-1); ++j) { + u[w] = u[r] - (d,spread); + u[w+1] = u[r] + (d,-spread); + w += 2; + ++r; + } + } + return u; +} + +/* Pohodlnější konstrukce stromů */ + +struct tnode { + int id; + pair pos; + pair direction; + tnode parent; + tnode sons[]; + int size; + int mode; // Drawing mode + void draw_vertex(tnode w) { vertex(w.id, mode=this.mode); } + void draw_edge(tnode w) { if (w.parent != null) tree_edge(w.parent.id, this.id); } +}; + +tnode tnodes[]; +pair tn_pos[]; +pair tn_ref; +real tn_edge_len = 1; + +void tn_init(pair refpos=(0,0)) +{ + tnodes = new tnode[]; + tn_pos = new pair[]; + tn_ref = refpos; + tree_init(tn_pos); +} + +tnode tn_add(int parent_id, real angle) +{ + tnode v; + v.id = tnodes.length; + v.sons = new tnode[]; + v.mode = v_white; + tnodes.push(v); + pair d = (0,1) * dir(angle); + d = d * (-tn_edge_len/d.y); + if (parent_id < 0) { + v.pos = tn_ref; + } else { + v.parent = tnodes[parent_id]; + v.parent.sons.push(v); + v.pos = v.parent.pos + d; + } + tn_pos.push(v.pos); + return v; +} + +void tn_draw() +{ + for (tnode v: tnodes) + v.draw_edge(v); + for (tnode v: tnodes) + v.draw_vertex(v); +} diff --git a/tex/adsmac.tex b/tex/adsmac.tex index 6c83007..3162d5b 100644 --- a/tex/adsmac.tex +++ b/tex/adsmac.tex @@ -98,13 +98,9 @@ \prenesteditemizeskip=\smallskipamount \postnesteditemizeskip=\smallskipamount -\let\ucwitemize=\itemize -\def\itemize{\preitemizeskip=0pt\interitemskip=\medskipamount\ucwitemize} -\def\tightitemize{\preitemizeskip=\dimexpr\medskipamount-\smallskipamount\relax\interitemskip=\smallskipamount\ucwitemize} - -\let\ucwnumlist=\numlist -\def\numlist{\preitemizeskip=0pt\interitemskip=\medskipamount\ucwnumlist} -\def\tightnumlist{\preitemizeskip=\dimexpr\medskipamount-\smallskipamount\relax\interitemskip=\smallskipamount\ucwnumlist} +\let\ucwlist=\list +\def\list{\preitemizeskip=0pt\interitemskip=\medskipamount\ucwlist} +\def\tightlist{\preitemizeskip=\dimexpr\medskipamount-\smallskipamount\relax\interitemskip=\smallskipamount\ucwlist} \abovedisplayskip=\bigskipamount \abovedisplayshortskip=\abovedisplayskip @@ -282,7 +278,7 @@ \def\sepfont{\setfonts[LMRoman/30]\bf} \def\pagenumfont{\setfonts[LMRoman/10]\rm} -\protected\def\captionfont{\setfonts[Montserrat/9]\setmath[//]\fixsetmath\baselineskip=11bp} +\protected\def\captionfont{\setfonts[LMSans/9]\setmath[//]\fixsetmath\baselineskip=11bp} %%% Nepovinné argumenty %%% -- GitLab