Какую хеш-функцию использовать для блум-фильтра с 128-битными ключами?

https://github.com/joeyrobert/bloomfilter использует класс Random для хэш-функции, которая является убийца производительности.
Я пытаюсь ввести класс с byte [] s вместо общего аргумента (T) и избавиться от

    private int Hash(T item) {
        return item.GetHashCode();
    }

Я знаю, что есть огромное преимущество в производительности, но я понятия не имею, как заменить _random.Next(_bitSize) здесь:

#region Public Methods
/// <summary>
/// Adds an item to the bloom filter.
/// </summary>
/// <param name="item">Item to be added</param>
public void Add(T item)
{
    _random = new Random(Hash(item));

    for (int i = 0; i < _numberOfHashes; i++)
        _bitArray[_random.Next(_bitSize)] = true;
}

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

Я знаю, что есть много других проблем с кодом, которые могут сделать его быстрее / безопаснее. Я исправил их (в основном) и просто застрял на последнем, прежде чем выдвигать свои изменения.
Любая помощь действительно приветствуется.


person Behrooz    schedule 01.09.2013    source источник
comment
Я прочитал это: stackoverflow .com / questions / 2753467 / Что, как я легко могу предположить, даже медленнее, чем `` Случайное ''.   -  person Behrooz    schedule 01.09.2013
comment
Что забавно, так это то, что использование random.Next в некоторой степени противоречит цели наличия _numberOfHashes в первую очередь ... Потому что хеши перестают быть действительно независимыми. Вы можете просто использовать несколько легких хешей (например, Fletcher, xxhash, Murmur). Или, поскольку ключ всего 128 бит ... возможно, сохранить весь ключ. (На случай, если я не совсем понял: я бы подумал о том, чтобы полностью потерять случайный шаг и напрямую использовать комбинацию независимых хешей)   -  person sehe    schedule 01.09.2013
comment
@sehe: Спасибо за комментарии. Дело в том, что в моем случае входные значения являются предварительно вычисленными значениями sha1 и полностью уникальны. Я просто не знаю, как преобразовать массив произвольной длины в целые числа _numberOfHashes и не потерять скорость, точность и мои разум.   -  person Behrooz    schedule 01.09.2013


Ответы (2)


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

Фильтр Блума - это, по сути, битовый вектор, в котором вы устанавливаете биты. Если вы хотите выяснить, существует ли элемент, фильтр цветения выдаст вам истину, если элемент возможно существует, и ложь, если элемент наверняка не существует .

(Я делаю это в простом текстовом редакторе, поэтому в коде могут быть ошибки)

Я предполагаю, что ваше хеш-пространство может использовать 32-битные целочисленные вычисления; если у вас очень большая таблица цветения, вы, вероятно, захотите использовать 64-битное целое число.

Самая простая (и, вероятно, самая быстрая) реализация фильтра Блума:

byte[] bloomFilter = new byte[MyBloomFilterSize];

foreach (var item in myItems) 
{
    int hash = Hash(item) & 0x7FFFFFFF;
    int bit = 1 << (hash & 7); // you have 8 bits
    int index = (hash >> 3) % MyBloomFilterSize;
    bloomFilter[hash % MyBloomFilterSize] |= bit;
}

Вы можете поэкспериментировать с изменением byte[] на uint[] или ulong[]; Я не уверен, что это имеет значение.

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

public bool PossiblyExists(MyItem item)
{
    int hash = Hash(item) & 0x7FFFFFFF;

    int bit = 1 << (hash & 7); // you have 8 bits
    int index = (hash >> 3) % MyBloomFilterSize;
    return (bloomFilter[hash % MyBloomFilterSize] & bit) != 0;
}

Единственное, что здесь остается, - это скорость, с которой вы можете вычислить хеш. Если вы используете целое число, я бы просто умножил его на большое простое число; если вы используете байт [] фиксированной длины SHA256 (что вы, кажется, делаете), вам нужно сделать его целым (или длинным).

Я использую небольшой трюк с Buffer.BlockCopy для преобразования типов. В целях безопасности я предпочитаю использовать несколько байтов из данных, но поскольку SHA256 уже должен быть случайным, простой BitConverter.ToInt32(data, [0..28]) также должен помочь.

public int CalculateHash(byte[] data) 
{
    // Data = >128 bits = >16 bytes -- which is the same as >4 integers

    int[] tmp = new int[4];
    Buffer.BlockCopy(data, 0, tmp, 0, data.Length);
    return tmp[0] ^ tmp[1] ^ tmp[2] ^ tmp[3];
}

Это должно сработать.

person atlaste    schedule 30.04.2014
comment
Да, я делал это совершенно неправильно с этой случайной штукой. За ответ, проект все еще находится в разработке, и вы только что дали мне новую идею, чтобы что-то изменить. - person Behrooz; 31.07.2014
comment
Однако он не давал мне случайных данных, я просто использовал их как сложную хеш-функцию (тогда я читал внутренний код). - person Behrooz; 31.07.2014
comment
Да, я заметил семя, просто это не имело большого смысла. Во всяком случае, HTH. - person atlaste; 01.08.2014

Эффективная реализация могла бы быть, например, следующей. Если у вас есть хеш-функция, которая возвращает 64 бита, лучше использовать ее вместо murmur3_64. Предупреждение: я не тестировал.

void Add(string item) {
    ulong hash = murmur3_64((ulong) item.GetHashCode());
    uint a = (uint) (hash >> 32);
    uint b = (uint) hash;
    for (int i = 0; i < k; i++) {
        _bitArray[reduce(a, _bitSize)] = true;
        // "Less Hashing, Same Performance: Building a Better Bloom Filter"
        a += b;
    }
}

ulong murmur3_64(ulong x) {
    x = (x ^ (x >> 33)) * 0xff51afd7ed558ccdL;
    x = (x ^ (x >> 23)) * 0xc4ceb9fe1a85ec53L;
    x = x ^ (x >> 33);
    return x;
}

uint reduce(uint hash, uint n) {
    // http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
    return (hash * n) >> 32;
}
person Thomas Mueller    schedule 14.11.2018