diff --git a/README.md b/README.md
index a697af3..04e1300 100644
--- a/README.md
+++ b/README.md
@@ -98,7 +98,13 @@
- Какие средства обобщенного программирования есть в Go?
- Какие технологические преимущества языка Go вы можете назвать?
- Какие технологические недостатки языка Go вы можете назвать?
-
+10. Популярные задачи на собеседованиях
+ - На вход подаются два неупорядоченных слайса любой длины. Надо написать функцию, которая возвращает их пересечение
+ - Написать генератор случайных чисел
+ - Слить N каналов в один
+ - Сделать конвейер чисел
+ - Написать WorkerPool с заданной функцией
+ - Сделать кастомную waitGroup на семафоре
## Как мне добавить свой вопрос-ответ?
- [Ознакомтесь с шаблоном составления](TEMPLATE.md)
diff --git a/docs/POPULAR _TASKS.md b/docs/POPULAR _TASKS.md
new file mode 100644
index 0000000..ca0f515
--- /dev/null
+++ b/docs/POPULAR _TASKS.md
@@ -0,0 +1,329 @@
+### Популярные задачи на собеседованиях
+
+### 1. На вход подаются два неупорядоченных слайса любой длины. Надо написать функцию, которая возвращает их пересечение
+
+Можно решить сортировкой, за более долгое время, но без выделения дополнительной памяти.
+А можно выделить дополнительную память и решить за линейное время.
+
+Надо посчитать количество появлений элементов первого массива (лучше брать тот, что покороче) — используем для этого словарь.
+Потом пройтись по второму массиву и вычитать из словаря те элементы, которые есть в нем.
+По ходу добавляем в результат те элементы, у которых частота появлений больше нуля.
+
+ - [x] Советуем посетить [Математические операции над множествами](https://github.com/goavengers/go-datastructure#-point_right-%D0%BC%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%B0-sets)
+
+```go
+package main
+
+import (
+ "fmt"
+)
+
+// На вход подаются два неупорядоченных массива любой длины.
+// Необходимо написать функцию, которая возвращает пересечение массивов
+func intersection(a, b []int) []int {
+ counter := make(map[int]int)
+ var result []int
+
+ for _, elem := range a {
+ counter[elem]++
+ }
+
+ for _, elem := range b {
+ if count, ok := counter[elem]; ok && count > 0 {
+ counter[elem] -= 1
+ result = append(result, elem)
+ }
+ }
+ return result
+}
+
+func main() {
+
+ a := []int{23, 3, 1, 2}
+ b := []int{6, 2, 4, 23}
+ // [2, 23]
+ fmt.Printf("%v\n", intersection(a, b))
+ a = []int{1, 1, 1}
+ b = []int{1, 1, 1, 1}
+ // [1, 1, 1]
+ fmt.Printf("%v\n", intersection(a, b))
+}
+```
+
+### 2. Написать генератор случайных чисел
+
+В принципе, легкая задача, на базовые знания по асинхронному взаимодействию в Go.
+Для решения я бы использовал небуфферезированный канал. Будем асинхронно писать туда случайные числа и закроем его, когда закончим писать.
+
+Плюс ее можно использовать в немного измененном виде в задаче на [слияние N каналов](#3).
+
+```go
+package main
+
+import (
+ "fmt"
+ "math/rand"
+ "time"
+)
+
+func randNumsGenerator(n int) <-chan int {
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+ out := make(chan int)
+ go func() {
+ for i := 0; i < n; i++ {
+ out <- r.Intn(n)
+ }
+ close(out)
+ }()
+ return out
+}
+
+func main() {
+ for num := range randNumsGenerator(10) {
+ fmt.Println(num)
+ }
+}
+```
+
+### 3. Слить N каналов в один
+
+Даны n каналов типа chan int. Надо написать функцию, которая смерджит все данные из этих каналов в один и вернет его.
+
+Мы хотим, чтобы результат работы функции выглядел примерно так:
+
+```go
+for num := range joinChannels(a, b, c) {
+ fmt.Println(num)
+}
+```
+
+Для этого напишем функцию, которая будет асинхронно читать из исходных каналов, которые ей передадут в качестве аргументов, и писать в результирующий канал, который вернется из функции.
+
+Создаем канал, куда будем сливать все данные.
+Он будет небуферезированный, потому что мы не знаем, сколько данных придет из каналов.
+
+Дальше асинхронно прочитаем из исходных каналов и закроем результирующий канал для мерджа, когда все чтение закончится.
+Чтобы дождаться конца чтения, просто обернем этот цикл по каналам в wait group.
+
+```go
+package main
+
+import (
+ "sync"
+)
+
+func joinChannels(chs ...<-chan int) <-chan int {
+ mergedCh := make(chan int)
+
+ go func() {
+ wg := &sync.WaitGroup{}
+
+ wg.Add(len(chs))
+
+ for _, ch := range chs {
+ go func(ch <-chan int, wg *sync.WaitGroup) {
+ defer wg.Done()
+
+ for id := range ch {
+ mergedCh <- id
+ }
+ }(ch, wg)
+ }
+
+ wg.Wait()
+ close(mergedCh)
+ }()
+
+ return mergedCh
+}
+```
+
+```go
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+
+ a := make(chan int)
+ b := make(chan int)
+ c := make(chan int)
+
+ go func() {
+ for _, num := range []int{1, 2, 3} {
+ a <- num
+ }
+ close(a)
+ }()
+
+ go func() {
+ for _, num := range []int{20, 10, 30} {
+ b <- num
+ }
+ close(b)
+ }()
+
+ go func() {
+ for _, num := range []int{300, 200, 100} {
+ c <- num
+ }
+ close(c)
+ }()
+
+ for num := range joinChannels(a, b, c) {
+ fmt.Println(num)
+ }
+}
+```
+
+### 4. Сделать конвейер чисел
+
+Даны два канала.
+В первый пишутся числа.
+Нужно, чтобы числа читались из первого по мере поступления,
+что-то с ними происходило (допустим, возводились в квадрат) и результат записывался во второй канал.
+
+Довольно частая задача, более подробно можно почитать [тут](https://blog.golang.org/pipelines).
+
+Решается довольно прямолинейно — запускаем две горутины.
+- В одной пишем в первый канал.
+- Во второй читаем из первого канала и пишем во второй.
+
+Главное — не забыть закрыть каналы, чтобы ничего нигде не заблокировалось.
+
+```go
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ naturals := make(chan int)
+ squares := make(chan int)
+
+ go func() {
+ for x := 0; x <= 10; x++ {
+ naturals <- x
+ }
+
+ close(naturals)
+ }()
+
+ go func() {
+ for x := range naturals {
+ squares <- x * x
+ }
+
+ close(squares)
+ }()
+
+ for x := range squares {
+ fmt.Println(x)
+ }
+}
+```
+
+### 5. Написать WorkerPool с заданной функцией
+
+Довольно распространенная задача, плюс подобные задачи встречаются на практике.
+
+Нам нужно разбить процессы на несколько горутин — при этом не создавать новую горутину каждый раз,
+а просто переиспользовать уже имеющиеся.
+- Для этого создадим канал с джобами и результирующий канал.
+- Для каждого воркера создадим горутину, который будет ждать новую джобу, применять к ней заданную функцию и пулять ответ в результирующий канал.
+
+```go
+package main
+
+import (
+ "fmt"
+)
+
+func worker(id int, f func(int) int, jobs <-chan int, results chan<- int) {
+ for j := range jobs {
+ results <- f(j)
+ }
+}
+
+func main() {
+
+ const numJobs = 5
+ jobs := make(chan int, numJobs)
+ results := make(chan int, numJobs)
+
+ multiplier := func(x int) int {
+ return x * 10
+ }
+
+ for w := 1; w <= 3; w++ {
+ go worker(w, multiplier, jobs, results)
+ }
+
+ for j := 1; j <= numJobs; j++ {
+ jobs <- j
+ }
+
+ close(jobs)
+
+ for i := 1; i <= numJobs; i++ {
+ fmt.Println(<-results)
+ }
+}
+```
+
+Семафор можно легко получить из канала.
+Чтоб не аллоцировать лишние данные, будем складывать туда пустые структуры.
+
+В нашем случае мы хотим сделать семафор, который будет ждать выполнения пяти горутин.
+- Для этого просто добавим вместо обычного канала буфферизированный.
+- И внутри каждой горутины положим в него значение.
+- А в конце будем дожидаться, что все ок — мы вычитаем все значения из канала.
+
+```go
+package main
+
+import (
+ "fmt"
+)
+
+type sema chan struct{}
+
+func New(n int) sema {
+ return make(sema, n)
+}
+
+func (s sema) Inc(k int) {
+ for i := 0; i < k; i++ {
+ s <- struct{}{}
+ }
+}
+
+func (s sema) Dec(k int) {
+ for i := 0; i < k; i++ {
+ <-s
+ }
+}
+
+func main() {
+ numbers := []int{1, 2, 3, 4, 5}
+ n := len(numbers)
+
+ sem := New(n)
+
+ for _, num := range numbers {
+ go func(n int) {
+ fmt.Println(n)
+ sem.Inc(1)
+ }(num)
+ }
+
+ sem.Dec(n)
+
+}
+```
+
+### 6. Сделать кастомную waitGroup на семафоре
\ No newline at end of file