Я только что встретил сценарий в своем проекте, где мне нужно сравнить разные древовидные объекты для равенства с уже известными экземплярами и считал, что какой-то алгоритм хэширования, который работает на произвольном дереве, будет очень полезен.
Возьмем, например, следующее дерево:
O / \ / \ O O /|\ | / | \ | O O O O / \ / \ O O
Где каждый O
представляет node дерева, является произвольным объектом, имеет связанную хэш-функцию. Таким образом, проблема сводится к: учитывая хэш-код узлов древовидной структуры и известную структуру, что является достойным алгоритмом для вычисления (относительно) коллизионного хеш-кода для всего дерева?
Несколько замечаний о свойствах хэш-функции:
- Хэш-функция должна зависеть от хеш-кода каждого node внутри дерева, а также от его позиции.
- Переупорядочение дочерних элементов node должно отчетливо изменить полученный хеш-код.
- Отражение любой части дерева должно отчетливо изменять полученный хеш-код
Если это помогает, я использую С# 4.0 здесь, в моем проекте, хотя я в первую очередь ищу теоретическое решение, поэтому псевдокод, описание или код на другом императивном языке будет в порядке.
UPDATE
Ну, вот мое собственное предлагаемое решение. Несколько из этих ответов были очень полезны.
Каждый node (поддерево/лист node) имеет следующую хеш-функцию:
public override int GetHashCode()
{
int hashCode = unchecked((this.Symbol.GetHashCode() * 31 +
this.Value.GetHashCode()));
for (int i = 0; i < this.Children.Count; i++)
hashCode = unchecked(hashCode * 31 + this.Children[i].GetHashCode());
return hashCode;
}
Хорошая вещь об этом методе, как я вижу, заключается в том, что хэш-коды могут быть кэшированы и пересчитываться только при изменении node или одного из его потомков. (Спасибо Ватине и Джейсону Орендорфу за это).
В любом случае, я был бы признателен, если бы люди могли прокомментировать мое предлагаемое решение здесь - если оно хорошо справится с работой, то здорово, иначе любые возможные улучшения будут приветствоваться.