Сканирование Грэхема — нахождение выпуклой оболочки множества точек

Еще один алгоритм по нахождению выпуклой оболочки множества точек. Теперь уже не такой тормозной (не O=n^3, а O=n log n ) — Сканирование Грэхема.


</pre>
/// <summary>
/// Сканирование Грэхема.
/// </summary>
/// <param name="set"></param>
/// <returns></returns>
public static IEnumerable<Point> ConvexHull(Point[] points)
{
var upper = new List<Point>();
var lower = new List<Point>();

//сортируем точки P и Q в лексикографическом порядке: сначала по x,
//и если у нескольких точек Px = Qx, то сортируем их также по y
points = points.OrderBy(p => p.X).ThenBy(p => p.Y).ToArray();

upper.AddRange(points.Take(2));
//если в upper больше двух точек и если последние три точки upper не
//образуют поворот направо, то удаляем среднюю из них
for (int i = 2; i < points.Count(); i++)
{
upper.Add(points[i]);
while (upper.Count() > 2 && !IsRightTurn(upper.ElementAt(upper.Count - 1),
upper.ElementAt(upper.Count - 2), upper.ElementAt(upper.Count - 3)))
{
upper.RemoveAt(upper.Count() - 2);
}
}

lower.Add(points.ElementAt(points.Count() - 1));
lower.Add(points.ElementAt(points.Count() - 2));
//если в lower больше двух точек и если последние три точки lower не
//образуют поворот направо, то удаляем среднюю из них
for (int i = points.Count() - 2; i >= 0; i--)
{
lower.Add(points.ElementAt(points.Count() - 1));
while (lower.Count() > 2 && !IsRightTurn(lower.ElementAt(lower.Count - 1),
lower.ElementAt(lower.Count - 2), lower.ElementAt(lower.Count - 3)))
{
lower.RemoveAt(lower.Count() - 2);
}
}

//удаляем первую и последнюю точки в lower, чтобы избежать дублирования с upper
lower.RemoveAt(lower.Count() - 1);
lower.RemoveAt(0);

return upper.Concat(lower);
}
<pre>

Вначале сортируем все точки по X. На случай, если вдруг будет несколько точек с одним X, сортируем их лексикографически — также и по Y.

Теперь добавляем первые две точки в список upper (верхняя часть выпуклой оболочки) и начинаем обход всех точку в цикле for. Добавляем третью и далее в цикле while начинаем проверку — есть ли в upper как минимум 3 точки и образуют ли последние три поворот направо. Если три точки есть, но поворота направо не происходит — удаляем среднюю из этих трех и продолжаем работу while.

Аналогичным образом поступаем с «нижней» частью выпуклой оболочки — список lower.

Чтобы избежать дублирования точек в lower и upper, в lower удаляем первую и последнюю точки.

Возвращаем upper с присоединенным к нему в конец lower.

 

Поворот направо можно определить по положению третьей точки относительно отрезка, образуемого первой и второй точкой:


/// <summary>
/// Представляет ли последовательность трех точек повторот направо.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
/// <returns></returns>
public static bool IsRightTurn(Point a, Point b, Point c)
{
var seg = new LineSegment(a, b);
return WherePoint(seg, c) < 0;
}

/// <summary>
/// Определяет, где находится точка относительно вектора.
/// (bx-ax)*(py-ay)-(by-ay)*(px-ax)
/// </summary>
/// <returns>i. i больше 0, если точка слева от вектора, 
/// a меньше 0, если точка справа от вектора,
/// и = 0, если точка на векторе, прямо по вектору или сзади вектора</returns>
public static int WherePoint(LineSegment vector, Point p)
{
return (vector.Q.X - vector.P.X) * (p.Y - vector.P.Y)
- (vector.Q.Y - vector.P.Y) * (p.X - vector.P.X);
}

Ниже классы точки и отрезка, используемого в алгоритме:


</pre>
/// <summary>
/// Направленный отрезок (p -> q). В общем это вектор.
/// </summary>
public class LineSegment
{
public Point P { get; private set; }
public Point Q { get; private set; }

public LineSegment(Point p, Point q)
{
this.P = p;
this.Q = q;
}
}

public class PointNamed
{
public int X { get; set; }
public int Y { get; set; }
public string Name { get; set; }
public Point Point;

public PointNamed(string name, Point point)
{
this.Name = name;
this.X = point.X;
this.Y = point.Y;
this.Point = point;
}
}
<pre>
Рубрика: Без рубрики | Метки: , | Оставить комментарий

Нахождение выпуклой оболочки для множества точек. Медленный вариант.

Давно хотел начать разбирать алгоритмы вычислительной геометрии и вот начал.

Это, самый первый алгоритм из книги «Вычислительная геометрия. Алгоритмы и приложения».  В книге он называется SlowConvexHull. Работает он неидеально — из-за того, что в нем нет разницы в том, где находится точка — на/перед/позади отрезка. Хотя это не проблема алгоритма, а проблема моей реализации.

Писал его на C#. Думаю, надо бы понемногу начинать осваивать что-то на Python для операций вроде нахождения положения точки относительно отрезка. Вначале меня эта формулировка очень смутила, потому что не подумал, что у этого отрезка есть направление. Вероятно, аналогичных операций еще очень много и не уверен, стоит ли все их реализовывать самостоятельно по формулам. Читать далее

Рубрика: Без рубрики | Метки: , | Оставить комментарий

Добавил подставление шаблона объекта в шаблон в SamplesToTextsMatcher

Добавил то, что, вроде как, может быть актуальным при сопоставлении шаблона и текста: подстановку одного паттерна в другой. Например, нужно часто подставлять шаблон синонимов персоны в какой-то другой шаблон. Допустим, шаблон персоны такой:   (Иванов | Ивонов| Ivanov | Ivonov | #Ivanov | #Ivonov)

И есть другие шаблоны, в которых шаблон для этой персоны нужно использовать. Например, что-то типа этого:   (победитель | победил | (одержал /2 победу) | выиграл) /3 (соревнования | кубок | состязание) /5 (Иванов | Ивонов| Ivanov | Ivonov | #Ivanov | #Ivonov)

Вместо блока синонимов можно использовать {иванов}, предварительно дав такое название первому шаблону. В итоге запрос будет таким:

(победитель | победил | (одержал /2 победу) | выиграл) /3 (соревнования | кубок | состязание) /5  {иванов}

Подставляемых шаблонов может быть сколько угодно. Тестов для этого функционала пока мало делал — но пока что, кажись, работает. Пока не делал проверки валидности для фигурных скобок. Например, их закрытия и допустимых символов наименования шаблона и пр.

Для того чтобы использовать шаблоны в шаблонах в конструктор контекста опциональным аргументом добавляется словарь с ключом — именем шаблона и связанным списком терминалов и нетерминалов (не преобразованным в обратную польскую нотацию) из другого объекта контекста — значением.

Dictionary<string, LinkedList<Expression>> extras = null
Рубрика: Без рубрики | Оставить комментарий

Моя библиотека для сопоставления строки и шаблона текста

Описал на основном блоге свою библиотеку для сопоставления строки и шаблона текста. Публикация ВОТ ТУТ.

Рубрика: Без рубрики | Оставить комментарий

Сделал библиотеку для предобработки текстов, формирования term-text матрицы

Вероятно, доделал основу библиотеки для предобработки текстов, нужную мне периодически. Надоело каждый раз писать одно и то же — решил сделать библиотеку. Функционал ее очень простой — на вход ей подаются тексты — например, их можно импортировать из txt файлов. В одном файле может быть несколько текстов, разделенных заранее определенной последовательностью символов. Далее задается список операций, которые можно произвести над текстом в любом количестве и любой последовательности. Сейчас доступны следующие операции:

  • токенизация (на слова, предложения, абзацы)
  • стемминг (для русского языка)
  • удаление email
  • удаление URL
  • хештегов
  • удаление JS и HTML
  • удаление пунктуации
  • удаление коротких слов
  • удаление самостоятельных чисел (в строке «по небу летело 5 пингвинов» будет удалено «5»)
  • удаление стоп слов (для русского языка)
  • формирование n-грамм

Применив операции к текстам, получаем очищенные от ненужного хлама тексты. Каждый текст представлен объектом класса Token (даже исходный нетокенизированный). При этом Token включает в себя список токенов (по умолчанию он null). Допустим, текст был подвергнут токенизации, в результате чего мы получили список слов — эти токены можно (но не обязательно) сделать дочерними токенами объекта текста.

Далее из полученных токенов (хранящих в себе id текста) или токенов + текстов можно сформировать term-text матрицу. Матрицу можно выгрузить в csv (реализацию для других форматов пока не сделал — собственно, думаю, и этого достаточно, и только это мне и нужно было). Подумал, что и импорт, и экспорт тоже стоит добавить в библиотеку.

Библиотека находится на гитхабе ТУТ. Вероятно, многое еще придется подправить и добавить: подправлю и добавлю.

Разрабатывая ее, попробовал немного TDD. Библиотека сделана на C#, .NET Standart. Тесты на XUnit. В солюшене есть небольшое консольное демо приложение.

По плану дальше сделать WPF и/или ASP.NET CORE MVC приложение, где будет использоваться эта библиотека (так как не пробовал ранее писать ничего ни на WPF ни на ASP.NET CORE). Для десктопных приложений использовал только WinForms (ну и Swing и JAVA FX на джава больше года назад)  — надо заполнить этот пробел.

Рубрика: Без рубрики | Метки: , , | Оставить комментарий

Баг в инверсии матриц

В библиотеке для машинного обучения, которую сейчас пишу, кажется, косяк в расчете инверсии для матрицы. Очень странно. Может быть, буду искать баг, а может и забью. Может быть, как-нибудь потом поразбираюсь дальше… Может быть, какие-то особенности инверсии, которые я не знаю. Хотя странно очень — раньше не наблюдались проблемы… Как бы то ни было — начинаю разбирать F#. Для работы с матрицами, пожалуй, буду использовать не свою библиотеку (просто для верности и из нежелания искать баг в инверсии, если дело вообще в ней). В общем-то, свою цель библиотека частично выполнила — я разобрал основы работы с матрицами и написал несколько методов регрессии… По большому счету — на этом можно и остановиться в разработке этой библиотеки. Хотя… хз…

Рубрика: Без рубрики | Метки: , , , , , | Оставить комментарий

Парная регрессия — параболы второго, третьего и пр порядка

В библиотеку машинного обучения добавил еще одну регрессию — парную регрессию n-порядка. Класс ВОТ ТУТ. Формат использования такой же, как и у других регрессий. Вот пример:

Matrix mParabolaRegression = Matrix.GetMatrixFromTXT("data\\parabola_regression.txt", '\t');
            NOrderSimpleParabolaRegression nospr = new NOrderSimpleParabolaRegression();
            Matrix z = nospr.GetRegressionCoefficients(mParabolaRegression, 2);
            double yVal = nospr.GetYForVectorX(z, 84.0);

GetMatrixFromTXT — статический метод получения матрицы из файла. Указывается путь к файлу и разделитель столбцов.

Далее создается объект парной регрессии параболы n-порядка.

Далее получаем коэффициенты для формулы расчета зависимой переменной y: y = a + bx + cx^2

Используя коэффициенты рассчитываем y для заданного предиктора x:

double yVal = nospr.GetYForVectorX(z, 84.0);

То есть в примере ищем y для x = 84.

Рубрика: Без рубрики | Метки: , , , | Оставить комментарий

Множественная линейная регрессия на C#

Поскольку на прошлой неделе сделал реализацию работу с матрицами на c# — самое время ими воспользоваться. Собственно, пока что сделал только множественную линейную регрессию. Класс для работы с ней находится ВОТ ТУТ.

Для начала создаем объект множественной линейной регрессии: Читать далее

Рубрика: Без рубрики | Метки: , , , , , | Оставить комментарий

C#. Матрицы: Умножение на скаляр. Инвертирование, транспонирование, детерминант. Сложение, вычитание и умножение

Для реализации множественной линейной регрессии потребовалось поработать с матрицами — а именно, научиться их перемножать, транспонировать и инвертировать. Поскольку ничего такого раньше делать не умел, решил самостоятельно реализовать это в библиотеке машинного обучения, которую сейчас пишу (такой вот способ изучения машинного обучения с нуля придумал для себя).

Библиотека находится на github ВОТ ТУТ. Пока что всё в ней написано на c#. Может быть, еще добавится что-то функциональное — но никак не могу выбрать между F# и всем остальным… По уму-то — нужен F#, так как всё же это у меня .NET. Но вот F#, кажется, сейчас уже никому не нужен, поэтому не знаю, какой язык осваивать.

Итак, создаем матрицу. Конструктор принимает двухмерный массив double — собственно, саму матрицу:

double[,] a = new double[3, 3];
            sqvArr[0, 0] = -1;
            sqvArr[0, 1] = -2;
            sqvArr[0, 2] = 2;
            sqvArr[1, 0] = 2;
            sqvArr[1, 1] = 1;
            sqvArr[1, 2] = 1;
            sqvArr[2, 0] = 3;
            sqvArr[2, 1] = 4;
            sqvArr[2, 2] = 5;

            Matrix matrixA = new Matrix(a);

Читать далее

Рубрика: Без рубрики | Метки: , , | Оставить комментарий

Простая линейная регрессия на C#

Как уже писал, занимаюсь освоением самых- самых основ machine learning. В курсах и литературе по ML обычно начинают с простой линейной регрессии. Предварительно обучив по массиву значений одной независимой переменной (X) и массиву значений зависимой от ней второй переменной (Y), можно для какого-либо x получить предположительное y. Например, по росту предположить вес.

Код написал на c#. Он находится на гитхабе — ВОТ ТУТ . Это библиотека, в которой на настоящий момент нет ничего, кроме класса простой линейной регрессии 🙂 Читать далее

Рубрика: Без рубрики | Метки: , , , | 1 комментарий