Обычные функции хеша предназначены для случайного разброса различных значений в некоторых диапазонах. Одноразрядное различие в аргументах может привести к тому, что в результатах будут десятки бит. По этой причине обычные хэш-функции не подходят для ситуации, описанной в вопросе.
Альтернативой является создание массива P с записями, которые индексируются в таблицу B пределов бинов.Учитывая некоторое значение x, мы находим бит j
, принадлежащий (или иногда близлежащему ящику) через j = P[⌊x·r⌋]
, где r
- это отношение, которое зависит от размера P и максимального значения в B. Эффективность этого подхода зависит от значений в B и размеру P.
Поведение функций, таких как P[⌊x·r⌋]
, можно увидеть с помощью кода python, показанного ниже. (Метод примерно одинаковый на любом языке программирования. Однако ниже приведены советы для Python-to.) Предположим, что код хранится в файле histobins.py и загружается в интерпретатор ipython с помощью команды import histobins as hb
. Затем команда, как hb.betterparts(27, 99, 9, 80,155)
производит выходной сигнал, как
At 80 parts, steps = 20 = 7+13
At 81 parts, steps = 16 = 7+9
At 86 parts, steps = 14 = 6+8
At 97 parts, steps = 13 = 12+1
At 108 parts, steps = 12 = 3+9
At 109 parts, steps = 12 = 8+4
At 118 parts, steps = 12 = 6+6
At 119 parts, steps = 10 = 7+3
At 122 parts, steps = 10 = 3+7
At 141 parts, steps = 10 = 5+5
At 142 parts, steps = 10 = 4+6
At 143 parts, steps = 9 = 7+2
эти параметры, чтобы установить betterparts nbins=27, topsize=99, seed=9, plo=80, phi=155
, который создает тестовый набор из 27 бункеров для значений от 0 до 99, со случайной затравки 9, и размера P от 80 до 155- 1. Число «шагов» - это количество раз, когда два цикла в тестовых частях() работают во время теста с 10 * nbins значениями от 0 до topsize. Например: «В 143 частях, этапы = 9 = 7 + 2» означает, что, когда размер P равен 143, из 270 испытаний, 261 раз P[⌊x·r⌋]
произвел правильный индекс сразу; В 7 раз индекс должен был быть уменьшен, и в два раза он должен был быть увеличен.
Общая идея метода заключается в том, чтобы избавиться от пространства на время. Другим компромиссом является время подготовки и время работы. Если вы собираетесь делать миллиарды поисков, стоит потратить несколько тысяч проб, чтобы найти хорошее значение | P |, размер P. Если вы собираетесь делать всего несколько миллионов поисков, это может быть лучше просто выбрать некоторое большое значение | P | и работать с ним, или, возможно, просто запускать более качественные компоненты в узком диапазоне. Вместо того, чтобы делать 75 тестов, как указано выше, если мы начнем с более больших | P | меньшее количество тестов может дать достаточно хороший результат. Например, 10 тестов через «hb.betterparts (27, 99, 9, 190200)» производит
At 190 parts, steps = 11 = 5+6
At 191 parts, steps = 5 = 3+2
At 196 parts, steps = 5 = 4+1
Пока P вписывается в некоторый уровень кэша (наряду с другими соответствующими данными), что делает | P | больше ускорит доступ. Итак, делая | P | насколько это практично, это хорошая идея. Как | P | становится больше, разница в производительности между одним значением | P | и следующий становится все меньше и меньше. Ограничивающие факторы на скорости затем включают время для умножения и время для установки в цикле. Одним из подходов для более быстрого умножения может быть выбор мощности 2 в качестве множителя; вычислить | P | соответствовать; затем использовать сдвиги или добавлять к экспонентам вместо умножения. Один из подходов к сокращению времени при создании циклов заключается в том, чтобы переместить оператор if bins[bin] <= x < bins[bin+1]:
(или его эквивалент C, см. Ниже) перед инструкциями while и делать только в том случае, если инструкция if
терпит неудачу.
Код Python показан ниже.Обратите внимание, в переводе с Python на C,
• # начинает комментарий
• def
начинает функцию
• заявление как ntest, right, wrong, x = 10*nbins, 0, 0, 0
присваивает значения соответствующих идентификаторов
• заявление как return (ntest, right, wrong, stepdown, stepup)
возвращает кортеж из 5 значений, которые вызывающий абонент может назначить кортеж или соответствующие идентификаторы
• сферы действия def, while,
или if
заканчивается с линией не отступом дальше, чем def, while,
или if
• bins = [0]
инициализирует список (выдвижная индексируемая массив) со значением 0
в качестве начального входа
• bins.append(t)
добавляет значение Т в конце списка bins
• for i,j in enumerate(p):
запускает цикл по элементам Iterable р (в данном случае, р представляет собой список), в результате чего индекс i
и соответствующие записи j == p[i]
доступные внутри цикл
• range(nparts)
обозначает список значений 0, 1, ... nparts-1
• range(plo, phi)
стендов для списка значений пнули, PLO + 1, ... фи-1
• if bins[bin] <= x < bins[bin+1]
средства if ((bins[bin] <= x) && (x < bins[bin+1]))
• int(round(x*float(nparts)/topsize)))
фактически завершает x·r
вместо вычислений ⌊x·r⌋
как рекламируется выше
def makebins(nbins, topsize):
bins, t = [0], 0
for i in range(nbins):
t += random.random()
bins.append(t)
for i in range(nbins+1):
bins[i] *= topsize/t
bins.append(topsize+1)
return bins
#________________________________________________________________
def showbins(bins):
print ''.join('{:6.2f} '.format(x) for x in bins)
def showparts(nbins, bins, topsize, nparts, p):
ratio = float(topsize)/nparts
for i,j in enumerate(p):
print '{:3d}. {:3d} {:6.2f} {:7.2f} '.format(i, j, bins[j], i*ratio)
print 'nbins: {} topsize: {} nparts: {} ratio: {}'.format(nbins, topsize, nparts, ratio)
print 'p = ', p
print 'bins = ',
showbins(bins)
#________________________________________________________________
def testparts(nbins, topsize, nparts, seed):
# Make bins and make lookup table p
import random
if seed > 0: random.seed(seed)
bins = makebins(nbins,topsize)
ratio, j, p = float(topsize)/nparts, 0, range(nparts)
for i in range(nparts):
while j<nbins and i*ratio >= bins[j+1]:
j += 1
p[i] = j
p.append(j)
#showparts(nbins, bins, topsize, nparts, p)
# Count # of hits and steps with avg. of 10 items per bin
ntest, right, wrong, x = 10*nbins, 0, 0, 0
delta, stepdown, stepup = topsize/float(ntest), 0, 0
for i in range(ntest):
bin = p[min(nparts, max(0, int(round(x*float(nparts)/topsize))))]
while bin < nbins and x >= bins[bin+1]:
bin += 1; stepup += 1
while bin > 0 and x < bins[bin]:
bin -= 1; stepdown += 1
if bins[bin] <= x < bins[bin+1]: # Test if bin is correct
right += 1
else:
wrong += 1
print 'Wrong bin {} {:7.3f} at x={:7.3f} Too {}'.format(bin, bins[bin], x, 'high' if bins[bin] > x else 'low')
x += delta
return (ntest, right, wrong, stepdown, stepup)
#________________________________________________________________
def betterparts(nbins, topsize, seed, plo, phi):
beststep = 1e9
for parts in range(plo, phi):
ntest, right, wrong, stepdown, stepup = testparts(nbins, topsize, parts, seed)
if wrong: print 'Error with ', parts, ' parts'
steps = stepdown + stepup
if steps <= beststep:
beststep = steps
print 'At {:3d} parts, steps = {:d} = {:d}+{:d}'.format(parts, steps, stepdown, stepup)
#________________________________________________________________
Теоретически невозможно (в общем случае). Если это возможно, то поиск номера в отсортированном списке становится O (1). – ElKamina
да, я думаю, что вы правы, вещь с хэш-файлом fn - просто вы хотите вычислить значение из ключа и надеяться на его разумную уникальность, я прошу хэш fn отобразить конкретное значение бина - это будет нужно выполнить смехотворно сложный хэш fn. поэтому для подведения итогов ответа нет, вы не можете использовать хэш fn для этой цели – bph
@ElKamina В общем, вы можете делать больше цифр, чем просто сравнивать их. –