Российский государственный гуманитарный университет
Опубликован: 13.07.2022 | Доступ: свободный | Студентов: 257 / 9 | Длительность: 11:54:00
Специальности: Программист
Лекция 6:

Структуры данных и алгоритмы

Быстрая сортировка

Рассмотрим реализацию алгоритма быстрой сортировки с помощью функций на множестве термов, представляющих списки. Алгоритм заключается в следующем. Если список пуст, то отсортированный список также пуст. Если список не пуст, то его хвост разбивается на два списка: список элементов, меньших головы, и список элементов, больше или равных головы. Алгоритм применяется к обоим спискам, затем два полученных упорядоченных списка соединяются в один список.

Для его реализации ниже используются функции ls и gr, которые для элемента a и списка возвращают списки элементов, меньших a и больше или равных a, соответственно. Определение функций имеет вид:

In[8]:= ls[a_, l_] := llist[a, l, nil]
llist[_, nil, l_] := l
llist[a_, cons[h_, t_], l_] := If[h < a, 
llist[a, t, cons[h, l]], llist[a, t, l]]
gr[a_, l_] := glist[a, l, nil]
glist[_, nil, g_] := g
glist[a_, cons[h_, t_], g_] := If[h >= a, 
glist[a, t, cons[h, g]], glist[a, t, g]]
ls[2, x]
gr[2, x]
 Out[14]= cons[1, nil]
 Out[15]= cons[3, cons[5, cons[2, cons[4, nil]]]]

С помощью функций ls и gr функция quicksort быстрой сортировки списка определяется следующим образом:

In[16]:= quicksort[l_] := qsort[l, nil]
qsort[nil, s_] := s
qsort[cons[h_, t_], s_] := qsort[ls[h, t],
cons[h, qsort[gr[h, t], s]]]
quicksort[x]
Out[19]= cons[1, cons[2, cons[3, cons[4, cons[5, nil]]]]]

Отметим, что операция соединения списков в данной реализации не используется.

Сортировка слиянием

Рассмотрим алгоритм сортировки списка слиянием. Его рекурсивное описание имеет вид: список разбивается на два равных по длине списка, или почти равных, если он содержит нечетное число элементов. Затем алгоритм применяется к обоим спискам. После этого выполняется операция слияния двух упорядоченных списков в один упорядоченный список.

Для того чтобы реализовать операцию разделения списка на два примерно равных по длине списка, используем функцию q, которая возвращает целую часть частного от деления длины списка на 2, а также функции take и drop, которые возвращают список, состоящий из первых n элементов, и список без первых n элементов исходного списка, соответственно. Ниже приведено определение этих функций (обычно в логических языках разделение одного списка на два реализуется проще):

In[20]:= length[l_] := len[l, 0]
len[nil, n_] := n
len[cons[_, t_], n_] := len[t, n + 1]
q[l_] := Quotient[length[l], 2]
take[_, 0] := nil
take[cons[h_, t_], c_] := cons[h, take[t, c - 1]]
drop[l_, 0] := l
drop[cons[_, t_], c_] := drop[t, c - 1]
k = q[x]
take[x, k]
drop[x, k]
 Out[28]= 2
 Out[29]= cons[4, cons[2, nil]]
 Out[30]= cons[5, cons[3, cons[1, nil]]] 

Встроенная функция Quotient от целых чисел m и n возвращает целую часть частного от деления m на n. Остаток от деления m на n возвращает встроенная функция Mod.

Функция mergesort сортировки списка слиянием определяется следующим образом:

In[31]:= mergesort[nil] := nil
mergesort[cons[a_, nil]] := cons[a, nil]
mergesort[l_] := merge[mergesort[take[l, q[l]]],
 mergesort[drop[l, q[l]]]]
merge[nil, l_] := l
merge[l_, nil] := l
merge[cons[a_, t_], cons[b_, s_]] := If[a < b,
 cons[a, merge[t, cons[b, s]]], 
 cons[b, merge[cons[a, t], s]]]
mergesort[x]
Out[37]= cons[1, cons[2, cons[3, cons[4, cons[5, nil]]]]] 

Функция merge по двум упорядоченным спискам возвращает упорядоченный список, который является их слиянием.

Рассмотрим алгоритмы преобразования представления целых и дробных частей рациональных чисел между десятичной системой

Для описания и реализации алгоритмов используем дек целых чисел.

Дек целых чисел

На множестве термов, представляющих деки, определим функции

  • addFront добавления первого элемента дека;
  • front возвращения первого элемента дека;
  • removeFront возвращения дека без первого элемента;
  • addRear добавления последнего элемента дека;
  • rear возвращения последнего элемента дека;
  • removeRear возвращения дека без последнего элемента.

Все эти функции аналогичны функциям, определенным выше для списков, стеков и очередей.

Определим также функцию isEmpty, которая возвращает значение True, если дек пуст, и False, если не пуст.

Определение вышеперечисленных функций имеет вид:

In[1]:= x = cons[1, cons[2, cons[3, nil]]];
addFront[nil, a_] := cons[a, nil]
addFront[cons[h_,t_],a_]:=cons[h,addFront[t,a]]
front[cons[a_, nil]] := a
front[cons[_, t_]] := front[t]
removeFront[cons[a_, nil]] := nil
removeFront[cons[h_,t_]]:=cons[h,removeFront[t]]
addRear[d_, a_] := cons[a, d]
rear[cons[h_, _]] := h
removeRear[cons[_, t_]] := t
isEmpty[nil] := True
isEmpty[_] := False
{addFront[x, 5], addRear[x, 7]}
{front[x], rear[x]}
{removeFront[x], removeRear[x]}
isEmpty[x]
 Out[13]= {cons[1, cons[2, cons[3, cons[5, nil]]]], 
 cons[7, cons[1, cons[2, cons[3, nil]]]]}
 Out[14]= {3, 1}
 Out[15]= {cons[1, cons[2, nil]], cons[2, cons[3, nil]]}
 Out[16]= False
Алгоритм преобразования целой части числа из десятичного представления в p-ичное

Псевдокод алгоритма преобразования неотрицательного целого десятичного числа в p-ичное выглядит следующим образом.

n = 6, p = 2, deque = []
цикл пока n > 0
deque = addRear(deque, n mod p)
n = n div p
конец цикла

Определим функцию intToP, которая по заданному целому неотрицательному числу n и основанию системы счисления p возвращает p-ичное представление числа n в виде дека его цифр. Для этого используем вспомогательную функцию f:

intToP(n,p)=f(n,p,[]);\\
f(n,p,d)=\begin{cases}
d,& n=0\\
f(n \div p, p, addRear(d, n \mod \quad p)), \& n >0
\end{cases}

Пример 3. Найдем представление числа 6 в двоичной системе счисления с помощью функции intToP. Имеем:

intToP(6, 2)= f(6, 2, []) = f(3, 2, [0]) = f(1, 2, [1, 0]) = f(0, 2, [1, 1, 0]) = [1, 1, 0].

На языке Wolfram определение функций intToP и f имеет вид:

 In[17]:= intToP[0, _] := cons[0, nil]; intToP[_, 1] := nil;
intToP[n_Integer?Positive, p_Integer?Positive] :=
f[n, p, nil]
f[0, _, d_] := d
f[n_, p_, d_] := f[Quotient[n, p], p, 
addRear[d, Mod[n, p]]]
intToP[6, 2]
intToP[43000, 60]
 Out[21]= cons[1, cons[1, cons[0, nil]]]
 Out[22]= cons[11, cons[56, cons[40, nil]]]