spamsink: (lenin)
[personal profile] spamsink


Дан массив целых неотрицательных чисел. Требуется как можно эффективнее найти сумму попарных хэмминговых расстояний между ними.

Upd: Раскрыл все комменты. Ожидаемый ответ был эквивалентен этому

Date: 2016-04-14 04:46 pm (UTC)
From: [identity profile] sab123.livejournal.com
Попарных - это как? Соседние числа? Или каждое с каждым?

Date: 2016-04-14 04:51 pm (UTC)
From: [identity profile] lev.livejournal.com
define "хэмминговых расстояний" plz

Date: 2016-04-14 04:56 pm (UTC)
From: [identity profile] mopexod.livejournal.com
А хэммингово расстояние между двумя числами - количество бит в XOR-е между ними?

Date: 2016-04-14 04:59 pm (UTC)
From: [identity profile] excubitus.livejournal.com
Для каждого разряда по отдельности посчитать. Подсчитать, сколько в младших разрядах нулей и единиц, перемножить, сдвинуть числа вправо. Повторять для всех разрядов, или пока остались ненулевые числа (не уверен, что из этого эффективнее), накапливая результат.

Date: 2016-04-14 05:26 pm (UTC)
From: [identity profile] kgeorgiy.livejournal.com
Посчитаем a[i] -- число единиц в i-м бите во всех числах, тогда ответ: b n^2 - sum_i=0..b (a[i]^2 - (n-a[i]^2)), где b -- номер максимального установленного бита.

Видимо, при использовании пирамидальной схемы с битовыми трюками, это можно сделать за O(n+b).

Date: 2016-04-14 06:56 pm (UTC)
From: [identity profile] alikr.livejournal.com
Что то типа суммы произведений (суммы в каждом бите x (общее число элементов - сумма в каждом бите))

Date: 2016-04-14 06:59 pm (UTC)
From: [identity profile] juan-gandhi.livejournal.com
А можно сначала построить дерево?

Date: 2016-04-14 08:45 pm (UTC)
From: [identity profile] juan-gandhi.livejournal.com
Разницу между ветками считать.
Впрочем, нет смысла.
Поразбираюсь еще.

Date: 2016-04-14 07:18 pm (UTC)
From: [identity profile] sab123.livejournal.com
Похоже, что оно связано с предыдущей задачей про минимальное нужное количество бит для сложения :-)

В классическом виде алгоритм подсчета бит, который рассматривает слово как массив N однобитных чисел, делит на два массива по N/2 2-битных чисел, складывает и т.д. использует числовое пространство неэффективно. То есть, например, для 32-битного слова после

a = (a & 0x55555555) + ((a>>1) & 0x55555555)

каждое 2-битное число может хранить значения вплоть до 3, а на самом деле они окажутся не больше 2. Поэтому туда можно добавить еще одну половинку расслоенного числа, которая потом пойдет обрабатываться дальше "забесплатно". Если в процессоре есть достаточно регистров, можно выстроить целую пирамиду таких частичных результатов, которые будут добавляться по мере возможности. Xраниться будут что-нибудь типа 32-битного числа/массива (64 бит не надо, у нас нет столько чисел в массиве, и вообще верхнюю категорию достаточно log2(arraysize)), 16-битного, 8-битного, 4-битного, 2-битного, остающейся второй половинки 2-битного.

Но оптимальный порядок сложения будет запутанным, про него надо подумать отдельно.

Date: 2016-04-14 07:38 pm (UTC)
From: [identity profile] kcmamu.livejournal.com
O(числа битов данных) устроит? А то ведь и быстрее можно.

Date: 2016-04-14 08:14 pm (UTC)
From: [identity profile] aa-kir.livejournal.com
Интересно, а как можно быстрее если каждый бит данных надо прочитать?

Date: 2016-04-15 03:13 am (UTC)
From: [identity profile] aa-kir.livejournal.com
Ну да. При фиксированной разрядности, если есть N чисел, то O(N) - это то же самое, что O(число битов). Так что быстрее чем O(число битов) никак невозможно...

Date: 2016-04-14 09:19 pm (UTC)
From: [identity profile] sab123.livejournal.com
Кстати, да, вариант посчитать количество единиц и нулей в конкретном бите всех чисел мне в голову приходил, но я его отбросил и не прочухал, что O у него получается линейная от количества чисел при фиксированном размере слова (а при нефиксированном - от числа бит вообще), а не квадратичная как у суммирования (при любых оптимизациях суммирования).

А сделать логарифмическую зависимость от числа бит в слове, наверное, можно по принципу, аналогичному подсчету бит, выстроив пирамидку. Примерно так для 8-битных слов:

for (l = 0; l < arraysize; l += 8) {
    for (k = 0..1) {
        for (j = 0..1) {
            for (i = 0..1) {
                index = i + j*2 + k*4;
                if (index >= arraysize)
                    break;
                v = array[index];
                inter1[0] += v & 0x55;
                inter1[1] += (v>>1) & 0x55;
            }
            inter2[0] += inter1[0] & 0x33;
            inter2[1] += (inter1[0]>>2) & 0x33;
            inter1[0] = 0;

            inter2[2] += inter1[1] & 0x33;
            inter2[3] += (inter1[1]>>2) & 0x33;
            inter1[1] = 0;
        }

        count[0] += inter2[0] & 0x7;
        count[1] += (inter2[0] >> 4) & 0x7;
        inter2[0] = 0;

        count[2] += inter2[1] & 0x7;
        count[3] += (inter2[1] >> 4) & 0x7;
        inter2[1] = 0;

        count[4] += inter2[2] & 0x7;
        count[5] += (inter2[2] >> 4) & 0x7;
        inter2[2] = 0;

        count[6] += inter2[3] & 0x7;
        count[7] += (inter2[3] >> 4) & 0x7;
        inter2[3] = 0;
    }
}


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

А потом сумма будет Sum[for i =0..7](2*count[i]*(arraysize-count[i]))

Впрочем нет, при таких раскладах - не будет логарифмичности. Для логарифмичности нужно делать больше итераций по k и j, чем 2. Гм, надо подумать, сколько их можно делать.
Edited Date: 2016-04-14 09:23 pm (UTC)

Date: 2016-04-14 10:01 pm (UTC)
From: [identity profile] kcmamu.livejournal.com
a[0...N-1] -- вход
b[0...log_2 N], c[0...log_2 N] -- рабочие массивы (сперва в них нули)
прочее -- одиночные переменные

for(i = 1; i <= N; i++)
for(d = a[i-1], j = 0; 1; j++) // в среднем крутится раза два 
if(i & (1 << j)) { c[j] = d; break; }
else { e = b[j] ^ c[j] ^ d; d = b[j] & (c[j] | d) | c[j] & d; b[j] = e; c[j] = 0; }

ответ = 0;
for(i = 0; i < разрядность; i++)
{
for(d = j = 0; j <= log_2 N; j++)
d += (!!(b[j] & (1 << i)) + !!(c[j] & (1 << i)) << j;

ответ += d * (N - d);
}
Edited Date: 2016-04-14 11:23 pm (UTC)

Date: 2016-04-14 08:33 pm (UTC)
From: [identity profile] vgramagin.livejournal.com
Я не очень понимаю, что такое хэммингово расстояние для целых чисел. Оно же для строк.

Date: 2016-04-14 08:46 pm (UTC)
From: [identity profile] juan-gandhi.livejournal.com
А, знаю. use bags

Date: 2016-04-14 11:40 pm (UTC)
From: [identity profile] cema.livejournal.com
Is Hamming distance the edit distance on the bitwise representation of the numbers, or something else?

Date: 2016-04-15 10:46 pm (UTC)
From: [identity profile] soloviewoff.livejournal.com

That is Levenshtein's, another guy's.  Though they match here I think as there are replacements only, no insertions or deletions.

Date: 2016-04-15 07:56 am (UTC)
From: [identity profile] http://users.livejournal.com/_guard_/
O(N)*M, если все числа не больше 2^M (что на практике константа, так?):

s = 0
for j in [0...M]:
  k = 0
  for i in [0...N]:
    k += numbers[i] % 2
    numbers[i] = numbers[i] // 2
  s += k * (N - k) # <-- число пар 1 и 0 в j-м разряде
Edited Date: 2016-04-15 07:57 am (UTC)

Date: 2016-04-15 02:46 pm (UTC)
From: [identity profile] http://users.livejournal.com/_guard_/
А ну да, можно в м-массиве накапливать поразрядно счетчики единиц, потом подсчитать

Date: 2016-04-15 11:48 am (UTC)
From: [identity profile] b00ter.livejournal.com
Я так понимаю, что неотрицательность - это неспроста, да?

Date: 2016-04-23 07:45 am (UTC)
From: [identity profile] outputlogic.livejournal.com
Похоже что надо использовать свойство XOR: (a^b)^(b^c)=a^c.
В процессе прохождения массива, надо накапливать частичные суммы, а также делать ХOR всех накопленных пар по диагонали.
Вот пример для массива {a,b,c,d}
1. a^b
2. a^b^c, b^c
3. a^b^c^d, b^c^d, c^d

В процессе делаем XOR пар по диагонали
(a^b)^(b^c)=a^c
(a^b^c)^(b^c^d)=a^d
(b^c)^(c^d)=b^d

Profile

spamsink: (Default)
spamsink

February 2026

S M T W T F S
12345 67
8 91011 121314
15161718 192021
22 2324 25262728

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Mar. 5th, 2026 10:13 pm
Powered by Dreamwidth Studios