Данил Низамов

Пользователь

Регистрация: 17.08.2021

Сообщения: 468

Рейтинг: 320

Данил Низамов

Регистрация: 17.08.2021

Сообщения: 468

Рейтинг: 320

Расскажите, на каких задачах вас валили на собеседованиях? Есть какие-то задачи-гробы, которые похоронили вашу попытку попасть в компанию? Я один раз на алгоритмической секции яндекса завалился на вот такой задаче. Она супер ультра простая, сейчас я ее даже студентам не даю, но я тогда был совсем новичком в проге и не справился FeelsBadMan.png?1592047203

Написать функцию kDiffPairs(arr, k), которая принимает на вход массив целых чисел arr и целое число k и возвращает количество таких пар чисел в массиве, разность которых по модулю равна k.

Например, если arr = [3, 5, 8, 3, 2, 8] и k = 3, то функция должна вернуть 3, так как в массиве ровно три подходящие пары чисел:

  • arr[1] = 5 и arr[2] = 8 (8 - 5 = 3)
  • arr[1] = 5 и arr[5] = 8 (8 - 5 = 3)
  • arr[1] = 5 и arr[4] = 2 (5 - 2 = 3)

Если не встречались с такой задачей, попробуйте за 10 минут придумать решение за O(N) или хотя бы за O(NlogN) и написать в комментариях

Фациал Камшот

Пользователь

Регистрация: 24.09.2019

Сообщения: 410

Рейтинг: 13

Фациал Камшот

Регистрация: 24.09.2019

Сообщения: 410

Рейтинг: 13

Я хз что это

haHAA

Пользователь

Регистрация: 25.03.2017

Сообщения: 1109

Рейтинг: 742

haHAA

Регистрация: 25.03.2017

Сообщения: 1109

Рейтинг: 742

img

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

Энивей, не дай бог увидеть такое на собесе на должность, где платят меньше, чем в MAANG. Они же оттуда понабрались алгоритмических задач, но платят почему-то меньше roflanLico.png?1616515069

Kujivunia

Пользователь

Регистрация: 10.01.2015

Сообщения: 5535

Рейтинг: 959

Нарушения: 10

Kujivunia

Регистрация: 10.01.2015

Сообщения: 5535

Рейтинг: 959

Нарушения: 10

haHAA сказал(а):

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

Энивей, не дай бог увидеть такое на собесе на должность, где платят меньше, чем в MAANG. Они же оттуда понабрались алгоритмических задач, но платят почему-то меньше roflanLico.png?1616515069

Нажмите, чтобы раскрыть...

Так с сортировкой же в любом случае logN будет, быстрее не получится, не? 

Данил Низамов

Пользователь

Регистрация: 17.08.2021

Сообщения: 468

Рейтинг: 320

Данил Низамов

Регистрация: 17.08.2021

Сообщения: 468

Рейтинг: 320

Kujivunia сказал(а):

Так с сортировкой же в любом случае logN будет, быстрее не получится, не? 

Нажмите, чтобы раскрыть...

Да, в большинстве случаев сортировка дает сложность минимум O(N*logN). В очень редких случая можно отсортировать за O(N), но для этого должны быть специфичные данные

Здесь сортировка это странная идея, я бы ее не рассматривал

SonicWaveAnd

Пользователь

Регистрация: 19.11.2018

Сообщения: 1931

Рейтинг: 660

SonicWaveAnd

Регистрация: 19.11.2018

Сообщения: 1931

Рейтинг: 660

I=0

For x in range(0,Len(Arr)):

   D=Arr[x]-arr[x+1]

 

   If abs(D)==k:

      I+=1

 Print(I)

В голове так понравилось

Понятное дело не стал писать прям функцию, главное момент идеи

FreeM@n

Пользователь

Регистрация: 06.03.2013

Сообщения: 1981

Рейтинг: 4209

FreeM@n

Регистрация: 06.03.2013

Сообщения: 1981

Рейтинг: 4209

img

Что-то вроде такого можно наговнокодить сходу:

def kDiffPairs(arr,k):

    counter = 0

    def increase_counter():

        nonlocal counter

        counter+=1    

    for i, elem in enumerate(arr):

        &increase_counter() for elem_ in arr&i:] if abs(elem_ - elem) == k]

    return counter

Бтв д2ру ломает код, вместо символа & должна быть открывающая квадратная скобка [

iChrome[BY]

Пользователь

Регистрация: 21.05.2014

Сообщения: 184

Рейтинг: 181

iChrome[BY]

Регистрация: 21.05.2014

Сообщения: 184

Рейтинг: 181

SonicWaveAnd сказал(а):

I=0

For x in range(0,Len(Arr)):

   D=Arr[x]-arr[x+1]

   If abs(D)==k:

      I+=1

 Print(I)

В голове так понравилось

Понятное дело не стал писать прям функцию, главное момент идеи

Нажмите, чтобы раскрыть...

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

SonicWaveAnd

Пользователь

Регистрация: 19.11.2018

Сообщения: 1931

Рейтинг: 660

SonicWaveAnd

Регистрация: 19.11.2018

Сообщения: 1931

Рейтинг: 660

iChrome[BY] сказал(а):

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

Нажмите, чтобы раскрыть...

Да я внимательно сейчас задачу прочитал

Задумался теперь

Islamic_crusader

Пользователь

Регистрация: 29.11.2017

Сообщения: 5130

Рейтинг: 5262

Islamic_crusader

Регистрация: 29.11.2017

Сообщения: 5130

Рейтинг: 5262

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

Данил Низамов

Пользователь

Регистрация: 17.08.2021

Сообщения: 468

Рейтинг: 320

Данил Низамов

Регистрация: 17.08.2021

Сообщения: 468

Рейтинг: 320

Islamic_crusader сказал(а):

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

Нажмите, чтобы раскрыть...

Ну потому что это неэффективное решение. Тебе скажут "молодец, но давай подумаем над более эффективным решением".

 

FreeM@n сказал(а):
Что-то вроде такого можно наговнокодить сходу:

def kDiffPairs(arr,k):

    counter = 0

    def increase_counter():

        nonlocal counter

        counter+=1    

    for i, elem in enumerate(arr):

        &increase_counter() for elem_ in arr&i:] if abs(elem_ - elem) == k]

    return counter

Бтв д2ру ломает код, вместо символа & должна быть открывающая квадратная скобка [ &/QUOTE]

 

 

Нажмите, чтобы раскрыть...
 

По-моему здесь O(N^2). Не пойдет

SonicWaveAnd

Пользователь

Регистрация: 19.11.2018

Сообщения: 1931

Рейтинг: 660

SonicWaveAnd

Регистрация: 19.11.2018

Сообщения: 1931

Рейтинг: 660

I=0

 

For x in range(0,Len(Arr)):

    For m in range(x+1, Len(Arr)):

         D=Arr[x]-arr[m]

 

         If abs(D)==k:

            I+=1

 Print(I)

Поправил

@iChrome[BY] 

Скажите что я лс  , я и так это знаюroflanPominki.png?1616515180

Данил Низамов

Пользователь

Регистрация: 17.08.2021

Сообщения: 468

Рейтинг: 320

Данил Низамов

Регистрация: 17.08.2021

Сообщения: 468

Рейтинг: 320

SonicWaveAnd сказал(а):

I=0

 

For x in range(0,Len(Arr)):

    For m in range(x+1, Len(Arr)):

         D=Arr[x]-arr[m]

         If abs(D)==k:

            I+=1

 Print(I)

Поправил

@iChrome[BY] 

Скажите что я лс  , я и так это знаюroflanPominki.png?1616515180

Нажмите, чтобы раскрыть...

 

Работать будет, но это неэффективное решение. Здесь сложность по времени O(N^2), а нам нужно хотя бы O(N*logN)

iChrome[BY]

Пользователь

Регистрация: 21.05.2014

Сообщения: 184

Рейтинг: 181

iChrome[BY]

Регистрация: 21.05.2014

Сообщения: 184

Рейтинг: 181

public static int countOfPairs(List source, Integer desired) {

   Set occurrences = new HashSet<>();

   int result = 0;

   for (Integer value : source) {

       Integer difference1 = value - desired;

       Integer difference2 = desired + value;

       if (occurrences.contains(difference1)) {

           result++;

       } else if (occurrences.contains(difference2)) {

           result++;

       }

       occurrences.add(value);

   }

   return result;

}

Добавляем элементы в Set, поиск вхождения в котором работает за O(1). Потом проходимся один раз по массиву, ищем в Set элемент разница с которым будет давать нужное значение. Т.к. проходимся по массиву только один раз, то сложность алгоритма равна O(N)

 

p.s. дженерики форум не захотел отображать

Glazic

Пользователь

Регистрация: 08.06.2013

Сообщения: 6373

Рейтинг: 3657

Glazic

Регистрация: 08.06.2013

Сообщения: 6373

Рейтинг: 3657

iChrome[BY] сказал(а):
public static int countOfPairs(List source, Integer desired) {

   Set occurrences = new HashSet<>();

   int result = 0;

   for (Integer value : source) {

       Integer difference1 = value - desired;

       Integer difference2 = desired + value;

       if (occurrences.contains(difference1)) {

           result++;

       } else if (occurrences.contains(difference2)) {

           result++;

       }

       occurrences.add(value);

   }

   return result;

}

Добавляем элементы в Set, поиск вхождения в котором работает за O(1). Потом проходимся один раз по массиву, ищем в Set элемент разница с которым будет давать нужное значение. Т.к. проходимся по массиву только один раз, то сложность алгоритма равна O(N)

Нажмите, чтобы раскрыть...

Добавление элементов в сет уже N 

iChrome[BY]

Пользователь

Регистрация: 21.05.2014

Сообщения: 184

Рейтинг: 181

iChrome[BY]

Регистрация: 21.05.2014

Сообщения: 184

Рейтинг: 181

Glazic сказал(а):

Добавление элементов в сет уже N 

Нажмите, чтобы раскрыть...

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

Сама операция добавления в Set работает за O(1) благодаря тому, как работает хеш-функция

Glazic

Пользователь

Регистрация: 08.06.2013

Сообщения: 6373

Рейтинг: 3657

Glazic

Регистрация: 08.06.2013

Сообщения: 6373

Рейтинг: 3657

iChrome[BY] сказал(а):

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

Нажмите, чтобы раскрыть...

Да, память то резиновая.

SonicWaveAnd

Пользователь

Регистрация: 19.11.2018

Сообщения: 1931

Рейтинг: 660

SonicWaveAnd

Регистрация: 19.11.2018

Сообщения: 1931

Рейтинг: 660

Данил Низамов сказал(а):

 

Работать будет, но это неэффективное решение. Здесь сложность по времени O(N^2), а нам нужно хотя бы O(N*logN)

Нажмите, чтобы раскрыть...

Я понял про что ты хочешь до нести, чтобы улучшить быстродействие. Даже что-то подобное видел, где рассказывали что если будет гигантский массив. Он будет очень долго обрабатываться и там применяли какую то технику для быстродействияZeroTwoThinking.png?1621090694

Я к сожелению пока что пайтоном в процедурном ввиде владею и капелькой ООП

Плюс ещё bash, Linux, метрики ещё пытаюсь делать. И не получается окунуться, время бесит Koteyka.png?1619500420

 

Но даже так мне прям хорошо стало, что кто то написал работать будетmiyanohey.png?1621091349

 

Данил Низамов

Пользователь

Регистрация: 17.08.2021

Сообщения: 468

Рейтинг: 320

Данил Низамов

Регистрация: 17.08.2021

Сообщения: 468

Рейтинг: 320

Glazic сказал(а):

Да, память то резиновая.

Нажмите, чтобы раскрыть...

Ничего страшного, здесь максимальная затрата памяти это создать копию массива. Это ок, так как во времени очень много выигрываем

iChrome[BY]

Пользователь

Регистрация: 21.05.2014

Сообщения: 184

Рейтинг: 181

iChrome[BY]

Регистрация: 21.05.2014

Сообщения: 184

Рейтинг: 181

Glazic сказал(а):

Да, память то резиновая.

Нажмите, чтобы раскрыть...

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

В моем решении сложность O(N) и память O(N)

Ребята сверху скидывали решения за O(N^2) но зато по памяти там было O(1)

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

YoshkinKot

Пользователь

Регистрация: 20.06.2016

Сообщения: 13533

Рейтинг: 5384

YoshkinKot

Регистрация: 20.06.2016

Сообщения: 13533

Рейтинг: 5384

Данил Низамов сказал(а):

решение за O(N)

Нажмите, чтобы раскрыть...

Ну прямо честное-пречестное O(N) в принципе наверное не возможно, любые ограничения на модель памяти и вычислений — по сути деграднём до pointer machine при существенно великих целых числах, а их я напомню, бесконечное число.

А все машинки, что умеют точно такое перемалывать за O(N) — это какие-то монстры с бесконечными лентами и телепортацией в ячейку.

 

А если на них есть ограничения, то есть M = O(1), то можно вот такой мем сделать:

 

Давайте сделаем ленивый лес полных бинарных деревьев высоты ...4 3 2 1 2 3 4..., который будет эмулировать бесконечную двунаправленную ленту памяти, индексами которых являются целые числа.

 

Доступ к ячейке всегда можно организовать за log(M), где M — номер ячейки.

В самом деле ячейка находится в бинарном дереве под ≈ O(log(M)) номером, с поправкой на направление, спустится к самой ячейке в дереве можно будет тоже за O(log(M)). Суммарно понятно, O(log(M)) + O(log(M)) = O(log(M))

 

Теперь с помощью ленты просто будем делать следующее:

def diff(numbers, k):

    freq = Cells()

    for number in numbers:

        freq[number] += 1

 

   

    return sum(freq[number + k] for number in numbers)

 

 

ну и всё любая операция с Cells это O(log(M)), памяти ячейки, если реализовать их лениво, жрут O(N ∙ log(M)) памяти, и в итоге O(log(M) ∙ N) по памяти и времени получаем.

 

Но, теперь вспоминаем, что M = O(1), и по итогу решение за O(N)

 

iChrome[BY] сказал(а):
public static int countOfPairs(List source, Integer desired) {

   Set occurrences = new HashSet<>();

   int result = 0;

   for (Integer value : source) {

       Integer difference1 = value - desired;

       Integer difference2 = desired + value;

       if (occurrences.contains(difference1)) {

           result++;

       } else if (occurrences.contains(difference2)) {

           result++;

       }

       occurrences.add(value);

   }

   return result;

}

Добавляем элементы в Set, поиск вхождения в котором работает за O(1). Потом проходимся один раз по массиву, ищем в Set элемент разница с которым будет давать нужное значение. Т.к. проходимся по массиву только один раз, то сложность алгоритма равна O(N)

 

p.s. дженерики форум не захотел отображать

Нажмите, чтобы раскрыть...

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

но сначала надо добавить в сет все числа, а потом считать вхождения

 

пример где ты повалишься [1, 1, 2], k = 1

   
step set before value result set after
1 {} 1 0 {1}
2 {1} 1 0 {1}
3 {1} 2 1 {1, 2}

 

ну и есть еще проблема: ты почему-то предполагаешь, что каждое число может быть в паре только с двумя другими индексами, хотя это явно не так и пар может быть с тремя и более индексами у одного числа

 

пример:

1 1 1 1 1 2 2 2 2 2 , каждая единица имеет пары с 5 индексами у двойки, а двойки имеют в паре 5 единиц

итого тут 25 пар минимум, ты же при всём желании насчитаешь 20: 10 чисел, каждое даст +2

 

так, короче можно на самом деле, но надо всё равно хранить frequency count на отрезке

 

что-то типо

на очередном шаге посчитаны пары для подотрезка <0, j), добавляем элемент j

тогда надо внести следующие изменения:

 

1) freq[ value ] += 1

 

 

2) result += freq[value + k] + freq[value - k]

 

 

с первым шагом я думаю всё ясно

 

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

 

амортизированное матожидание работы операций хешмапы при хорошей хешфункции — E[T'(n)] = O(1)

ну и потому матожидание работы алгосы — O(n)

 

но надо понимать, что матожидание — это матожидание, средняя температура по больнице

если там какое-то вечно плохое распределение... ну ладно это можно пофиксить рандомным выбором хешфукнции

 

но тем не менее можно нарваться на случайный O(n²), особенно когда счёт идёт на миллионы операций

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

iChrome[BY]

Пользователь

Регистрация: 21.05.2014

Сообщения: 184

Рейтинг: 181

iChrome[BY]

Регистрация: 21.05.2014

Сообщения: 184

Рейтинг: 181

YoshkinKot сказал(а):

ну и есть еще проблема: ты почему-то предполагаешь, что каждое число может быть в паре только с двумя другими индексами, хотя это явно не так и пар может быть с тремя и более индексами у одного числа

Нажмите, чтобы раскрыть...

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

 

YoshkinKot сказал(а):

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

но сначала надо добавить в сет все числа, а потом считать вхождения

Нажмите, чтобы раскрыть...

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

 

YoshkinKot сказал(а):

но надо понимать, что матожидание — это матожидание, средняя температура по больнице

если там какое-то вечно плохое распределение... ну ладно это можно пофиксить рандомным выбором хешфукнции

Нажмите, чтобы раскрыть...

Да, понятно, что при плохой хеш функции поиск в сете станет работать за O(N) и тогда весь алгоритм превращается в O(N^2).

Спасибо за большой ответ и найденную ошибку)

Meepka

Пользователь

Регистрация: 08.10.2012

Сообщения: 1928

Рейтинг: 465

Meepka

Регистрация: 08.10.2012

Сообщения: 1928

Рейтинг: 465

Данил Низамов сказал(а):

Расскажите, на каких задачах вас валили на собеседованиях? Есть какие-то задачи-гробы, которые похоронили вашу попытку попасть в компанию? Я один раз на алгоритмической секции яндекса завалился на вот такой задаче. Она супер ультра простая, сейчас я ее даже студентам не даю, но я тогда был совсем новичком в проге и не справился FeelsBadMan.png?1592047203

Написать функцию kDiffPairs(arr, k), которая принимает на вход массив целых чисел arr и целое число k и возвращает количество таких пар чисел в массиве, разность которых по модулю равна k.

Например, если arr = [3, 5, 8, 3, 2, 8] и k = 3, то функция должна вернуть 3, так как в массиве ровно три подходящие пары чисел:

  • arr[1] = 5 и arr[2] = 8 (8 - 5 = 3)
  • arr[1] = 5 и arr[5] = 8 (8 - 5 = 3)
  • arr[1] = 5 и arr[4] = 2 (5 - 2 = 3)

Если не встречались с такой задачей, попробуйте за 10 минут придумать решение за O(N) или хотя бы за O(NlogN) и написать в комментариях

Нажмите, чтобы раскрыть...

Ну так покажи решение за  O(N)

 

YoshkinKot сказал(а):

 

def diff(numbers, k):

    freq = Cells()

    for number in numbers:

        freq[number] += 1

   

    return sum(freq[number] * freq[number + k] for number in numbers)

 

Но, теперь вспоминаем, что M = O(1), и по итогу решение за O(N)

 

Нажмите, чтобы раскрыть...

Как у тебя проход по массиву дважды вдруг уложился в O(n)?

YoshkinKot

Пользователь

Регистрация: 20.06.2016

Сообщения: 13533

Рейтинг: 5384

YoshkinKot

Регистрация: 20.06.2016

Сообщения: 13533

Рейтинг: 5384

Meepka сказал(а):

Ну так покажи решение за  O(N)

 

Как у тебя проход по массиву дважды вдруг уложился в O(n)?

Нажмите, чтобы раскрыть...

O(n) + O(n) = O(n)

Meepka сказал(а):

Ну так покажи решение за  O(N)

 

Как у тебя проход по массиву дважды вдруг уложился в O(n)?

Нажмите, чтобы раскрыть...

там не вложенный цикл

ну и это «условный» O(n), потому что я потребовал max({a_i}) = O(1)

 

Zacateca

Пользователь

Регистрация: 22.12.2017

Сообщения: 34332

Рейтинг: 13379

Нарушения: 35

Zacateca

Регистрация: 22.12.2017

Сообщения: 34332

Рейтинг: 13379

Нарушения: 35

Данил Низамов сказал(а):

Расскажите, на каких задачах вас валили на собеседованиях? Есть какие-то задачи-гробы, которые похоронили вашу попытку попасть в компанию? Я один раз на алгоритмической секции яндекса завалился на вот такой задаче. Она супер ультра простая, сейчас я ее даже студентам не даю, но я тогда был совсем новичком в проге и не справился FeelsBadMan.png?1592047203

Написать функцию kDiffPairs(arr, k), которая принимает на вход массив целых чисел arr и целое число k и возвращает количество таких пар чисел в массиве, разность которых по модулю равна k.

Например, если arr = [3, 5, 8, 3, 2, 8] и k = 3, то функция должна вернуть 3, так как в массиве ровно три подходящие пары чисел:

  • arr[1] = 5 и arr[2] = 8 (8 - 5 = 3)
  • arr[1] = 5 и arr[5] = 8 (8 - 5 = 3)
  • arr[1] = 5 и arr[4] = 2 (5 - 2 = 3)

Если не встречались с такой задачей, попробуйте за 10 минут придумать решение за O(N) или хотя бы за O(NlogN) и написать в комментариях

Нажмите, чтобы раскрыть...

создай двумерный массив и записывай туда в x[0] - уникальное число, а в x[1] - количество повторений.

 

Вернуть макс значение x[n][1].

Можно сделать через DICT, где ключ - число, а значение - кол-во повторений.

 

Либо можно спихнуть массив в словарь (сбросить дубликаты),  сам массив конвертнуть в строку (с разделителем) и искать через text.count( element of dict)