Pull to refresh

Comments 42

UFO just landed and posted this here
Любая прямая, проведенная через центры любых квадратов является искомой, при этом квадраты можно вертеть на чем хочешь.
UFO just landed and posted this here
UFO just landed and posted this here
Очевидно чтобы проще можно было однозначно задать квадрат.
Нужно дать координаты, например, верхней левой вершины и длину стороны.
Если это условие выбросить, то нужно еще одну величину указывать
Не сумели сформулировать красивую задачу. Видимо, задача такого типа

D2. Найти пересечение прямой и выпуклого многоугольника
Задача 2. Решение — создать длинный массив последовательных значений и сложение производить путем сдвига индекса сначала на одно число, а потом на другое. Для этого сначала обрезать массив от числа a и до конца, а затем выбрать в нем элемент с индексом b. В условии задачи нет ограничений на использование вспомогательных структур памяти.
Т.е. как то так (на питоне) sum = numbers[a:][b]
просто каждый бит считать с помощью full adder через OR, AND и XOR
UFO just landed and posted this here
1. Отсортировать массив в порядке возрастания и вернуть первые К элементов
При определенно малых k, использование кучи неэффективно. Потому нужно переходить к сортировкам/куче только когда простой поиск минимума с числом операций (n*k — Sk) > n*log(n), Sk — сумма арифметической прогрессии от 1 до k, которой в принципе можно пренебречь.
первая задача делается легко и красиво с помощью Stream, буквально в одну строчку :)
// задача 1: поиск "K" наименьших в массиве
int K = 5;
List<Integer> src_1 = Arrays.asList(45, 36, 55, 12, 8, 9, 33, 24, 15);
List<Integer> res_1 = src_1.stream().sorted().limit(K).collect(toList());
res_1.stream().reduce(0, (acc,elem) -> { System.out.println("res[" + ++acc + "] = " + elem); return acc; });

по поводу задачи 2 не понял немного, что требуется. применить статическую функцию Integer::sum()?
или речь о чём-то другом?

третья задача удивительно лёгкая, но раз такие прения в комментах — не буду говорить принцип решения :))
иэх, что-то вредность во мне проснулась :)

в третьей задаче надо провести прямую через центры квадратов;
правильность такого решения доказывается в уме, на уровне геометрии 7-го класса (или с какого там класса её начинают изучать)
ой, только сейчас удосужился дочитать текст публикации до конца :))
жаль, нельзя стирать свои комменты :)
написали в личку по поводу приза )
могу предложить такой способ сложения двух чисел без сложения:

шутка, конечно :)
условиям не удовлетворяет из-за умножения и др. операций
можна ещё в виде нейронной сети сделать сумматор. ))
Первую задачу задали на интервью в Гугле. «Найти k минимальных элементов в массиве размера N, для о-о-очень большого N.» — «Отсортировать и взять k первых?» — «А сложность какая?» — «Ну шо-то типа O(N log(N)).» — «А быстрее можно?» — «Можно взять бинарную кучу размера k и пройти по массиву, вставляя туда элементы.» — «А сложность какая?» — «Ну шо-то типа O(N log(k)).» — «А быстрее можно?» — «Думаю, для общего случая — нет, нельзя.» — «Ну ОК, я тоже так думаю.»

Через пару дней катался на велосипеде по берегу реки и вдруг подумал: «А что если сортировать квиксортом по-возрастанию, но не весь массив, а только левую, минимальную половину? До тех пор, пока её размер не станет равен k? Сложность будет, сложность будет, эээ, N + N/2 + N/4 + N/8 +… это больше или меньше, чем N log(k)?» Сходу не сообразил, чему равна сумма последовательности, забил.

Еще через пару дней вспомнил, нагуглил, что сумма вида N + N/2 + N/4 +… = 2N. Получается, сложность моего алгоритма будет O(2N), но константы из Big-O надо выкинуть, т. е. O(N). Т. е. лучше, чем то, что я до этого придумал с бинарной кучей. Ура, блин.

А ещё через пару дней нагуглил, что этот алгоритм называется quickselect и его в пятьдесят лохматом году изобрёл Хоар, вместе с quicksort. Какой удар со стороны классика…
только левую, минимальную половину
А вы уверены что левая половина минимальна?

сумма вида N + N/2 + N/4 +… = 2N
не могли бы пояснить, с чего вы это взяли, правильно ли я понял что сумма четырех элементов 8 + 8 + 1 + 1 должна быть равна сумме половине элементов 1+1?

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

По условию задачи
Интересно если есть массив с 1 1 2 3 4, три минимальных чему равны, 112 или 123…
А вы уверены что левая половина минимальна?

Как работает квиксорт? Берём какой-то элемент Х исходного массива, перераспределяем остальные элементы массива так, что в левую «половину» идут те, которые меньше Х, в правую «половину» — остальные. Да, левая половина по-определению «минимальна», любой её элемент меньше любого элемента из правой. Она необязательно «половина», т. е. имеет то же кол-во элементов, что и правая — если мы неудачно выбрали Х, в ней может вообще не оказаться ни одного элемента. Но в среднем слева и справа окажется плюс-минус поровну. Далее квиксорт рекурсивно делит каждую «половину» опять «пополам», но т. к. у нас не стоит задачи отсортировать весь массив, большая половина нас не интересует, мы берём только меньшую, и так каждый раз.

не могли бы пояснить, с чего вы это взяли

Вот доказательство для суммы бесконечного ряда 1/2 + 1/4 + 1/8 + 1/16 +… = 1. В нашем случае к нему надо прибавить 1 (1 + 1/2 + 1/4 + 1/8 + 1/16 +… = 2), и умножить всё это на N, получаем 2N.

правильно ли я понял что сумма четырех элементов 8 + 8 + 1 + 1 должна быть равна сумме половине элементов 1+1?

Нет, с чего бы? Это формула для суммы арифметической прогрессии, ваша последовательность 8 + 8 + 1 + 1, во-первых, не является арифметической прогрессией.

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

Нет, совершенно не обязано соблюдаться ни одно из этих условий.

если есть массив с 1 1 2 3 4, три минимальных чему равны, 112 или 123

1 1 2, три минимальных уникальных элемента множества — это несколько другая задача.
А что если сортировать квиксортом по-возрастанию, но не весь массив, а только левую, минимальную половину?
Все равно надо пробегать и сравнивать весь массив рекурсивно, так как массив «отсортирован» будет только после всего рекурсивного прохода. То есть «минимальная половина» определяется все равно на последнем, «листовом» проходе.

Кажется ваше «N + N/2 + N/4 +… = 2N» в данном случае не применимо, либо я не понял к чему вы это применили.

Разве сложность не будет log(k/2)+log(k/4)+…?
То есть «минимальная половина» определяется все равно на последнем, «листовом» проходе.

Нет, эта модификация квиксорта работает немного не так. Фактичиски на каждом шаге мы бросаем правую часть неотсортированной, только забираем оттуда наименьшие числа. Соответственно на выходе у нас получаться отсортированными первые k элеменов, а оставшаяся правая часть останется несортированной.
Тоесть мы пользуемся тем, что в квиксорте сама сортировка оказывается ленивой — сначала мы выбираем наименьшие числа (рекурсивно забуриваемся в самый левый конец с наименьшими числами), и только после того, как мы выходим из рекурсии множество оказывается отсортированным.
UFO just landed and posted this here
В наихудшем случае что квиксорт, что квикселект вырождаются в пузырьковую сортировку со сложностью O(N^2).
без использования "+" или любых других арифметических операторов.
Знак равенства/неравенства, это арифметический оператор? Тоже запрещен?
1. Сортируем массив и откусываем сколько надо. Выше уже было.

2. Java8
http://stackoverflow.com/questions/17130521/java-method-to-sum-any-number-of-ints
int[] listOfNumbers = {5,4,13,7,7,8,9,10,5,92,11,3,4,2,1};
System.out.println(IntStream.of(listOfNumbers).sum());

3. Квадрат — штука симметричная. Прямая проходящая через центр квадрата(центр — точка пересечения диагоналей), разделит его на два равных четырехугольника. Прямая, заданная двумя центрами квадратов, разделит оба пополам.

Зачем предлагать приз, который не нужен тому, кто справиться с заданиями?
Может награждать тех, кто красиво не справиться?
1й вариант у вас не самый быстрый
2й вариант вообще не ответ
1. про оптимизации ни слова не было.
2. а запустите — вдруг работает !? я Вам даже источник копипаста указал.
так там + есть, а по правилам нельзя.
«без использования „+“ или любых других арифметических операторов»
.sum() я бы назвал вызовом метода, т.е. функции. И да, возможно(реализацию не разбирал), это неявный вызов "+", но под условия…

(a & b) << 1
сдвиг это умножение на основание системы счисления, умножение арифметическая операция…
Вроде бы не писали этого решения 1 задачи.

Используем K-тую порядковую статистику (http://e-maxx.ru/algo/kth_order_statistics)
После чего делаем ещё 1 проход, и все что меньше копируем в ответ. Сложность O(N+K).
Если есть одинаковые элементы искусственно делаем их уникальными, добавив порядковый номер к элементу.

2 задача — классика

int sum(int a, int b)
return b==0?a:sum(a^b,(a&b) << 1);

2 слагаемое — биты переноса. Рано или поздно они закончаться (не позже чем двоичный логарифм от числа).

А с 3 задачей (квадратами) есть такая известная задача о бутерброде, это её частный случай. Для любых 2 хороших фигур, решение будет.
Вроде бы не писали этого решения 1 задачи.

Комментарий именно этот алгоритм и описывает
Если дан большой b(N) исходный массив и лучше не очень большое K…

1. Создать массив размера a(K), вначале заполнить a() первыми K элементами из b(), сортируем a(),
последний элемент a(K-1) есть max массива a().
Бежим 1 раз по оставшимся элементам массива b() и сравниваем с последним a(K-1),
если найден меньший элемент, заменяем последний элемент a(K-1) на найденый, сортируем a(), бежим дальше.

сортировку можно заменить одноразовым пробеганием по a() для поиска наибольшего элемента, тогда надо будет запоминать его положение в a() вместо использования последнего элемента.
В разделе 13.5 дважды упоминается LinkedListMap. Такого класса в Java (как минимум в SE) не существует. По смыслу там LinkedHashMap.
По словам автора публикации тоже самое в оригинале, скорее всего опечатка со стороны автора книги.
Забавно смотреть, как люди извращаются :)) У меня ещё вчера сформировалась такая мысль относительно оптимального алгоритма решения 1-й задачи:

Если K << N (число K много меньше количества элементов в массиве), то достаточно:

1) выполнить на исходном массиве «K» итераций таких алгоритмов сортировки, как сортировка выбором ( или даже сортировка пузырьком :) ); легко видеть, что алгоритмы выбора и пузырька работают следующим образом: найти минимальное значение, поставить на 1-е место; найти минимальное среди оставшихся значений и поставить на 2-е место; ...; найти минимальное среди оставшихся (N-(K-1)) значений и поставить на K-е место;…
2) взять первый «K» значений полученного массива и выдать в качестве массива.

Это будет довольно быстрый алгоритм — по сложности условно O(kN), а по правилам BigO-нотации — видимо, O(N)!
Конечно, если число «K» стремится по значению к «N», данный алгоритм будет стремиться к сложности используемой сортировки (а сортировки выбором и пузырьком, увы, не самые лучшие), то ситация меняется в худшую сторону

думаю, что такой ход мыслей будет понятен даже самым начинающим программистам :)
Тьфу, не успел доредактировать!

Хотел сказать, что если есть массив на 1000000 элементов, а найти надо первые 10 минимальных элементов, то не нужно сортировать массив целиком — это же в лучшем случае N*logN.
Эффективнее будет — использовать фрагмент более общего алгоритма сортировки, но ограничить нужным числом итераций и получится N*10.

В общем же случае, не дай бог сортировать весь большой массив выбором или пузырьком.
Таким образом, в случае использования на практике надо различать случаи, когда можно обойтись частным случае N*k, а когда выгоднее выполнить сортировку массива целиком…
Соотношения между N и K, когда выгоднее задействовать полную сортировку через qsort() вместо «фрагментарной» сортировки выбором — нужно вычислять эмпирически… Возможно, есть и математический подход, но это уже кому не лень :)
Вы не рассмотрели случай K = N/2.
И на нем quickselect выиграет, потому что в условии не сказано, что наименьшие числа нужно вернуть в отсортированном порядке.
Quickselect в среднем выиграет для любого k > 2.
Sign up to leave a comment.