LINUX.ORG.RU

[задачка] Найти непарные числа


0

2

Проходил недавно собеседование. Задали такую задачу: есть файл на несколько терабайт, на каждой строке 64битный int, известно, что каждое число повторяется в файле 2 раза, кроме одного. Найти это одно число. Какую-либо структуру данных в памяти/на диске нельзя создавать. Для 1го непарного числа решается легко: взять XOR от всех чисел, в итоге останется искомое. Второй вопрос я решить не смог: что делать, если непарных чисел два?

Сходу придумал такое но это «в лоб». И может работать очень долго.

Есть у кого-нибудь альтернативные варианты?

PS Это не «домашнее задание», собеседование уже давно закончено. Просто интересно, как это решать.

☆☆☆☆☆

Какую-либо структуру данных в памяти/на диске нельзя создавать.

unsigned int arr[] = { 1, 1, 14, 2, 3, 4, 5, 3, 2, 4, 14, 7, 8, 5, 8, 9, 11, 10, 10, 11, 12, 12 };

/0
Ограничения задачи слишком расплывчаты.

blexey ★★★★★
()
Ответ на: комментарий от blexey

Ну это тестовый массив. Не буду же я генерить несколько терабайт тестовых данных.

DELIRIUM ☆☆☆☆☆
() автор топика

Бей файл на пополам и ксорируй куски отдельно. Если один =0, забудь про него и бей второй пополам, и т.д. Если оба ксора не =!0 - это и есть искомые числа.

anonymous
()

В один регистр сумму по модулю 2, как обычно. Во второй - простую арифметическую сумму. Потом придется решить небольшую систему уравнений.

Deleted
()

Пусть a и b - искомые элементы. Посчитаем a xor b. Он очевидно не ноль, иначе a=b. Пусть i - ненулевая позиция a xor b. Заменим все числа, у которых в i-й позиции единица на 0xffffffff. Остальные оставим как прежде. Снова посчитаем xor. Из этих двух xor'ов можно восстановить a и b, что оставляется автору в качестве упражнения.

anonymous
()

Второй вопрос я решить не смог: что делать, если непарных чисел два?

Храним предыдущий результат xor (т.е. до нахождения искомого числа), после нахождения искомого, исключаем это действие и продолжаем дальше. ИМХО, для этого удобней использовать while (i < arr_size), вместо for() и инкрементации делать внутри цикла, ибо придется использовать инкрементацию на шаг вперед от текущего i.

gh0stwizard ★★★★★
()

1. Любой разряд, где общий xor даёт единицу, считаем, например, равным 0.
2. Следующие разряды по одному подбираем так, чтобы количество чисел, имеющих подбираемое с подобранным в соответствующих разрядах, было нечётное.

alfix
()
Ответ на: комментарий от anonymous

А кто сказал что парные числа ПОДРЯД идут ? :)

Jetty ★★★★★
()
arr=[1,1,14,2,3,4,5,3,2,4,14,7,8,5,8,9,11,10,10,11,12,12]

es=64

ax,m,r,s=0,1,0,0

for n in arr: ax^=n

while (m&ax)==0: m,s=m*2,s+1

for n in xrange(es):
    if n!=s:
	m,c=m+2**n,0
	for i in arr: c^=(i&m==r)
	if c==0: r+=2**n

print r,r^ax
alfix
()

Нельзя структурку, но 64 бита можно. Это они злобно. Если иметь 2 * 64 *64 бит, то задание становится тривиальным и однопроходным (скорее всего это быстрее чем читать 2 раза, хотя сложность алгоритма существенно выше). Можно собирать xor по тем у кого в определенной позиции 0 в одно место, а тем у кого 1 в другое. Итого 128 мест (кил). Можно, конечно, и общий xor хотя он нафиг не нужен. Далее в одной из 64 пар обнаруживаем оба числа.

io ★★
()
Ответ на: комментарий от alfix

Хреново описал, лучше бы: с помощью не 0 в xor определяем индикатор (разбивает на подмножества пар, причем непарные в разных), сразу видно, что сводится к 1 случаю и не надо плясать 64 раза:

pairs = [1,1,14,2,3,4,5,3,2,4,14,7,8,5,8,9,11,10,10,11,12,12]

ind, xor = 1, reduce(lambda x,y: x^y, pairs)
while not (xor & ind):
    ind *= 2

res = 0
for e in pairs:
    if e & ind: res ^= e

print res, res ^ xor
anonymous
()
Ответ на: комментарий от io

Нельзя структурку, но 64 бита можно.

Возможно имеется ввиду, что нельзя динамически выделять память.

anonymous
()

есть файл на несколько терабайт, на каждой строке 64битный int,

текстом или бинарно, little endian, big endian? :)

Harald ★★★★★
()
Ответ на: комментарий от Harald

А какая разница, какой там endiannes? Текст/бинарно в данном случае тоже похер, учитывая 64битность (диапазон) и размер файлов.

DELIRIUM ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Deleted

Тривиальный пример:
1+1+1+4=7 1^1^1^4=5
1+1+0+5=7 1^1^0^5=5
показывает, что с решением системы уравнений имеем некоторые проблемы.
Впрочем ничто не мешает использовать другие операции.

io ★★
()
Ответ на: комментарий от DELIRIUM

Это на чем? Сходу не понял.

Питон же.

anonymous
()
Ответ на: комментарий от anonymous

Может быть достаточно и одного прохода, кстати:

arr=[1,1,14,2,3,4,5,3,2,4,14,7,8,5,8,9,11,10,10,11,12,12]

[ax,r]=reduce(lambda n,e: [n[0]^e,n[1]^(e&-(len(bin(e).split('1'))&1))],arr,[0,0])

if r in [0,ax]: r=reduce(lambda n,e: [n[0]^(e&-(e&n[1]>0)),n[1]],arr,[0,2**len(bin(ax).split('1')[-1])])[0]

print r,r^ax

alfix
()
Ответ на: комментарий от anonymous

А если так?:

arr=[1,1,14,2,3,4,5,3,2,4,14,7,8,5,8,9,11,10,10,11,12,12]

ax=reduce(lambda n,e: n^e,arr,0)
m=2**len(bin(ax).split('1')[-1])
r=reduce(lambda n,e: n^(e&-(e&m>0)),arr,0)

print r,r^ax

alfix
()
Ответ на: комментарий от alfix

Получше. ,0 - вероятно лишний здесь (в первом reduce). Ну и &-(e&m>0) таки хак (по-крайней мере мне понадобилось секунд 20 чтобы распарсить).

anonymous
()
Ответ на: комментарий от Suntechnic

А поясни сущность хака, пожалуйста.

Получается либо n^e, либо n (один из способов избежать if в лямбде). Используется: n & -1 = n & 0b111... = n.

anonymous
()
Ответ на: комментарий от anonymous

Точнее, во втором предложении s/n/e/.

anonymous
()
Ответ на: комментарий от io
arr=[1,1,14,2,3,4,5,3,2,4,14,7,8,5,8,9,11,10,10,11,12,12]

es,x,ax=64,0,0

for e in arr:
    ax,b=ax^e,bin(e)[2:]
    for n in xrange(len(b)):
	if b[n]>0: x^=e*(2**((n+es-len(b))*es))

r=(x/2**(len(bin(x).split('1')[-1])/es*es))&(2**es-1)

print r,r^ax
alfix
()
Ответ на: комментарий от anonymous

Вот что ты человека сбиваешь, насколько я понял, все-таки !=0

ms-dos32
()
Ответ на: комментарий от io

Вот так:

arr=[1,1,14,2,3,4,5,3,2,4,14,7,8,5,8,9,11,10,10,11,12,12]

es,r,ax=64,0,0

for e in arr:
    ax,b=ax^e,bin(e)[2:]
    for n in xrange(len(b)):
	if b[n]>'0': r^=e*(2**((n+es-len(b))*es))

r=(r/2**(len(bin(r).split('1')[-1])/es*es))&(2**es-1)

print r,r^ax

alfix
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.