Комментарии 114
Скрытый текст
Отсортировать массив.
Далее каждый эл-т сравнить с соседними. Если ни одному не равен, то он искомый.
Далее каждый эл-т сравнить с соседними. Если ни одному не равен, то он искомый.
Видите там в условии — в один проход? Отсортировать массив в один проход нельзя
В таком случае и ваше тривиальное решение не удовлетворяет условию, так как прохода 2.
Нет, потому что второй проход не по изначальному массиву, а по другому, константного размера. Более конкретно — "в один проход" означает, что элементы входного массива можно прочитать по разу, и только подряд, от первого к последнему
В таком случае и решение Denxc работает — считываем оригинальный массив только раз, сортируем его в другой (вставками, например), проходим по второму.
Память не константная нужна для этого.
Выделяем память размером с исходный массив, сортируем пузырьком (константная память).
Выделяемая память зависит от размеров исходного массива. А размер заранее неизвестен.
В задаче необходимо использовать всегда одинаковую память вне зависимости от исходных данных.
В задаче необходимо использовать всегда одинаковую память вне зависимости от исходных данных.
Выделяем ВСЮ доступную память — это будет достаточно константно для всех случаев? :)
Память размером с исходный массив — это не константная память =)
Да, если длина входного массива заранее не известна и не постоянна, это я не учел. Не, можно конечно выделить заранее память, куда гарантированно влезет отсортированный массив, но это сильно кривое решение.
Числа надо полагать целые (иначе как проверять на равенство 2 флота), тогда заводим один массив размером с диапазон 32-битных чисел, за один проход по исходному массиву строим гистограмму, а за второй проход по вспомогательному массиву находим в гистограмме 2 бина со значением 1 :-)
Есть идея, но до решения не получилось довести (а может, и нельзя так).
Скрытый текст
Сделать xor всех элементов массива друг с другом, получим xor различающихся чисел. Я пытался придумать ещё одну операцию, чтобы получить больше информации, и её основе восстановить эти два числа, но в голову ничего не пришло.
Должно получиться
Пусть массив называется N.
Создаем массив из 32 32-битных чисел. uint32_t A[32];
Проходим по массиву N:
Проходим по массиву A — некоторые из чисел будут нулями, два оставшихся — искомые числа.
Создаем массив из 32 32-битных чисел. uint32_t A[32];
Проходим по массиву N:
- Если в числе N[i] установлен бит j, xor-им N[i] с A[j].
Проходим по массиву A — некоторые из чисел будут нулями, два оставшихся — искомые числа.
Правка
В массиве A будут либо нули (у числа 1 и у числа 2 на этой позиции 0), либо число 1 ( у числа 1 на этой позиции 1, а у числа 2 на этой позиции 0), либо число 2(аналогично), либо число 1 xor число 2. (у обоих единица) Поскольку, чтобы вычислить число 1 xor число 2 достаточно пройтись xor по всему массиву, задача решена =)
Есть один нюанс.
Скрытый текст
Возможно, останется только одно число. Но это уже не страшно, можно ещё при проходе по массиву поксорить всё, и тогда второе число будет легко получить.
P.S> Не обновил комментарии перед отправкой(
P.S> Не обновил комментарии перед отправкой(
Кажется, вот контрпример
Это 4 трехбитных числа:
1 1 0 0
0 0 0 1
1 1 0 1
1 1 0 0
0 0 0 1
1 1 0 1
Второй комментарий важен, без него не работает, конечно же.
Можно тогда более подробно описать, что происходит с A после прохода по массиву?
Скрытый текст
Пусть X, Y — искомые числа.
Xj == Yj == 0 -> A[j] == 0
Xj == 0, Yj == 1 -> A[j] == Y
Xj == 1, Yj == 0 -> A[j] == X
Xj == 1, Yj == 1 -> A[j] == X xor Y
Xj == Yj == 0 -> A[j] == 0
Xj == 0, Yj == 1 -> A[j] == Y
Xj == 1, Yj == 0 -> A[j] == X
Xj == 1, Yj == 1 -> A[j] == X xor Y
Скрытый текст
и мол кроме A считаем xor всего, чтобы сравнивать со значениями в A, чтобы понять кто есть кто?
Скрытый текст
Да, считаем xor всего. Сравнением со значениями в А можно найти как минимум одно из заданных чисел (если они различаются лишь в одном бите), ну а при наличии одного числа и xor двух искомых чисел можно легко вычислить второе.
Круто, кажется работает! Можно вас попросить обьединить все шаги в один комментарий, чтобы я мог сослаться из основного поста?
Полный алгоритм
Пусть массив называется N, искомые числа — X и Y. N[i] — i-й элемент в массиве N, Xj — j-й бит в числе X.
Создаем массив из n n-битных чисел. e.g. uint32_t A[32], обнуляем его;
Создаем переменную NXored, обнуляем.
Проходим по массиву N:
После прохождения цикла, NXored будет содержать X xor Y, а массив А будет содержать не больше четырех уникальных чисел: ноль, X, Y, и X xor Y:
Xj == Yj == 0 -> A[j] == 0
Xj == 0, Yj == 1 -> A[j] == Y
Xj == 1, Yj == 0 -> A[j] == X
Xj == 1, Yj == 1 -> A[j] == X xor Y
Поскольку числа X и Y различны, то как минимум в одном бите у них будут различия, значит в массиве А точно содержится одно из искомых чисел. Проходим по массиву А, ищем значение отличное от нуля и от NXored — пусть это будет X. После этого легко вычисляется второе искомое число: Y = X xor NXored.
Пример для 8-битных чисел на языке С: http://ideone.com/zHSWiT.
P.S. Памяти потребуется n2 бит для массива, n бит для NXored, 2n для сохранения результата (хотя тут можно и сэкономить). Всего n (n + 3) бит. Решая простенькое квадратное уравнение, получаем, что имея 4 Кб памяти, можно оперировать 179-битными числами :)
Создаем массив из n n-битных чисел. e.g. uint32_t A[32], обнуляем его;
Создаем переменную NXored, обнуляем.
Проходим по массиву N:
- Если в числе N[i] установлен бит j, xor-им N[i] с A[j].
- В любом случае xor-им N[i] с NXored
После прохождения цикла, NXored будет содержать X xor Y, а массив А будет содержать не больше четырех уникальных чисел: ноль, X, Y, и X xor Y:
Xj == Yj == 0 -> A[j] == 0
Xj == 0, Yj == 1 -> A[j] == Y
Xj == 1, Yj == 0 -> A[j] == X
Xj == 1, Yj == 1 -> A[j] == X xor Y
Поскольку числа X и Y различны, то как минимум в одном бите у них будут различия, значит в массиве А точно содержится одно из искомых чисел. Проходим по массиву А, ищем значение отличное от нуля и от NXored — пусть это будет X. После этого легко вычисляется второе искомое число: Y = X xor NXored.
Пример для 8-битных чисел на языке С: http://ideone.com/zHSWiT.
P.S. Памяти потребуется n2 бит для массива, n бит для NXored, 2n для сохранения результата (хотя тут можно и сэкономить). Всего n (n + 3) бит. Решая простенькое квадратное уравнение, получаем, что имея 4 Кб памяти, можно оперировать 179-битными числами :)
Скрытый текст
У вас здесь использование неопределённого значения. Если одно из «уникальных» чисел 0, то ваш код выведет мусор, X и Y нужно проинициализировать как 0 и NXored. А сам алгоритм несколько лучше, я и ещё несколько людей использовали 64 или 65 накопителей.
Прошу прощения, не могли бы вы написать данный алгоритм на любом удобном вам языке программирования. Просто хочется разобраться и не обременять вас уточняющими вопросами)
В частности, не могу понять что изначально содержится в массиве A.
В частности, не могу понять что изначально содержится в массиве A.
Держите! http://ideone.com/zHSWiT
https://habrahabr.ru/post/243819/, задача 5?
Идея
Считаем xor по группам, т. е. xor всех чисел, у которых установлен 1-ый бит, xor всех чисел, у которых установлен 2-ой бит, и так далее. Кроме того считаем xor всех чисел вообще. Далее в xor-е всех чисел ищем установленный бит, берем xor группы чисел, в которых установлен этот бит, он будет равен одному из неизвестных чисел, а второе найти не трудно.
Почему? Потому что, если в xor-е всех чисел на k-ой позиции стоит 1, то значит наши неизвестные числа в этой позиции отличаются, а значит одно из них попадет в группу, а второе нет.
Почему? Потому что, если в xor-е всех чисел на k-ой позиции стоит 1, то значит наши неизвестные числа в этой позиции отличаются, а значит одно из них попадет в группу, а второе нет.
Отлично!
гениально!
Если вы пролистали до этого комментария, то, возможно, вы уже сдались. Тогда попробуйте еще подумать вот с этой подсказкой:
Небольшая подсказка в правильном направлении
XORим вместе все числа с нечетными значениями и, отдельно, XORим вместе все числа с четными значениями.
Дальше сами...
Дальше сами...
Идея, возможно отношения к решению не имеет
Если про XOR'ить все числа, получим A XOR B, где A и B — наши числа. А как их получить — пока не знаю
НЛО прилетело и опубликовало эту надпись здесь
Скрытый текст
Решение не будет слишком отличаться от оригинального, вместо сложения по модулю два (xor), нужно будет использовать сложение по модулю три. И реализовать тритичный регистр.
НЛО прилетело и опубликовало эту надпись здесь
Тут самая сложная задача, если в общей сумме не будет ни одной единицы. Например:
Входные числа:
10
01
11
Суммы по битам
1 21
2 12
Общая сумма
22
Если не будет ни одной единицы, и число двоек будет больше двух, то точного решения нельзя получить.
10
01
11
Суммы по битам
1 21
2 12
Общая сумма
22
Если не будет ни одной единицы, и число двоек будет больше двух, то точного решения нельзя получить.
В комментах видны все уровни, от "что это вообще такое", до "слишком просто". Спасибо за расширение задачи
Действительно, задача слишком известная, чтобы считаться задачкой на выходные, она требует понимания классических приёмов программирования (по крайней мере, в сфере разработки алгоритмов), а кто занимался олимпиадами, просто скорее всего заранее знает решение или его идею.
Я всего лишь хочу порекомендовать автору более надёжно защищаться от троллей в подобных задачах. Например, можно было написать, что время работы алгоритма должно быть O(N), тогда не было бы мыслей о сортировках. Объём памяти можно было бы ограничить ещё сильнее (выше изложено одно из правильных решений, там чётко ясно, сколько будет памяти).
Здесь ещё можно попридираться к формулировке в том, что число N вообще говоря ограничено сверху, так как всего 232 разных чисел и если каждое встречается максимум 2 раза, то ясно, что вообще говоря задача может быть решена за константное время :) Было бы лучше сказать, что каждое число встречается чётное число раз, а два из них — нечётное. Тогда N уже точно не ограничено ничем.
Ну и, возможно, имело бы смысл давать задачи, которые действительно мало известны. Но это имхо.
Я всего лишь хочу порекомендовать автору более надёжно защищаться от троллей в подобных задачах. Например, можно было написать, что время работы алгоритма должно быть O(N), тогда не было бы мыслей о сортировках. Объём памяти можно было бы ограничить ещё сильнее (выше изложено одно из правильных решений, там чётко ясно, сколько будет памяти).
Здесь ещё можно попридираться к формулировке в том, что число N вообще говоря ограничено сверху, так как всего 232 разных чисел и если каждое встречается максимум 2 раза, то ясно, что вообще говоря задача может быть решена за константное время :) Было бы лучше сказать, что каждое число встречается чётное число раз, а два из них — нечётное. Тогда N уже точно не ограничено ничем.
Ну и, возможно, имело бы смысл давать задачи, которые действительно мало известны. Но это имхо.
"На выходные" — имелось ввиду, что можно подумать в бэкграунде, пока занимаешься другими делами, а код писать особо не нужно.
Под катом было более четкое ограничение — памяти всего 4K (и даже разбор тривиального "решения") Похоже уточнения под катом никто не читает, буду знать.
Под катом было более четкое ограничение — памяти всего 4K (и даже разбор тривиального "решения") Похоже уточнения под катом никто не читает, буду знать.
Нет, почему же, я внимательно прочитал задачу с пояснениями и решил сказать, что можно было бы сделать ограничение на память более точным (я же так и написал "ограничить ещё сильнее"). Я всего лишь внёс ряд предложений по улучшению подобных задач, не более того. Просто у меня есть опыт постановки задач таким образом, чтобы никакой троллинг в принципе был невозможен. Давать подобные задачи — вполне хорошая затея, особенно когда они будут не из классики олимпиадного программирования. Поддерживаю.
Заголовок спойлера
если бы задача формулировалась по-другому, а именно был бы массив из N чисел, среди которых все парные, а есть непарное, то можно было бы пробежаться по массиву XOR'я каждый следующий элемент. На выходе имеем искомое число.
В данном случае я смогу получить только XOR-сумму этих чисел. Затраты памяти константные. Дальше пока не додумал.
В данном случае я смогу получить только XOR-сумму этих чисел. Затраты памяти константные. Дальше пока не додумал.
Если просто поксорить, то не очень понятно будет что мы получим, по идее надо подсчтитать каждый бит, тогда нечётные числа — это те биты, которые принадлежат непарным числам, однако даже у непарных чисел могли какие-то биты совпасть и надо определить — какие. То есть надо сделать какую-то зависимость остальных бит в числе от уникальных, ну типа CRC, например инвертировать все биты в зависимости от N-го получим не 32 суммы, а 33*32 суммы и сможем восстановить все биты. Наверняка это всё можно сделать гораздо проще.
Ничего не понял :) можно как-то конкретнее, что предлагается? Какой конкретно CRC, как потом узнавать биты?
С инверсией я погорячился, она не подходит, поскольку хоть число и не парное, но нам важны совпадающие биты, а они, как раз будут парные в любом случае. Насчёт CRC это только идея, существует полно всяких видов корректирующих кодов.
Основная идея была в том, что если показать зависимость бит, то по этой зависимости можно восстановить сами биты. Но я не учёл, что мы в любом случае не получаем абсолютные значения, а получаем или/или, то есть и сами корректирующие коды хоть и будут отличными для этих двух чисел, но сумма по модулю будет 11111 в идеальном случае, а самих кодов мы не получим.
Фактически должно было быть так: есть число A, есть какое-то преобразование, зависящее от значения самого числа A->A', значение сложения для парных не изменится, для непарных A+A mod 2 и A'+A' mod 2 будут различны, можно попытаться восстановить отсюда биты. Например, пусть это будет циклический сдвиг если первый бит равен 1це:
два непарных числа:
101
001
суммы по модулю чисел и их сдвига:
100 и 111
второе число отражает зависимость бит внутри числа, а первое зависимость бит одного непарного от второго. Можно составить систему уравнений. В общем. над придумать такое преобразование. что бы в итоге можно было получить простую зависимость с однозначным решением.
Основная идея была в том, что если показать зависимость бит, то по этой зависимости можно восстановить сами биты. Но я не учёл, что мы в любом случае не получаем абсолютные значения, а получаем или/или, то есть и сами корректирующие коды хоть и будут отличными для этих двух чисел, но сумма по модулю будет 11111 в идеальном случае, а самих кодов мы не получим.
Фактически должно было быть так: есть число A, есть какое-то преобразование, зависящее от значения самого числа A->A', значение сложения для парных не изменится, для непарных A+A mod 2 и A'+A' mod 2 будут различны, можно попытаться восстановить отсюда биты. Например, пусть это будет циклический сдвиг если первый бит равен 1це:
два непарных числа:
101
001
суммы по модулю чисел и их сдвига:
100 и 111
второе число отражает зависимость бит внутри числа, а первое зависимость бит одного непарного от второго. Можно составить систему уравнений. В общем. над придумать такое преобразование. что бы в итоге можно было получить простую зависимость с однозначным решением.
Кстати выше уже привели решение: парные числа нас не интересуют, они при xor ни на что не влияют, а все числа можно разделить на две группы: у которых установлен какой-то бит и на те у кого этот же бит сброшен, парные так и останутся парными — каждая пара в свою группу, а непарные по любому, отличному биту попадут в разные группы, xor в этой группе уже даст это число, поскольку оно там одно.
Вот код (делал побыстрому) для Lazarus, но он у меня работает неверно, откуда-то берётся третье число в xor'ах
const
Count = 50;
type
MasTyp = array [1..Count + 2] of byte;
procedure Init(var mas: MasTyp);
var
alone, i: byte;
flag: boolean;
begin
Randomize;
for i := 1 to Count div 2 do
begin
mas[i] := Random(256);
mas[Count - i + 1] := mas[i];
end;
mas[Count + 1] := 0;
mas[Count + 2] := 0;
flag := True;
while flag do
begin
alone := Random(256);
for i := 1 to Count div 2 do
begin
if mas[i] = alone then
break;
end;
if i = (Count div 2) then//зависит от компилятора
begin
flag := False;
mas[Count + 1] := alone;
end;
end;
flag := True;
while flag do
begin
alone := Random(256);
for i := 1 to Count div 2 do
begin
if mas[i] = alone then
break;
end;
if i = (Count div 2) then//зависит от компилятора
begin
flag := False;
mas[Count + 2] := alone;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
mas: MasTyp;
i, n, bit, value1, value2: byte;
xor_bit, xor_byte_of_bit: array [1..8] of byte;
begin
Init(mas);
Form1.Memo1.Lines.Text := '';
for i := 1 to Count + 2 do
Form1.Memo1.Lines.Text := Form1.Memo1.Lines.Text + (IntToStr(Mas[i]) + ' ');
for i := 1 to High(xor_bit) do
begin
xor_bit[i] := 0;
xor_byte_of_bit[i] := 0;
end;
//Основной цикл---------------------------------------------------------------
for i := 1 to Count + 2 do
begin
for n := 1 to 8 do
begin
bit := Ord((mas[i] and (1 shl (n - 1))) > 0);
xor_bit[n] := xor_bit[n] xor bit;
if bit = 1 then
xor_byte_of_bit[n] := xor_byte_of_bit[n] xor mas[i];
end;
end;
//Основной цикл---------------------------------------------------------------
Form1.Memo1.Append('value1 ' + BinStr(mas[Count + 1], 8));
Form1.Memo1.Append('value2 ' + BinStr(mas[Count + 2], 8));
Form1.Memo1.Append('Xor бит');
for i := High(xor_bit) downto 1 do
begin
Form1.Memo1.Lines.Text := Form1.Memo1.Lines.Text + (IntToStr(xor_bit[i]) + ' ');
end;
Form1.Memo1.Append('Xor по группам бит');
for i := High(xor_byte_of_bit) downto 1 do
begin
Form1.Memo1.Lines.Text := Form1.Memo1.Lines.Text +
(IntToStr(xor_byte_of_bit[i]) + ' ');
if (i - 1) mod 8 = 0 then
Form1.Memo1.Lines.Text := Form1.Memo1.Lines.Text + ' ';
end;
//Поиск в группах
value1 := 0;
value2 := 0;
for i := 1 to 8 do
begin
if xor_bit[i] = 1 then
begin
if value1 = 0 then
value1 := i;
if xor_byte_of_bit[value1] <> xor_byte_of_bit[i] then
begin
value2 := i;
break;
end;
end;
end;
Form1.Memo1.Append('value1 ' + IntToStr(xor_byte_of_bit[value1]));
Form1.Memo1.Append('value2 ' + IntToStr(xor_byte_of_bit[value2]));
end;
Вывод (сначала массив, а искомые числа всегда последние два)
73 126 189 4 215 110 8 156 149 76 37 4 211 167 108 208 35 251 221 50 104 67 135 220 178 178 220 135 67 104 50 221 251 35 208 108 167 211 4 37 76 149 156 8 110 215 4 189 126 73 180 6
value1 10110100
value2 00000110
Xor бит
1 0 1 1 0 0 1 0
Xor по группам бит
180 0 180 180 0 178 6 0
value1 6
value2 180
value1 10110100
value2 00000110
Xor бит
1 0 1 1 0 0 1 0
Xor по группам бит
180 0 180 180 0 178 6 0
value1 6
value2 180
Нашёл ошибку, второе число нельзя искать среди групп, его надо вычислять ксоря найденное первое с общим ксором. 178 тут верно, это они оба попали в одну группу, но алгоритм это учитывает, а программа не учитывает вот что: если числа отличаются в одном бите. то они никак оба не будут выведенны, потому что мы не делаем xor по нулю, а только по еденице и в единственной отличающейся группе у второго ноль.
измения в код
xor_full := 0;
//Основной цикл---------------------------------------------------------------
for i := 1 to Count + 2 do
begin
xor_full := xor_full xor mas[i];
...
//Поиск в группах
value1 := 0;
value2 := 0;
for i := 1 to 8 do
begin
if xor_bit[i] = 1 then
begin
value1 := xor_byte_of_bit[i];
value2 := xor_full xor value1;
break;
end;
end;
Form1.Memo1.Append('value1 ' + IntToStr(value1));
Form1.Memo1.Append('value2 ' + IntToStr(value2));
Самое тупое и простое решение, не эффективное O(n^2)
текущий минмум = 0 (точнее минимально возможное число, способное быть записано в 32бита, т.е. для чисел со знаком это -2,147,483,648)
цикл в (размер массива/2) итераций
{
текущее количество = 0
для каждого элемента массива
{
ищем количество чисел равных текущему минимуму (если элемент равен текущему минимуму то увеличиваем количество)
ищем следующий минимум (число больше текущего минимума и меньше или равно текущему элементу)
}
если количество чисел было больше 1 то выводим текущий минимум
}
цикл в (размер массива/2) итераций
{
текущее количество = 0
для каждого элемента массива
{
ищем количество чисел равных текущему минимуму (если элемент равен текущему минимуму то увеличиваем количество)
ищем следующий минимум (число больше текущего минимума и меньше или равно текущему элементу)
}
если количество чисел было больше 1 то выводим текущий минимум
}
Черт, ошибку нашел у себя:
размер внешнего цикла нужно исправить на деление с округлением в большую сторону +1 или просто тупо +2, иначе для случая когда искомые числа максимальные для массива — они не будут найдены)
либо цикл бесконечный и выходим, когда не сможем найти следующий минимум (но тогда будет доп условие)
размер внешнего цикла нужно исправить на деление с округлением в большую сторону +1 или просто тупо +2, иначе для случая когда искомые числа максимальные для массива — они не будут найдены)
либо цикл бесконечный и выходим, когда не сможем найти следующий минимум (но тогда будет доп условие)
Такие задачи у меня всегда вызывают чувство ущербности…
И вроде программистом себя считаешь и вроде на работу ходишь каждый день, а всё равно, никуда не заглядывая, решить не можешь.
Да чего там… подглядывая в спойлеры, мало чего понял :(
Жду решения с детальным разжевыванием!
Спасибо.
И вроде программистом себя считаешь и вроде на работу ходишь каждый день, а всё равно, никуда не заглядывая, решить не можешь.
Да чего там… подглядывая в спойлеры, мало чего понял :(
Жду решения с детальным разжевыванием!
Спасибо.
Ну, в реальной работе (к сожалению) такое нужно крайне немногим и крайне редко. Для меня это скорее радостная возможность вспомнить молодость и размять мозги.
НЛО прилетело и опубликовало эту надпись здесь
А ее и ненужно разбирать. Код на XOR реально эффективный будет, а map — медленный отстой (в этом случае). Читаемость заменяется комментариями, а код обрамляется в функцию (типа черный ящик).
Ну, шанс что выпадет именно такая задача вообще бесконечно мал, слишком уж искусственно. А вот шанс использовать битовую магию, про которую задача — почему нет.
Прием с xor указателей в списке вполне рабочий, к примеру.
Разумеется, если память и/или производительность неважна — лучше писать проще.
Прием с xor указателей в списке вполне рабочий, к примеру.
Разумеется, если память и/или производительность неважна — лучше писать проще.
Легкий сарказм
Вариабельное решение на js:
Если повезет — то дубляжи будут попадаться достаточно часто и размер объекта r будет скакать в пределах 4к. Превысило? Ну что ж, не повезло.
var r={};
var a=[1,1,2,5,7,7,11,12,11,12];
a.forEach((x)=>{
if(!r[x])
r[x]=1;
else
delete r[x];
});
Если повезет — то дубляжи будут попадаться достаточно часто и размер объекта r будет скакать в пределах 4к. Превысило? Ну что ж, не повезло.
Искомые числа: X1 и X2
Кол-во элементов: CA
Считаем сумму всех чисел (SA), XOR всех чисел (XA)
Получаем: SA = 2a + 2b + 2c +… + X1 +… + X2 +…
XA = X1 ^ X2
Перебираем первое число от 0 до 0xfffffff так, чтобы выполнялось равенство: (SA + X1 + (X1 ^ XA)) / CA — целое
Кол-во элементов: CA
Считаем сумму всех чисел (SA), XOR всех чисел (XA)
Получаем: SA = 2a + 2b + 2c +… + X1 +… + X2 +…
XA = X1 ^ X2
Перебираем первое число от 0 до 0xfffffff так, чтобы выполнялось равенство: (SA + X1 + (X1 ^ XA)) / CA — целое
Решение
Решение простое: заводим 65 «аккумуляторов»: в нулевой xor’ятся абсолютно все числа массива по мере прохода по нему. Т.к. a^a=0, a^b=b^a, a^0=a, a^(b^c)=(a^b)^c, то в аккумуляторе в результате оказывается а0=u1^u2, где u1 и u2 — искомые уникальные числа. Получив u1^u2 нужно как‐то это разделить на u1 и u2, для этого используем то, что u1≠u2 (свойство уникальности). Т.к. u1≠u2, то хотя бы один бит в u1 отличен от u2, поэтому оставшиеся 64 аккумулятора используются так: в n’й аккумулятор (n=1..64) xor’ятся числа, (n//2)‐й бит которых равен (n%2) (// — целочисленное деление). Таким образом образуются 32 группы по два аккумулятора: в первой группе 1‐й и 2‐й, во второй 3‐й и 4‐й, … Из‐за того, что существует хотя бы один бит, отличный от u1 и u2, существует такая группа аккумуляторов, что u1 учавствовал в определении значения одного аккумулятора, в определении значения которого не учавствовал u2. В такой группе один из аккумуляторов равен u1, а другой u2. В группах, где u1 и u2 имеют совпадающий бит один из аккумуляторов 0, а другой — u1^u2. Если u1 или u2 равен 0, то все группы будут иметь одинаковые пары значений.
Код на Python:
PS: комментарии пока не читал.
Код на Python:
#!/usr/bin/python3.4
import random
from functools import partial
from numpy.random import random_integers, shuffle
from numpy import array, uint32, unique, array_equal
def create_test_array(randsize=10000):
random_uints32 = partial(random_integers, 0, 2 ** 32 - 1)
doubles = unique(random_uints32(size=randsize))
u1 = next(iter(doubles))
while u1 in doubles:
u1 = random_uints32()
u2 = next(iter(doubles))
while u2 in doubles:
u2 = random_uints32()
arr = array(doubles, dtype=uint32)
arr.resize([len(doubles) * 2 + 2])
arr[len(doubles):len(doubles) * 2] = doubles
arr[-2] = u1
arr[-1] = u2
shuffle(arr)
return u1, u2, arr
def find_uniques(arr):
acc0 = uint32(0)
accs = array([[acc0] * 2] * 32)
masks = array([uint32(1 << i) for i in range(32)])
for val in arr:
acc0 = acc0 ^ val
for bitidx, subaccs in enumerate(accs):
subaccs[int(bool(val & masks[bitidx]))] ^= val
p1 = array([acc0, uint32(0)])
p2 = array([uint32(0), acc0])
for subaccs in accs:
if array_equal(subaccs, p1) or array_equal(subaccs, p2):
continue
return subaccs
return p1
def main():
u1, u2, arr = create_test_array()
fu1, fu2 = find_uniques(arr)
print('expected: ', sorted([u1, u2]))
print('found : ', sorted([fu1, fu2]))
if __name__ == '__main__':
main()
PS: комментарии пока не читал.
Все так!
Уточнение
Я тут после прочтения комментариев понял, что вообще‐то acc0 не нужен: в группах всегда будет либо (0, u1^u2), либо (u1, u2). Т.е. можно забить на acc0 и возвращать первую пару, где оба аккумулятора не нули. А если таких нет, то вернуть самую первую пару: это вариант, когда u1 или u2 0. Эта переменная вообще появилась из‐за того, что первым соображением было «если всё xor’ить, получим u1^u2; теперь думаем, как получить отсюда u1 и u2». В комментариях некоторые люди здесь и застряли.
def find_uniques(arr):
accs = array([[uint32(0)] * 2] * 32)
masks = array([uint32(1 << i) for i in range(32)])
for val in arr:
for mask, subaccs in zip(masks, accs):
subaccs[int(bool(val & mask))] ^= val
for subaccs in accs:
if subaccs[0] != 0 and subaccs[1] != 0:
return subaccs
return accs[0]
оставю тут. сложность O(n). память O(1). guildalfa.ru/alsha/node/29
Напишу всё же свое предположение, хотя оно не укладывается в более жесткие требования предложенные Zealint.
Берем первые 10 простых чисел P={2, 3, 5, 7, 11, 13, 17, 19, 23, 29}. Их сумма 129. Выделяем bool d[129]; Реализуем функцию Mods(x), которая возвращает массив из 10 чисел: i-e число равно (x % P[i]) + сумма P[0..i-1].
Для всех элементов i=1..N
Затем для x=0..232-1 вычисляем y = xy ^ x и проверяем что xor массивов Mods(x) и Mods(y) совпадает с d. Если совпадает, выходим по break.
Наверное так.
Для всех элементов i=1..N
- M=Mods(a[i]). Инвертируем d[M[j]] для j=0..9
- Считаем xy ^= a[i]
Затем для x=0..232-1 вычисляем y = xy ^ x и проверяем что xor массивов Mods(x) и Mods(y) совпадает с d. Если совпадает, выходим по break.
Наверное так.
Можно например сначала сложить все числа в конечном поле, затем сложить квадраты этих чисел. В итоге пары сократятся и получится два уравнения: сумма двух числе и сумма их квадратов. Уравнения эти сводятся к выичслению квадратного корня в конечном поле, что можно сделать алгоритмом Шенкса. Проще конечно xor-ом, но если хочется загрузить того кто собеседование проводит, то конечные поля Галуа — самое то.
ооооо! В конченом поле — это как? По модулю? И почему пары сократятся?
Квадраты бесполезны, поскольку x2+y2=(x+y)2 — мы не получаем новой информации. Нужно
Решение
использовать кубы. x3+y3=(x+y)(x2+xy+y2). Таким образом мы узнаем x2+xy+y2. Как мы видели выше, сумму квадратов мы уже знаем. Вычитая ее, получаем xy. Остается решить квадратное уравнение и найти x и y. Это единственное (из предложенных в комментариях) правильное решение задачи, поскольку 32 аккумулятора — это не O(1), а O(w), где w — размер машинного слова.
Так поясните, что подразумевается под сложениями и умножениями, и как сокращаются пары других чисел?
Сложение — xor. Умножение выполняется сложнее. Вначале нужно выполнить битовое умножение без переносов, а потом посчитать CRC. Еще материал по этой теме: https://habrahabr.ru/post/212095/
Практически это делается так:
shl = x => x = x << 1 ^ (x < 0? 0x8d: 0); // x * 2 + 2**32 + 0x8d
mul = (a, b) => b && ((b & 1? a: 0) ^ shl(mul(a, b >>> 1)));
А потом применяем вот так:
mul(0x6789, 0x9876);
shl = x => x = x << 1 ^ (x < 0? 0x8d: 0); // x * 2 + 2**32 + 0x8d
mul = (a, b) => b && ((b & 1? a: 0) ^ shl(mul(a, b >>> 1)));
А потом применяем вот так:
mul(0x6789, 0x9876);
Скрытый текст
Размер исходных чисел строго ограничен константой — 32 бита. Далее, размер входного потока по условиям задачи тоже строго ограничен (максимум, у нас два раза каждое число встречается), поэтому, строго говоря, тут любое решение будет константным как по времени, так и по памяти. Если же отбросить троллинг и сделать вид, что задача поставлена правильно, то завести 32 аккумулятора будет вполне законным решением. Ваше решение разве не зависит от размера слова? Так ли это?
Ваше решение разве не зависит от размера слова?
Зависит — примерно как 3*N бит. А решение со счётчиками — примерно N^2 бит. Конечно, ограничения задачи становятся слегка другими, но O(N)<O(N^2).
При чём тут O(N^2)? В решении с 32-мя счётчиками будет один проход по циклу. Имеем те же O(N) битовых операций и O(1) памяти, в виде 32-х чисел размером 32 бита. Ну, плюс пара служебных переменных.
А в случае 16384-битных слов? Всё равно 32 счётчика?
Правда, я не уверен, что решение с x^3 вообще работает.
Правда, я не уверен, что решение с x^3 вообще работает.
Судя по сообщению Mrrl под
Чтобы не возникало недопонимания не нужно переназначать переменные из условия.
N
имел ввиду разрядность числа, а не то, что указано в условии. Т.е. если считать разрядность числа в задаче переменной и равной b, то будет O(Nb) битовых операций и O(b²) памяти в случае с b счётчиками и O(b) памяти для его предложения.Чтобы не возникало недопонимания не нужно переназначать переменные из условия.
Даже если так, то это уже обсуждение другой задачи. А в этой задаче размер слова чётко обозначен, и это с практической точки зрения даёт надежду на вполне эффективный алгоритм. Менять условия или пытаться решить более общую задачу вместо исходной — это не всегда хорошая идея. А в случае жёстких требований на время работы даже плохая. Не вижу также смысла пытаться ставить в качестве аргумента теоретическое время работы через символ "O" (который обозначает время для b->oo), которое никакого отношения к реальности обычно не имеет, особенно при малых значениях этого b.
Очевидно, что подразумевается, что размер чисел равен машинному слову. Я не вижу другого разумного способа формализовать задачу. В стандартной модели вычислений, операции над машинными словами выполняются за O(1) и одно машинное слово занимает O(1) памяти.
Ну так и я тоже не вижу никаких теоретических отличий между Вашими переменными "x" и "y" в конечном поле (по какому-то модулю, явно зависящему от размера слова), и набором переменных из решения с xor, размер которых будет в точности совпадать с размером слова, потому что размер слова не стремится к бесконечности и потому что в условиях задачи (некорректно поставленной) указано, что память не должна зависеть от размера массива, а будет ли она квадратичной или линейной по числу бит в слове, не важно. Что же касается практической эффективности обоих решений, то это тоже большой вопрос, что будет быстрее — 32 xor'a или вычисления кубов по модулю. Хотя я, конечно, не буду спорить о том, что любые правильные методы сами по себе заслуживают внимания и каждый из них по-своему хорош.
Чтобы N стремился к бесконечности, битность чисел тоже должна стремиться к бесконечности. Даже в варианте где разрешены повторения парных чисел, очень странно указать 32 бита и не подразумевать, что числа имеют размер машинного слова. Тогда уж надо указывать 30 бит или 50 бит (компьютеры давно уже 64-битные, а это самое круглое число меньше 64).
Некорректно поставленная задача означает лишь то, что ее нужно преобразовать в корректно поставленную наиболее естественным способом, используя общепринятые умолчания. После чего решить. При объяснении решения, в зависимости от обратной реакции вопрошающего, возможно преобразовать решение обратно. Подход требующий 32 слова в памяти не является решением получившейся корректно поставленной задачи, значит это не правильное решение. Если бы оно подразумевалось, нужно было бы писать в условии не O(1) памяти, а O(32) памяти. Ну или сразу писать корректную формулировку с переменной длиной машинного слова.
PS Конечное поле тут не по модулю простого числа. Поле должно иметь характеристику 2, чтобы парные числа сокращались.
Некорректно поставленная задача означает лишь то, что ее нужно преобразовать в корректно поставленную наиболее естественным способом, используя общепринятые умолчания. После чего решить. При объяснении решения, в зависимости от обратной реакции вопрошающего, возможно преобразовать решение обратно. Подход требующий 32 слова в памяти не является решением получившейся корректно поставленной задачи, значит это не правильное решение. Если бы оно подразумевалось, нужно было бы писать в условии не O(1) памяти, а O(32) памяти. Ну или сразу писать корректную формулировку с переменной длиной машинного слова.
PS Конечное поле тут не по модулю простого числа. Поле должно иметь характеристику 2, чтобы парные числа сокращались.
Задачке лет 5… это классика…
Скрытый текстtype
TMyElement= integer;
PMyElement= ^TMyElement;
TMyArray= array of TMyElement;
TBitInfo= record
Mask: TMyElement;
Sum: TMyElement;
end;
const
BitCount = SizeOf(TMyElement)*8;
LastBitNo= BitCount-1;
//поиск 0..2 непарных среди Count элементов массива, начиная с p
procedure Find012(p: PMyElement; Count: integer; var Res: TMyArray);
var
bi: array[0..LastBitNo] of TBitInfo;
t: TMyElement;
q: PMyElement;
i: integer;
begin;
if (p=nil) or (CountLastBitNo;
end;
end;
end;
end;
Скрытый текстtype
TMyElement= integer;
PMyElement= ^TMyElement;
TMyArray= array of TMyElement;
TBitInfo= record
Mask: TMyElement;
Sum: TMyElement;
end;
const
BitCount = SizeOf(TMyElement)*8;
LastBitNo= BitCount-1;
//поиск 0..2 непарных среди Count элементов массива, начиная с p
procedure Find012(p: PMyElement; Count: integer; var Res: TMyArray);
var
bi: array[0..LastBitNo] of TBitInfo;
t: TMyElement;
q: PMyElement;
i: integer;
begin;
if (p=nil) or (CountLastBitNo;
end;
end;
end;
end;
Вроде рабочий вариант решения, но с некоторыми ограничениями jsfiddle.net/669a3L45
Элементарно.
Int main()
{
vector v = {1,2,3,3,1,2,4,7};
//vector v = {1,2,7,4,1,2,3,3};
//vector v = {1,2,3,4,1,2,3,7};
int p = 0;
int s = 0;
int pp = 0;
int ss = 0;
for( size_t i = 0; i < v.size(); i++ )
{
if( i < (v.size() / 2) )
p = p ^ v[i];
else
s = s ^ v[i];
if( (i%2) == 0 )
pp = pp ^ v[i];
else
ss = ss ^ v[i];
}
if( (p^pp) == p || (s^ss) == s )
{
cout
Int main()
{
vector v = {1,2,3,3,1,2,4,7};
//vector v = {1,2,7,4,1,2,3,3};
//vector v = {1,2,3,4,1,2,3,7};
int p = 0;
int s = 0;
int pp = 0;
int ss = 0;
for( size_t i = 0; i < v.size(); i++ )
{
if( i < (v.size() / 2) )
p = p ^ v[i];
else
s = s ^ v[i];
if( (i%2) == 0 )
pp = pp ^ v[i];
else
ss = ss ^ v[i];
}
if( (p^pp) == p || (s^ss) == s )
{
cout
Моя попытка#include
int main()
{
unsigned bit[32] = {}, all = 0, v, i;
while ( std::cin >> v ) {
all ^= v;
for ( i = 0; i < 32; ++i ) {
if ( v&(1
int main()
{
unsigned bit[32] = {}, all = 0, v, i;
while ( std::cin >> v ) {
all ^= v;
for ( i = 0; i < 32; ++i ) {
if ( v&(1
Мда, тэги не прошли. И редактировать не могу.
В общем, посмотрел другие решения, и эта идея уже была приведена несколько раз. Памяти требуется на 32-е xor суммы.
В общем, посмотрел другие решения, и эта идея уже была приведена несколько раз. Памяти требуется на 32-е xor суммы.
На 33. Плюс ещё куча вещей вроде памяти под исполняемый код и временные переменные вроде i и v, которые есть, но на которые всем (включая меня) при подсчёте памяти было наплевать.
Проще, наверное, показать код, чем описать:
Скрытый текст
static void findunique(int nr, uint32_t *a)
{
uint32_t s[32][2] = {};
int i;
int j;
for (i = 0; i < nr; ++i)
for (j = 0; j < 32; ++j)
s[j][!!(a[i] & (1 << j))] ^= a[i];
for (j = 0; j < 32; ++j)
if (s[j][0] != 0 && s[j][1] != 0)
printf("%u %u\n", s[j][0], s[j][1]);
}
Update: все-таки опишу.
Для каждого номера бита i, от 0 до 31, найдем сумму всех элементов массива, у которых i-й бит установлен (s[i][1]) и сумму элементов, у которых i-й бит не установлен (s[i][0]), все суммы по модулю 2. Всякий парный элемент участвует точно в тех же суммах, что и его пара и, таким образом, общий вклад от пары во всякой сумме равен 0.
Предположим, сначала, что непарные элементы A и В отличны от нуля. Тогда отличны от нуля только те суммы s[i][j], в которых участвует один или оба непарных элемента. Но непарные элементы отличаются хотя бы одним битом (иначе они были бы парой) k, тогда один из них равен s[k][0], а другой — s[k][1].
Если же один из непарных элементов равен нулю, то для любого k, s[k][0] и s[k][1] и дают искомые числа.
Для каждого номера бита i, от 0 до 31, найдем сумму всех элементов массива, у которых i-й бит установлен (s[i][1]) и сумму элементов, у которых i-й бит не установлен (s[i][0]), все суммы по модулю 2. Всякий парный элемент участвует точно в тех же суммах, что и его пара и, таким образом, общий вклад от пары во всякой сумме равен 0.
Предположим, сначала, что непарные элементы A и В отличны от нуля. Тогда отличны от нуля только те суммы s[i][j], в которых участвует один или оба непарных элемента. Но непарные элементы отличаются хотя бы одним битом (иначе они были бы парой) k, тогда один из них равен s[k][0], а другой — s[k][1].
Если же один из непарных элементов равен нулю, то для любого k, s[k][0] и s[k][1] и дают искомые числа.
кхм…
я не программист, но всё же… попробую предложить свой ход решения (увы, словами, а не на «компьютерном языке»)
1. получаем из последовательности число
2. заносим его в первую свободную ячейку памяти
3. проверяем все предыдущие ячейки памяти на равенство значений в них этому числу; если находится ячейка, значение в которой равно значению в последней ячейке (тем же XOR), обе ячейки «уничтожаем» (очищаем и высвобождаем для дальнейшего использования — например, если занято n ячеек, из которых оказываются равны n и m (m<n), то переносим значение ячейки n-1 в ячейку m, после чего уменьшаем n на единицу)
4. возвращаемся на шаг 1.
Да, этот вариант потребует массы вычислений (в худшем случае — если у нас идет сначала последовательность от 1 до 4 294 967 296, а затем — та же последовательность с двумя пропущенными значениями), однако можно предположить, что число переборов в реальности сократится (хотя вопрос — во сколько раз?), поскольку значения будут поступать неупорядоченно, а значит — пары будут взаимно погашаться. Если значения «приходят» с каким-то интервалом, НЕдостаточным для обработки, то, возможно, нам потребуется уже не 4 ГБ памяти для хранения входящей последовательности, а 8ГБ (в случае, если вычислительной мощности хватает исключительно на запись значений, но не хватает на какую-либо их обработку).
Хороший вопрос (с позиции теории вероятности): каким объёмом памяти можно обойтись для решения этой задачи, скажем, в 99% случаев, при условии, что скорости вычислений хватает для расчётов, а данные не приходят слишком часто?
я не программист, но всё же… попробую предложить свой ход решения (увы, словами, а не на «компьютерном языке»)
1. получаем из последовательности число
2. заносим его в первую свободную ячейку памяти
3. проверяем все предыдущие ячейки памяти на равенство значений в них этому числу; если находится ячейка, значение в которой равно значению в последней ячейке (тем же XOR), обе ячейки «уничтожаем» (очищаем и высвобождаем для дальнейшего использования — например, если занято n ячеек, из которых оказываются равны n и m (m<n), то переносим значение ячейки n-1 в ячейку m, после чего уменьшаем n на единицу)
4. возвращаемся на шаг 1.
Да, этот вариант потребует массы вычислений (в худшем случае — если у нас идет сначала последовательность от 1 до 4 294 967 296, а затем — та же последовательность с двумя пропущенными значениями), однако можно предположить, что число переборов в реальности сократится (хотя вопрос — во сколько раз?), поскольку значения будут поступать неупорядоченно, а значит — пары будут взаимно погашаться. Если значения «приходят» с каким-то интервалом, НЕдостаточным для обработки, то, возможно, нам потребуется уже не 4 ГБ памяти для хранения входящей последовательности, а 8ГБ (в случае, если вычислительной мощности хватает исключительно на запись значений, но не хватает на какую-либо их обработку).
Хороший вопрос (с позиции теории вероятности): каким объёмом памяти можно обойтись для решения этой задачи, скажем, в 99% случаев, при условии, что скорости вычислений хватает для расчётов, а данные не приходят слишком часто?
Ваше решение имеет алгоритмическую сложность O(n2), и имеет очень узкое применение: либо на маленьких массивах, либо на таких массивах, где парные элементы всегда расположены рядом друг с другом.
Иначе вполне можно получить ситуацию, где для каждого из миллиарда элементов проводится миллиард сравнений, что дает 1018 операций. Даже если выполнять 3 миллиарда сравнений в секунду, этот процесс займет 10 лет.
Иначе вполне можно получить ситуацию, где для каждого из миллиарда элементов проводится миллиард сравнений, что дает 1018 операций. Даже если выполнять 3 миллиарда сравнений в секунду, этот процесс займет 10 лет.
да, логично… тогда попробую предположить другой вариант: каждое значение является адресом массива из 2^32 бит, когда приходит число n (1<=n<=2^32), мы проверяем значение этого конкретного бита: если оно равно нулю, это будет означать, что такое значение ещё не встречалось (или встречалось, но чётное число раз, то есть имеет пару). второй такой же массив можно использовать для проверки, обнулялось ли значение бита, что важно знать, например, если ищем «третьего лишнего»:
если бит n в первом массиве = 0, то добавляем к нему единицу;
если бит n в первом массиве = 1, то обнуляем его, а бит n второго массива увеличиваем на единицу
теперь, по окончании проверки, у нас будут в первом массиве единицы в тех битах, которые соответствуют числам, встретившимся лишь однажды (то есть, по сути дела, не имеющие пары).
по идее, проверять и менять бит в массиве памяти — не такая сложная задача (да и вместо 4 ГБ можно будет обойтись 0,5 ГБ для одного массива или 1 ГБ для двух)… единственное, что в такой постановке задачи каждый байт оказывается значим, а следовательно — невозможно решить эту задачу с меньшим доступным объёмом памяти… на мой взгляд, любые свёртки этого пространства до меньшего объёма приведут к выпадению информации о найденных числах…
да, и… какую алгоритмическую сложность имеет решение теперь?
если бит n в первом массиве = 0, то добавляем к нему единицу;
если бит n в первом массиве = 1, то обнуляем его, а бит n второго массива увеличиваем на единицу
теперь, по окончании проверки, у нас будут в первом массиве единицы в тех битах, которые соответствуют числам, встретившимся лишь однажды (то есть, по сути дела, не имеющие пары).
по идее, проверять и менять бит в массиве памяти — не такая сложная задача (да и вместо 4 ГБ можно будет обойтись 0,5 ГБ для одного массива или 1 ГБ для двух)… единственное, что в такой постановке задачи каждый байт оказывается значим, а следовательно — невозможно решить эту задачу с меньшим доступным объёмом памяти… на мой взгляд, любые свёртки этого пространства до меньшего объёма приведут к выпадению информации о найденных числах…
да, и… какую алгоритмическую сложность имеет решение теперь?
Да, теперь сложность O(n), все отлично. Но вот памяти уходит — мама не горюй =)
Дело в том, что эта структура хранит информацию обо всех числах, и подходит для решения задачи о любом количестве непарных чисел — 2, 3, 1000000, неважно. А в условии речь идет строго о двух числах, и для этой конкретной задачи такая структура избыточна.
на мой взгляд, любые свёртки этого пространства до меньшего объёма приведут к выпадению информации о найденных числах…
Дело в том, что эта структура хранит информацию обо всех числах, и подходит для решения задачи о любом количестве непарных чисел — 2, 3, 1000000, неважно. А в условии речь идет строго о двух числах, и для этой конкретной задачи такая структура избыточна.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Задачка про парные числа