Ха sh () пользовательский объект не "случайный" - PullRequest
0 голосов
/ 29 января 2020

Вкратце:

Я заметил, что применение функции hash() к пользовательскому объекту всегда возвращает значение, кратное 4, плюс 1. Есть ли причина для этого и способ остановить это?

Полагаю, если это всегда случай, я мог бы вычесть 1 и разделить на 4, чтобы получить "плотное" распределение значений hash, но я Я не уверен, если это действительно так.

Подробно / с примером:

В Python, у меня есть куча пользовательских объектов и (короткий) список значений. Для каждого объекта я хочу выбрать значение в списке, чтобы

  • Все значения были одинаково вероятны; и
  • Я получаю одно и то же значение для каждого объекта, если я повторю процесс в более поздний момент времени.

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

Моя идея заключалась в том, чтобы использовать для этого значение hash() объектов (для них не реализован определяемый пользователем метод __hash__) таким образом:

value = list_of_values[hash(object) % len(list_of_values)]

Однако я заметил, что некоторые значения из списка никогда не выбирались, и при проверке значения hash всегда кратны 4, плюс 1 - что является проблемой, если длина list_of_values равна кратное 4.

Мне бы помогли либо

  • способ заставить функцию hash возвращать значение, которое не кратно любому значению; или
  • подтверждение того, что оно всегда , кратное 4, независимо от класса, операционной системы (32 или 64 бита, Windows или Linux или Ma c), и др * * .; тысяча сорок-девять или
  • другой способ создания одинакового полуслучайного числа из объекта.

Ответы [ 2 ]

0 голосов
/ 29 января 2020

Если вам нужны повторяющиеся случайные последовательности, вызовите random.seed, используя известное значение.

>>> random.seed(3)
>>> [random.choice("abc") for x in range(10)]
['a', 'c', 'c', 'a', 'b', 'c', 'b', 'c', 'c', 'a']
>>> random.seed(3)
>>> [random.choice("abc") for x in range(10)]
['a', 'c', 'c', 'a', 'b', 'c', 'b', 'c', 'c', 'a']

Тот факт, что оба списка идентичны, не является совпадением.


На основе на ваш комментарий, я думаю, вы можете использовать defaultdict.

>>> from collections import defaultdict
>>> import random
>>> values = [1,2,3,4]
>>> d = defaultdict(lambda: random.choice(values))
>>> [d['foo'] for _ in range(20)]
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]

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

0 голосов
/ 29 января 2020

Забавно, как написание вопроса фокусирует разум. При наборе моего вопроса мне пришел ответ: я сначала беру модуль по значению hash с простым числом, а затем по модулю с длиной списка:

value = list_of_values[(hash(object) % prime) % len(list_of_values)]

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

(Чтобы понять, почему последнее важно: предположим, что list_of_values имеет длину 100, а значение prime равно 113. В этом случае первое по модулю (hash(object) % prime) дает значение от 0 до 112, что в равной степени вероятно. Если я тогда возьму по модулю с 100, числа 0..99 остаются прежними, а те 100..112 уменьшаются до 0..12. Это означает, что первые 13 элементов имеют двойную вероятность выбора по сравнению с другими элементами.)

...