Как стать автором
Обновить

Пять распространенных проблем кандидатов (по результатам 600 технических собеседований)

Время на прочтение9 мин
Количество просмотров53K
Всего голосов 55: ↑23 и ↓32-9
Комментарии76

Комментарии 76

O(n^2) == экспоненциально? Однако.

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

Именно такие собесодователи как в статье и отравили весь процесс найма.

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

Похоже на кальку с английского, там степени экспонентами кличут.

НЛО прилетело и опубликовало эту надпись здесь

В начале статьи так и написано, что это публикация перевода чужой статьи. К сожалению - бездумная.

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

Более того, даже с массивом эта задача решается за O(N*logN) и без дополнительной памяти, квадратная сложность - это наивная реализация, над которой даже думать не нужно)

Ужас! — Картинка очень соответствует!


есть ли у них серьезные трудности в составлении различных алгоритмов

Я не один десяток лет программирую. Алгоритм можно знать, а можно не знать. Но "составлять"? Это как? — Нпр., автор упоминает поиск в ширину и поиск в глубину, если соискатель не знает, чем один отличается от другого, то вряд ли за час собеседования сможет придумать велосипед. Упомянуто решето Эратосфена для поиска простых чисел. Его нельзя не знать. И за отведенный час не придумать.


«У вас есть совокупность целых чисел. Напишите алгоритм, который находит два числа. В сумме они должны дать заданное значение. Когда алгоритм находит пару чисел, он перестает считать и показывает найденные значения. Если такие числа не найдены, возвратите два “пустых” значения».

Я занимаюсь разработкой кода довольно долго. Если быть точным, с 1982 года. Ни в одном языке, на котором я когда-либо работал, не существует структуры данных, которая бы называлась «совокупность». Что же можно сказать о поставленной задаче?
Большинство кандидатов тут же ответят, что «совокупность» чисел — это массив. Конечно, можно решить эту задачу с помощью массива для хранения числовых значений. В этом случае трудоемкость алгоритма будет O(n^2), потому что вы будете перебирать данные экспоненциально: для каждого элемента нужно проверять оставшиеся числа. Но есть и более оптимальный способ решить поставленную задачу за O(n) время, выбрав иную структуру данных.

Если "перебирать данные экспоненциально", то трудоемкость НЕ будет O(n^2). Но откуда O(n^2)? Понятно, что a+b=b+a, следовательно вложенный цикл должен начинаться не с самого первого числа:


for i:=1 to n-1 do
 for j:=i+1 to n do
  if A[i]+A[j]=x then // x  -- заданное значение
   begin
     a := i; b = j; exit; end;
a := 0; b :=0; // два “пустых” значения

Но O(n) кажется слишком оптимистичной оценкой.

PS Исправлюсь: совокупность из 2 чисел, м.б. названа совокупностью чисел. В этом случае надо их сложить, и если получим заданное значение, то вернуть эти числа, иначе вернуть два “пустых” значения. В этом случае можно оценить, как O(n) :)

НЛО прилетело и опубликовало эту надпись здесь

Предстаим квадратную матрицу размером n на n. Код


for i:=1 to n-1 do
 for j:=i+1 to n do
 write(A[i,j])

напечатает ровно n2/2-n/2 элементов. При этом есть теоретики, которые скажут, что
О(n2/2-n/2)= О(n2), но мне обычно важно на практике, что код не перебирает лишние цифры.

Даже на практике разница между ϴ(N2) и ϴ(N) намного важнее чем разница между N2 и N2/2

Вариант 1.


for i:=1 to n-1 do
 for j:=i+1 to n do

Вариант 2.


for i:=1 to n do
 for j:=1 to n do

Есть разница?

Разница-то есть, но вот такой вариант, при его возможности, лучше ваших обоих:


for i:=1 to n do
  {...};
for j:=1 to n do
  {...};

Не понял, как я найду пары всех чисел с этим подходом?

Ниже уже несколько раз написали.

О(n^2/2-n/2) = О(n^2), и это следует из определения. Я точно не помню, но своими словами O(f(n)) означает, что найдутся такие k1 и k2, что k1*f(n) < f(n) < k2*f(n). Сама f(n) между двумя функциями может изгибаться как угодно

но мне обычно важно на практике, что код не перебирает лишние цифры.

Ассимптотическая сложность алгоритма не имеет к этому никакого отношения. Коэфиценты k1 и k2 могут быть сколь угодно большими или маленькими

Если хотите посчитать число операций, зачем вам вообще тогда нужно O? Просто считайте: n^2/2-n/2, а O(n^2/2-n/2) показывает полное непонимание того, что вы делаете

Я точно не помню, но своими словами O(f(n)) означает, что найдутся такие k1 и k2, что k1f(n) < f(n) < k2f(n).

Уточнение: это вы определение ϴ написали. Буква "O" задаёт только верхнюю границу (абсолютного значения).

О(n^2/2-n/2) = О(n^2), и это следует из определения

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


Θ(n2.78041).

Θ(n2.7799).

В 1990 Копперсмит и Виноград[6] опубликовали алгоритм, который в модификации Вильямс Василевской[7] 2011 года умножает матрицы со скоростью O(n2.3727)

А вы не путайте множитель и показатель степени.

Случай теория vs практика?
На практике ускорение в 2 раза м.б. значительнее, чем Θ(n^2.78041) или Θ(n^2.7799).

В данном случае так и есть.

Печально, когда теория противоречит практике.

Это не противоречие.

Как не противоречие, если в 2 раза разница?!

Извините, но мне показалось, вы просто троллите.

Ваш код выполняется за O(n^2). То, что вложенный цикл начинается с i + 1, на оценку не влияет.

Решение за O(n):

val_to_pos = {x: i for i, x in enumerate(arr)}
for i, x in enumerate(arr):
    pair = target - x
    pair_pos = val_to_pos.get(pair)
    if pair_pos == i and must_be_distinct:
        continue
    if pair_pos is not None:
        return (x, pair)
return None

s=set(arr) - это точно O(n) ?

pair in s - это точно O(1) ?

Это автоматически балансирующие размер хеш-таблицы.
Так что да, set(arr) - это точно O(n), если у объектов операцию хеширования писали прямыми руками.
И `key in set` - это точно O(1), если мы говорим про амортизированную сложность в среднем и не предполагаем, что тут произошла атака на алгоритм хеширования.

Но O(n) кажется слишком оптимистичной оценкой.

Первый проход по массиву: складываем все числа в hashtable

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

Причем здесь "совокупность" и невозможность использования массива я не понял.

Вставка в hashtable и проверка наличия не равны O(1), так что O(n) - слишком оптимистичная оценка.

Где именно не равны?

https://en.wikipedia.org/wiki/Hash_table

Time complexity in big O notation 

Algorithm     Average    Worst case
Space         O(n)[1]    O(n)
Search        O(1)    O(n)
Insert        O(1)    O(n)
Delete        O(1)    O(n)

O(n) будет только тогда, когда хранилище сильно несбалансированно и его надо увеличивать, или хеш-функция неподходящая. Если важна скорость, для частных случаев это можно учесть и сделать так, что O(1) будет практически всегда.

Поэтому в любых задачах где надо оценить сложность, для хеш-таблиц всегда дают оценку O(1)

Первый проход по массиву: складываем все числа в hashtable

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

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

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

Я придумал много алгоритмов (недавние примеры: 1, 2, 3, 4, 5, 6). Секрет успеха: знаю многие готовые и не изобретаю велосипед.

"Эратосфена для поиска простых чисел. Его нельзя не знать. И за отведенный час не придумать."

Не знал, за час придумал)))

А какие алгоритмы Вы при этом знали? Знание одних, помогает придумывать… Много еще алгоритмов изобрели?

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

Когда то изобрел двоичное дерево поиска, не потому, что умный а потому что когда учился вообще не было в доступе литературы нормальной, о том, что преподавали в университете вообще разговора не идет. Надо было быстро искать по уникальным идентификаторам uint32, пришлось выкручиваться. Потом когда переехал в питер и появился доступ к интернету нормальный, с удивлением узнал, что это оказывается классика )))

П.С. больше ничего такого не изобретал, просто вот два раза звезды сошлись )))

Упомянуто решето Эратосфена для поиска простых чисел. Его нельзя не знать. И за отведенный час не придумать.

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

Гениально! Но полагаете, что не нужно знать известных алгоритмов, а каждый раз придумывать свой? BTW в Вики ряд модификаций — какую Вы придумали?

Алгоритм можно знать, а можно не знать. Но "составлять"? Это как?

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

Например задачи которые решаются через динамическое программирование. Тогда можно не знать к примеру "Флойда — Уоршелла" и спокойно его реализовать.

Это когда читаешь условие задачи и понимаешь что не один готовый алгоритм не подходит

Для этого нужно знать готовые алгоритмы. А еще полезно книжки посмотреть и в сетке поискать, у коллег спросить совета.

Ужас какой, зачем вам знать это самое решето?

Решето Эратосфена является популярным способом оценки производительности компьютера.

Таааак, и вы постоянно оцениваете производительность компьютера таким образом?

ИМХО чем примитивнее алгоритм, тем точнее оценка. Нпр., решение СЛУ по Гауссу не лучший тест.

Заканчиваем споры и сомнения. Если на входе "совокупность" - это упорядоченный двунаправленный список или массив, то просто движемся с двух концов, пока не получим нужную сумму. Если на данный момент сумма больше необходимого - двигаем правый элемент, если меньше - левый. O(n) в чистом виде

Интересное решение. А как доказать его правильность или это эвристика?

А что тут доказывать-то? Для любого упорядоченного по возрастанию массива A верно следующее:


Если A[i] + A[j] < N, то для любого i < k < j верно что A[i] + A[k] < N
Если A[i] + A[j] > N, то для любого i < k < j верно что A[k] + A[j] > N


Это следует непосредственно из упорядоченности массива. А дальше алгоритм получается тривиально.

Если A[i] + A[j] < N, то для любого i < k < j верно что A[i] + A[k] < N
Если A[i] + A[j] > N, то для любого i < k < j верно что A[k] + A[j] > N

Из этих верных утверждений не следует (не видно, что следует), что A[i] + A[k] = N или A[k] + A[j] = N.

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


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

выкинуть все варианты сравнений, где равенства точно не будет, и проверить оставшиеся.

Не все варианты сравнений проверяются. Почему не может быть, что на предыдущих шагах не выкинуто A[i], которое с очередным A[j] даст N?

Потому что выкидываются только те, которые заведомо не дадут такой комбинации.

Не математично! Нужно показать, что не дадут. Я вижу что алгоритм работает. И не могу придумать контпримера, но это не значит, что его нет. Пока, извините, но похоже на эвристику.

Что значит "не математично"?


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


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


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


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


Вам правда всё это расписывать требуется?

Вам правда всё это расписывать требуется?

Правда, т.к. много лет работая с проф. математиками привык не доверять слишком общим утверждениям — "дьявол кроется в деталях" (с)

Идея правильная, но с примерами - не очень здорово. Я собирал несколько групп разработки, и всегда главным вопросом было: "как ты это будешь решать"? В процессе такого общения можно очень быстро понять: кто перед тобой.

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

И ещё (субъективно) что-то зачастили со статьями о "правильных собеседованиях".

Спасибо за мнение!
Для нас это первая публикация тут (не считая моих прошлых публикаций для Онтико :)).

Расскажите, пожалуйста, о чём на тему HR Вам было бы интересно почитать? Может быть, есть примеры каких-то публикаций, которые Вам "зашли"?

И Вам спасибо.)

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

Интересно есть ли услуга "тайный покупатель", но в роли "тайный соискатель", для проверки работы HR ?..

О-о, это мы можем. :)


Поделюсь идеей с коллегами - точно что-нибудь сделаем. :)

Спасибо!

Если честно, после такого собеса, я бы просто ушёл по-английски, не в коем случае не сужу о низком уровне хард скиллов автора, а вот софт скиллс я бы подтянул, но это ИМХО.

ИМХО хороший вопрос соискателю: о каком алгоритме хотите поговорить?
Допустим, он ответил: альфа-бета.
Говорите: ok, предположим, что я ничего об этом не знаю — объясните оновы и зачем нужен?

И снова остался нераскрытым вопрос о реальной необходимости знаний решета Эратосфена для разработчика в компании, чей примитивный продукт можно сделать вообще на no-code платформе

Распространенная проблема № 1: поспешный переход к написанию кода

На одном из интервью в 2012 году я стал свидетелем того, как такое недостаточное планирование серьезно подвело претендента

можно потратить слишком много времени на проектирование структуры кода

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

Вы ждете того же самого, но за 15 минут и применимо к очередной дурацкой задачке с литкода? И еще и пеняете, что человек в стрессовой ситуации делает не то, что вам нравится?

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

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

А может быть они об этом и не знают.

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

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

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

Мне нравится Ваше замечание. А есть ли способ "распознать", какой именно интервьюер перед тобой находится и что ему может быть нужно?
К сожалению, до сих пор встречаются интервьюеры, чьей целью может быть в принципе лишь самоутверждение перед кандидатом - как быть в таком случае?

Ну... лишь бы делал дело хорошо.)

Какой претенциозный бред. Человека на километр нельзя подпускать к процессу найма.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий