Я только что получил работу в Snapchat. Вот какие советы могу дать по поиску работы. Около года назад, будучи на службе в армии США, я начал учиться программированию шутки ради. После долго периода обучения я получил свою первую работу в Snapchat в Венис-Бич.
Найти работу оказалось непросто. Я столкнулся с множеством отказов, предвзятых мнений и сомнений. Но этот опыт позволил мне разобраться в схемах, позволяющих добиться успеха в долгосрочной перспективе, но не гарантирующий успех за один день – таких, как поиск работы разработчика ПО.
Для того, чтобы найти именно это место, мне потребовалась большая доля удачи (нужное время, подходящие контакты, год поиска стартапов в Лос-Анджелесе). Не думаю, что описание этапов поиска работы вам чем-то пригодятся. И все потому, что я не скажу вам ничего нового:
- Занимайтесь разработкой сторонних проектов;
- Решайте практические задачи;
- Постройте собственную сеть;
- И рассылайте сотни резюме.
Шаги, которые вы предпринимаете, и акценты, которые вы расставляете, зависят исключительно от вашей личности и определенных обстоятельств. Выводы, к которым я пришел, помогут вам в поиске работы вне зависимости от обстоятельств.
Я хочу поделиться с вами своими мыслями, попутно немного рассказывая о динамическом программировании. Надеюсь, вам это пригодится.
Как типичный разработчик ищет работу?
Во время поиска своей первой работы в сфере программирования я познакомился с некоторыми реальными историями программистов-самоучек и недавних выпускников курсов о том, как они искали работу. Судя по их историям, алгоритм поиска работы примерно следующий:
- Научиться кодированию
- Отточить свои навыки
- Наладить некоторые связи
- Попрактиковаться
- Разослать резюме
- Пройти собеседование
- Получить приглашение на работу
Я считаю, что общий недостаток заключается в том, что люди уделяют слишком много внимания причинно-следственным связям между тем, что они сделали, и тем, что произошло в итоге:
Я сделал А, потом случилось Б. Соответственно, А приводит к Б.
Из-за того, что люди подвержены такой переоценке прошедших событий, результаты кажутся им предсказуемыми. Если следовать простой инструкции, можно найти хорошую работу.
И да, и нет. Исходя из своего опыта, могу сказать, что в долгосрочной перспективе, если вы действительно увлечены программированием и постоянно самосовершенствуетесь, в конце концов вам удастся найти достойную работу (вне зависимости от того, есть ли у вас профильное образование). Спрос на разработчиков ПО постоянно растет. Но в краткосрочной перспективе этот процесс происходит хаотично, он зависит от многих переменных, над которыми в не властны: потребностей компании в кадрах, рыночных тенденций, какие технологии востребованы в данный момент.
Когда я начинал искать работу в Лос-Анджелесе, я рассылал сотни резюме, пытаясь найтихоть что-нибудь. Я был готов писать коды в обмен на еду, если бы мне представилась такая возможность. Вот некоторые из ранних ответов на мои запросы:
"Вы пишете отличные чистые коды в Javascript. Вы были очень дружелюбны, а общение с вами доставило нам огромное удовольствие. Однако вы пишете коды не достаточно быстро и эффективно. Нам необходимы по-настоящему сильные кадры, а в вас мы этой силы не увидели. Это означает, что мы не можем сотрудничать с вами".
"Мы высоко оценили ваши навыки и получили большое удовольствие от интервью. Ваше желание работать, умение взаимодействовать с людьми и природная любознательность – это именно то, что мы ищем в кандидате. К сожалению, если говорить о сроках выполнения заказов, нам необходим человек с бОльшим опытом разработки пользовательского интерфейса".
"Извините за задержку. Процесс отбора кандидатов оказался сложнее, чем мы ожидали. Я оповещу вас по почте на следующей неделе, когда мы будем готовы принять решение".
А затем молчание.
Все это бред. Я потратил на выполнение тестового задания 6 часов, а у компании нет времени написать мне письмо?
Получать подобные ответы было довольно неприятно. Но никогда не упускайте возможности научиться чему-то новому, когда вы сталкиваетесь со сложностями.
«Боль неизбежна. Страдание – личный выбор каждого». – Харуки Мураками.
Задача о ранце
Позвольте описать этапы моей умозрительной схемы с помощью типичной для интервью задачи о ранце.
Вот в чем задача:
У вас имеется набор занятий, из которых вы можете выбирать, чтобы повысить свои шансы получить работу. Каждое из этих занятий занимает определенное количество времени, но также обеспечивает определенное количество опыта. Однако у нас ограничено время на поиск работы, поэтому выполнить все не получится. Наша цель – максимизировать количество опыта, выбрав оптимальный набор действий.
Как составить оптимальный набор занятий из имеющихся при условии ограничений по времени?
Решение 1: Грубая сила
Переформулируя задачу, скажу, что вы хотите выбрать набор занятий, который:
- Может быть выполнен в пределах имеющихся временных рамок
- Максимизирует количество очков опыта (ОО), которые вы приобретаете
Интуитивно хочется применить тот алгоритм, который мы используем в повседневной жизни. Мы попробуем разные вариации занятий, следя за тем, соответствует ли результат затраченным усилиям, и успеваем ли мы уложиться во временные рамки. Мы будем рассматривать все возможные комбинации, пока не придем к той, которая максимизирует ОО.
Задача заключается в том, что подобный подход с учетом времени действительно сложен. По мере увеличения нашего вклада (числа занятий, которые мы можем выбрать), увеличивается количество времени, затрачиваемого на принятие решения, причем с гораздо большей скоростью.
Если у нас имеется 6 потенциальных занятий, мы начинаем вычислять все возможные комбинации, начиная с 6 одиночных занятий.
Затем нам необходимо создать все возможные комбинации из 2 занятий. Для каждого из 6 исходных занятий мы подбираем комбинации с 5 оставшимися (каждое занятие может выполняться только один раз).
Затем создаем комбинации из 3 занятий. Для этого нам необходимо взять каждую комбинацию из 2 занятий и создать комбинации с каждым из 4 оставшихся занятий.
В конечном итоге у нас получится примерно следующее (6 * 5 * 4 *3 * 2 * 1), что составляет O(n!). А так как мы считаем сумму всех элементов каждой комбинации каждый раз, когда необходимо вычислить общее время и ОО, конечная временная сложность будет выглядеть следующим образом: O(n! * n).
Представьте, что вместо выполнения этого алгоритма на компьютере, способном выполнять триллионы операций в секунду, вам придется использовать свой мозг, который способен работать над выполнением стороннего проекта или изучением нового фреймворка JavaScript MV* не более 10 часов (при самом оптимистичном раскладе).
Или вместо 6 занятий вы можете выбирать из тысяч, за которые можно взяться, чтобы подготовиться к поиску работы.
Пробовать каждую из возможных комбинаций для того, чтобы подготовить себя к поиску работы – совершенно бездарное занятие. Вывод, который следует сделать из этого примера состоит в том, что существуют тысячи вещей, которые вы можете сделать, чтобы повысить свои шансы найти работу, но невозможно испробовать их все. Необходим более действенный метод определения оптимального набора занятий.
Бектрекинг
Очевидно, что как программисты (и хакеры), мы хотим оптимизировать процесс принятия решения.
Давайте разберемся во всем с самого начала.
1. Каков критический участок имеет наше решение с применением грубой силы?
Когда мы ищем критический участок, мы, как правило, пытаемся определить самую сложную часть процесса, то есть n! в нашем алгоритме O(n! * n) .
Критический участок (или самая сложная часть нашей задачи поиска работы) заключается в том, что необходимо постоянно вырабатывать разные комбинации и проверять их. Каждый раз, когда мы добавляем ее один вариант, появляется большое количество новых комбинаций, которые необходимо опробовать.
Теперь должен признаться в том, что немного сбил вас с пути. Моя задача поиска работы, как вид задачи с рюкзаком, относится к разряду NP-трудных задач. Если коротко, то задачу называют NP-трудной, если не существует известного оптимального пути ее решения или подтверждения того, что найденное решение верно. Так что, если вы не являетесь гениальным ученым в области ВТ, вам вряд ли удастся найти оптимально эффективное решения сочетания всех занятий.
Но это абсолютно нормально! Иногда при поиске работы и прохождении интервью мы руководствуемся ложными убеждениями. До тех пор, пока мы чему-то учимся, мы не теряем времени даром. Даже если нам не удается найти всеобъемлющего эффективного решения задачи, мы все еще способны найти более оптимальное, чем то, которым мы сейчас пользуемся.
Давайте продолжим.
2. Ведет ли мой алгоритм к выполнению необязательной или повторяющейся работы?
Именно на этом этапе мы можем максимально улучшить процесс. Одна вещь, которую мы должны изменить, состоит в том, что для каждой возможной комбинации мы должны перебирать все действия в наборе, чтобы рассчитать общий ОО и общее время из этого набора действий. Это повторяющаяся работа, так как мы выполняем один и тот же набор действий снова и снова.
Если бы мы только сохранили общие ОО и время набора в виде переменной, то могли бы просто добавлять ОО и время каждого нового занятия к общему значению. Таким образом, из O(n! * n) мы бы получили O(n!).
Это полезно, но не способно качественно ускорить процесс решения задачи.
Как еще можно оптимизировать процесс?
Мы также просчитываем большое количество комбинаций, что не может привести к верному решению. Это необязательная работа.
Вот список занятий для примера:
Постоянные занятия = [
{имя: 'сторонний проект', время: 10, ОО: 12},
{имя: 'алгоритмы’, время’: 3, ОО: 7},
{имя: 'разработка сетей’, время: 1, ОО: 0,5},
{имя: 'упражнение', время: 2, ОО: 1,5},
{имя: 'дизайн систем', время: 4, ОО: 4},
{имя: 'создание взаимозависимостей CSS', время: 3, ОО: 4}
];
Допустим, всего у нас есть 8 часов для подготовки к поиску работы. Как наш алгоритм грубой силы проверит комбинации?
На основании порядка ряда занятий сначала проанализируем только те варианты, которые содержат сторонний проект. Эти варианты невозможны, так как он занимает 10 часов, а у нас в распоряжении всего 8. Но наш алгоритм, основанный на грубой силе, не знает этого и будет проверять все комбинации, включающие сторонний проект.
Он будет рассматривать комбинацию [сторонний проект, алгоритмы] как верную, а это не так. Он также проверит на верность [сторонний проект, алгоритмы, разработка сетей], а это также не верно. Кроме того, он проверит [сторонний проект, алгоритмы, разработка сетей, упражнения] и снова ошибется. Видите, как много ненужной работы приходится выполнять?
А что, если сделать наш алгоритм немного более интеллектуальным? Таким, чтобы он мог проверить, может ли наше текущее состояние (занятия, которые мы выбрали на данный момент) привести к правильному решению. Если да (особенно при условии, что набор занятий укладывается в заданный промежуток времени), тогда мы продолжаем отбирать новые занятия и проверять их на валидность. Если нет, мы останавливаемся и отменяем последнее выбранное занятие.
Например, если у нас есть всего 8 часов, мы в первую очередь проверяем, может ли комбинация, содержащая только сторонний проект, привести к правильному решению. Как мы уже определили ранее, такого быть не может, так как эта работа требует больше ресурсов, нежели те, которыми мы обладаем.
Так что мы отбрасываем сторонние проекты и начинаем пробовать различные комбинации, начиная с алгоритмов. Проверяя, могут ли текущие выбранные занятия привести к правильному решению, мы избегаем комбинаций, содержащих сторонний проект, так как они заведомо ложные. Такой подход носит название бектрекинг. Мы проверяем, могут ли наши действия привести нас к верному решению. Если нет, мы возвращаемся на шаг назад и делаем другой выбор.
Это решение осуществляет два процесса оптимизации, которые мы обсуждали ранее:
- Придерживаться общих ОО и времени. Мы не можем высчитать О(1) вместо общего набора О(n)
- Проверять, может ли текущий набор действий привести к верному решению перед тем, как добавить следующий элемент
Несмотря на то, что бектрекинг экономит много сил, он не упрощает общую сложность нашего алгоритма. Он все еще O(n!), мы продолжаем рекурсивно проверять наиболее возможные комбинации.
Но применение бектрекинга определенно наталкивает на мысль, как продолжить работать над задачей. При решении задачи грубой силой нам приходилось собирать и проверять все возможные комбинации. С помощью бектрекинга мы выясняем, ведет ли выбранный путь к правильному решению задачи.
Есть ли путь решить, стоит ли добавлять занятия к уже выполняемым или нет? Это было бы гораздо легче, чем пытаться сразу создать полноценную комбинацию. Это помогло бы решить нашу непростую задачу (по поиску оптимальной комбинации) для ряда более мелких задач (по решению о добавлении нового занятия).
Динамическое программирование
Динамическое программирование – это метод, позволяющий разбить большую задачу оптимизации (какой набор занятий я должен выбрать?) на ряд выполнимых задач принятия решения (следует ли мне включать это занятие в оптимальное решение или нет?). Разделяй и властвуй.
Динамическое программирование – типичный путь решения трудных NP-задач (вроде задачи с рюкзаком) и, по случайному стечению обстоятельств, отличный помощник в поиске работы. Сложно определить, какой набор действий подготовит вас к поиску работы. Не существует эффективного пути проверить, насколько оптимален выбранный путь.
Но гораздо проще разбить общий период времени на отдельные дни и недели. И попытаться определить, какие из занятий следует выполнять в каждый из этих маленьких отрезков времени.
Для решения задачи поиска работы с использованием динамического программирования, разобьем ее на мелкие составляющие (как мне оптимизировать короткий промежуток времени?), а затем используем каждое из решений мелких задач для создания решения крупной.
Звучит запутано? Давайте разбираться:
Постоянные занятия = [
{имя: 'сторонний проект', время: 10, ОО: 12},
{имя: 'алгоритмы’, время’: 3, ОО: 7},
{имя: 'разработка сетей’, время: 1, ОО: 0,5},
{имя: 'упражнение', время: 2, ОО: 1,5},
{имя: 'дизайн систем', время: 4, ОО: 4},
{имя: 'создание взаимозависимостей CSS', время: 3, ОО: 4}
];
Каким было бы оптимальное решение, если бы время подготовки t=0? Если бы время было нулевым, мы бы не могли выполнить ни одного занятия и оставили бы набор пустым []. Теперь нам необходимо оптимальное решение при t=1.
Во-первых, давайте посмотрим, какие занятия возможно выполнить: мы не можем заняться сторонним проектом (t=10) и алгоритмами (t=3). Единственное, за что мы можем взяться, это разработка сетей (t=1).
Так что нам необходимо решить, проведет ли добавление разработки сетей к оптимальному решению для t=0 также к оптимальному решению.
Если мы добавим разработку сетей, то получим ОО=0,5. Неплохо. Если мы не добавим разработку сетей, мы больше ничего не успеем сделать и получим ОО=0. 0,5 лучше, чем 0, так что если мы имеем в распоряжении всего 1 час, стоит взяться за разработку сетей. Оптимальное решение для общего времени t=1 – [разработка сетей].
Каким будет оптимальное решение для t=2? Какое из тех занятий, которые мы еще не рассматривали, возможно при t=2? Только упражнения. Если мы решим добавить упражнения, на которые требуется 2 часа, у нас больше не останется время на что-либо еще, а наше решение [упражнения] даст ОО=1,5.
Сравним оптимальное решение с упражнениями (при котором опыт равен 1,5) и оптимальное решение без упражнений (при котором опыт равен 0,5). Поскольку решение, содержащее упражнения, лучше, оставляем его. (В реальной жизни, при условии ограниченного времени, я всегда предпочту уделить время себе, нежели заниматься подготовкой).
Теперь становится по-настоящему интересно: каким будет оптимальное решение для t=3? Снова, какие занятия можно выбрать при t=3? Мы можем выбирать из [алгоритмов, упражнений, разработки сетей].
Если мы выберем алгоритмы, на которые уходит 3 часа, у нас не останется времени на что-то другое. Так что наше решение – [алгоритмы].
Если мы выберем упражнения, на которые уходит 2 часа, у нас остается еще час. Что же делать в оставшееся время? Мы знаем, что оптимальное решение для t=1 – [разработка сетей], так что нам не надо заново это вычислять. Лучшего варианта для t=1 у нас нет. Итак, еще одно возможное решение – [упражнения, разработка сетей]. Сравнивая все решения, мы приходим к выводу, что лучше всего заняться алгоритмами.
Вот схема действия динамического программирования: в каждый отрезок времени мы проверяем решение, стоит или нет добавлять определенное занятие. Мы сравниваем все решения и выбираем оптимальное.
Решения для бОльших периодов времени строятся из оптимальных решений для более коротких отрезков. Это позволяет рекурсивно решать функцию динамического программирования.
Для своего первого примера я отобрал ряд упражнений исходя из времени, которое необходимо затратить на их выполнение (от меньшего к большему). Поскольку элементы отсортированы по времени, это позволяет быстро определить, какие из них возможно выполнить за данный отрезок времени.
Вы спросите, какой смысл был в трех приведенных примерах? Я не только украдкой сообщил вам некоторые вопросы, которые задают на технических интервью, но и показал, как я пришел к своей умозрительной схеме решения задач.
Существует почти бесконечное число комбинаций занятий, которыми можно заняться, и не существует эффективного пути определить самую оптимальную из них. Многие из путей ведут к неверному решению, совсем как многие разосланные резюме и назначенные интервью не ведут к получению работы.
Вы могли бы испробовать все комбинации, чтобы подготовиться к поиску работы (метод грубой силы), но поскольку мы всего лишь люди с ограниченным запасом времени, это неэффективный метод достижения цели.
Мы могли бы оптимизировать свой подход, отслеживая эффективность избранного пути на каждом новом этапе (бектрекинг). Например, если при поиске работы мы постоянно обращаемся к специалистам по подбору персонала, но они не помогают, возможно стоит вернуться на шаг назад и рассмотреть другие варианты.
И последнее, поскольку работа не ищется в один день, можно попытаться оптимизировать каждый отдельный день и затем собрать их все воедино (динамическое программирование). В этом случае мы имеем дело с выполнимыми задачами (стоит ли мне заняться алгоритмами сегодня?) вместо решения особо сложных задач (что мне стоит сделать в ближайший месяц для того, чтобы подготовиться к поиску работы?).
Наконец, я просто хотел рассказать вам о трех подходах. Хотя они и не были объективно эффективны, в конечном счете они все же привели к решению задачи. В процессе поиска работы необходимо помнить, что надо постоянно идти вперед - и вы придете к достижению своей цели.
Мои советы по поиску работы разработчика
Поддамся желанию дать вам два совета, исходя из собственного опыта.
- Очень сложно оценить себя во время интервью и при выполнении тестовых заданий – так что просто сфокусируйтесь на процессе. Ни во время, ни сразу после интервью вы не узнаете, хорошо ли справились.
- Успехи и провалы мимолетны. Они не должны влиять на ваше душевное состояние.
Если вы находитесь в поиске своей первой работы в сфере программирования, надеюсь, вам это пригодится, или хотя бы вдохновит. Я бы хотел закончить лучшим советом, который мне дали, когда я искал работу:
«Не думай о том, достаточно ли ты хорош, думай о том, любишь ли ты программировать и готов ли ты тяжело работать. Если да, то у тебя все получится»,- перефразировано с подкаста, посвященного стартапам, Эдгара Пабона.
Комментарии
Страницы
Можно не только на БД. Можно и в карауле. Там же просто проблема была - скука смертная... Развлекались как могли.
На БД один офицер от скуки чинил ... оборудование. - Его по идее надо было отвести на ремонт, но чего то не отвозили (а это бумажки собирать, вести, потом забирать ...) , так он его и чинил. Не всё, конечно, но чего смог.
Армия - это постоянная скука (С), - поэтому там столько и происшествий - что от скуки люди и придумывают себе занятия на ... потеху ли, или на учёбу ли - тут уж кто во что горазд.
Ну, в Вашем, логик, карауле, возможно, можно и в бане париться... И армии разные. В моей армии была пахота.
А Логик шо - офицером служил?
Бает поди. (С)
Хм. Ну какая пахота на БД? (С) - Только что света белого не видишь. Ибо типа в бункере то.
Иногда кнопки нажимаешь. А так всё время ждёшь, ждёшь и ждёшь.
Страницы