Николай Паламарчук "Functional Programming basics for PHP developers"Fwdays
Functional Programming becomes very popular nowadays. What is it? Is it a hype or panacea? Should you deal with it as a PHP programmer? Let's find out!
Основы языка Питон: функции, элементы функционального программирования, списочные выражения, генераторы. Презентация к лекции курса "Технологии и языки программирования".
Презентация для JuJa вебинара о том, как писать рекурсивные программы с примером о вычислении факториала и чисел Фибоначчи. Также рассказывается о поиске линейном, бинарном, в глубину и в ширину, как работает поисковая система.
Презентация сделана для новичков в деле программистов.
Хочу знать, сколько уникальных посетителей было на моём сайте за произвольный...Ontico
Что нужно хранить для того, чтобы была возможность ответить на этот вопрос?
Для точного ответа нужно через равные интервалы времени сохранять множество посетителей сайта (пусть это для простоты будут IP-адреса), которых мы за прошедший интервал увидели. Понятное дело, что такой объём информации хранить нереально, а даже, если получится, придётся объединять большое количество множеств и считать элементы в том множестве, которое получилось в итоге. Это очень долго. Не спасает ситуацию даже переход от точных алгоритмов к приблизительным: гарантировать точность либо не получится, либо придётся использовать объём памяти и вычислительные ресурсы, сопоставимые с точным алгоритмом.
В 80-х годах появились первые вероятностные алгоритмы для приблизительной оценки количества элементов в множестве. При большом количестве уникальных элементов эти алгоритмы дают приблизительную оценку, которая отличается от истинного значения в (1±e), e<1>0.5. То есть они могут вернуть оценку, которая сильно отличается от истинного значения с некоторой вероятностью (1-p). Чем больше требуется точность, и чем меньше нужна вероятность ошибки, тем больше ресурсов требуют алгоритмы. Сохраняя внутреннее состояние одного из таких алгоритмов через равные промежутки времени в базе данных, мы можем оценить приблизительное количество уникальных посетителей не только за произвольный интервал времени, но и за произвольное объединение любых интервалов времени, например, мы можем посчитать общее количество уникальных IP, которых мы наблюдали в промежутке времени с 17:00 до 18:00 в течение последней недели.
В 2000-ные в научном сообществе велась активная работа по достижению теоретически оптимальных характеристик (т.е. потребление памяти, сложность добавления нового элемента, сложность запроса) вероятностных приблизительных алгоритмов для оценки кардинальности (количества элементов в множестве), разрабатывался необходимый инструментарий.
Первый такой алгоритм был предложен в 2010 году. О нём-то мы и поговорим.
Similar to Лекция 6. Фибоначчиевы кучи (Fibonacci heaps) (20)
Семинар 5. Многопоточное программирование на OpenMP (часть 5)
Лекция 6. Фибоначчиевы кучи (Fibonacci heaps)
1. Лекция 6Фибоначчиевыкучи(Fibonacci heap)
КурносовМихаил Георгиевич
E-mail: mkurnosov@gmail.com
WWW: www.mkurnosov.net
Курс “Алгоритмы и структуры данных”
Сибирский государственный университет телекоммуникаций и информатики (Новосибирск)
Осенний семестр, 2014
2. Значение (Value)
Приоритет(Priority)
Слон
3
Кит
1
Лев
15
Очередь с приоритетом (Priority queue)
2
Очередь с приоритетом (Priority queue) – это очередь, в которой элементы имеют приоритет (вес)
Поддерживаемые операции:
oInsert(key, value)–добавляет в очередь значение valuec приоритетом (весом, ключом) key
oDeleteMin/DeleteMax–удаляет из очереди элемент с мин./макс. приоритетом (ключом)
oMin/Max–возвращает элемент с мин./макс. ключом
oDecreaseKey–изменяет значение ключа заданного элемента
oMerge(q1, q2)–сливает две очереди в одну
3. Двоичная куча (Binary heap)
3
Двоичная куча (пирамида, сортирующее дерево, binary heap) –это двоичное дерево, удовлетворяющее следующим условиям:
a)приоритет(ключ) любой вершины не меньше ( ≥), приоритета её потомков
b)дерево является полным двоичным деревом (complete binary tree)–все уровни заполнены, возможно за исключением последнего
4. Двоичная куча (Binary heap)
4
max-heap
Приоритет любой вершины не меньше (≥), приоритета потомков
min-heap
Приоритет любой вершины не больше (≤), приоритета потомков
5. Очередь с приоритетом (Priority queue)
5
В таблице приведены трудоемкости операций очереди с приоритетом (в худшем случае, worst case)
Символом ‘*’ отмечена амортизированная сложность операций
Операция
Binary heap
Binomial heap
Fibonacci heap
Pairing heap
Brodalheap
FindMin
Θ(1)
O(logn)
Θ(1)*
Θ(1)*
Θ(1)
DeleteMin
Θ(logn)
Θ(logn)
O(logn)*
O(logn)*
O(logn)
Insert
Θ(logn)
O(logn)
Θ(1)*
Θ(1)*
Θ(1)
DecreaseKey
Θ(logn)
Θ(logn)
Θ(1)*
O(logn)*
Θ(1)
Merge/Union
Θ(n)
Ω(logn)
Θ(1)
Θ(1)*
Θ(1)
6. Фибоначчиевыкучи (Fibonacci heaps)
6
Фибоначчиевыкучи(Fibonacci heaps) эффективны, когда количество операций DeleteMin() и Delete(x) относительно мало по сравнению с количеством других операций(Min, Insert, Merge, DecreaseKey)
Фибоначчиевыкучи нашли широкое применение в алгоритмах на графах(поиск минимального остовногодерева, поиск кратчайшего пути в графе)
Например, в алгоритме Дейкстрыфибоначчиевыкучи (min-heap) можно использовать для хранения вершин и быстрого уменьшения текущего кратчайшего пути до них (DecreaseKey)
Авторы: Michael L. Fredman, Robert E. Tarjan, 1984
FredmanM.L., TarjanR.E. Fibonacci heaps and their uses in improved network optimization algorithms. –Journal of the Association for Computing Machinery. – 1987, 34 (3). –pp. 596-615 (http://www.cl.cam.ac.uk/~sos22/supervise/dsaa/fib_heaps.pdf)
7. Фибоначчиевыкучи (Fibonacci heaps)
7
Фибоначчиевакуча (Fibonacci heap)–это совокупность деревьев, которые удовлетворяют свойствам кучи (min-heap или max-heap)
Деревья могу иметь различные степени
Максимальная степень D(n) узла в фибоначчиевойкуче из nэлементов: 푫(풏)≤퐥퐨퐠풏
23
Min
7
3
18
39
52
38
41
17
24
30
26
46
35
min-heap(5 деревьев, 14 узлов)
8. key–приоритет узла (вес, ключ)
value–данные
parent–указатель на родительский узел
child–указатель на один из дочерних узлов
left –указатель на левый сестринский узел
right –указатель на правый сестринский узел
degree–количество дочерних узлов
mark–были ли потери узлом дочерних узлов
Узел фибоначчиевойкучи
8
Дочерние узлы (включая корни) объединены в циклический дважды связный список (doubly linked list)
Каждый узел фибоначчиевойкучи (дерева) содержит следующие поля:
parent
key
value
mark
degree
left
right
child
9. Узел фибоначчиевойкучи
9
Если узел является единственным дочерним узлом
x.left= x.right= x
parent
key
value
mark
degree
left
right
child
10. Фибоначчиевыкучи (Fibonacci heaps)
10
Доступ к фибоначчиевойкуче осуществляется по указателю на корень дерева с минимальным ключом (или с максимальным ключом, в случае max-heap)
23
Min
7
3
18
39
52
38
41
17
24
30
26
46
35
min-heap(5 деревьев, 14 узлов)
11. Добавление узла в кучу (Insert)
11
23
7
18
39
52
38
41
17
24
30
26
46
35
3
23
7
18
39
52
38
41
17
24
30
26
46
35
3
21
Добавление узла с ключом 21
Создаем в памяти новый узел (21) и помещаем его слева от узла с минимальным ключом (3)
12. Добавление узла в кучу (Insert)
12
functionFibHeapInsert(heap, key, value)
node = AllocateMemory();
node.key= key
node.value= value
node.degree= 0
node.mark= false
node.parent= NULL
node.child= NULL
node.left= node
node.right= node
/* Добавляем node в список корней heap */
FibHeapAddNodeToRootList(node, heap.min)
ifheap.min= NULL ORnode.key< heap.min.keythen
heap.min= node
heap.nnodes= heap.nnodes+ 1
returnheap
end function
TInsert= O(1)
13. Добавление узла в список корней
13
Необходимопоместить новый узел nodeв список корней кучи h
3
Случай 1: список корней содержит одно деревоДобавляем новый узел слева от корня дерева и корректируем циклические связи
h.left= h = h.right
3
21
Insert(21)
h.left= node
h.right= node
node.right= h
node.left= h
14. 7
Добавление узла в список корней
14
Необходимо поместить новый узел nodeв список корней кучи h
Случай 2: список корней содержит больше одного дереваДобавляем новый узел слева от корня дерева и корректируем циклические связи
h.left≠h ≠ h.right
3
21
Insert(21)
lnode= h.left
h.left= node
node.right= h
node.left= lnode
lnode.right= node
3
3
7
или
7
15. Добавление узла в список корней
15
functionFibHeapAddNodeToRootList(node, h)
if h = NULLthen
return
ifh.left= h then
/* Случай 1: список h содержит 1 корень */
h.left= node
h.right= node
node.right= h
node.left= h
else
/* Случай 2: список h содержит > 1 корня */
lnode= h.left
h.left= node
node.right= h
node.left= lnode
lnode.right= node
end if
end function
TAddNodeToRootList= O(1)
16. Поиск минимального узла (Min)
16
В фибоначчиевойкуче поддерживается указатель на корень дерева с минимальным ключом
Поиск минимального узла выполняется за время O(1)
23
Min
7
3
18
39
52
38
41
17
24
30
26
46
35
min-heap(5 деревьев, 14 узлов)
17. Поиск минимального узла (Min)
17
functionFibHeapMin(heap)
return heap.min
end function
TMin= O(1)
19. Слияние фибоначчиевыхкуч (Union)
19
functionFibHeapUnion(heap1, heap2)
heap = AllocateMemory()
heap.min= heap1.min
FibHeapLinkLists(heap1.min, heap2.min)
if(heap1.min = NULL) OR
(heap2.min != NULL ANDheap2.min.key < heap1.min.key)
then
heap.min= heap2.min
end if
heap.nnodes= heap1.nnodes + heap2.nnodes
FreeMemory(heap1)
FreeMemory(heap2)
returnheap
end function
TUnion= O(1)
20. Имеется два списка корней –два двусвязных циклических списка
Каждый список задан указателем на одну из вершин (корень дерева)
Требуется слить два списка в один
Слияние пары списков корней
20
5
3
7
17
31
19
24
Heap 1
Heap 2
21. Имеется два списка корней –два двусвязных циклических списка
Каждый список задан указателем на одну из вершин (корень дерева)
Требуется слить два списка в один
Слияние пары списков корней
21
5
3
7
17
31
19
24
Heap 1
Heap 2
Требуется корректировка 4 указателей
Объединение списков выполняется за время O(1)
22. functionFibHeapLinkLists(heap1, heap2)
ifheap1 = NULL ORheap2 = NULL then
return
left1 = heap1.left
left2 = heap2.left
left1.right = heap2
heap2.left = left1
heap1.left = left2
left2.right = heap1
returnheap1
end function
Слияние пары списков корней
22
TLinkLists= O(1)
23. Удаление минимального узла (DeleteMin)
23
Получаем указатель на минимальный узел z
Удаляем из списка корней узел z
Перемещаем в список корней все дочерние узлы элемента z
Уплотняем список корней(Consolidate)–связываем корни деревьев одинаковой степени, пока в списке корней останется не больше одного дерева (корня) каждой степени
23
7
18
39
52
38
41
17
24
30
26
46
35
3
21
Min
25. functionFibHeapDeleteMin(heap)
z = heap.min
ifz = NULL then
returnNULL
foreachx inz.childdo
/* Добавляем дочерний узел x в список корней */
FibHeapAddNodeToRootList(x, heap)
x.parent= NULL
end for
/* Удаляем z из списка корней */
FibHeapRemoveNodeFromRootList(z, heap)
ifz = z.rightthen
heap.min= NULL
else
heap.min= z.right
FibHeapConsolidate(heap)
end if
heap.nnodes= heap.nnodes–1
returnz
end function
Слияние пары списков корней
25
O(logn)
26. Уплотнение списка корней (Consolidate)
26
Уплотнение списка корней выполняется до тех пора, пока все корни не будут иметь различные значения поля degree
1.Находим в списке корней два корня xи yс одинаковой степенью (x.degree= y.degree) такие, что x.key≤ y.key
2.Делаем yдочерним узлом x(и наоборот в случае max-heap);поле x.degreeувеличивается, а пометка y.markснимается (если была установлена)
Процедура Consolidate использует вспомогательный массив указателей A[0, 1, …, D(n)], 퐷(푛)≤log푛
Если A[i] = y, то корень yимеет степень i
27. functionFibHeapConsolidate(heap)
fori= 0 toD(heap.nnodes) do
A[i] = NULL
/* Цикл по всем узлам списка корней */
for each w inheap.mindo
x = w
d = x.degree
whileA[d] != NULL do
y = A[d] /* Степень y совпадает со степенью x */
if x.key> y.keythen
FibHeapSwap(x, y) /* Обмениваем x и y */
FibHeapLink(heap, y, x)
A[d] = NULL
d = d + 1
end while
A[d] = x
end for
Уплотнение списка корней (Consolidate)
27
O(logn)
28. /* Находим минимальный узел */
heap.min= NULL
fori= 0 toD(heap.nnodes) do
ifA[i] != NULL then
/* Добавляем A[i] в список корней */
FibHeapAddNodeToRootList(A[i], heap)
ifheap.min= NULL ORA[i].key < heap.min.keythen
heap.min= A[i]
end if
end if
end for
end function
Уплотнение списка корней (Consolidate)
28
function D(n)
returnfloor(log(2, n))
end function
29. function FibHeapLink(heap, y, x)
x.degree= x.degree+ 1
/* Делаем y дочерним узлом x */
FibHeapRemoveNodeFromRootList(y, heap)
y.parent= x
FibHeapAddNodeToRootList(y, x.child)
y.mark= FALSE
end function
Уплотнение списка корней (Consolidate)
29
TLink= O(1)
46. Уплотнение списка корней (Consolidate)
46
23
7
21
39
18
38
41
24
30
26
46
35
A:
0
1
2
3
-
x
17
52
Ищем новый минимальный узел (устанавливаем heap.min)
Проходим по указателям массива A[0, 1, …, D(n)] = A[0, …, 3] и запоминаем указатель на корень с минимальным ключом(требуется O(logn) шагов)
47. Уменьшение ключа (DecreaseKey)
47
23
21
39
18
38
41
24
30
46
35
17
52
7
Изменяем ключ заданного узла на новое значение
Если нарушены свойства кучи (min-heap): ключ текущего узла меньше ключа родителя, то осуществляем восстановление свойств кучи
26
newkey
48. Уменьшение ключа (DecreaseKey)
48
function FibHeapDecreaseKey(heap, x, newkey)
ifnewkey> x.keythen
return /* Новый ключ больше текущего значения ключа */
x.key= newkey
y = x.parent
ify != NULL ANDx.key< y.keythen
/* Нарушены свойства min-heap: ключ родителя больше */
/* Вырезаем x и переносим его в список корней */
FibHeapCut(heap, x, y)
FibHeapCascadingCut(heap, y)
end if
/* Корректируем указатель на минимальный узел */
ifx.key< heap.min.keythen
heap.min= x
end function
49. Уменьшение ключа (DecreaseKey)
49
function FibHeapCut(heap, x, y)
/* Удаляем x из списка дочерних узлов y */
FibHeapRemoveNodeFromRootList(x, y)
y.degree= y.degree-1
/* Добавляем x в список корней кучи heap */
FibHeapAddNodeToRootList(x, heap)
x.parent= NULL
x.mark= FALSE
end function
TCut= O(1)
Функция FibHeapCutвырезает связь между xи его родительским узлом y, делая xкорнем
50. Уменьшение ключа (DecreaseKey)
50
function FibHeapCascadingCut(heap, y)
z = y.parent
ifz = NULL then
return
ify.mark= FALSE then
y.mark= TRUE
else
FibHeapCut(heap, y, z)
FibHeapCascadingCut(heap, z)
end if
end function
Функция FibHeapCascadingCutреализует каскадное вырезание над y
57. Уменьшение ключа (DecreaseKey)
57
23
21
39
18
38
41
24
30
5
17
52
7
26
15
x
Изменим ключ 35 до значения 5
Устанавливаем в узле 35 новый ключ 5
Нарушены свойства кучиmin-heap: 5 < 25(x.key< y.key)
Выполняет FibHeapCut(<5>,<26>)
Выполняем FibHeapCascadingCut(<26>)
y
58. Уменьшение ключа (DecreaseKey)
58
23
21
39
18
38
41
24
30
5
17
52
7
26
15
x
y
Выполняет FibHeapCut(<15>,<24>)
oПереносим <15> в список корней кучи
oУстанавливаем <15>.mark= FALSE
59. Уменьшение ключа (DecreaseKey)
59
23
21
39
18
38
41
24
30
5
17
52
7
26
15
z
y
Выполняем FibHeapCascadingCut(<26>)
oИзменяем значение <26>.markс FALSE на TRUE
60. Уменьшение ключа (DecreaseKey)
60
23
21
39
18
38
41
24
30
5
17
52
7
26
15
Корректируем указатель на минимальный элемент
5 < 7, указатель не изменяется
61. Удаление заданного узла (Delete)
61
function FibHeapDelete(heap, x)
FibHeapDecreaseKey(heap, x, -Infinity)
FibHeapDeleteMin(heap)
end function
62. Очередь с приоритетом (Priority queue)
62
В таблице приведены трудоемкости операций очереди с приоритетом (в худшем случае, worst case)
Символом ‘*’ отмечена амортизированная сложность операций
Операция
Binary heap
Binomial heap
Fibonacci heap
Pairing heap
Brodalheap
FindMin
Θ(1)
O(logn)
Θ(1)*
Θ(1)*
Θ(1)
DeleteMin
Θ(logn)
Θ(logn)
O(logn)*
O(logn)*
O(logn)
Insert
Θ(logn)
O(logn)
Θ(1)*
Θ(1)*
Θ(1)
DecreaseKey
Θ(logn)
Θ(logn)
Θ(1)*
O(logn)*
Θ(1)
Merge/Union
Θ(n)
Ω(logn)
Θ(1)
Θ(1)*
Θ(1)
63. Задания
63
Прочитать в CLRS 3ed. §“19.2 Операции над объединяемыми пирамидами”
Разобраться с доказательством амортизированной вычислительной сложности операций (метод потенциалов)
Прочитать в CLRS 3ed. §“19.4Оценка максимальной степени”