интервьюер: Приветствую, хотите кофе или что-нибудь еще? Нужен перерыв?
я: Нет, кажется я уже выпил достаточно кофе!
интервьюер: Отлично, отлично. Как вы относитесь к написанию кода на доске?
я: Я только так код и пишу!
интервьюер: ...
я: Это была шутка.
интервьюер: OK, итак, вам знакома задача "fizz buzz"?
я: ...
интервьюер: Это было да или нет?
я: Это что-то вроде "Не могу поверить, что вы меня об этом спрашиваете."
интервьюер: OK, значит, нужно напечатать числа от 1 до 100, только если число делится нацело на 3, напечатать слово "fizz", если на 5 — "buzz", а если делится на 15, то — "fizzbuzz".
я: Я знаю эту задачу.
интервьюер: Отлично, кандидаты, которые не могут пройти эту задачу, у нас не сильно уживаются.
я: ...
интервьюер: Вот маркер и губка.
я: [задумался на пару минут]
интервьюер: Вам нужна помощь, чтобы начать?
я: Нет, нет, все в порядке. Итак, начнем с пары стандартных импортов:
import numpy as np
import tensorflow as tf
интервьюер: Эм, вы же правильно поняли проблему в fizzbuzz, верно?
я: Так точно. Давайте обсудим модели. Я думаю тут подойдет простой многослойный перцептрон с одним скрытым слоем.
интервьюер: Перцептрон?
я: Или нейронная сеть, как вам угодно будет называть. Мы хотим чтобы на вход приходило число, а на выходе была корректное "fizzbuzz" представление этого числа. В частности, мы хотим превратить каждый вход в вектор "активаций". Одним из простых способов может быть конвертирование в двоичный вид.
интервьюер: Двоичный вид?
я: Да, ну, знаете, единицы и нули? Что-то вроде:
def binary_encode(i, num_digits):
return np.array([i >> d & 1 for d in range(num_digits)])
интервьюер: [смотрит на доску с минуту]
я: И нашим выходом будет унитарное кодирование fizzbuzz представления числа, где первая позиция означает "напечатать как есть", второе означает "fizz" и так далее.
def fizz_buzz_encode(i):
if i % 15 == 0: return np.array([0, 0, 0, 1])
elif i % 5 == 0: return np.array([0, 0, 1, 0])
elif i % 3 == 0: return np.array([0, 1, 0, 0])
else: return np.array([1, 0, 0, 0])
интервьюер: OK, этого, кажется, достаточно.
я: Да, вы правы, этого достаточно для настройки. Теперь нам нужно сгенерировать данные для тренировки сети. Это будет нечестно использовать числа от 1 до 100 для тренировки, поэтому давайте натренируем на всех оставшихся числах вплоть до 1024:
NUM_DIGITS = 10
trX = np.array([binary_encode(i, NUM_DIGITS) for i in range(101, 2 ** NUM_DIGITS)])
trY = np.array([fizz_buzz_encode(i) for i in range(101, 2 ** NUM_DIGITS)])
интервьюер: ...
я: Теперь нашу модель нужно адаптировать для tensorflow. Сходу я не сильно уверен какую толщину слоя взять, может 10?
интервьюер: ...
я: Да, пожалуй 100 будет лучше. Мы всегда можем изменить это позже:
NUM_HIDDEN = 100
Нам понадобится входная переменная шириной в NUM_DIGITS, и выходная переменная с шириной в 4:
X = tf.placeholder("float", [None, NUM_DIGITS])
Y = tf.placeholder("float", [None, 4])
интервьюер: Как далеко вы планируете зайти с этим?
я: Ах, всего два слоя — один скрытый слой и один слой для вывода. Давайте используем случайно-инициализированные веса для наших нейронов:
def init_weights(shape):
return tf.Variable(tf.random_normal(shape, stddev=0.01))
w_h = init_weights([NUM_DIGITS, NUM_HIDDEN])
w_o = init_weights([NUM_HIDDEN, 4])
И мы готовы определить нашу модель. Как я сказал ранее, один скрытый слой, и, давайте используем, ну, не знаю, ReLU активацию:
def model(X, w_h, w_o):
h = tf.nn.relu(tf.matmul(X, w_h))
return tf.matmul(h, w_o)
Мы можем использоваться softmax кросс-энтропию как нашу функцию стоимости и попробовать минимизировать её:
py_x = model(X, w_h, w_o)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(py_x, Y))
train_op = tf.train.GradientDescentOptimizer(0.05).minimize(cost)
интервьюер: ...
я: И, конечно, предсказание будет просто наибольшим выходом:
predict_op = tf.argmax(py_x, 1)
интервьюер: Пока вы не заблудились, проблема, которую вы должны были решить это генерация fizz buzz для чисел от 1 до 100.
я: Ох, отличное замечание, predict_op функция вернет число от 0 до 3, но мы же хотим "fizz buzz" вывод:
def fizz_buzz(i, prediction):
return [str(i), "fizz", "buzz", "fizzbuzz"][prediction]
интервьюер: ...
я: Теперь мы готовы натренировать модель. Поднимем tensorflow сессию и проинициализируем переменные:
with tf.Session() as sess:
tf.initialize_all_variables().run()
Запустим, скажем, 1000 эпох тренировки?
интервьюер: ...
я: Да, наверное этого будет мало — пусть будет 10000, чтобы наверняка.
Ещё, наши данные для тренировки последовательны, что мне не нравится, так что давайте размешаем их на каждой итерации:
for epoch in range(10000):
p = np.random.permutation(range(len(trX)))
trX, trY = trX[p], trY[p]
И, каждая эпоха будет тренировать в пачках по, я не знаю, ну пусть 128 входов.
BATCH_SIZE = 128
В итоге, каждый проход будет выглядеть так:
for start in range(0, len(trX), BATCH_SIZE):
end = start + BATCH_SIZE
sess.run(train_op, feed_dict={X: trX[start:end], Y: trY[start:end]})
и потом мы можем вывести погрешность тренировочных данных, ведь почему бы и нет?
print(epoch, np.mean(np.argmax(trY, axis=1) ==
sess.run(predict_op, feed_dict={X: trX, Y: trY})))
интервьюер: Вы серьёзно?
я: Да, мне кажется это очень полезно видеть, как прогрессирует точность.
интервьюер: ...
я: Итак, после того, как модель натренирована, время fizz buzz. Наш вход будет всего лишь двоичное кодирование числе от 1 до 100:
numbers = np.arange(1, 101)
teX = np.transpose(binary_encode(numbers, NUM_DIGITS))
И затем, наше вывод это просто fizz_buzz функция, применённая к выходной модели:
teY = sess.run(predict_op, feed_dict={X: teX})
output = np.vectorize(fizz_buzz)(numbers, teY)
print(output)
интервьюер: ...
я: И это будет ваш fizz buzz!
интервьюер: Этого достаточно, правда. Мы с вами свяжемся.
я: Свяжемся, звучит многообещающе.
интервьюер: ...
Постскриптум
Я не получил эту работу. Но я попробовал на самом деле запустить этот код (код на Github), и, оказалось, что он даёт несколько неправильный вывод! Большое спасибо, машинное обучение!
In [185]: output
Out[185]:
array(['1', '2', 'fizz', '4', 'buzz', 'fizz', '7', '8', 'fizz', 'buzz',
'11', 'fizz', '13', '14', 'fizzbuzz', '16', '17', 'fizz', '19',
'buzz', '21', '22', '23', 'fizz', 'buzz', '26', 'fizz', '28', '29',
'fizzbuzz', '31', 'fizz', 'fizz', '34', 'buzz', 'fizz', '37', '38',
'fizz', 'buzz', '41', '42', '43', '44', 'fizzbuzz', '46', '47',
'fizz', '49', 'buzz', 'fizz', '52', 'fizz', 'fizz', 'buzz', '56',
'fizz', '58', '59', 'fizzbuzz', '61', '62', 'fizz', '64', 'buzz',
'fizz', '67', '68', '69', 'buzz', '71', 'fizz', '73', '74',
'fizzbuzz', '76', '77', 'fizz', '79', 'buzz', '81', '82', '83',
'84', 'buzz', '86', '87', '88', '89', 'fizzbuzz', '91', '92', '93',
'94', 'buzz', 'fizz', '97', '98', 'fizz', 'fizz'],
dtype='<U8')
Наверное, нужно взять более глубокую нейронную сеть.
Комментарии (127)
gena_glot
24.05.2016 02:13-50Идея fizz buzz — проверить умение логически мыслить и читать тело задачи.
Задача решается в два if. Если число делится на 3 и на 5, значит оно делится и на 15. Написать в два if не так просто, учитывая output. Но надо сделать именно в два if (else стейтмент можно вводить).
Именно так задача давалась в оригинале.
Попробуйте решить строго в два ifencyclopedist
24.05.2016 02:19+48Вы это серьёзно? (Или ваш комментарий настолько тонок, что я не понимаю?)
gena_glot
24.05.2016 04:25-31Еще раз условие задачи для тех кто не понял:
есть понятие пробы. Проба позволяет узнать значение.
есть if () — в скобках мы пробируем значение.
нам надо написать программу, которая:
1. если число делится на 3 выводит «fizz»
2. если число делится на 5 выводит «buzz»
3. если число делится на 3 и на 5 выводит «fizzbuzz».
4. если число не делится на 3, на 5, на 3 и на 5 НЕ выводит ни fizz, ни buzz, а выводит само число
Example output:
1 2 fizz 4 buzz 6 7… 14 fizzbuzz 16 17
Попробуйте Randl, не знаю С++, что у вас выведется на экране.
Можно написать
for { if else; if else }
можно
for { if; if }
можно
for { if else; if}
и т.п.
оператор print()
Попробуйте честно решитьIIvana
24.05.2016 04:38+3Вангую, что вам опять посыпятся минусы, но вместо этого я встречно предложу вам решить эту задачку вообще без проб, проверок и условий. Раз уж вы так настаиваете. Попробуйте честно решить (С).
алгоритм под спойлером :)Заводим мутабельный массив, заполняем числами, потом пробегаемся по нему с 3 с шагом 3 — пишем в значение элемента физз, вторая пробежка — с 5 с шагом 5 — пишем базз, третья — с 15 с шагом 15 — пишем физзбазз.IIvana
24.05.2016 04:46+6ЗЫ и похоже, ваш алгоритм как и у героя статьи, тоже эвристический :)
Example output:
… 6 ....
VolCh
24.05.2016 07:27Формально будут проверки на выход из цикла. Ну и третья пробежка излишняя, если инициировать значения пустой строкой, а на второй и третьей не устанавливать новые значения, а конкатенировать с имеющимися.
IIvana
24.05.2016 07:50+1Если заполнять пустой строкой, то потом все равно придется делать проверку — на пустоту строки, и выводить индекс. А у нас надо же без проверок :) Ну, кроме проверки на прекращение цикла — но ее и постановщик ограничений за проверку не считает. А так, я уже под впечатлением от обсуждения создал тему на одном форуме — с ограничениями без единого if, switch / case, тернарного оператора, любых циклов for, while, do, goto, foreach и прочих функциональных мапов, фолдов и фильтров (допустим, на С++) — решить эту задачку.
kasperos
24.05.2016 10:55+4print(«1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz ...»);
так сойдет?
DennyRolling
24.05.2016 09:59+6ну что же так, массив, числа какие-то: только рекурсия темплейтов, только хардкор
k06a
25.05.2016 08:57+1Чуть упростил принтер https://gist.github.com/k06a/f3cbc6533dec54bb301a880c8f231aa4
DennyRolling
25.05.2016 23:42+2а я тряхнул стариной и зафигачил на дефайнах, раскрывающихся в сто тернарных операторов (каждый из которых может быть вычислен компилятором): http://codepad.org/5t1na6Rs
neosapient
28.05.2016 17:30-3// Могу решить вообще без if-else!
// Достаточно подготовить массив и обращаться по индексу.
#include
#include
char *arr[]={«1», «2», «fizz», «4», «buzz», «fizz», «7», «8», «fizz», «buzz», «11», «fizz», «13», «14», «fizzbuzz», «16», «17», «fizz», «19», «buzz», «fizz», «22», «23», «fizz», «buzz», «26», «fizz», «28», «29», «fizzbuzz», «31», «32», «fizz», «34», «buzz», «fizz», «37», «38», «fizz», «buzz», «41», «fizz», «43», «44», «fizzbuzz», «46», «47», «fizz», «49», «buzz», «fizz», «52», «53», «fizz», «buzz», «56», «fizz», «58», «59», «fizzbuzz», «61», «62», «fizz», «64», «buzz», «fizz», «67», «68», «fizz», «buzz», «71», «fizz», «73», «74», «fizzbuzz», «76», «77», «fizz», «79», «buzz», «fizz», «82», «83», «fizz», «buzz», «86», «fizz», «88», «89», «fizzbuzz», «91», «92», «fizz», «94», «buzz», «fizz», «97», «98», «fizz»};
void echo(int i)
{
printf("%i -> %s\n", i, arr[i-1]);
}
int main(void) {
echo(rand()%100 + 1);
return 0;
}
// жаль, за мой ответ голосовать нельзя ))kasperos
30.05.2016 09:07+1Немного более универсальный подход с массивом:
// 0,1,2… 14
string array a[15]={«fizzbuzz»,«num»,«num»,«fizz»,«num»,«buzz»,«fizz»,«num»,«num»,«fizz»,«buzz»,«num»,«buzz»,«num»,«num»};
string func(int n){
int i=n%15;
if (a[i]==«num») then return string(n);
else return a[i];
}
язык описания немного намешан, но думаю что принцип ясен, если использовать подмену (скрытая проверка условия)
можно избавиться от явного условия «if». Необходимо только на вход подавать целое число больше нуля.kasperos
30.05.2016 09:47Чуть позже дошло что нужно в первом присваивании получать сразу строку из массива для того чтобы второй раз по массиву не елозить.
*
string s=a[n%15];
if(s==«num») then return string(n);
else return s;
*
encyclopedist
24.05.2016 10:17+29Вам ставят минусы не потому, что с вами не согласны или не поняли условие задачи, а потому, что пост совсем не о том, как решать FizzBuzz. Вообще.
Wesha
24.05.2016 22:16+1пост совсем не о том, как решать FizzBuzz. Вообще.
Это-то понятно, но дело в том, что настоящий программист просто физически не способен пройти мимо интересной задачи!
SCareAngel
24.05.2016 13:13+1Без if
import math
def fizzbuzz(i):
return [str(i), 'fizz', 'buzz', 'fizzbuzz'][3 — math.ceil((i % 3) / 2) * 2 — math.ceil((i % 5) / 4)]
for i in range(1, 100):
print(fizzbuzz(i))DrNemo
24.05.2016 17:55+3вы тестировали код?)
SCareAngel
26.05.2016 18:16Одним глазком
Wesha
26.05.2016 18:24Моё почтение, Михаил Илларионович!
SCareAngel
26.05.2016 18:27+1Не намекаете ли вы на то, что у меня 'fizz' и 'buzz' поменялись местами?
Wesha
26.05.2016 18:57Нет, это старая шутка из серии "весь вечер Кутузов строил девушкам
глазкиглазок" ;)
Randl
24.05.2016 02:36+4Идея FizzBuzz — отсеять совсем неумеющих программировать совсем.
Написать его даже в 2 if без else несложно.
С++. Может подумаете самостоятельно?for (uint8_t i = 1; i <= 100; ++i) { std::string s = std::to_string(i); if (!(i%3)) { std::cout << "fizz"; s = ""; } if (!(i%5)) { std::cout << "buzz"; s= ""; } std::cout << s << std::endl; }
Amareis
24.05.2016 03:25+6По-моему тут куда лучше было бы условие написать честно, i%3 == 0. Читаемость значительно повысится, а символов печатать всего на два пробела больше.
tangro
24.05.2016 09:44+7Да вообще эти сишные хаки в стиле «я знаю, что 0 это тоже самое, что NULL и тоже самое что false» — раздражают. if тестирует условие, так будьте добры — пишите в if условия!
Randl
24.05.2016 11:29+1Верно. Просто сначала написал
i%3
, а потом изменил на противоположное условие и добавил!
. Вот и вышло не очень красиво
gena_glot
24.05.2016 05:40+1Задача давалась Джоном Холландом отцом генетических алгоритмов, в 70-х годах.
Ключевой момент вашего решения — операция s = ""
Обычно схема fizzbuzz идет следующим образом:
1. Напишите fizzbuzz — написал в 3 if.
2. Напишите fizzbuzz в 2 if. Пишут что и вы.
3. Избавьтесь от изменения переменных. Тут продолжите свои размышления.
Если вы считаете, что в условиях 3 задача не решается — докажите что решений нет. Если решается — решите.
Неумеющий программировать — уходит на пункте 1
Умеющий программировать — решает пункт 2
Математик — решает пункт 3.
Правило следующие:
if { block } then { block } else { block}; if {block} then {block} else {block} — два if
if; if — два if
if then else if; if — недопустимо. Три if
промежуточные результаты можно сохранить в неизменяюю переменную.
Нельзя менять переменную, в вашем случае s = "" недопустимо для пункта 3.
можно вычислять любые выражения, битовые сдвиги, числа — все допустимо.
Обрамляющий цикл — классический for или while.senia
24.05.2016 06:07В такой формулировке не понятно зачем там второй if.
gena_glot
24.05.2016 06:49Ну у нас максимум два if. Задача Холланда, не моя.
максимум два раза можно узнать результат — это значит пробу сделать.
if (i mod 3 = 0) — опробировали 1 раз
if (i mod 3 = 0 && i mod 5 = 0) — опробировали 1 раз.
Результат первой пробы можно использовать во второй пробе.senia
24.05.2016 10:14И как вы с такими if отличите fizz от fizzbuzz?
i == 3:
i mod 3 = 0 == true
i mod 3 = 0 && i mod 5 = 0 == true
i == 15
i mod 3 = 0 == true
i mod 3 = 0 && i mod 5 = 0 == trueSkidanovAlex
24.05.2016 19:52+1> i == 3:
>…
> i mod 3 = 0 && i mod 5 = 0 == true
это неверноsenia
24.05.2016 21:18Действительно. Там же &&, а не || — перепутал.
Тогда тот же вопрос про n vs buzz: 2 — false, false; 5 — false, false. Суть не изменилась.SkidanovAlex
25.05.2016 00:35Ну да, автор комментария показывал пример того, что он понимает под двумя if-ами, он не предлагал рабочее решение для задачи.
Я подозреваю, что в задаче подразумевается ответ «решения нет», который доказывается как-нибудь через то, что у нас грубо говоря есть только три листа в дереве ветвлений, а надо четыре. Но задачу надо очень сильно формализовать, чтобы убить всякие решения с масками, индексами в массивы итд.
Scf
24.05.2016 09:21+2Вот вам вариант вообще без if-ов.
object FizzBuzz { def main(args: Array[String]) = { val fb = Array[String]( "fizzbuzz", "fizz", "fizz", "fizz", "fizz", "buzz", "a", "a", "a", "a", "buzz", "a", "a", "a", "a") for (i <- 1 to 100) { println(fb(5 * (i % 3) + i % 5).replace("a", i.toString)) } } }
erlyvideo
24.05.2016 11:46-1очень плохой ответ. Элегантный, хакерский, но такой код должен идти с нормальным текстом в комментарии и объяснении, зачем был выбран такой сложный алгоритм
Ogoun
24.05.2016 12:41+1Можно добавить один if чтобы не выводить пустые строки, и сократить массив.
variants = ('', 'fizz', 'buzz', 'fizzbuzz') for i in range(101): v = variants[(i % 3 == 0) + (i % 5 == 0) * 2] if v != '': print("{0}: {1}".format(i, v))
Scf
24.05.2016 13:09Какие такие пустые строки? Мой вариант пустрых строк не выводит…
Но про неявную конверсию bool -> int — хорошая идея.Ogoun
24.05.2016 13:25+2Да, прошу прощения, недочитал задачу, решил что нужно вывести только если делятся, а не с заменой.
Соответственно решение с поправкой будет еще короче:
variants = ('{}', 'fizz', 'buzz', 'fizzbuzz') print("\n".join([variants[(i % 3 == 0) + (i % 5 == 0) * 2].format(i) for i in range(101)]))
Scf
24.05.2016 13:23О, совсем забыл — в скале нет имплиситных конвертаций булеана в инт. Не С++ все же.
Demogor
25.05.2016 19:55+1C 1 if'ом можно вообще без хитрых алгоритмов:
var test=(e)=>{ var q={ 3: 'fizz', 5: 'buzz', 15: 'fizzbuzz' }; var result=e; for(var i in q){ if(e%i==0){ result=q[i]; } } return result; }; var t=[]; for(var i=0; i<100; i++) t.push(test(i)); console.log(t.join(' '))
Вот без — изящно.
findoff
24.05.2016 10:56+3На правах наркомании и отсутствия чувства юмора…
console.clear(); // что бы совсем без if'ов и пустых строк... var a = [ function(){}, console.log.bind(null, 'fizz'), console.log.bind(null, 'buzz'), console.log.bind(null, 'fizz buzz'), ]; for(var i=1; i<=100; ++i){ // получаем в виде битов, через черную магию -> складываем a+b*2 -> и соотвественно выводим [ничего,1е,2е,оба] a[Math.floor(((i-1)%3)/2) + Math.floor(((i-1)%5)/4)*2](i); }
Randl
24.05.2016 11:59+2Без if'ов вообще:
Зато с массивомfor (uint8_t i = 1; i <= 100; ++i) { std::array<std::string, 4> print = {std::to_string(i), "fizz", "buzz", "fizzbuzz"}; bool three = i % 3 == 0, five = i % 5 == 0; std::cout << print[three +five * 2] << std::endl; }
erlyvideo
24.05.2016 11:45идея fizz buzz прежде всего в том, что бы проверить, способен ли программист услышать менеджера и сделать минимально и достаточно необходимое количество действий, что бы решить задачу так, как его попросили. Без tensorflow.
На fizzbuzz как раз всплывают такие случаи, когда человек вместо двух аккуратных ифов, которые он делает с первого раза, возникают монстры типа нейросети.
Но статья очень смешная.Neikist
28.05.2016 17:30+1А если человека берут работать с нейросетями? Тогда логично ведь делать описанным в статье способом.
Artemeey
24.05.2016 21:48Почему на ваш ответ столько минусов?
Amareis
24.05.2016 21:55Проблема в том, что статья-то совсем не про физзбазз, это такое введение в тензорфлоу. Да и вообще-то задачка-то нужна только для того, чтобы выяснить умеет ли человек код писать.
Wesha
24.05.2016 22:20и вообще-то задачка-то нужна только для того, чтобы выяснить умеет ли человек код писать.
Вспомнилось:
Мужик в лесу заблудился, орёт "Ау! Ау!"
Подходит медведь: "Мужик, чего орешь?"
Мужик: "Заблудился, может кто услышит."
Медведь: "Ну я услышал. Тебе легче стало?"
Человек явно умеет решать задачи при помощи TensorFlow. Вам легче стало? ;)
IIvana
24.05.2016 03:51+24Правильно, что не взяли на работу — требовался детерминированный алгоритм, а не эвристика, которая в результате ошиблась :)
bingo347
24.05.2016 03:56-21на js в одну строку
for(let s, i = 0; i < 100; i++) s = '', (i % 3 || (s += 'Fizz')), (i % 5 || (s += 'Buzz')), console.log(s || i);
и незачем мудритьSmagold
24.05.2016 08:32+8У вас он с нуля считает ;)
Статья кстати была не про это, в интернете и так вариантов куча, от красивых до производительных.
Daniro_San
24.05.2016 14:34Раз уж на то пошло, то почему бы и нет…
input([ "FizzBuzz" if not x%3 and not x%5 else "Fizz" if not x%3 else "Buzz" if not x%5 else x for x in range(1, 101)])
как то писал и C++ однострочник, но там настоящий ужас
Daniro_San
24.05.2016 14:37И да, у вас тело на одной строке с условием цикла — это не совсем кошерный однострочник
IIvana
24.05.2016 04:01+1Не, на самом деле герой статьи молодец :) За мой опыт устройства на работу меня тоже далеко не во все места брали, но как я потом радовался, что меня не взяли в те места! Если это реальная история, то все закончилось лучшим исходом — и для героя, и для компании :)
brainick
24.05.2016 04:42+3Остаётся только добавить, что Joel Grus автор отличной книги по машинному обучению http://shop.oreilly.com/product/0636920033400.do которую при некотором усилии можно, сами понимаете как достать.
Demogor
24.05.2016 07:54+13А вывод простой: хочешь провалить интервью — покажи интервьюеру, что он ламер.
khim
24.05.2016 11:58+6Правда, но не совсем. Тут немного другое. Не «покажи интервьюеру, что он ламер», а «покажи интервьюеру, что ты на него плевать хотел».
Я пару раз встречался с людьми, которые обнаруживали ошибки и неточности в моих вопросах и задачах. Было неудобно, да, но на оценку и шансы принятия на работу это влияет только положительно.
А вот когда кандидат сознательно манкирует вопросами интервьюера и вместо решения задачи, о которой его просили, демонстрирует какой он умный и хороший, но не порождает решения, которого от него ждут… это чистый «No Hire».
Ну представьте себе: вы приняли такого на работу, попросили написать что-нибудь… ну мелочь какую-нибудь, но такую, без которой у вас убытков на миллионы, а он вместо этого проделал массу бессмысленной работы, но задачу не решил и в убытки вы таки влетели… зачем оно вам?Randl
24.05.2016 18:05ИМХО давать FizzBuzz на интервью — это дурной тон. Ещё б спросили сколько настройщиков пианино в мире.
Scf
24.05.2016 18:11+4Вы недооцениваете количество людей, которые эту задачу не решат. В моей практике собеседований сложные вопросы просто не нужны — достаточно послушать, что кандидат расскажет про свой проект и поспрашивать основы + простейшие задачки типа "найти максимальную глубину двоичного дерева".
Randl
24.05.2016 18:22Во первых, вопрос в том, на какую позицию интервью. Одно дело джуниор, другое — миддл или сениор.
Во вторых, я имел ввиду не сложность задачи, а её разрекламированность. Ну серьёно, о FizzBuzz знают все. Имейте совесть придумать аналогичную простую задачу если это то, что вам нужно. Сам факт вопроса "вам знакома задача "fizz buzz"?" означает, что интервьюер ожидает, что задача мне знакома.khim
24.05.2016 19:00+3Сам факт вопроса «вам знакома задача „fizz buzz“?» означает, что интервьюер ожидает, что задача мне знакома.
И что? Вам теперь сложно её написать?
Задача может быть сколько угодно «широко разрекламированной», но даже при всём при этом соискатели, в большинстве своём — неспособны её написать. Вы можете? Отлично — возьмите и напишите, а не ломайте комедию.
Во первых, вопрос в том, на какую позицию интервью. Одно дело джуниор, другое — миддл или сениор.
Вот как раз для сениора — эта задачка в самый раз.
Ибо встречал я таких «сениоров»: могут часами песни петь про всякие разные супер-пупер технологии (я и словей-то таких не знаю), рассказывать как у них команда «бороздит просторы Большого театра»… написать FuzzBuzz — не могут. Вот никак.
Обычно дальнейший разговор показывает, что человек последние N лет код не пишет от слова совсем и только «руководит проектами». Ну и на что он претендует, пытаясь устроиться на инженерную должность? Что? У нас не было вакансий «чистого менеджера?»… Действительно не было — потому что подобных должностей у нас нет, извините.
P.S. Я был бы счастлив перенестись как-нибудь ночью в мир где FuzzBuzz на собеседовании реально смысла не имеет ибо все соискатели способны его написать за 2 минуты. В нашем, реальном, мире — всё не так. Увы.Randl
24.05.2016 19:30И что? Вам теперь сложно её написать?
Не сложно. Но я ожидаю задачи своего уровня на собеседовании. И уровня потенциальной работы. И ожидаю, что интервью будет не с первой строчки гугла. И допускаю, что такая задача вкупе с поведением интервьюра может вызвать желание немного над ним поиздеваться.
Вот как раз для сениора — эта задачка в самый раз.
Так дайте ему сложную задачу. Кто не разбирается — одинаково обе не решит.
Scf
24.05.2016 19:41+1Сложные задачи требуют много времени для решения, а зачем мучать и кандидата, и интервьювера, если все нужные выводы можно сделать на основе простой задачи, которую решат за 5 минут?
Ну серьезно, практика показывает, что кандидат, который а) написал на бумажке сортировку пузырьком и б) внятно рассказал, чем хорошо и плохо делать очереди на основе таблицы в оракле, будет неплохо и архитектуру проектировать, и сложные задачи решать.
nightshadows
24.05.2016 20:20Вы много людей собеседовали? Я вот достаточно, и обычно как раз миддл и сеньор. Минимум две трети людей, прошедших даже через скайп интервтью, на очном такую задачу адекватно решить не могут. Серьёзно, об этой задаче знает подавляюще малое число миддлов даже. И если человек не понимает, почему его такое спрашивает и начинает строить сильно умного — это no hire, неумение общаться. Нам с ним не по пути (по крайней мере, в крупных и уважаемых компаниях ему врядли работать).
Randl
24.05.2016 20:42+4Я собеседования только проходил.
Разработчики особенно толковые, могут позволить себе выбирать. И издеваясь так над интервьюером человек обычно отдает себе отчёт, что это no hire.
TheTony
24.05.2016 07:56+1Не люблю эту задачу. Зато у меня тоже есть свое «аналогичное решение» — на SQL…
4elive8
24.05.2016 08:33+14Интересная статья! О том как нужно ввести себя, если компания разочаровала:)
sim0nsays
24.05.2016 08:41+58Ну да, как можно нанимать человека, который делает модель без кросс-валидации и сразу ей показывает весь датасет и оверфитит.
ShashkovS
24.05.2016 12:08+2Да и вообще: нужно было обогатить тестовые данные остатками от деления на 3 и 5 да делать решающее дерево. Работать быстрее будет! Ох уж эти любители перцептронов…
SkidanovAlex
25.05.2016 00:39+6Уоу уоу, полегче! Он тренирует на 101-1024, а тестирует на 1-100. Как можно оферфититься на примеры, которые никогда не видел?
dmitry_ch
24.05.2016 08:54+9За это отдельное спасибо:
интервьюер: Отлично, отлично. Как вы относитесь к написанию кода на доске?
я: Я только так код и пишу!
Порадовали, отличный пост!
А на точке
интервьюер: [смотрит на доску с минуту]
бедный интервьюер понял, что либо он идиот, либо его тролят )))
ittakir
24.05.2016 09:19+8Если кто не понял, эта замечательная статья — про введение в TensorFlow, а не про то, на каком языке круче писать FizzBuzz'ы.
usdglander
24.05.2016 09:19Странно что я не заметил у статьи тега «Юмор».
PavelMSTU
24.05.2016 09:27+26Да, он необходим.
Так же как закадровый смех в американских ситкомах.
А то непонятно, что пост — это сарказмusdglander
24.05.2016 09:57+23Я оценил Ваш сарказм.
Тег — это не указатель на эмоции, какие должен вызывать пост, а инструмент поиска и организации.
voidnugget
24.05.2016 09:24+2Выводы:
- Либо ищут человека способного рубить леса пилочкой для ногтей, везде лепят популярные решения, без понимания их ограничений и целевых задач
- Либо задача давалась для того что бы соискатель сказал пару ласковых интервьюеру, написал саму простую реализацию на питоне, и не пытался гнуть ель резинкой от трусов
avost
24.05.2016 13:13+3Вывод здесь другой совершенно. А именно — ещё никогда не было так просто замутить нейронную сеть…
darkAlert
24.05.2016 12:22-2Спасибо, автор! Возьму на вооружение. А то реально бесит, когда суют всякие олимпиадно-школьные задачки на интервью (не смотря на то, на какую позицию ты идешь).
vstartsev
24.05.2016 13:12Начало было замечательным. Но видимо интервьюер расколол любителя нейросетей, найдя у него ошибку)
nicelight_nsk
24.05.2016 22:02и все же… для не умеющих сходу решать задачу в два if(готов яростно ловить минусы, но я не кодер от природы) статья была про красивый троллинг или про ксрасивый кейс нейросети?
saslanov
24.05.2016 22:23+3Сразу вспоминается поговорка «Когда у тебя есть только молоток, все похоже на гвоздь» :)
gaki
25.05.2016 07:16К вопросу о затроллении интервьюера, может, кто еще не видел классический пост о функции копирования файла.
Wesha
26.05.2016 21:51Он мне так понравился, что я его Вам (и остальным хабравчанам) художественно перевёл
gaki
27.05.2016 13:08Вот еще мне нравится интервью с кандидатом на должность бизнес-аналитика о том, как передвинуть гору Фудзи: https://angryaussie.wordpress.com/2007/11/01/pointless-interview-questions/
Ну и про дизайн велосипеда для слепых тоже отлично придумано (в самом низу, где про вентилятор):
http://thedailywtf.com/articles/Riddle-Me-An-Interview
motus
26.05.2016 22:29+3хехе. а я вчера на регулярных выражениях написал по приколу :)
Слабо?seq 0 30 | sed -r 's/^(([0369]|[147][0369]*[258 ]|([258]|[147][0369]*[147])([0369]|[258][0369]*[147])*([147]|[258][0369]*[258]))*(([147]|[258][0369]*[258])([0369]|[147 ][0369]*[258])*5|0))$/\1\tFizzBuzz/; s/^(([0369]|[147][0369]*[258]|([258]|[147][0369]*[147])([0369]|[258][0369]*[147])* ([147]|[258][0369]*[258]))*)$/\1\tFizz/; s/^([0-9]*[05])$/\1\tBuzz/'
Scf
27.05.2016 11:04Где-то ошибка — должно быть 21 Fizz, а регулярка выводит просто 21.
sed (GNU sed) 4.2.2
motus
27.05.2016 19:17хм. у меня работает. я, видимо, неправильно скопировал из терминала — я вижу, там лишние пробелы есть, которых быть не должно. сейчас скопирую еще раз:
seq 0 30 | sed -r 's/^(([0369]|[147][0369]*[258]|([258]|[147][0369]*[147])([0369]|[258][0369]*[147])*([147]|[258][0369]*[258]))*(([147]|[258][0369]*[258])([0369]|[147][0369]*[258])*5|0))$/\1\tFizzBuzz/; s/^(([0369]|[147][0369]*[258]|([258]|[147][0369]*[147])([0369]|[258][0369]*[147])*([147]|[258][0369]*[258]))*)$/\1\tFizz/; s/^([0-9]*[05])$/\1\tBuzz/'
это должно работать
OksikOneC
28.05.2016 17:29>>Наш вход будет всего лишь двоичное кодирование числе от 1 до 100
А вот тут проблема: а почему числа от 1 до 100 должны генериться? А как же постулат о атомарности и сохранности данных? По хорошему, эту последовательность лучше хранить в таблице базы данных. Соответственно, на доске надо бы еще привести скрипты для генерации таблицы. А для чего? Ну для SQL Server конечно, мы ж взрослы люди, в конце концов. Но мы же в 2016-ом? Поэтому нужна человеческие вьюшка. А куда ж без нее? Затем, раз мы все уж храним в базе, то автору б не мешало все его коды оформить в виде CLR модуля, написать команду компиляции оного куда? В какую-то dll, наверное. Дальше на доске скрипты разрешения CLR, подключения dll. Ну и конечно, создать пользовательские функцией с вызовом всего обвяза из ранее подключенной сборки (обучение, выводы — вот это все). И только потом, как написал коллега выше — go, nodejs и микросервисы.
Где это всё, автор? Почему архитектуру не продумал? Почему сразу стал код писать? Что это? Вопиющая некомпетентность? Спешка? Слабость характера?
Правильно сделали, что не взяли.
;)
AnatoliD
28.05.2016 17:31проблема в том, что на РЕАЛЬНОМ производстве, к которому интернет имеет косвенное отношение и задачи там совсем другие, проблем в том, что там такие вопросы для школьников не задают, так ценятся люди совсем другие — умеющие проектировать и реализовывать до уровня железа стенды, системы управления оборудованием и т.д.
Собственно говоря одна из причин, почему в России не могут решить проблемы с импортозамещением или проще — осуществить новую ре — индустриализацию, состоит как раз в том, что в стране тех. специалистов (согласно статистике) только 10%, при этом специалистов занимающихся железом возможно сотые доли процента среди всех специалистов…
Versusnja
28.05.2016 17:31Даже такой замудреный вариант решения задачи — через перцептрон, можно было решить куда проще — в первую очередь используя используя sklearn, вместо tensorflow. Он лаконичнее решает простые задачи по машинному обучению.
При этом сама конструкция перцептрона вызывает сомнение. На вход нет нужды подавать весь этот мусор. Это создает лишние шумы. Лучше подавать сразу остаток от деления на 3 и 5. Только 2 инпута.
Ну и использовать softmax тоже нет нужды. Он хорош для multi-class аутпута. А это не тот случай. Правильнее было бы просто linear.
Может тогда взяли бы на работу? А точность уж наверняка была бы 100% :)
King_Lamer
30.05.2016 17:10-2Вот еще вариант решения без использования временной переменной
for (int i = 1; i < 101; i++)
{
Console.WriteLine((«fizzbuzz» + i.ToString())
.Replace((i%3 != 0)? «fizz»: i.ToString(), "")
.Replace((i%5 != 0)? «buzz»: i.ToString(), ""));
}
Мопед не мой, попросили разместить)))
prijutme4ty
02.06.2016 12:30+2Правильно не взяли, он неправильно использует инструмент. 100 слоев на такой маленькой выборке не обучить. При том, что деление на пять тривиально проверяется двухслойной сеткой, да и деление на три наверняка можно сделать при помощи двухслойной-трехслойной рекуррентной сети.
Randl
Надо было делать на brainfuck. Тогда бы взяли