Суммарное хеширование коллекций строк - PullRequest
2 голосов
/ 03 мая 2011

существует ли алгоритм для Java, который позволит мне продолжать добавлять объекты String и удалять старые, чтобы, если бы я добавил String, а затем удалил его, целочисленный хэш был бы таким же?

Редактировать: строки в хэше уникальны.

Какой-то псевдокод:

h = hash
add(h, "hi!") == 51;
add(h, "hello again!") == 532;
rem(h, "hello again!") == 51;

Я знаю, что вы можете сделать это с помощью коллекций Java, но реализации по умолчанию должны продолжать проходить всю коллекцию для сбора хеш-кодов. Это действительно неэффективно для больших коллекций. Я не против использования внешней библиотеки, если она существует.

Заранее спасибо,
Chris

1 Ответ

2 голосов
/ 03 мая 2011

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

Рассмотрим следующий код:

interface Accumulator<T, U>
{
    public void add(T t);
    public void subtract(T t);
    public U get();
}

class SumHasher implements Accumulator<String,Integer>
{
    @Override private int accumulator = 0;
    @Override public void add(String t) { accumulator += t.hashCode(); }
    @Override public void subtract(String t) { accumulator -= t.hashCode(); }
    @Override public Integer get() { return accumulator; }
}

class XorHasher implements Accumulator<String,Integer>
{
    @Override private int accumulator = 0;
    @Override public void add(String t) { accumulator ^= t.hashCode(); }
    @Override public void subtract(String t) { accumulator ^= t.hashCode(); }
    @Override public Integer get() { return accumulator; }
}

Их объединяет то, что сложение и XOR являются операциями, которые ассоциативны и имеют инверсии.Вы можете выполнять их в любом порядке и отменять их в любом порядке, так что если вы add() для каждого элемента в Set<T>, а затем subtract() для каждого элемента в наборе (не обязательно в том же порядке), выгарантированно получит 0.

Конечно, есть другие операции, которые удовлетворяют этому свойству, но я не уверен, что они есть.(Умножение не сработает, если вы не гарантируете, что ни один из накопленных предметов не имеет значения 0. В этом ответе использовались f (x, h) = ((x ^ h) + h) ^ h и g (x, h) = ((x ^ h) - h) ^ h как обратные, но эти функции не ассоциативны: накапливающиеся элементы в разных порядках дают разные результаты.

Редактировать : Я думал о еще одном простом: побитовая перестановка (для которой побитовое вращение является особым случаем) на основе входного значения.В Java вы можете реализовать побитовое вращение, используя (x << k) | (x >>> (32-k)), где xцелое число, а k - это целое число от 0 до 31 (например, взять любые произвольные 5 бит из другого числа). >>> - это , а не опечатка: вам нужно использовать его, потому что обычный >> делаетsign-extension. Упс, это работает, только если элементы в наборе удалены в обратном порядке.

Редактировать 2 : Наконец, вы можете реализовать этот подход в более общем виде следующим образом:

abstract class AbstractHashCodeAccumulator<T> implements Accumulator<T, Integer>
{
    private int accumulator = 0;
    abstract protected int combine(int a, int h);
    abstract protected int uncombine(int a, int h);
    @Override public void add(T t) { accumulator = combine(accumulator, t.hashCode());
    @Override public void subtract(T t) { accumulator = uncombine(accumulator, t.hashCode());
    @Override public Integer get() { return accumulator; }
}

class SumHasher extends AbstractHashCodeAccumulator<String>
{
    @Override protected int combine(int a, int h)   { return a+h; }
    @Override protected int uncombine(int a, int h) { return a-h; }
}

class XorHasher extends AbstractHashCodeAccumulator<String>
{
    @Override protected int combine(int a, int h)   { return a^h; }
    @Override protected int uncombine(int a, int h) { return a^h; }
}

Проблема этого подхода заключается в том, чтов некотором смысле он не похож на хэш, а именно требует упорядоченности, тогда как хеширование обычно требует беспорядка / энтропии / необратимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...