Эффективный метод получения одного номера, который не может быть создан из любой комбинации XORing

Если в диапазоне [0.. 2 64] есть какое-либо число, которое не может быть сгенерировано какой-либо композицией XOR из одного или нескольких чисел из заданного набора, существует эффективный метод, который печатает хотя бы один из недостижимых номеров или заканчивается информацией, что нет недостижимых номеров? У этой проблемы есть имя? Это похоже на другую проблему или у вас есть идея, как ее решить?

Ответ 1

Каждое число можно рассматривать как вектор в векторном пространстве (Z/2) ^ 64 над Z/2. Вы в основном хотите знать, если заданные векторы охватывают все пространство, а если нет, то для создания одного не натянутого (за исключением того, что span всегда включает нулевой вектор - для этого вам понадобится специальный случай, если вы действительно хотите один или несколько), Это может быть достигнуто путем устранения Гаусса.

В этом конкретном векторном пространстве устранение Гаусса довольно просто. Начните с пустого набора для основы. Делайте следующее, пока не будет больше номеров. (1) Отбросьте все числа, равные нулю. (2) Сканирование младших бит набора оставшихся номеров (младший бит для x - x & ~(x - 1)) и выберите один с битом младшего разряда. (3) Положите это в основу. (4) Обновите все остальные номера с помощью того же бита, установленного с помощью XORing с новым базовым элементом. Никакое оставшееся число не имеет этого бита или бит младшего разряда, поэтому мы заканчиваем после 64 итераций.

В конце, если есть 64 элемента, то подпространство - это все. В противном случае мы отправили меньше 64 итераций и немного пропустили: число с только этим битом не было натянуто.

К нулевому случаю: нуль является опцией тогда и только тогда, когда мы никогда не выбрасываем число (т.е. входные векторы независимы).


Пример над 4-разрядными номерами

Начните с 0110, 0011, 1001, 1010. Выберите 0011, потому что у него установлены бит. Основа теперь {0011}. Другими векторами являются {0110, 1010, 1010}; обратите внимание, что первый 1010 = 1001 XOR 0011.

Выберите 0110, потому что он имеет бит бит бит. Базис теперь {0011, 0110}. Другими векторами являются {1100, 1100}.

Выберите 1100. Базис теперь {0011, 0110, 1100}. Другие векторы - {0000}.

Бросьте 0000. Мы закончили. Мы пропустили бит высокого порядка, поэтому 1000 не находится в промежутке.

Ответ 2

Как отмечает рэп-музыка, вы можете думать о проблеме как о поиске базы в векторном пространстве. Тем не менее, нет необходимости фактически полностью его решать, просто найти, можно ли это сделать или нет, а если нет: укажите примерное значение (то есть двоичный вектор), который не может быть описан в терминах поставленного набора,

Это можно сделать в O (n ^ 2) в терминах размера входного набора. Это следует сравнить с <сильным > исключением Гаусса, которое является O (n ^ 3), http://en.wikipedia.org/wiki/Gaussian_elimination.

64 бит вообще не проблема. С примером кода python ниже 1000 бит с набором с 1000 случайными значениями от 0 до 2 ^ 1000-1 занимает около секунды.

Вместо того, чтобы выполнять удаление Гаусса, достаточно, чтобы выяснить, можем ли мы переписать матрицу всех битов треугольной формы, например: (для 4-разрядной версии:)

 original        triangular
 1110 14         1110 14
 1011 11          111  7
  111  7           11  3
   11  3            1  1
    1  1            0  0

Решение работает следующим образом: сначала все исходные значения с одним и тем же самым значимым битом помещаются вместе в список списков. Для нашего примера:

 [[14,11],[7],[3],[1],[]]

Последняя пустая запись означает, что в исходном списке не было нулей. Теперь возьмем значение из первой записи и заменим эту запись списком, содержащим только этот номер:

 [[14],[7],[3],[1],[]]

а затем сохраните xor сохраненного номера со всеми удаленными записями в нужном месте в векторе. Для нашего случая имеем 14 ^ 11 = 5, поэтому:

 [[14],[7,5],[3],[1],[]]

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

Теперь обработайте элемент 7,5 таким же образом. Держите 7, добавьте 7 ^ 5 = 2 в список:

 [[14],[7],[3,2],[1],[]]

Теперь 3,2 покидает [3] и добавляет 1:

 [[14],[7],[3],[1,1],[]]

И 1,1 уходит [1] и добавляет 0 к последней записи, допускающей значения без заданного бита:

 [[14],[7],[3],[1],[0]]

Если в конце вектор содержит по крайней мере одно число в каждом векторном элементе (как в нашем примере), база завершена и любое число подходит.

Здесь полный код:

# return leading bit index ir -1 for 0.
# example 1 -> 0
# example 9 -> 3
def leadbit(v):
    # there are other ways, yes...
    return len(bin(v))-3 if v else -1

def examinebits(baselist,nbitbuckets):
    # index 1 is least significant bit. 
    # index 0 represent the value 0    
    bitbuckets=[[] for x in range(nbitbuckets+1)]  
    for j in baselist:
        bitbuckets[leadbit(j)+1].append(j)   
    for i in reversed(range(len(bitbuckets))):
        if bitbuckets[i]:
            # leave just the first value of all in bucket i
            bitbuckets[i],newb=[bitbuckets[i][0]],bitbuckets[i][1:]
            # distribute the subleading values into their buckets
            for ni in newb: 
                q=bitbuckets[i][0]^ni
                lb=leadbit(q)+1
                if lb:
                    bitbuckets[lb].append(q)
                else:
                    bitbuckets[0]=[0]
        else:
            v=2**(i-1) if i else 0
            print "bit missing: %d. Impossible value: %s == %d"%(i-1,bin(v),v)
            return (bitbuckets,[i])    
    return (bitbuckets,[])

Пример использования: (8 бит)

import random
nbits=8
basesize=8
topval=int(2**nbits)
# random set of values to try:
basel=[random.randint(0,topval-1) for dummy in range(basesize)]
bl,ii=examinebits(basel,nbits)

bl теперь является треугольным списком значений, вплоть до того момента, когда это было невозможно (в этом случае). Пропущенный бит (если таковой имеется) находится в ii [0].

Для следующего испытанного набора значений: [242, 242, 199, 197, 177, 177, 133, 36] треугольная версия:

base value: 10110001 177
base value:  1110110 118
base value:   100100 36
base value:    10000 16
first missing bit: 3 val: 8
( the below values where not completely processed )
base value:       10 2
base value:        1 1
base value:        0 0

Вышеприведенный список был напечатан следующим образом:

for i in range(len(bl)):
    bb=bl[len(bl)-i-1]
    if ii and len(bl)-ii[0] == i:
        print "example missing bit:" ,(ii[0]-1), "val:", 2**(ii[0]-1)
        print "( the below values where not completely processed )"
    if len(bb):
        b=bb[0]
        print ("base value: %"+str(nbits)+"s") %(bin(b)[2:]), b