Как найти прочный путь

Приветствую Вас, Хабровчане!

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

И да, я прекрасно понимаю, что я далеко не первый и не последний кто находит в себе силы попробовать на практике реализовать этот алгоритм. Как говорится, повторение — мать учения. Поэтому не нужно меня за это закидывать камнями, палками и чем бы то ни было. Я лишь предложу свой вариант реализации и продемонстрирую результаты. Как Вы уже догадались, писать будем на языке C#. Исходники найдете здесь.

Поехали!

Содержание

  • Математическая постановка задачи

  • Графы и алгоритм Дейкстры

  • Превращаем поверхность в граф

  • Численная реализация

  • Результаты расчетов. Простые препятствия

  • Лабиринты

  • Поиск оптимального пути на поверхности

  • Заключение

Математическая постановка задачи

В декартовой прямоугольной системе координат на плоскости Oxyзадана равномерная сетка:

{ x_i = i cdot dx,   i = 0, ldots, N-1 } \ { y_j = j cdot d y,  j = 0, ldots, M-1 }

где (x_i, y_j)— узлы сетки; dx, dy— шаги сетки;N, M— количество точек по осиOxиOy, соответственно. В каждом узле сетки задано значение z_{i, j} = z (x_i, y_j), представляющее собой высоту карты (ландшафта) местности в рассматриваемой точке. Значения z_{i, j}, образующие исследуемую поверхность, могут быть отрицательными — в таком случае высоту карты следует понимать как глубину относительно некоторой нулевой отметки z=0.

Заданы стартовая и целевая точка искомого оптимального маршрутаAиB, рисунок 1.

Рисунок 1. Пример ландшафта местности — исследуемая поверхность, заданная множеством точек с заданными началом, A, и концом, B, искомого маршрута.

Рисунок 1. Пример ландшафта местности — исследуемая поверхность, заданная множеством точек с заданными началом, A, и концом, B, искомого маршрута.

На выходе мы должны получить конечный список (массив) координат(x_k, y_k), следуя по которым мы доберемся из точкиAв точкуBнаиболее оптимальным образом.

Также в алгоритме будет учитываться параметр, задающий максимальный угол уклона (в градусах) для поиска подходящих соседей.

Так, ну а при чем тут графы и какой-то алгоритм ?!

Графы и алгоритм Дейкстры

Алгоритм Дейкстры представляет собой глобальный алгоритм поиска кратчайших путей на взвешенном графе. О том, что такое граф, и с чем его едят, что такое вершина, ребро, какие графы бывают — я здесь Вам рассказывать не буду. На тему графов написано немало хороших книг, на одну из которых я дам ссылку в конце статьи. А пока я буду предполагать, что Вы уже знакомы с основными понятиями теории графов. Хотя, как по мне, большинство терминов, которые будут описаны далее в статье должны быть понятны и ежу на интуитивном уровне.

Как я упомянул выше, алгоритм Дейкстры является глобальным, я бы даже сказал самым глобальным из всех подобных. Имеются и другие алгоритмы, которые в ряде случаев оптимизируют и ускоряют поиск, например, такие как алгоритм Ли, алгоритм A* и др. Очень хороший обзор на алгоритмы поиска (в т.ч. и Дейкстры) да еще и с примерами кода можете найти тут.

Ну а я просто захотел попробовать сделать это своими силами. Как говорится, хочешь сделать что-то хорошо — сделай это сам! =)

Так в чем же именно глобальность алгоритма Дейкстры?! Глобальность в том, что даже если нам будет необходимо найти кратчайший путь между двумя смежными вершинами графа, в котором 1000 вершин, нам так или иначе придется перебрать абсолютно все 1000 вершин.

Поясню это на примере взвешенного графа с 6-ю вершинами, рисунок 2.

Рисунок 2. Взвешенный граф с 6-ю вершинами. Синим цветом обозначен вес ребра.

Рисунок 2. Взвешенный граф с 6-ю вершинами. Синим цветом обозначен вес ребра.

Найдем кратчайший путь из вершиныAдо вершиныD. Даже не «запуская» на этом графе алгоритм Дейкстры, очевидно, что кратчайший путь изAвD будет путь A rightarrow C rightarrow E rightarrow F rightarrow D, длина которого равна 1+3+2+5=11. Так вот, несмотря на то, что вершиныAиDсмежные — нам все равно пришлось рассматривать ВСЕ вершины этого графа. В этом и заключается глобальность, и в то же время сложность этого алгоритма (я специально подобрал весовые коэффициенты для некоторых ребер намного большими чем другие).

Превращаем поверхность в граф

Чтобы решить нашу задачу с помощью алгоритма Дейкстры нам нужно каким-то образом «превратить» нашу исследуемую поверхность во взвешенный граф. Делать будем это так, — посмотрим на нашу поверхность сверху (перпендикулярно плоскости Oxy), вид будет примерно такой, рисунок 3:

Рисунок 3. Граф, построенный по исследуемой поверхности. В него добавлены "диагональные" ребра, позволяющие алгоритму двигаться в более широком диапазоне направлений.

Рисунок 3. Граф, построенный по исследуемой поверхности. В него добавлены «диагональные» ребра, позволяющие алгоритму двигаться в более широком диапазоне направлений.

Точки поверхности будут спроецированы на плоскостьOxy— это и будут вершины нашего графа. Но откуда появились эти «крестики» в каждом квадратике, спросите Вы ?! Эти крестики — тоже ребра графа, которые добавлены для того, чтобы у нас была возможность ходить не только по горизонтали и вертикали, но и по диагонали. Да, это сильно увеличит сложность алгоритма и время расчета, но зато путь будет еще более оптимальным.

Таким образом любую вершину подобного графа можно идентифицировать с помощью координаты (i, j), что нам очень пригодится в дальнейшем для хранения всех вершин графа в виде матрицы.

Наш граф должен быть взвешенным. Вот тут, конечно, можно поиграться и задавать весовые коэффициенты любым изощренным способом. Но я пока поступлю просто — вес(W)ребра, соединяющего смежные вершины V_1и V_2, заданные своими координатами(i, j)будет не что иное как расстояние в пространстве между соответствующими точками поверхности, из которой наш граф был построен:

W(V_1, V_2) = sqrt{(x_2 - x_1)^2+(y_2 - y_1)^2 + (z_2 - z_1)^2}

Рассматриваемый граф является связным (причем степень связности достаточно неплохая!, особенно после добавления диагональных ребер), поэтому «тупиковых» ситуаций возникнуть не должно и если все учтено правильно, алгоритм все-таки после долгих блужданий доберется из стартовой в целевую вершину и на своем пути «переберет» абсолютно все вершины графа.

Ну все…осталось запустить алгоритм Дейкстры!

Само собой при численной реализации этого алгоритма возникнут определенные сложности много костылей и вопросы. Их я уже буду комментировать в процессе объяснения написанного кода.

Численная реализация

Кодить, как я уже говорил будем на C#, поэтому постараемся взять все самое лучшее из возможностей этого языка. А возможностей у него немало.

Я создам в Visual Studio консольный проект (.NET 4.7.2). Ключевыми классами будут Point2D.cs, Vertex.cs и Graph.cs. Начнем с самого простого.

Класс Point2D будет содержать всего два свойства для хранения координат вершины графа:

public class Point2D
    {
        public int i { get; }
        public int j { get; }

        public Point2D(int i, int j)
        {
            this.i = i;
            this.j = j;
        }
    }

С классом Vertex уже чуть поинтереснее:

public class Vertex
    {
        public Point2D Coordinate { get; set; }
        public double Height { get; set; }
        public Point2D CameFrom { get; set; }
        public double Label { get; set; }
        public bool IsVisited { get; set; }
        public bool IsGoal { get; set; }
        public bool IsObstacle { get; set; }

        public Vertex(int i, int j, Point2D CameFrom = null, double Height = 0.0, double Label = double.MaxValue, bool IsVisited = false, bool IsGoal = false, bool IsObstacle = false)
        {
            Coordinate = new Point2D(i, j);
            this.CameFrom = CameFrom;
            this.Height = Height;           
            this.Label = Label;
            this.IsVisited = IsVisited;
            this.IsGoal = IsGoal;
            this.IsObstacle = IsObstacle;
        }
    }

Опишу кратко свойства.

Первое из них — это координаты вершины графа; Height — высота, значение функции z(x, y)в точке вершины, необходимое для расчета весового коэффициента и величины уклона между двумя смежными вершинами; CameFrom — здесь в процессе работы алгоритма будут храниться координаты вершины из которой мы попали в текущую вершину — на основании этого свойства мы в конце работы алгоритма сформируем наш искомый маршрут; Label — метка, хранящая значение длины пути из стартовой вершины в текущую; IsVisited — говорит нам посетили ли мы данную вершину в процессе расчета или нет; IsGoal — данное свойство будет истинным только для целевой вершины графа (та вершина, оптимальный путь к которой мы ищем). Это свойство понадобилось мне для того, чтобы алгоритм Дейкстры не завершился раньше времени и мы обошли абсолютно все вершины графа; IsObstacle — наш алгоритм будет также уметь обходить препятствия, например, искать кратчайший путь в лабиринте, это свойство позволяет задать определенные вершины как вершины-препятствия, чтобы выбрасывать их из рассмотрения наряду с уже посещенными.

Теперь о классе Graph.

Свойства класса Graph:

 public class Graph
    {
        /// <summary>
        /// Шаг сетки по оси Ox
        /// </summary>
        public double dx { get; }
        /// <summary>
        /// Шаг сетки по оси Oy
        /// </summary>
        public double dy { get; }
        /// <summary>
        /// Количество вершин по оси Ox
        /// </summary>
        public int N { get; }
        /// <summary>
        /// Количество вершин по оси Oy
        /// </summary>
        public int M { get; }
        /// <summary>
        /// Матрица вершин графа
        /// </summary>
        public Vertex[,] Vertices { get; }
        /// <summary>
        /// Предельная величина уклона, необходимая для обхода препятствий, в градусах
        /// </summary>
        public double MaxSlope { get; }
	}

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

Для расчета весов и уклона нам нужно будет получать «реальные» координаты вершины графа на плоскости Oxy(с учетом величины шагов dxи dy):

(double, double) GetRealXY(Vertex vertex)
        {
            double x = vertex.Coordinate.i * dx;
            double y = vertex.Coordinate.j * dy;

            return (x, y);
        } 

Вес ребра между смежными вершинами (расстояние между точками поверхности) находим так:

double Weight(Vertex v1, Vertex v2)
        {
            (double, double) x1y1 = GetRealXY(v1);
            (double, double) x2y2 = GetRealXY(v2);

            double xDiff = x1y1.Item1 - x2y2.Item1;
            double yDiff = x1y1.Item2 - x2y2.Item2;
            double zDiff = v1.Height - v2.Height;

            double sumOfSquares = Math.Pow(xDiff, 2.0) + Math.Pow(yDiff, 2.0) + Math.Pow(zDiff, 2.0);

            return Math.Sqrt(sumOfSquares);
        }

Следующий метод возвращает величину уклона между смежными вершинами (в градусах):

private double Slope(Vertex v1, Vertex v2)
        {
            double hypotenuse = Weight(v1, v2); // Вес ребра - это и есть по факту расстояние между точками
            double zDiffAbs = Math.Abs(v1.Height - v2.Height); // Модуль разности по высоте

            return Math.Asin(zDiffAbs / hypotenuse) * 180.0 / Math.PI; // Переводим радианы в градусы
        }  

Для удобства реализуем 8 методов для получения соседней вершины в зависимости от направления (я привел один из них):

private Vertex GetTopVertex(Vertex v) => Vertices[v.Coordinate.i, v.Coordinate.j + 1];

И еще 8 методов для определения принадлежности вершины той или иной части сетки (здесь я привел два из них):

private bool IsTopRightVertex(Vertex v1) => v1.Coordinate.i == N - 1 && v1.Coordinate.j == M - 1;

private bool IsVertexOnTheRightSide(Vertex v1) => v1.Coordinate.i == N - 1;

Эти методы понадобятся для получения всех смежных вершин для рассматриваемой вершины. Вот тут нам придется нарубить if-ов, т.к. количество смежных вершин не всегда одно и то же:

Метод GetAllAdjacentVertices(Vertex vertex) для получения всех смежных вершин

private List<Vertex> GetAllAdjacentVertices(Vertex vertex)
        {
            #region Рассматриваем угловые вершины

            if (IsTopRightVertex(vertex))
                return new List<Vertex>
                {
                    GetLeftVertex(vertex),
                    GetBottomLeftVertex(vertex),
                    GetBottomVertex(vertex)
                };

            if (IsBottomRightVertex(vertex))
                return new List<Vertex>
                {
                    GetTopVertex(vertex),
                    GetTopLeftVertex(vertex),
                    GetLeftVertex(vertex)
                };

            if (IsBottomLeftVertex(vertex))
                return new List<Vertex>
                {
                    GetTopVertex(vertex),
                    GetTopRightVertex(vertex),
                    GetRightVertex(vertex)
                };

            if (IsTopLeftVertex(vertex))
                return new List<Vertex>
                {
                    GetBottomVertex(vertex),
                    GetBottomRightVertex(vertex),
                    GetRightVertex(vertex)
                };

            #endregion

            #region Рассматриваем боковые вершины

            if (IsVertexOnTheTopSide(vertex))
                return new List<Vertex>
                {
                    GetLeftVertex(vertex),
                    GetBottomLeftVertex(vertex),
                    GetBottomVertex(vertex),
                    GetBottomRightVertex(vertex),
                    GetRightVertex(vertex)
                };

            if (IsVertexOnTheRightSide(vertex))
                return new List<Vertex>
                {
                    GetTopVertex(vertex),
                    GetTopLeftVertex(vertex),
                    GetLeftVertex(vertex),
                    GetBottomLeftVertex(vertex),
                    GetBottomVertex(vertex)
                };

            if (IsVertexOnTheBottomSide(vertex))
                return new List<Vertex>
                {
                    GetLeftVertex(vertex),
                    GetTopLeftVertex(vertex),
                    GetTopVertex(vertex),
                    GetTopRightVertex(vertex),
                    GetRightVertex(vertex)
                };

            if (IsVertexOnTheLeftSide(vertex))
                return new List<Vertex>
                {
                    GetTopVertex(vertex),
                    GetTopRightVertex(vertex),
                    GetRightVertex(vertex),
                    GetBottomRightVertex(vertex),
                    GetBottomVertex(vertex)
                };

            #endregion

            // Иначе вершина лежит "в середине карты" и нужно вернуть все 8 смежных вершин
            return new List<Vertex>
                {
                    GetTopVertex(vertex),
                    GetRightVertex(vertex),
                    GetBottomVertex(vertex),
                    GetLeftVertex(vertex),

                    GetTopRightVertex(vertex),
                    GetBottomRightVertex(vertex),
                    GetBottomLeftVertex(vertex),
                    GetTopLeftVertex(vertex)
                };
        }

Напишем метод, который будет «отсеивать неподходящих» соседей:

private List<Vertex> GetValidNeighbors(Vertex current)
        {
            // Из всех смежных вершин оставляем только те, которые 
            // 1. Еще не посещены
            // 2. Не являются вершинами-препятствиями
            // 3. Наклон к которым меньше заданной величины (например, 30 градусов)
            return GetAllAdjacentVertices(current).Where(v => !v.IsVisited && !v.IsObstacle && Slope(v, current) < MaxSlope).ToList();
        }

и метод, который из оставшихся соседей будет отсеивать целевую вершину и сообщать осталось ли что-то после отсеивания:

private bool HasValidAndNotGoalNeighbors(Vertex vertex, out List<Vertex> validAndNotGoalNeighbors)
        {
            validAndNotGoalNeighbors = GetValidNeighbors(vertex).Where(v => !v.IsGoal).ToList();
            return validAndNotGoalNeighbors.Any();
        }

Именно этот метод (в основном) и нужен для того, чтобы мы перебрали абсолютно все вершины графа (помните о глобальности, да??!). Так как бывают кейсы, когда мы натыкаемся на целевую вершину «раньше срока» (раньше того, как алгоритм пробежит по всем вершинам графа и найдет нам оптимальный маршрут).

Также в основном while-цикле метода по поиску пути (скоро мы уже дойдем до него =)) я буду накапливать в список посещенные вершины. Это тоже относится к замечанию выше и будет гарантировать нам «полный обход» всех вершин графа. Этот список посещенных вершин я буду использовать в следующем методе.

Метод для получения новой «текущей» вершины:

GetCurrent(List<Vertex> visitedVertices)

private Vertex GetCurrent(List<Vertex> visitedVertices)
        {
            List<Vertex> validAndNotGoalNeighbors = new List<Vertex>();

            foreach (Vertex v in visitedVertices)
                if (HasValidAndNotGoalNeighbors(v, out validAndNotGoalNeighbors))
                    break;

            // Если не нашлось ни одного подходящего соседа, значит мы дошли 
            // до целевой вершины. Алгоритм завершен
            if (!validAndNotGoalNeighbors.Any())
                return null;

            // Иначе находим и возвращаем соседа с минимальной меткой
            double minLabel = validAndNotGoalNeighbors.Min(v => v.Label);
            Vertex newCurrent = validAndNotGoalNeighbors.First(v => v.Label == minLabel);

            return newCurrent;
        }

И вот он!! Метод для поиска кратчайшего (оптимального) пути (и его длины):

public List<Point2D> FindShortestPathAndLength(Point2D startPoint, Point2D goalPoint, out double shortestPathLength)
        {
            shortestPathLength = 0.0;
            // Стартовой вершине присваиваем нулевую метку
            Vertex start = Vertices[startPoint.i, startPoint.j];
            start.Label = 0.0;
            // Целевую вершину пометим, что она целевая
            Vertex goal = Vertices[goalPoint.i, goalPoint.j];
            goal.IsGoal = true;
            // Помечаем стартовую вершину как текущую
            Vertex current = start;
  
            // В этом списке будем копить посещенные вершины
            List<Vertex> visitedVertices = new List<Vertex>();
  
            while (current != null)
            {
                // Находим подходящих (годных) соседей: которые еще не посещены, не являются препятствиями и т.п.
                List<Vertex> neighbors = GetValidNeighbors(current);

                foreach (Vertex neighbor in neighbors)
                {
                    double currentWeight = current.Label + Weight(current, neighbor);
                    if (currentWeight < neighbor.Label)
                    {
                        neighbor.Label = currentWeight;
                        neighbor.CameFrom = current.Coordinate;
                    }                    
                }

                // После того как все подходящие соседи рассмотрены (им расставлены метки), помечаем текущую вершину как посещенную
                current.IsVisited = true;
                // и добавляем ее в список посещенных вершин
                visitedVertices.Add(current);
                // Используем этот список посещенных вершин для поиска новой текущей вершины
                current = GetCurrent(visitedVertices);
            }

            // В конце работы алгоритма в целевой вершине в свойстве Label будет находиться длина искомого пути
            shortestPathLength = goal.Label;
            // Основываясь на свойстве CameFrom сформируем и вернем сам искомый путь
            return GetShortestPath(goal);
        }

Следующий метод на основе свойства CameFrom класса Vertex «собирает» и возвращает нам искомый путь:

GetShortestPath(Vertex goal)

private List<Point2D> GetShortestPath(Vertex goal)
        {
            List<Point2D> path = new List<Point2D>();
            
            path.Add(goal.Coordinate);
            Point2D cameFrom = goal.CameFrom;

            while (cameFrom != null)
            {
                Vertex vertex = Vertices[cameFrom.i, cameFrom.j];
                path.Add(vertex.Coordinate);
                cameFrom = vertex.CameFrom;
            }

            return path;
        }

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

Ну вот, в принципе, и все!

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

Стоит, наверное, все-таки упомянуть о двух перегруженных конструкторах класса Graph, один из которых инициализирует граф с помощью поверхности (для расчетов я использовал только Гауссиан, можете поиграться с другими поверхностями), а другой конструктор инициализирует граф с помощью матрицы из нулей и единичек (матрица-препятствий) для создания сложных лабиринтов.

Давайте наконец посмотрим на результаты!

Результаты расчетов. Простые препятствия

Начнем с простого примера. Создадим на сетке небольшое вертикальное препятствие, рисунок 4, и позволим алгоритму обойти его:

Рисунок 4. Поиск кратчайшего маршрута с вертикальным препятствием. Шаги dx = dy = 1. Количество вершин графа - 15 * 10 = 150. Черные вершины - препятствия, красные - кратчайший путь.

Рисунок 4. Поиск кратчайшего маршрута с вертикальным препятствием. Шаги dx = dy = 1. Количество вершин графа — 15 * 10 = 150. Черные вершины — препятствия, красные — кратчайший путь.

Вершины графа, которые попадают под черную линию имеют свойство IsObstacle = true, чтобы пометить их как вершины-препятствия. Параметр уклона MaxSlope здесь не имеет смысла, т.к. алгоритм работает в одной плоскости. Как видите, результат очень правдоподобный.

Из архива. Как я формировал это препятствие в csv-файле. Координаты пути

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

Рассмотрим следующий пример, рисунок 5:

Рисунок 5. Проход "свозь" препятствие.

Рисунок 5. Проход «свозь» препятствие.

В данном случае я бы не сказал, что это является ошибкой, ведь алгоритм учитывает как препятствие только те вершины графа, которые непосредственно помечены как вершины-препятствия (IsObstacle = true).

А вот если мы сформируем препятствие вот так, тогда алгоритм никуда не денется и ему придется его обойти, рисунок 6:

Рисунок 6. Обход ступенчатого препятствия.

Рисунок 6. Обход ступенчатого препятствия.

Лабиринты

Усложним работу для алгоритма и сформируем для него целый лабиринт препятствий, рисунок 7:

Рисунок 7. Поиск кратчайшего пути между двумя заданными точками в лабиринте. Красным отмечен кратчайший путь из вершины A в вершину B.

Рисунок 7. Поиск кратчайшего пути между двумя заданными точками в лабиринте. Красным отмечен кратчайший путь из вершины A в вершину B.

Увеличим лабиринт и изменим целевую вершину, рисунок 8:

Рисунок 8. Поиск кратчайшего пути в большом лабиринте.

Рисунок 8. Поиск кратчайшего пути в большом лабиринте.

Все исходные файлы по которым были построены все вышеперечисленные препятствия и лабиринты, а также координаты найденных путей находятся в проекте в соответствующих папках.

Поиск оптимального пути на поверхности

Ну и в конце приведу пример поиска оптимального пути между двумя точками на сложной поверхности. Именно оптимального, т.к. нельзя сказать, что он кратчайший, рисунок 9:

Рисунок 9. Поиска оптимального пути между двумя точками поверхности. Параметр MaxSlope= 20 градусов.

Рисунок 9. Поиска оптимального пути между двумя точками поверхности. Параметр MaxSlope= 20 градусов.

Для имитации холмов и оврага были использованы двумерные функции Гаусса с различными параметрами. Для имитации искусственных сооружений в класс Graph был добавлен метод:

CreateBuilding()

public void CreateBuilding(Point2D bottomLeftCoordinate, int width, int length, double height)
        {
            for (int i = bottomLeftCoordinate.i; i < bottomLeftCoordinate.i + width; i++)
            {
                for (int j = bottomLeftCoordinate.j; j < bottomLeftCoordinate.j + length; j++)
                    Vertices[i, j].Height = height;
            }
        }

Замечу, что здесь ни одна вершина графа не помечена как препятствие, алгоритм находит оптимальный путь благодаря заданию параметра MaxSlope. Если задать параметр MaxSlope очень большим — алгоритм может пойти прямиком через горы.

И еще картинки с других ракурсов.

Рисунок 10. Вид с другого ракурса. Без осей координат.

Рисунок 10. Вид с другого ракурса. Без осей координат.
Рисунок 11. Вид сверху.
Рисунок 11. Вид сверху.

Заключение

Спасибо за прочтение!

Надеюсь статья была интересной и полезной. Все доработки и оптимизацию алгоритма оставляю Вам.

Литература:

Робин Уилсон. Введение в теорию графов. Пятое издание. 2019

Всем добра и качественного кода!

На правильном ли вы пути? Подходящую ли профессию выбрали для себя? Не теряете ли время в погоне за ложными целями?

Для получения ясности в этих вопросах вам помогут три признака правильного пути, которые вы найдёте в этой статье. Вам стоит о них знать!

Беседа в кафе

— Сколько лет ты работаешь помощником адвоката? — спросил я.

— Пять лет.

— Как часто тебя хвалили за то, что делаешь свою работу хорошо?

— Очень редко… — она задумалась, — может, один или два раза.

— Ты говорила, что немного выступала на сцене в драматических спектаклях. Как часто тебя хвалили?

— Каждый раз. После каждого выступления мне говорили, что я очень хорошо играю.

— За пять лет работы помощником адвоката тебя хвалили несколько раз. Ты немного играла в театре, и каждое выступление вызывало восхищение.

— Да, — она задумалась.

— Нужны ли комментарии по поводу того, какой из путей правильный для тебя?

— Нет. Всё понятно.

Этот диалог между мной и одной моей знакомой идеально иллюстрирует первый признак правильного пути, о котором сейчас поговорим.

Первый признак правильного пути

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

Когда путь не подходит, люди хвалят редко и благодарят скорее из вежливости, чем от чистого сердца. Вы можете годами заниматься неправильной для вас деятельностью, но редко кто-то скажет: “Это твоё! Продолжай в том же духе!”

Но, возразите вы, первые результаты новичка не могут кого-то впечатлить, даже если это правильный путь для него. Вряд ли первые работы талантливого художника вызовут восторг. Чтобы впечатлить, ему нужно совершенствоваться.

Соглашусь лишь отчасти.

Если талант есть, первые работы будут выполнены особенно, с чувством. Это сразу бросается в глаза. Когда видишь первые результаты талантливого фотографа, становится ясно: человек хорошо чувствует фотографию.

Я знаю фотографа, который овладел всеми секретами съёмки и пользуется профессиональной техникой, но его работы всё равно не впечатляют. Даже спустя много лет я смотрю на его фотографии без эмоций.

Да, совершенствоваться нужно, но когда таланта нет в какой-либо области, восхищение окружающих не придёт с опытом.

Но как насчёт нетворческих профессий?

С художниками, фотографами, писателями и музыкантами всё понятно. Как насчёт бухгалтеров, сантехников, барменов или директоров? Принцип тот же.

Если вы руководитель от Бога, сотрудники неоднократно скажут вам об этом. По выражению их лиц и словам вы поймёте, что они восхищаются вами.

Бухгалтеры. Про одних говорят: “Это очень хороший бухгалтер! Нам повезло с ним”, а про других вообще ничего не говорят. Чувствуете разницу?

Хороший врач узнаёт от пациентов, что его посоветовали знакомые. У хорошего парикмахера постоянные клиенты постоянно довольны.

Чем бы вы ни занимались, если путь правильный, вы обязательно получите положительную обратную связь от людей. Долго ждать не придётся.

Второй признак правильного пути

Если вы на правильном пути, то движетесь вперёд быстро. Внешних препятствий либо нет вообще, либо они преодолеваются естественно и без особых усилий. Все события складываются идеально, а возможности сами идут в руки.

Если путь не ваш, вы движетесь медленно и встречаете одну преграду за другой. Препятствия даются нелегко и мешают получить быстрые результаты. Их преодоление отнимает время и силы.

Речь идёт только о внешних преградах: отказ чиновника, отсутствие материалов, фрилансер долго делает работу и так далее. Если путь ваш, такие преграды преодолеваются естественно. Иначе путь не ваш.

Чем внешние преграды отличаются от внутренних?

Внутренние преграды — это страхи, сомнения, отсутствие опыта и прочее. Они не относятся ко второму признаку и на правильном пути всё же возникают. Устраняются либо опытом, либо работой с негативными эмоциями.

Итак, второй показатель верного пути — это быстрые промежуточные результаты, не обусловленные внешними преградами.

Почему быстрое движение вперёд означает правильный путь?

Представьте, что пытаетесь ехать по снегу на роликовых коньках. Движение будет быстрым? Нет. С таким же успехом можно ехать по асфальту на лыжах.

Несовместимость поверхности и средства передвижения затрудняет движение вперёд.

Теперь возьмите сноуборд. Движение будет гладким и быстрым. Единственная возможная преграда — страх или отсутствие опыта, которые преодолеваются практикой. Но это внутренняя преграда, а не внешняя.

Поверхность в метафоре — это ваша база, ваш фундамент. Это сумма всех врождённых способностей и талантов. Средство передвижения — это вид деятельности: профессия, должность, занятие, специальность, ремесло.

Когда средство передвижения совместимо с поверхностью, движение будет лёгким. Ваши таланты входят в резонанс с видом деятельности, усиливая качество и амплитуду результатов.

Вы можете выбрать разные средства передвижения: коньки, лыжи, велосипед, скейтборд и так далее. Но поверхность изменить нельзя. Да и не нужно.

Как выявить свой базовый набор талантов?

Когда известно, по какой поверхности вам предстоит ехать, выбор средства передвижения не представляет никакой трудности. Трудность возникает, если неизвестно с каким покрытием вы имеете дело.

Вашу основу, сотканную из личных качеств и талантов, можно выявить через вспоминание всего, чем вам нравилось заниматься от раннего детства и до настоящего момента.

Для этого я создал несколько разных методик. Обязательно воспользуйтесь ими. А сейчас давайте перейдём к самому важному показателю правильного пути…

Третий признак правильного пути (ключевой)

При мысли о правильном пути ваше тело обязательно отзовётся приятным ощущением. Мысль о неверном пути будет вызывать неприятные ощущения или полное отсутствие какой-либо реакции.

Речь идёт именно об ощущениях, не эмоциях. Эмоциями могут быть радость, энтузиазм, воодушевление. Ощущения — это тепло, лёгкость, движение энергии, приятное расширение.

Почему этот индикатор работает безотказно?

Ваше подсознание знает о вашем фундаменте. Знает о ваших талантах и врождённых способностях. Знает, что принесёт вам признание и деньги, а что принесёт только разочарование.

Сознание не имеет прямого доступа к этой информации. Иначе, вы не читали бы сейчас эту статью. Зато ум всегда может обратиться к телесным реакциям, чтобы получить от подсознания правильный ответ.

Опустите внимание в тело, наблюдайте ощущения от головы до живота и думайте про выбранный путь. Появились приятные ощущения? Поздравляю! Вы сделали правильный выбор профессии, начали правильный проект или бизнес. Вас ждёт успех.

Обязательно пользуйтесь этим! Рекомендую настоятельно и категорично. Люди, игнорирующие этот принцип, обычно находятся не в том месте, не с теми людьми и занимаются не тем, что им подходит.

Парадокс третьего признака

Нередко возникают ситуации, когда ум полностью уверен в правильности решения, а тело реагирует отрицательно. Бывает наоборот: ум уверен, что решение не стоит принимать, а тело реагирует приятными ощущениями.

Однажды меня пригласили на фестиваль классического аниме 60х годов. Я не любитель жанра и был абсолютно уверен, что мне не понравится. Я хотел было отказаться, но решил проверить телесный отклик… Меня удивило насколько приятным ощущением отреагировало тело!

Я пошёл. Неожиданностей было много. Мультфильмы оказались очень смешными, атмосфера в кинозале была потрясающей, а после просмотра мы с друзьями классно провели время в кафе неподалёку.

Бывало наоборот. В 2012 году я взялся за долгосрочный проект, успех которого не вызывал сомнений. Телесные отклики молчали, что означает “нет”, но я проигнорировал это. Через 7 месяцев я закончил проект и он оказался провальным. Тело знало с самого начала!

Кстати, все эти 7 месяцев я постоянно боролся с внешними препятствиями. На протяжении всего пути обнаруживались новые подводные камни. Узнаёте второй признак?

Почему третий признак является ключевым?

Если есть приятные ощущения при мысли о выбранном пути, это автоматически гарантирует, что с первыми двумя признаками тоже всё будет в полном порядке. И движение будет быстрым и позитивные отзывы получите.

Я не прошу верить мне. Я прошу проверить на личном опыте.

“А вдруг, я убедил себя, что это моё, и тело реагирует позитивно на неверный путь?”

Хорошо. Допустим, вы действительно можете убедить себя, что неправильный путь подходит вам идеально. И не просто убедить, но ещё и вызвать приятные ощущения в теле.

Давайте это проверим.

Выберите профессию, к которой совершенно равнодушны. Например: проктолог, бухгалтер или кондуктор. Теперь убедите себя, что это работа вашей мечты. Внушите себе, что это приносит вам удовольствие и радость.

Наблюдайте ощущения в теле и повторяйте про себя: “Работай [название профессии]”. Повторяйте про себя эту мысль и усилием воли вызовите приятные ощущения в теле.

Попробуйте. Насколько мне известно, ещё никому не удавалось это сделать. Возможно, именно вы станете первым? ;-)

Этот простой эксперимент сразу даёт понять, что телесные отклики не контролируются сознанием. Тело самостоятельно решает: реагировать приятными ощущениями или нет. В этом вся ценность опыта!

“Всё-таки нужно много практиковаться, чтобы получить позитивные отзывы от людей”

Я знаю музыканта, который тоже так думает. 5 лет назад он сказал мне, что для успеха нужно просто писать музыку без остановки. Он следует этому убеждению и сегодня.

Результат? Его музыка до сих пор не вызывает восторг у слушателей. Как ни странно…

Знаю противоположный случай. Первые и далеко не идеальные работы другого музыканта сразу впечатлили людей. Прошло шесть лет. Теперь он известен в других странах, его приглашают на выступления, его альбомы издают.

Первый рассказ Стивена Кинга так понравился его маме, что она сделала ксерокопии и разослала всем родственникам. Реакция родственников была такой же, хотя рассказ был очень далёк от совершенства.

Первые статьи Ярослава Самойлова про отношения сразу вызвали положительную реакцию у женщин. Раньше он писал про повышение продаж, и реакция читателей была нулевой. Выбор правильной темы сделал своё дело.

Кстати, нашёл он свою нишу благодаря этому методу. Новый путь вызвал у него приятные ощущения, первые статьи были написаны быстро и сразу набрали положительные отзывы. Сегодня его приглашают на телевидение, и это только начало.

Как видите, победа по всем трём признакам!

“Я уверен, что путь правильный, но постоянно присутствуют внешние преграды”

Если путь верный, значит, у вас есть приятные ощущения при мысли о нём. Значит, люди благодарят вас и восхищаются результатом. Если всё так, к внешним препятствиям вы относитесь нормально, они воспринимаются как часть пути.

Но если внешние препятствия тяготят, вероятно, в теле так же отсутствует позитивный отклик и, скорее всего, люди не особо хвалят вас за то, чем вы занимаетесь. А значит, путь не ваш.

Исключения бывают редко. Самое главное — это наличие приятного отклика в теле. Когда он есть, по остальным признакам рано или поздно всё становится хорошо. Поэтому отклик проверяйте в первую очередь!

Заключение

Итак, вы узнали о трёх признаках правильного пути. Надеюсь, ваш путь оказался верным и по всем признакам у вас твёрдое “да”. Не расстраивайтесь, если проверка показала обратное. Я уверен, вы обязательно найдёте себя!

Давайте ещё раз вспомним три признака правильного пути:

  1. Люди хвалят и благодарят за то, чем вы занимаетесь;
  2. Вы довольно быстро движетесь вперёд и легко преодолеваете внешние препятствия;
  3. В теле возникают приятные ощущения при мысли о выбранном пути.

Пользуйтесь этими признаками, расскажите о них друзьям. Я желаю вам больших успехов и процветания на пути, который подходит вам идеально!

Напишите о своём опыте в комментариях.
До новых встреч!

Публикуя статью в сети, поставьте ссылку на оригинал. Спасибо! =8-)

Алгоритм Дейкстры. Разбор Задач

Время на прочтение
7 мин

Количество просмотров 36K

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

Если вы хотите найти ответить на вопросы, чем этот алгоритм лучше BFS (поиска в ширину), при каких условиях алгоритм применим, и какие теоретические и практические задачи можно с его помощью решать, читайте далее.

Введение

Алгоритм Дейкстры работает на ориентированных (с некоторыми дополнениями и на неориентированных) графах, и призван искать кратчайшие пути между заданной вершиной и всеми остальными вершинами в графе.

Как правило, граф обозначают как набор вершин и рёбер inline G = (V,E), где число рёбер может быть задано inline m, а вершин числом inline n.

Для каждого ребра в графе задан неотрицательный вес inline l_i, а также вершина, из которой осуществляется поиск оптимальных путей.

Алгоритм Дейкстры может найти кратчайший путь между вершинами inline s и inline t в графе, только если существует хотя бы один путь между этими вершинами. Если это условие не выполняется, то алгоритм отработает корректно, вернув значение «бесконечность» для пары несвязанных вершин.

Условие неотрицательности весов рёбер крайне важно и от него нельзя просто избавиться. Не получится свести задачу к решаемой алгоритмом Дейкстры, прибавив наибольший по модулю вес ко всем рёбрам. Это может изменить оптимальный маршрут. На рисунке видно, что в первом случае оптимальный путь между inline a и inline d (сумма рёбер на пути наименьшая) изменяется при такой манипуляции. В оригинале путь проходит через inline a rightarrow b rightarrow c rightarrow d, а после добавления семёрки ко всем рёбрам, оптимальный путь проходит через inline a rightarrow c rightarrow d.

Как ведёт себя алгоритм Дейкстры на исходном графе, мы разберём, когда выпишем алгоритм. Но для начала зададимся другим вопросом: «почему не применить поиск в ширину для нашего графа?». Известно, что метод BFS находит оптимальный путь от произвольной вершины в ориентированном графе до любой другой вершины, но это справедливо только для рёбер с единичным весом.

Свести задачу к решаемой BFS можно, но если заменить все рёбра неединичной длины inline n рёбрами длины inline 1, то граф очень разрастётся, и это приведёт к огромному числу действий при вычислении оптимального маршрута.

Чтобы этого избежать предлагается использовать алгоритм Дейкстры. Опишем его:

Инициализация:

Основный цикл алгоритма:

  • Пока все вершины не исследованы (или формально inline X neq V), повторяем:

В итоге исполнения этого алгоритма, массив inline A будет содержать все оптимальные пути, исходящие из inline s.

Примеры работы

image

Рассмотрим граф выше, в нём будем искать пути от inline a до всего остального.

Первый шаг алгоритма определит, что кратчайший путь до inline b проходит по направлению синей стрелки и зафиксирует кратчайший путь. Второй шаг рассмотрит, все возможные варианты inline A[v] + l_{vw} и окажется, что оптимальный вариант двигаться вдоль красной стрелки, поскольку inline 5 меньше, чем inline 3 + 3 = 6 и inline 3 + 6 = 9. Добавляется длина кратчайшего пути до inline c. И наконец, третьим шагом, когда три вершины inline a,b,c уже лежат в inline X, остается рассмотреть только два ребра и выбрать, лежащее вдоль зеленой стрелки.

Теперь рассмотрим граф с отрицательными весами, упомянутый выше. Напомню, алгоритм Дейкстры на таком графе может работать некорректно.

image

Первым шагом отбирается ребро вдоль синей стрелки, поскольку это ребро наименьшего веса из исходной вершины. Затем выбирается ребро inline c rightarrow d. Это зафиксирует навсегда неверный путь от inline a к inline d, в то время как оптимальный путь проходит через центр с отрицательным весом. Последним шагом, будет добавлена вершина inline b.

Оценка сложности алгоритма

К этому моменту мы разобрали сам алгоритм, ограничения, накладываемые на его работу и ряд примеров его применения. Давайте упомянем какова вычислительная сложность этого алгоритма, поскольку это пригодится нам для решения задач, ради которых затевалась эта статья.
Базовый подход, основанный на циклах, предполагает проход по всем рёбрам каждого узла, что приводит к сложности inline theta(mn).

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

Что еще можно сказать о куче:

  • это сбалансированное бинарное дерево,
  • ключ текущего узла всегда меньше, либо равен ключей дочерних узлов.

Интересную задачу с использованием куч я разбирал ранее в этом посте.

Используя кучу в алгоритме Дейкстры, где в качестве ключей используются расстояния от вершины в неисследованной части графа (в алгоритме это inline V-X), до ближайшей вершины в уже покрытом (это множество вершин inline X), можно сократить вычислительную сложность до inline O(mlog(n)). Доказательство справедливости этих оценок я оставляю за пределами этой статьи.

Далее перейдём к разбору задач!

Задача №1

Будем называть узким местом пути в графе ребро максимальной длины в этом пути. Путём с минимальным узким местом назовём такой путь между вершинами s и t, что не существует другого пути s rightarrow t, чьё узкое место меньше по длине. Требуется построить алгоритм, который вычисляет путь с минимальным узким местом для двух данных вершин в графе. Асимптотическая сложность такого алгоритма должна быть O(mlog{n})

Решение

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

В случае классической задачи, поиска пути минимальной длины между двумя вершинами графа, мы поддерживаем в каждой посещенной алгоритмом вершине графа минимальную длину пути до этой вершины. Здесь стоит оговориться, что будем именовать множество X посещенными вершинами, а V - X часть графа, для которой еще нужно найти величину пути или узкого места.

В отличии от классического алгоритма, решение этой задачи должно поддерживать величину актуального узкого места пути, приводящего в вершину v in X. А при добавлении новой вершины из V - X, мы должны смотреть не увеличивает ли ребро (v,u_1) величину узкого места пути, которое теперь приводит в u_1.
Если ребро (v, u_1) увеличивает узкое место, то лучше рассмотреть вершину u_2, ребро (v, u_2) до которой легче (v,u_1). Поиск неувеличивающих узкое место ребёр нужно осуществлять не только среди соседей определенного узла v, но и среди всех v in X, поскольку отдавая предпочтение вершине, путь в которую имеет наименьшее узкое место в данный момент, мы гарантируем, что мы не ухудшаем ситуацию для других вершин.

Последнее можно проиллюстрировать примером: если путь, оканчивающийся в вершине p имеет узкое место величины 3, и есть вершина q с ребром (p,q) веса 4, и r с ребром (p,r) веса 5, то предпочтение отдаётся q, алгоритм даст верный результат в обоих случая, если существует (q,r) веса 3 или веса 10.

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

A(u) = min_{v in X}left(max left[A(v), wleft(v,uright)right]right), , u in V - X

Стоит пояснить, что поиск по v in X осуществляется, только для существующих связей (v,u), а w(v,u) - это вес ребра (v,u).

image

Задача №2

Предлагается решить более практическую задачу. Пусть inline n городов, между ними существуют пути, заданные массивом edges[i] = [city_a, city_b, distance_ab], а также дано целое число mileage.

Требуется найти такой город из данных, из которого можно добраться до наименьшего числа городов не превысив mileage.

Стоит отметить, что граф неориентированый, т.е. по пути между городами можно двигаться в обе стороны, а длина пути между городами a и c может быть получена как сумма длин путей a -> b и b -> c, если есть маршрут a -> b -> c

Решение

С решением данной проблемы нам поможет алгоритм Дейкстры и описанная выше реализация с помощью кучи. Поставщиком этой структуры данных станет библиотека heapq в Python.

Будем использовать алгоритм Дейкстры для того, чтобы подсчитать количество соседних городов, расстояние до которых меньше mileage, для каждого из inline n городов. Соберем количества соседей в в одном месте и найдем минимум из них.

Поскольку наш граф неориентированный, то из любой его вершины inline s можно добраться до произвольной вершины inline t. Будем использовать алгоритм Дейкстры для того, чтобы для каждого из городов в графе построить кратчайшие пути до всех остальных городов, мы это уже умеем делать в теории. И чтобы, оптимизировать этот процесс, будем в его течении сразу отвергать пути, которые превышают mileage, а не делать постфактум, когда все пути получены.

Давайте опишем функцию решения:

def least_reachable_city(n, edges, mileage):
        """
        входные параметры:
            n --- количество городов,
            edges --- тройки (a, b, distance_ab),
            mileage --- максимально допустимое расстояние между городами 
            для соседства
        """
        # заполняем список смежности (adjacency list), в нашем случае это 
        # словарь, в котором ключи это города, а значения --- пары 
        # (<другой_город>, <расстояние_до_него>)

        graph = {}
        for u, v, w in edges:
            if graph.get(u, None) is None:
                graph[u] = [(v, w)]
            else:
                graph[u].append((v, w))
            if graph.get(v, None) is None:
                graph[v] = [(u, w)]
            else:
                graph[v].append((u, w))
        
        # локально объявим функцию, которая будет считать кратчайшие пути в 
        # графе от вершины, до всех вершин, удовлетворяющих условию
        def num_reachable_neighbors(city):
            # создаем кучу, из одного элемента с парой, задающей нулевую 
            # длину пути до самого исходного города
            heap = [(0, city)]
            # и массив, содержащий города и кратчайшие 
            # расстояния до них от исходного
            distances = {}
            # затем, пока куча не пуста, извлекаем ближайший 
            # от посещенных городов город
            while heap:
                currDist, neighb = heapq.heappop(heap)
                # если кратчайшее ребро ведет к городу, где мы уже знаем 
                # оптимальный маршрут, то завершаем итерацию
                if neighb in distances:
                    continue
                # в остальных случаях, и если сосед не является отправным 
                # городом, мы добавляем новую запись в массив кратчайших расстояний
                if neighb != city:    
                    distances[neighb] = currDist
                # обрабатываем всех смежных городов с соседом, добавляя их в кучу 
                # но только если: а) до них еще не известен кратчайший маршрут и б) путь до них через neighb не выходит за пределы mileage
                for node, d in graph[neighb]:
                    if node in distances:
                        continue
                    if currDist + d <= mileage:
                        heapq.heappush(heap, (currDist + d, node))
            # возвращаем количество городов, прошедших проверку
            return len(distances)
        
        # выполним поиск соседей для каждого из городов
        cities_neighbors = {num_reachable_neighbors(city): city for city in range(n)}
        # вернём номер города, у которого наименьшее число соседей
        # в пределах досигаемости
        return cities_neighbors[min(cities_neighbors)]

В функции выше, в комментариях, подробно описывается, как метод Дейкстры, реализованный на куче позволяет найти расстояния до всех городов, в пределах `mileage`. Основную сложность для понимания предстваляет цикл, работающий с кучей.

Заключение

Алгоритм Дейкстры это мощный инструмент в мире работы с графами, область применения его крайне широка. С его помощью можно оценить даже целесообразность добавления новой ветки метро, новой дороги или маршрута в компьютерной сети. Он прост в исполнении и интуитивно понятен, как другие жадные (greedy) алгоритмы. Вычислительная сложность решений задач с его помощью зачастую не выше inline O(m log(n)). При некоторых условиях может достигать линейной сложности (существует алгоритм линейной сложности, решающий первую задачу, при условии, что граф неориентированный).

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

Статья подготовлена в преддверии старта курса «Алгоритмы для разработчиков». Узнать о курсе подробнее, а также зарегистрироваться на бесплатный демоурок можно по ссылке.

Информация

[1] Условия задач взяты из книги «Algorithms Illuminated: Part 2: Graph Algorithms and Data Structures» от Tim Roughgarden,
[2] и с сайта leetcode.com.
[3] Решения авторские.

Жизнь человека – не просто биологическое явление. Это индивидуальный социально-исторический факт. Потому что каждое живое существо растёт и развивается, но только человек формируется, как личность со своим мировоззрением, ценностями, притязаниями и достижениями, а также направленностью. А ещё, что самое главное, с индивидуальными целями, планами, мечтами и стремлениями. Добиваясь и живя ими, человек проходит свой собственный путь в этом мире. Но не у каждого всё складывается именно так. Некоторые даже не знают, как найти свой путь в жизни. И существуют бесцельно. Эта тема часто рассматривается психологами, способными своими советами и рекомендациями помочь людям прийти к цели существования и найти свой путь в жизни. Вот об этом и хотелось бы поговорить.

как найти свой путь в жизни

За пределами пути

Сначала хотелось бы отметить, что жизнь человека принято делить на пять фаз. Особенно много внимания этому уделял немецкий психолог Карл Бюлер. И первая фаза, которая длится до 16-20 лет, находится за пределами жизненного пути. Потому что данный период предшествует самоопределению. У столь молодого человека ещё нет полноты возможностей, профессии и доступа ко всем шансам проявить себя.

Вторая фаза

Она, как правило, начинается с 16-20 лет и длится до 25-30. В этот довольно-таки длинный период молодости человек начинает пробовать себя в различных сферах деятельности, формировать связи с другими людьми, даже искать себе пару. На данном этапе начинается самоопределение.

Это – утверждение личностью индивидуальных позиций в различных ситуациях, формирование мировоззрения и ценностей, а также установка ориентира для самоорганизации. За ним, чаще всего, стоят надежды. Которые в данном случае ассоциируются с эскизами возможных путей жизни.

Преграды

На втором этапе многие сталкиваются с растерянностью, неуверенностью, страхом и неизвестностью. Нередко опасения связаны с тем, что человек осознаёт ответственность, которая ложится на его плечи в связи со сделанным им жизненным выбором. Ведь он сам определяет приемлемые для себя социальные нормы, доминирующие ориентиры, важнейшие ценности и многое другое. У многих людей этот период затягивается настолько, что они полностью теряют представления о том, как найти свой путь в жизни, и бесцельно существуют изо дня в день.

 как найти свой путь в жизни тест

Проблема выбора

Если человек долгое время пребывает в потерянном состоянии и не знает, ради чего он просыпается ежедневно, то нужно действовать. Лучше начать с определения модели своего пути.

«Жизнь как творчество» — этот вариант подходит людям, с наклонностями к экспериментам. Тем личностям, которые любят ставить эксперименты над своей судьбой. Их жизненные пути кажутся нестандартными людям, привыкшим к «обычной» модели. Которая называется «жизнью по правилам». Это наименее удачная модель. Потому что человек, ориентирующийся на неё, следует тем правилам, которые установлены обществом, властями, условными нормами. Он живёт по принципу «так, как принято», а не так, как ему хочется. И когда к нему приходит это осознание и чувство, что он потратил свои годы впустую, то становится слишком поздно. И горечь сожаления в такие моменты неимоверная.

Некоторым подходит вариант «жизнь как достижение». Каждый, кто выбирает его, выступает в роли человека, «сделавшего себя». Это трудоголики, прагматики, карьеристы – все люди, рассматривающие данное им время, как ценный ресурс для реализации как можно большего количества целей, способных обеспечить лучшую жизнь.

Ещё хотелось бы отметить вниманием модель под названием «жизнь против жизни». Люди, следующие ей, находятся в постоянном процессе борьбы. В переносном смысле слова, разумеется. И чаще всего борьба ведётся за социальные блага.

верный путь в жизни

Прислушаться к желаниям

Вот что необходимо сделать человеку в первую очередь, если он задумывается о том, как найти путь к новой жизни. Желания – это часть нашего естества. Они индивидуальны и изменчивы, у каждого свои. Но все они носят социальный характер. И желания, благодаря сознательной деятельности человека и его мышлению, рано или поздно перерождаются в некие жизненные цели. Которые и определяют смысл существования.

Верный путь в жизни человеку удастся определить, если он прислушается к самому себе. К тому, чего он хочет. Сильные желания всегда становятся целями, так как они являются мощной стимулирующей энергией для любой деятельности человека – будь то творчество, бизнес, или межличностные отношения.

Рекомендации специалистов

Абсолютно каждый психолог скажет своему пациенту: «Путь вашей жизни – это только ваше решение». И будет прав. Лишь человек имеет право ею распоряжаться. Но всё же есть немало дельных рекомендаций, которые способны помочь разобраться со своими стремлениями и желаниями.

Если человек действительно не знает, чего он хочет, то ему необходимо расширить сферу знаний и круг интересов. Как? Надо начать пробовать что-то новое, ранее не изведанное. Нередко путь жизни человека лежит за пределами его восприятия. И когда он охватывает своим вниманием те области, которые ранее были закрыты от него, он будто начинает видеть своё существование с другого ракурса. Возникает бесценное чувство долгожданной определённости – человек, наконец, находит то, что давно искал.

Также психологи рекомендуют не ограничивать себя. Некоторые могут считать, что эффективная работа возможна лишь в одном направлении. Безусловно, имеются такие цели, которые требуют «полного погружения». Но не нужно тратить всего себя на её осуществление. Рекомендуется комбинировать такую цель с другими, занимающими меньше сил и времени. Как правило, они прекрасно комбинируются и гармонируют.

 путь духовной жизни

Ориентир на знания и возможности

Найти начало пути в жизни человеку будет проще, если он проанализирует себя, как источник полезных ресурсов для его прохождения. Что он умеет делать? На совершение каких поступков способен? Как далеко может зайти, в погоне за смыслом жизни? Способности играют важную роль в процессе прохождения жизненного пути. Ведь они являются свойствами личностями, определяющими успешное осуществление того или иного рода деятельности.

Способности – наши внутренние психические регуляторы, обуславливающие возможность приобретения навыков, умений и знаний. Одни из них определяют успешность воспитания и обучения, а также формирование личностных качеств. Другие отвечают за производство новых идей, открытий. Способности – ресурсы человека, которыми он обязательно должен пользоваться во имя достижения целей. Знания об их наличии нередко помогают найти путь духовной жизни. Нужно лишь задать себе один вопрос: «На что я способен?», и дать на него ответы. Возможно, в одном из них и будет скрыт смысл жизни.

О ценностях

Наверняка каждый человек, принимая поздравления в честь того или иного события, слышал в свой адрес и такое: «Желаю тебе найти свой путь в жизни». Действительно, многие способны это сказать, но не всем дано поддержать выбор человека. Потому что конфликт ценностей всегда имел и будет иметь место быть.

А жизненный путь определяется именно ими. Потому что человек может обрести счастье лишь в том случае, если в его существовании будет нечто значимое, способное удовлетворить его многообразные потребности. И никак иначе. Человек, для которого первоочерёдную значимость имеет независимость, абсолютная свобода и развлечения, не будет полностью счастлив, если он обременит себя семьёй, детьми и ответственностью перед ними. И наоборот. Тот, кто больше всего мечтает о большой дружной семье, не обретёт счастья, если будет сутками пропадать на работе.

Так о чём речь? О том, что, определяя свой жизненный путь, человек должен ориентироваться не только на желания и возможности, но и на ценности. Они – составляющая его души.

путь к новой жизни

Прогнозирование

Если человек не знает, чего он хочет, и считает, что находится в безысходном положении, то это не так. Просто к решению вопросы нужно подойти с другой стороны. И ответить себе на следующий вопрос: «Чего я не хочу?» На него, как правило, люди находят ответы быстрее.

Только они должны быть конкретизированы. Понятное дело, что большинство ответит: «Я не хочу прожить свои годы бесцельно». Но что именно вложено в это утверждение? У каждого – что-то своё. Одни не хотят оказаться одинокими на старости лет. Другие не желают просуществовать всю жизнь в одном и том же городе. Для некоторых худшим страхом является бедность и нищета. Ещё кто-то боится покинуть этот свет, ничего после себя не оставив.

Вариантов — тысячи. Но суть такова: если нет ни малейшего представления о своих желаниях, то нужно отталкиваться от противоположного.

Тестирование

Оно действительно может помочь некоторым людям, которые озадачены вопросом касательно того, как найти свой путь в жизни. Тест такого рода можно найти в свободном доступе. Он направлен на исследование потребностей человека и его мотиваций. Всё, что ему нужно, – давать ответы на уже готовые вопросы. Варианты, как правило, имеются. Так что остаётся лишь отметить наиболее подходящий и близкий человеку.

В качестве примера можно привести несколько вопросов из разных блоков. Первый сформулирован так: «Время – деньги. Надо стремиться заработать как можно больше». Вопрос из второго блока звучит следующим образом: «Работа – это вынужденная жизненная необходимость». Далее, как правило, идут вопросы, направленные на выявление факторов, отнимающих у человека больше всего времени, а также на определение предрасположенности к тем или иным сферам деятельности. На всё вопросы, кстати, предлагаются такие варианты ответов: «Я согласен с этим», «50 х 50» и «Я так не считаю».

Конечно, после этого человеку не откроется истина касательно того, как найти свой путь в жизни. Тест, однако, может натолкнуть на размышления. А результаты подскажут направление, в котором стоило бы начать действовать.

желаю тебе найти свой путь в жизни

Самореализация

Как найти свой путь в жизни? Нужно делать то, что получается лучше всего. Или то, в чём себя всегда хотелось попробовать. При этом надо откинуть в сторону все сомнения. Многие именно из-за них закапывают свои мечты и желания. А ведь зря. Что терять? Недаром ведь говорят — лучше сделать и жалеть, чем жалеть, что не сделал. Даже если не получится – человек будет знать, что он попробовал.

Ведь свой потенциал очень важно реализовать. Это – базовая необходимость для каждого человека. Даже Абрахам Харольд Маслоу включил самореализацию в свою знаменитую пирамиду потребностей. И Аристотель говорил, что счастья можно достичь, лишь воплотив в действительность свои потенциальные возможности.

Но для этого человеку нужно избавиться от слова «надо». Практически каждый второй скажет: «Да, я бы хотел заниматься музыкой/хореографией/спортом/художеством/туризмом, но всё моё время занимает работа. Мне надо зарабатывать деньги, чтобы жить». Да, такова реальность. Деньги нужны. Но и перемены в жизни тоже необходимы! Можно найти другую работу, занимающую меньше времени. Или даже организовать своё дело. Работа, не приносящая радости и удовлетворения, является отличным поводом для увольнения и последующего образования бизнеса. А это, в свою очередь, не только полезное дело и персональный источник дохода, но и способ самореализации.

Осознание свободы

Вот что действительно важно. Человек, задумывающийся о том, как найти правильный путь в жизни, должен в первую очередь понять, что он абсолютно свободен. И волен делать всё, что ему хочется.

Но многие люди по жизни следуют чему угодно, но только не своим желаниям. Они слушают родителей, СМИ, руководствуются условными общепринятыми нормами и законами, существуют в соответствии с банальной моделью жизни. Словно забывают, что хозяевами своей жизни являются лишь они и никто другой.

Но свобода доступна каждому. Право на неё закреплено даже в Конституции. Свобода – это способность человека жить и действовать в соответствии с личными целями и интересами, руководствуясь познанием объективной необходимости. И ни в коем случае нельзя об этом забывать.

найти начало пути в жизни

Правила, которые нужно запомнить

Как найти правильный путь в жизни? Нужно следовать простым рекомендациям:

  • Помнить, что никогда не поздно изменить жизнь к лучшему.
  • Не забывать, что проблемы имеются у всех.
  • Воспринимать неудачи и неприятности как возможность получить опыт.
  • Регулярно испытывать (провоцировать) новые ощущения.
  • Заниматься любимым делом.
  • Забыть себя в прошлом и полюбить своё «Я» в настоящем.
  • Уделять внимание своим навыкам, саморазвитию и хобби.
  • Поставить цель, и ни за что от неё не отступать.

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


Загрузить PDF


Загрузить PDF

Жизнь – это путешествие, но путь не всегда бывает ясен. Каждый миг несет в себе чудо и новую возможность, но нужно знать, куда смотреть. Первое, что нужно запомнить: вы никогда не найдете свой путь, исследуя далекие горизонты будущего. Изменения начинаются с действий. Отправляйтесь в путь, двигайтесь шаг за шагом и смотрите в оба, чтобы не упустить возможности, которые перед вами открываются. Определите свои страсти и увлечения, и постарайтесь проводить больше времени за занятиями, которые имеют для вас особое значение.

  1. Изображение с названием Find Your Way Step 1

    1

    Познайте себя. Прежде чем вы сможете найти свой путь, вам нужно понять, где же вы находитесь в данный момент. Осознайте обстоятельства, в которых находитесь, даже если вы все еще не знаете, как их изменить. Постарайтесь понять, в чем именно состоит причина вашего чувства потерянности или дезориентации. Выработайте максимально четкое и ясное представление о своей жизни как она есть.

    • Подумайте о том, на что вы тратите свое время и энергию. Проанализируйте, что вы делаете каждый день, и постарайтесь разобраться в том, что из этого вас удовлетворяет и делает вашу жизнь полной, а что кажется вам бессмысленным. Подумайте о том, как свести к минимуму эти бесполезные занятия в своей жизни.
    • Попробуйте изложить эти мысли на бумаге. Напишите о своей жизни или составьте список, нарисуйте схему или карту, которая опишет то, как все ваши увлечения и обязательства связаны между собой. Возможно, вы обнаружите, что визуальное представление помогает вам лучше понять свою ситуацию.
  2. Изображение с названием Find Your Way Step 2

    2

    Начните двигаться. Найти свой путь может быть непросто, если вы исследуете далекие горизонты в поисках вдохновения. Даже если вы решите следовать по пути, на котором стоите в данный момент, по дороге вы все равно столкнетесь с бесчисленным количеством развилок и отклонений. В действительности, ваше путешествие не станет реальным, пока вы не начнете двигаться в каком-то направлении – в любом направлении. Освободитесь от инертности и наращивайте динамику. Высока вероятность того, что действие позволит вам почувствовать себя достаточно сильным, чтобы изменить многое другое в своей жизни.[1]

  3. Изображение с названием Find Your Way Step 3

    3

    Попробуйте сделать что-то. Будьте смелым и попробуйте. Это не обязательно значит, что нужно совершать что-то масштабное. Выберите какой-нибудь небольшой шаг, который вы можете предпринять, чтобы исследовать свой путь. Попробуйте и посмотрите, каково это. Если вам не понравится ход развития событий, вы всегда сможете переключиться на что-то другое.

    • Возможно, вы всегда мечтали стать музыкантом, но никогда не имели ни малейшего представления о том, как к этому прийти. Попробуйте предпринять какой-нибудь простой шаг: сходите на урок музыки и купите или одолжите дешевый инструмент. Пообещайте себе попробовать заниматься этим в течение нескольких недель.
    • Возможно, вы чувствуете, что застряли, и хотите перебраться в другой город. Предпримите какой-то небольшой шаг в этом направлении: съездите в этот город, чтобы осмотреть его, так сказать “на разведку”, или в свободное время поищите там работу и жилье. Ваше видение станет реальным только тогда, когда вы начнете воплощать его на практике.
  4. Изображение с названием Find Your Way Step 4

    4

    Начните с простого. Дорогу осилит идущий. Отдельные шаги могут казаться вам маленькими и незначительными, но они будут образовывать что-то большое и мощное, если продолжать двигаться. Такова природа пути: он не предстает перед вами весь в одночасье, путь – это путешествие длиною в жизнь. Ваш путь – это сумма каждого отдельного момента вашей жизни, всего того, что вы делаете и о чем мечтаете, и не существует никакой карты, которая показала бы вам, где именно вы окажетесь в конечном итоге.[2]

    • Началом пути может быть даже просто ваше решение попробовать что-то. Намерение – сильнейшая вещь.
  5. Изображение с названием Find Your Way Step 5

    5

    Не ищите оправданий. Легко сказать, что вы сделаете что-то, но не всегда легко довести дело до конца. Проявляйте инициативу и ничего не ждите. Чем дольше вы медлите, тем больше времени вам понадобится, чтобы, наконец, найти свой путь. Бойтесь застоя, а не препятствий.

    • Подмечайте каждый раз, когда придумываете оправдания. Научитесь распознавать знаки: возможно, вы планируете делать замечательные вещи, но поддаетесь голосу неуверенности в себе, когда приходит время действовать. Примите свои страхи, используйте их в свою пользу – в качестве стимула, топлива.

    Реклама

  1. Изображение с названием Find Your Way Step 6

    1

    Следуйте внутренней искре. Начните осознавать, какие чувства вызывают у вас определенные занятия и ситуации. Если какое-то занятие восхищает вас, захватывает, вызывает искренний интерес и желание углубиться в него – исследуйте его. Ваш путь может находиться прямо перед вами: откройтесь этой возможности. Вы не узнаете, пока не попробуете. Будьте смелы.[3]

  2. Изображение с названием Find Your Way Step 7

    2

    Примите себя. Примите свои радости и идеалы и стремитесь к тому, чтобы быть хозяином обстоятельств. Не пытайтесь подавить то, что делает вас по-настоящему счастливым. Конечно же, нужно работать над тем, чтобы стать лучшей версией себя, но не тратьте силы, пытаясь стать кем-то другим. Вы – уникальный и сильный человек, и у вас есть все необходимое, чтобы владеть собственной судьбой.[4]

    • Напомните себе, что вы никогда не найдете свой путь, если будете постоянно сомневаться в себе. Вам нужно будет сделать выбор и смело шагать в будущее.
  3. Изображение с названием Find Your Way Step 8

    3

    Выберите между одним и многими. В какой-то момент вы можете прийти к развилке на своем пути. Вы хотите делать одно и хотите делать другое. Возможно, вас заинтересуют три, четыре или еще больше занятий! Ваш путь может быть сосредоточен на чем-то одном, а может стать постоянным исследованием новых, захватывающих начинаний. Спросите себя, будете ли вы довольны единственным выбором, и стоит ли разделять свою энергию на разные категории.

    • Если вы решили отказаться от всех других вариантов ради достижения одной цели или одного пути, старайтесь придерживаться своего выбора, но дайте себе возможность снова открыться разным вариантам. Однако если вы решили всецело отдаться единственной работе или занятию, возможно, вам придется закрыть для себя какие-то другие двери.
    • Если вы решите посвятить себя двум увлечениям – скажем, музыке и психотерапии, – вы можете оказаться на трудном, но в принципе достижимом пути. Вам придется быть особенно дисциплинированным, если вы хотите достичь высот в каждом из выбранных направлений.
  4. Изображение с названием Find Your Way Step 9

    4

    Придерживайтесь того, что вас удовлетворяет. Если что-то дарит вам чувство радости, смысла, восхищения, вдохновения – продолжайте этим заниматься. Посмотрите, куда это вас приведет. Возможно, у вас до сих пор нет четкого представления более полного масштаба своего “пути”, но вам стоит довериться этому чувству и позволить ему вести вас.

    • Помните: это занятие не обязательно должно быть вашим единственным увлечением, на которое вы направляете всю свою энергию! Ваш путь может быть сочетанием многих вещей.[5]
  5. Изображение с названием Find Your Way Step 10

    5

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

    Реклама

  1. Изображение с названием Find Your Way Step 11

    1

    Проанализируйте свои убеждения. Подвергать сомнению и анализу то, чему вас учили в детстве, – это нормально. Многих детей воспитывают так, что в итоге они разделяют взгляды своих родителей, своей компании и своего общества, а эти взгляды могут быть не совсем полезными в вашей взрослой жизни. Спросите себя, действительно ли верны и полезны ваши представления о мире.

    • Принятие новой информации, которая противоречит или опровергает то, чему вас учили, может вызывать стресс. Помните: это не обязательно значит, что вы должны отбросить все, чему вас учили, – просто полезно было бы относиться к этому сознательно. Хорошенько обдумайте каждую истину и решите, которые из них служат вашей долгосрочной цели.
    • Помните, что сомнение в определенных убеждениях может отдалить вас от друзей и членов семьи. Если вы выросли в обществе, где глубоко чтят и соблюдают религиозные традиции, ваши родственники и окружающие могут не одобрять вашего сомнения в этих традициях.
  2. Изображение с названием Find Your Way Step 12

    2

    Осознайте, что на вас влияет. Вряд ли вы найдете свой путь в полной изоляции от остального мира. Подумайте о том, с кем вы проводите свое время, и как эти люди, возможно, влияют на ваше предназначение. Если вы проводите свой день с активными и вдохновляющими людьми, то вам может быть проще заниматься именно тем, что для вас имеет смысл. Окружайте себя людьми, которые помогают вам расти.[6]

    • Иногда вы можете обнаружить, что у других людей слишком много власти над вашим выбором. Подумайте о том, не способствует ли это вашему чувству потерянности.
  3. Изображение с названием Find Your Way Step 13

    3

    Будьте терпеливы. Поймите, что вам не удастся за день найти свой путь. Чтение этой статьи – всего лишь небольшая ступенька глубокого личностного пути самопознания. Не забывайте, что ждать подходящей возможности – это нормально. Не стоит бросаться на первый мало-мальски приличный шанс, который вам попадется. Но не стоит и слишком долго ждать!

    • Если возможность неидеальна – не бойтесь отпустить ее и подождать того, чего вы действительно хотите. Например: не стоит выходить замуж за своего первого парня, если вам это кажется не совсем правильным. Не стоит принимать первую же работу, которую вам кто-то предложит, не рассмотрев другие варианты.
    • С другой стороны, стоит быть осторожным и не гнаться за совершенством. Иногда лучше всего выбрать тот вариант, который перед вами. Если слишком долго ждать, можно упустить десятки замечательных возможностей!

    Реклама

Советы

  • Не стоит слишком много читать о том, как познать себя. Наберитесь сил и начните вносить соответствующие изменения в свою жизнь.
  • Займитесь самоанализом. Подумайте о том, кто вы и где хотите быть. Никто не знает вас лучше, чем вы сами.
  • Если у вас есть цель, попытайтесь честно понять, что мешает вам ее достичь. Возможно, вы сами себя удерживаете?
  • Вам не нужно иметь план на всю оставшуюся жизнь – просто место, с которого можно начать. Путь будет открываться перед вами постепенно, когда вы будете по нему идти.

Реклама

Об этой статье

Эту страницу просматривали 58 184 раза.

Была ли эта статья полезной?

Понравилась статья? Поделить с друзьями:
  • Как составить рецензию на ответ
  • Как составить предложение презент симпл вопрос
  • Как найти свои устройства apple
  • Синяя скрижаль как найти
  • Как найти мужчину спонсора в краснодаре