Почти наверняка можно что-то быстрое придумать =) Задача сильно упрощается от того, что нет вставок и удалений при поиске разницы...
Возникла следующая задача: Есть число x. Необходимо перечислить все числа, которые отличаюстя от него не более чем на k разрядов. На форуме вроде был код, перечисляющий все числа в которые содержится ровно n единиц, только я даже не представляю по каким словам его можно найти.
_basmp_ Честно говоря я не понимаю принцип построения таблиц. Например если L=6, во второй таблице должны быть числа 3,5,9 или так и останутся 3 и 5? А что в этом случае будет в третьей таблице? Я просто хочу сделать разбиение хеша на части и построение по ним индексов. Если мы ищем хеши отличающиеся не более чем на L/K разрядов, то хотя бы одна половина тоже должна отличаться не более чем на L/2K разрядов. Если левая половина отличается на L/2K разрядов, то правая половина не может отличаться более чем на L/2K разрядов, а вот если левая половина точно совпадает, то правая половина может отличаться на L/K разрядов и в этом случае проще перебирать все элементы с заданной левой половиной.
Black_mirror Из таблиц убрано все, что можно получить вращением, те 9 = 3 ror 1; 6 = 3 rol 1; 12 = 3 rol 2; 10 = 5 rol 1; Таблицы выше L/2 получаются из предыдущих путем инвертирования значащих элементов таблицы n' = L-n (можно этого не делать, а представить их явно) Те таблица 3 должна иметь один элемент - 14. Когда строил таблицу получилось, что 1 отличие - 4 числа 2 отличия - 6 чисел 3 отличия - 4 числа 4 отличия - 1 число При нечетном кол-ве разрядов (3х) будет 3, 3, 1. Половин или удвоений имхо не выйдет. А что стоит разложить сперва по коротким хешам, отобрать подобные. Для них построить более точную таблицу, опять отобрать итд. Отборов 3х должно быть достаточно.
_basmp_ Я вообще спрашивал про таблицу для L=6. При L=32 чисел которые содержат ровно 8 единиц 10518300 штук. Сколько из них попадут в 8ю таблицу?
Вот накатал как мог. Для массива это посложнее делать придётся. Да и непонятно зачем)) Неужели перебором что-то делать надо? Код (Text): uint r=rand(); for(;;) { uint t=(r&-r)+r; if(!t) break; r=r&~t; while(!(r&1)) r>>=1; r=(r>>1)|t; BinaryDump(r); }
Можно как вариант на первое время проиндексировать все двух пары чисел из двух байт. В каждом хеше взять все пары байт, которые стоят рядом, и которые стоят через одну позицию. Если хеши не различаются больше чем на половину байт, то это поможет относительно быстро их опознавать.
Прога, что перечисляет и выводит все числа с кол-вом единиц 1 <= K <= L Код (Text): #include <stdio.h> #define L 32 #define K 8 int m[L]; int Step(int n, int k){ if( m[n] == (1 << (L-1-n)) ){ if( n == k-1 ) return 0; if( !Step(n+1, k) ) return 0; m[n] = m[n+1] << 1; }else if( !m[n] ) m[n] = 1; else m[n] <<= 1; return 1; } void main(){ int i,j,r; for( i = 0; i<L; i++) m[i] = 0; j=0; while(Step(0,K)){ r = 0; for(i=0; i<K; i++) r |= m[i]; j++; printf("%d) xor mask == %xh\n", j, r); } } При L=32 и K=от 1 до 8, таких чисел 15033172
Black_mirror твою задачу примерно так можно решить: хэши суём в бин. дерево; затем ищем хэши, у коих самые верхние d бит различаются с эталоном; дальше убиваем поддерево по полученной маски и повторяем цикл пока поддеревьев больше не будет. как сделать счётчик сам разберёшься. ПРИМЕР: эталон - 1101001. ищем два различия: 1101001 0111001 1010001 0110101 первый цикл найдёт 0111001 и удалит поддерево 011.
ну, и замечу: "убить поддерево" не значит, что его надо убивать фактически - на каждом шаге производить сортировку не есть подход разумный. можно все "убиваемые" поддеревья метить как неиспользуемые с сохранением указателей на них. на каждой итерации просто производиться восстановление древа без сортировки засчёт сохранёных указателей. ЗЫ мля, можно было и покороче написать
Версия программы с дополнительными индексами: Код (Text): #include <windows.h> #include <stdio.h> unsigned myrand() { static DWORD seed=777; return seed=seed*134775813+1; } #define N 0x1000000 //число хешей unsigned H[N]; //хеши unsigned V[N]; //признак что хеш уже обрабатывался unsigned mark=0; int IH[65536]; //IH[i] - индекс первого хеша, старшая часть которого равна i int LH[N]; //H[LH[i]] - следующий хеш, имеющий совпадающую с H[i] старшую часть int IL[65536]; //IL[i] - индекс первого хеша, младцая часть которого равна i int LL[N]; //H[LL[i]] - следующий хеш, имеющий совпадающую с H[i] младшую часть int _mask[65536+17]; int* masks[17]; int bits(unsigned v) { int r=0; while(v) { v&=v-1; r++; } return r; } int cc; int findH(unsigned *hash, unsigned key, int *list,int *index,unsigned k2, int d) { int d2=d/2; int r=0; for(int i=0;i<=d2;i++)//выбираем количество отличающихся бит { int* m=masks[i];//m=список масок с i разрядами while(*m>=0)//перебираем маски { int k=index[k2^*m];//xor-им половину ключа с выбранной маской while(k>=0)//перебираем хеши у которых одна половина совпадает с k2^*m { cc++; if(V[k]!=mark&&bits(hash[k]^key)<=d)//проверяем, что хеш отличается не более чем на d разрядов { V[k]=mark;//помечаем что такой хеш уже встречался r++; } k=list[k]; } m++; } } return r; } int find(unsigned *hash, unsigned key, int d) { return findH(hash,key,LH,IH,key>>16,d)+findH(hash,key,LL,IL,key&65535,d); } int main() { int i=0; //подготовка xor-масок for(int d=0;d<17;d++) { masks[d]=_mask+i; for(int j=0;j<65536;j++) { if(bits(j)==d) _mask[i++]=j; } _mask[i++]=-1; } //генерация случайных хешей for(i=0;i<N;i++) H[i]=myrand(); //инициализация списков хешей с одинаковыми левыми и правыми половинами for(i=0;i<65536;i++) IH[i]=IL[i]=-1; for(i=N;i--;) { LL[i]=IL[H[i]&65535]; IL[H[i]&65535]=i; LH[i]=IH[H[i]>>16]; IH[H[i]>>16]=i; } //ищем хеши отличающиеся от заданного на D бит for(int D=0;D<=16;D++) { mark++; cc=0; int r=0; int t1=GetTickCount(); //линейный поиск for(i=0;i<N;i++) { if(bits(H[i]^H[0])<=D) { r++; } } int t2=GetTickCount(); t1=t2-t1; //немного оптимизированная функция r=find(H,H[0],D); t2=GetTickCount()-t2; //r=A/B/n=C //A - сколько хешей нашла функция //B - сколько хешей функция проверяла //C - общее количество printf("d=%d t1=%d t2=%d r=%d/%d/n=%d\n",D,t1,t2,r,cc,N); } getchar(); return 0; } Замеры: d=0 t1=1735 t2=0 r=1/495/n=16777216 d=1 t1=1718 t2=0 r=1/495/n=16777216 d=2 t1=1719 t2=16 r=1/8670/n=16777216 d=3 t1=1734 t2=0 r=18/8670/n=16777216 d=4 t1=1719 t2=31 r=157/70393/n=16777216 d=5 t1=1719 t2=31 r=944/70393/n=16777216 d=6 t1=1719 t2=125 r=4523/357302/n=16777216 d=7 t1=1719 t2=125 r=17588/357302/n=16777216 d=8 t1=1734 t2=469 r=58669/1289751/n=16777216 d=9 t1=1734 t2=469 r=168280/1289751/n=16777216 d=10 t1=1719 t2=1281 r=420209/3525806/n=16777216 d=11 t1=1719 t2=1312 r=924219/3525806/n=16777216 d=12 t1=1735 t2=2765 r=1804514/7625670/n=16777216 d=13 t1=1766 t2=2797 r=3163772/7625670/n=16777216 d=14 t1=1781 t2=4812 r=5004911/13479765/n=16777216 d=15 t1=1797 t2=4860 r=7214212/13479765/n=16777216 d=16 t1=1797 t2=6968 r=9563344/20070447/n=16777216 Вместо хешей использовались псевдослучайные числа длиной 32 разряда в количестве 2^24 штук. Хеши имеющие одинаковые левые или правые части были объеденены в списки. Программа перебирала все возможные левые и правые части, которые отличаются от заданного ключа не более чем на d/2 разряда, потом просматривала соответствующие списки. Выводы в общем не особо утешительные, для d=L/4-1 выигрыш получается всего лишь в 10 раз. Хотя может будет достаточно и d=L/8-1. Там выигрыш примерно в пару тысяч раз, правда если учесть что для каждого из N файлов нужно будет искать похожие, то есть сложность N^2, всё равно в итоге получится многовато. Может попарно сравнивать хеши и не нужно?! Может разбиение на группы похожих можно провести каким-то другим алгоритмом?!