алгоритмическая задачка. Найти "лишнее" за один проход
Нужно придуматькто поставил такую задачу? решение точно существует? или это ты сам?
подсказка для совсем ленивых: xor
ты не путаешь? в баянной задаче все кроме одного повторяются.
блин, точно, я напутал. Да, все повторяются кроме одного. Я так понял, что все кругом знают решение. Ну хорошо, посижу, порадуюсь за всех.
тест на внимательность — FAILED.
ща тоже тогда подумаю над задачкой.
подобных задачек много в книге Шеня. Название книжки не помню, но там начинают с совсем тривиальных (переворот массива без дополнительной памяти) и приходят к не столь очевидным. У меня в свое время ее читать по диагонали не получалось. Кое-где после чтения условия очередной задачи, притормаживал
Круто, блин. Никогда о такой не слышал.
в исходной формулировке (на правах бреда, жаль, что числа у тебя long):
тебе нужна очень большая таблица простых чисел
Как поможет таблица простых чисел для решения этой задачи в исходной формулировке?
их можно перемножать
нужна очень большая таблица простых чиселона вроде не очень большая будет
кажется даже, что простых чисел из первой полусотни хватит
апд. нет, не хватит. это только для перемножения двух лонгов
Блин, клевые там задачки.
Всё ранво не пойму.
моя идея была в том, что можно просто на каждом шаге брать из таблицы то число, порядковый номер которого равен числу из массива и смотреть, делится ли наше индуктивное расширение на него, если нет, то умножать на него и двигаться дальше.
Имеется ввиду, каждому лонгу сопоставить простое число с таким порядковым номером равным значению лонга?
Да, понял, но потупил
Проще наверное иметь большой битовый массив
return $_ if $seen{ $_ };
$seen{ $_ } = 1;
}
на каждом шаге брать из таблицы то число, порядковый номер которого равен числу из массива и смотреть, делится ли наше индуктивное расширение на него, если нет, то умножать на него и двигаться дальше.это, конечно, формально один проход, но в реальности это хуже, чем n^2.
точнее это показывает, что понятие однопроходности нужно сначала определить формально
> о в реальности это хуже, чем n^2
почему хуже, если ассоциативный массив реализован деревом?
правильное решение использует одну переменную типа long
почему хуже, если ассоциативный массив реализован деревом?посчитай сложность умножений
точнее это показывает, что понятие однопроходности нужно сначала определить формальнопоэтому обычно просят не за один проход, а за O(n). Автор темы мог попутать.
Ну, и в любом случае, нет смысла в решении за n^2, т.к. тогда работает тривиальное.
и найти в остортированом списке за один проход.
точно, не туда посмотрел
radix sortдля long?
ну да. именно что для лонг. хотя - для любых целых
А в чем проблема, формально это будет O(n правда константа будет больше чем log(n т.к. n<2^64, а в radix sort 64 прохода по массиву. n < 2^64 т.к. числа не повторяются.
за какое время ты отсортируешь массив long-ов с помощью radix sort?
офигеть, аутентичная ссылка...
#define For(a, b) for(a=0; a<b; a++)
void radix_sort(unsigned int a[], int n)
{
unsigned int *t, *a2=a;
unsigned int *b=(unsigned int*) malloc(n*sizeof(int;
unsigned int *b2;
unsigned char *bp;
int i, j, npos, temp;
int c[256][4];
b2 = b;
memset(c, 0, sizeof(int)*256*4);
bp = (unsigned char*) a;
For(i,n)
For (j,4) {
c[*bp][j]++;
bp++;
}
For(j,4) {
npos = 0;
For(i,256) {
temp = c[i][j];
c[i][j] = npos;
npos += temp;
}
}
For(j,4) {
bp = (unsigned char*) a2 + j;
For(i,n) {
b[c[*bp][j]++] = a2[i];
bp += 4;
}
t = a2; a2 = b; b = t;
}
free(b2);
}
Формально она работает за O(N но кушает O(N) памяти. В баяне ограничение по памяти было O(1).
Есть гипотеза, что больше половины элементов массива - это один и тот же элемент.Проверить гипотезу за время O(n) и память O(1).
Если это числа, то можно считать биты на каждой позиции, потом определить где чего больше и составить число, за второй проход проверить сколько раз оно встречается.
почитать кол-во для каждого бита, затем полученное число проверить одним проходом.
считать количество различных соседних элементов (в т.ч. последнего с первым). если это число < N, то гипотеза верна.
считать количество различных соседних элементов (в т.ч. последнего с первым). если это число < N, то гипотеза верна.
Если масив будет состоять из 3-х равных частей, в каждой из которых буду одинаковые элемненты, то он не удовлетворяет гипотезе, а число переходов между соседями будт равно 3.
тогда такой метод: найти медиану массива (это будет наше число, если оно есть а затем вторым проходом подсчитать кол-во элементов, не равных медиане. если это кол-во меньше N/2, то нашли число и гипотеза подтвердилась.
Не всегда можно найти медиану за O(n).
поэтому обычно просят не за один проход, а за O(n). Автор темы мог попутать.только в этой задаче O(n) формально не имеет смысла, так как n не может превосходить 2^32+1
http://ranger.uta.edu/~gdas/Courses/Fall2004/advAlgos/studen...
так никто не делает, конечно, но можно.
если делать как QuickSort, то не всегда. Но можно всегда, например в этой презентации нарисовано: так никто не делает, конечно, но можно.
задача с собеседования?
Странная презентация, а медиану медиан как искать?
По этой презентации получается не O(n) а в лучшем случае O(n * log_5(n.
точнее это показывает, что понятие однопроходности нужно сначала определить формальной
Median of Medians algorithm гуглом ищется описание
не следуте что T(n) = O(n)
а то у нас и квик сорт O(n т.к. в нем
Т(n) ≤ T(n/2) + T(n/2) + O(n)
намекает, что в этом алгоритме не медиана ищется, а что-то, что разбивает массив на две части, каждая из которых гарантированно не меньше какой-то части всего массива (скажем четверти). Этого достаточно, чтобы квиксорт работал гарантированно за O(n logn но это не значит, что найденное число будет медианой.
я так понял,
в общем, да, с собеседования. Я, правда, там год назад был, но, вот, она никак мне покоя не давала, одну в интернете потом нашел. Еще одну сам, вроде бы, решил. Но это уже потом, конечно.
Там каждый раз отсекается по какой то части массива, и ищется в оставшейся части, количество шагов зависит от n.
я бы даже сказал, что собеседование это, пожалуй, самое жесткое на моей памяти, даже пожестче гугла. Хотя конторка, вроде, нивал онлайн, клепает игрушки. А требования вон какие. 2 из 3х за час не решил - досвиданья - вот и все собеседование.
конторка нивалДа мы уже поняли, что ты условия задач не запоминаешь, баянов не знаешь, конторку вот от нивала, оказывается, тоже отличить не можешь.
Во обще алгоритм о поиске k-ого по порядку числа в массиве, может и будет там O(n но статья и вики этого не доказывают. А копать глубоко неохота.Видимо, всё-таки нельзя за линейное время найти k-е по порядку число, так как даже медиану нельзя найти, так как иначе не интересно было бы искать за линейное время хотя бы "приближённую медиану", т.е. медиану медиан. Док-ва не знаю, конечно.
Там каждый раз отсекается по какой то части массива, и ищется в оставшейся части, количество шагов зависит от n.
А статья вики этого не доказывает, потому что это не ставит такую цель: сама медиана мало кому нужна. Важно, чтоб квиксорт гарантированно работал за O(nlogn).
мне эту задачку товарищ рассказывал, который там сейчас работает, и которому давали эту задачку в том числе.
ну ладно, хорошо хоть в гугл тебя взяли, тоже в принципе ничего, хотя нивал круче, конечно.
ну, я не говорил, что меня туда взяли, просто, пока ходил по собеседованиям, много где успел побывать, в том числе и там и там.
А, хотя "Нивал Онлайн" — это может быть, конечно, не торт Нивал. Но все же, традиции должны бы сохраниться, а в Нивале они были на высоком уровне.
это не совсем тот, но, как я понял, требования к программерам и там и там жесткие
а то сначала наотбирают себе, а потом сиди там пиши игровую логику, которую и школьник написать может
Есть гипотеза, что больше половины элементов массива - это один и тот же элемент.Проверить гипотезу за время O(n) и память O(1)
Задачка прикольна тем, что алгоритм тривиален:
n, cnt = None, 0
for n1 in lst:
if n1 == n: cnt += 1
elif cnt > 0: cnt -= 1
else: n, cnt = n1, 1
Ну и потом, если нужно не только найти число, но и проверить гипотезу, ещё раз пройти и посчитать.
А вот доказательство того, что он работает, заставляет задуматься на минутку.
Решение в треде уже есть, а то я не следил?
Вы будете смеяться, но сегодня мне дали тестовое задание с задачей из первого поста. Ограничение по времени O(N) где N - размер массива.Тебе дали именно такую как в первом посте, или как чуть пониже там написано?
Решение в треде уже есть, а то я не следил?
А то автор признался, что он напутал условие.
решение задачи четвёртого поста в третьем
Тебе дали именно такую как в первом постеДа, только без "однопроходности", а с O(N).
если битовая длина элементов константа то его и отсортировать можно за O(N)
Не пройдет, там числа не превосходят M.
Корректное условие ниваловской задачи такое: есть массив длины word типа word[]. Надо за один проход (именно один проход, а не О(N определить есть ли повторяющиеся элементы (если есть, то выдать значение любого из таких). Элементы массива можно портить. Размер разрешенной дополнительной памяти - константа.
Ыыыыы. У меня такая была )
Можно использовать массив, в котором могут поместиться все возможные wordы. Его размер - константа, и даже не очень большая в Windows.
А где-нибудь в инете есть какие-нибудь ресурсы посвященные таким вот задачкам? Не топкодер и ему подобные, где задачки на алгоритмы, а именно такие головоломные задачки (иногда между ними сложно провести разницу, но думаю, что смысл понятен)?
но провести грань действительно сложно, там хватает и алгоритмических вещей
мне такую давали, я даже решил
"за один проход" всё-таки не правильная формулировка, иначе нет смысла портить массив
Дан список (указатель на голову элементы выглядят так:
struct ListElement {
ListElement *next; // следующий элемент в списке
ListElement *other; // какой-то другой элемент из списка
};
Надо сделать копию списка за O(n) времени.
projecteuler.netЯ бы все-таки его отнес к разряду топкодера: там задачки все-таки из разряда тех, что можно втупую решить, правда, это будет долго считаться (хотя на эйлере обычно задачки небольшой размерности а здесь именно такая задачка, которую нужно придумать как сделать, чтобы вообще сделать. Это не столько программирование, сколько именно идейные такие вещи
но ближе к концу частенько встречаются именно идейные вещи
некоторые задачи решаются вообще с помощью только листочка бумаги
А чего тут решать-то?
За один проход все нумеруешьа нумерацию как и где хранишь?
Теряю.
Супер!
Новый список выделяется в виде массива, это дает нам N*sizeof(ListElement*) лишней памяти, т.к. последовательные ссылки в новом списке тривиальны. Дальше немного похимичить, скопировать туда-сюда ссылки исходного списка, и все получится
Новый список выделяется в виде массиваочень хакерски, т.к. это только в низкоуровневых языках такое пройдет: ни managed-языки, ни ФЯ, ни скрипты - такое не схавают.
Питон схавает, там списки (вроде бы) массивами хранятся. Но в таких языках вообще сложно говорить о расходе памяти - например, не очевидно, что N элементов, связанных ссылками, вразброс занимают столько же места, как и в массиве.
увы! даже если есть компилятор, который способен скомпилировать такой метакод,
в результате не будет копии списка
А в ФЯ вообще quicksort за O(N^2) выполняется. Так что не согласен, массивы в подобных задачах имеют право на жизнь, особенно если в условии синтаксис C используется.
Не совсем понял почему. И это совершенно честный C. На выходе будет список из тех же элементов, связанных в том же порядке, но по странному совпадению лежащими в памяти подряд.
хотя, у меня такое подозрение, что это займёт времени больше, чем решить задачу правильно
кстати, язык фиксирован: это C (ну, может C++)
Питон схавает, там списки (вроде бы) массивами хранятся.вот только, например, операция удаления элемента поменяется после такого хакнутого копирования.
если раньше надо было просто переставить ссылки при удалении(+вызвать free, если нет gc то теперь придется знать что есть еще массив, и с ним как-то мудрить.
но всё равно, химия там совсем не понятная... я вот сейчас пытался представить,
как ты будешь устанавливать указатели правильно и не смог
2. в массиве .next указывает на соответсвующий элемент исходного списка
3. в списке .next указывает на соответсвующий элемент массива
4. в массиве .other указывает на соответсвующий элемент массива (можно сделать, т.к. есть ссылки из списка на массив)
5. восстанавливаем .next в списке
6. восстанавливаем .next в массиве
Первый проход - идем по оригинальному листу, и создаем два новых листа. Один из них - это будет собственно искомая копия. В копии пока заполняем только next, other оставляем пустым. Второй лист будет из ссылок other из оригинального листа. Так же на каждой итерации в конце обработки элемента из оригинального листа, присваиваем полю other в обрабатываемом элементе ссылку на элемент-копию.
Второй проход - идем по списку-копии, и по второму списку выставляем коректные other очевидным способом (cur->other->other)
Решение с лесенкой, похоже, правильное.
угу, по модулю массива, решение похоже на правильное.
там в оригинальном списке other в конце концов правильные будут?
там в оригинальном списке other в конце концов правильные будут?в этом решении, оригинальный список не модифицируется
в этом решении, оригинальный список не модифицируетсяно требуется память на 3 списка в сумме, не так ли?
А как ФЯ схавают указатель на другой элемент списка?
А как ФЯ схавают указатель на другой элемент списка?а в чем отличие между указателем из C и ссылкой из других языков - кроме терминологии?
А в ФЯ вообще quicksort за O(N^2) выполняется. Так что не согласен, массивы в подобных задачах имеют право на жизнь, особенно если в условии синтаксис C используется.Я бы скорее сказал, что в ФЯ невозможно написать qsort.
Не совсем понял почему. И это совершенно честный C. На выходе будет список из тех же элементов, связанных в том же порядке, но по странному совпадению лежащими в памяти подряд.Фишка в том, что каждый элемент списка должен быть выделен динамически и никто не гарантирует, что 100 выделений пойдут в памяти подряд.
Я бы скорее сказал, что в ФЯ невозможно написать qsort.Ня?
Это read-only список
Под такое описание можно написать алгоритм и он будет работать за O(n log n так что вы оба неправы.
Краткое описание алгоритма
* выбрать элемент, называемый опорным.
* сравнить все остальные элементы с опорным, на основании сравнения разбить множество на три — «меньшие опорного», «равные» и «большие», расположить их в порядке меньшие-равные-большие.
* повторить рекурсивно для «меньших» и «больших».
Напиши, пожаалуйста.
ru.wikipedia.orgНу, кстати, приведенное определение неверно.
let rec qsort = function
| [] -> []
| pivot :: rest ->
let lessOrEqual, greater = List.partition (fun x -> x <= pivot) rest
qsort lessOrEqual @ (pivot :: qsort greater)
Дай верное со ссылкой.
Дай верное со ссылкой.недостает отсутствия дополнительной памяти, иначе от qsort'а почти ничего и не остается =)
недостает отсутствия дополнительной памяти, иначе от qsort'а почти ничего и не остается =)Ты с дубу рухнул? Там O(log n) памяти надо.
Приведённый мной вариант использует столько же памяти, сколько и времени.
Можно исповедовать практический подход и понять, что quicksort так назван потому что он работает бы...подождите-ка...стро! И тут уже говорить о том, что твой замечательный алгоритм, который:
1. создает тучу копий исходного массива,
2. ужасно выбирает pivot. Ну либо если его выбирать хотя бы просто из середины приводит к дополнительным O(n) операциям на каждый уровень вложенности (как в списке добраться до середины?)
3. разделяет не в лучших традициях qsort: там важно именно идти один раз по массиву, меняя элементы местами, а не создавать две выборки из исходного участка,
является quicksort'ом не приходится.
Так что все же приведенный код — это не
Можно исповедовать практический подход и понять, что quicksort так назван потому что он работает бы...подождите-ка...стро!То есть за O(n log n как и мой.
А всё остальное - это твои домыслы.
В оригинальном qsort не было никакого выбора элемента.
То есть за O(n log n как и мой.нет, просто быстро, асимптотика у qsort хуже, чем у Heap Sort или MergeSort, но тем не менее, его применяют чаще. (если быть более точным, то у MergeSort есть своя большая область применения, например, именно его стоит писать в ФЯ, а не заниматься извратом).
А всё остальное - это твои домыслы.Это не домыслы, это следствие более детальной проработки вопроса.
В оригинальном qsort не было никакого выбора элемента.Это в каком "оригинальном"? В самом простейшем, который дает N^2 на упорядоченных массивах? У него тоже своя сфера применения: для массивов, полученных от генератора случайных чисел.
И еще: по пункту 3 что там? Пойми, все три пункта одинаково важны.
В оригинальном qsort не было никакого выбора элемента.Был рандомный.
Он имеет в виду qsort хоара, там все же действительно брался первый элемент. Но это очень непрактично.
Вот, например,
Он имеет в виду qsort хоара, там все же действительно брался первый элемент. Но это очень непрактично.Hoare, C. A. R. "Partition: Algorithm 63," "Quicksort: Algorithm 64," Comm. ACM 4(7 321, 1961
ALGORITHM 63
PARTITION
C. A. R. HOARE
Elliott Brothers Ltd., Borehamwood, Hertfordshire, Eng.
procedure partition (A,M,N,I,J); value M,N;
array A; integer M, N, I, J;
comment. (...)
begin
real X; integer F;
F : = random ( M , N ) ; X : = A[F]; <--------------------- Вот оно
(...)
end
ALGORITHM 64
QUICKSORT
C. A. R. HOARE
Elliott Brothers Ltd., Borehamwood, Hertfordshire, Eng.
procedure
quicksort (A,M,N); value M,N;
array A; integer M, N;
comment. Quicksort is a very fast and convenient method of
sorting an array in the random - access store of a computer. (...)
begin
integer I,J ;
if M < N then begin
partition (A,M,N,I,J);
quicksort (A,M,J);
quicksort (A,I,N )
end
end
Прошу прощения, так учили в школе
А на философии в универе всегда говорили: "читайте оригиналы", да
Тут даже не нужно быть теоретиком, достаточно понимать, что, кроме асимптотики, у алгоритма есть еще всякие свойства: объем дополнительной памяти, cache-friendliness, устойчивость, всякие неприятные особенности (N^2 у qsort, который в предложенном функциональном варианте возникает то ли чаще, то ли в весьма нежелательных случаях). Если все это учесть, то алгоритм, похожий на qsort, в ФЯ, судя по всему, невозможен.
Да, и nlogn памяти никуда не годится. Она, в отличии от времени, конечная.
Хм, прикольно.Просто у Кнута как основной вариант идет выбор первого элемента
Прошу прощения, так учили в школе.
А на философии в универе всегда говорили: "читайте оригиналы", да
Вспомнился перл в универе. Одногруппник преподу: "Ну моя программа работает в 99.5% случаев, вот доказательство, из неравенства Чебышева. Процент легко увеличить, увеличив размер выборки."
Видимо, всё-таки нельзя за линейное время найти k-е по порядку число, так как даже медиану нельзя найти, так как иначе не интересно было бы искать за линейное время хотя бы "приближённую медиану", т.е. медиану медиан.Решил, всё-таки покаяться, т.к. наткнулся сейчас на это.
Можно всё-таки найти k-е по порядку число за линейное время.
Но это как раз делается через медиану медиан и недо-квиксорт.
text: http://ocw.mit.edu/NR/rdonlyres/Electrical-Engineering-and-C...
video: http://ocw.mit.edu/OcwWeb/Electrical-Engineering-and-Compute...
UPD. сейчас посмотрел, в вики всё это тоже написано... походу, никто не читает ссылок
Кстати, недавно где-то читал, что квиксорт с двумя пивотами и с разделением на 3 части быстрее работает.
там рекурентная формула T(n) ≤ T(n/5) + T(3n/4) + O(n)
делается предположение, что T(n) ≤ cn
из этого выводится, что T(n) ≤ cn и следовательно сложность линейная.
Или я чего то не понимаю, или это неккоректное доказательство.
из этого выводится, что T(n) ≤ cn и следовательно сложность линейная.Или я чего то не понимаю, или это неккоректное доказательство.Ну, из такой рекуррентности можно вывести, что если T(n) ≤ cn для n ≤ 3N/4, то T(n) ≤ cn для n ≤ N, так что все хорошо
или ты не про это?
То ли в в pdf неаккуратно написанно доказательство, то ли я невнимательно прочитал.
То ли в в pdf неаккуратно написанно доказательствоЧитайте книжку лучше. Пдфка каждой своей страницей какбе намекает, какую книжку нужно читать. Глава 9, если быть точным.
здесь как раз "обслуживаются" две вещи:
Θ(n) — для индукционного перехода, и
initial conditions — для базы.
Оставить комментарий
redzor
Что-то предыдущий пост не написался. Итак, есть массив чисел лонг. Там все числа разные, кроме одного, оно 2 раза повторяется. Нужно придумать однопроходный алгоритм, чтобы найти это число. Сломал уже голову в нескольких местах. Может есть, кто знает решение?