Вам понадобится сортированный набор, ограниченный определенным количеством предметов. Возможно, некоторые библиотеки сторонних коллекций предоставляют его, в противном случае вы можете сделать это как-то так: Limited SortedSet . Важно то, что метод add
такого отсортированного набора должен возвращать false
, если коллекция заполнена, а добавленный элемент выходит за пределы, а true
в противном случае.
Теперь сделайте цикл по CSV-файлам. Внутри тела цикла читайте записи из файла CSV и добавляйте их в набор до тех пор, пока add
не вернет false (это будет означать, что коллекция заполнена и никакие новые записи из текущего CSV не будут больше, чем текущие - время перейти к следующему файлу).
Когда цикл будет завершен, ответом будет результирующий набор.
Обычно простая хеш-функция работает, беря "составные части" ввода (символы в случае строки), умножая их на степени некоторой константы и складывая их вместе в некотором целочисленном типе. Так, например, типичный (хотя и не очень хороший) хэш строки может быть таким:
(first char) + k * (second char) + k^2 * (third char) + ...
Тогда, если будет введена группа строк с одинаковым первым символом, тогда все результаты будут одинаковыми по модулю k, по крайней мере пока целочисленный тип не переполнится.
[Например, строковый hashCode Java устрашающе похож на этот - он выполняет обратный порядок символов с k = 31. Таким образом, вы получаете поразительные отношения по модулю 31 между строками, которые заканчиваются одинаково, и поразительные отношения по модулю 2 ^ 32 между строками, которые одинаковы, за исключением около конца. Это не сильно портит поведение хеш-таблицы.]
Хеш-таблица работает, принимая модуль хеш-функции по количеству сегментов.
В хеш-таблице важно не создавать коллизий в вероятных случаях, поскольку коллизии уменьшают эффективность хеш-таблицы.
Теперь предположим, что кто-то помещает в хеш-таблицу целую группу значений, которые имеют некоторые отношения между элементами, например, все они имеют один и тот же первый символ. Я бы сказал, что это довольно предсказуемая модель использования, поэтому мы не Я не хочу, чтобы это приводило к слишком большому количеству коллизий.
Оказывается, что «из-за природы математики», если константа, используемая в хэше, и количество сегментов взаимно просты , то коллизии в некоторых общих случаях сведены к минимуму. Если они не являются взаимно простыми , то между входными данными существуют довольно простые отношения, для которых коллизии не минимизированы. Все хэши получаются равными по модулю общего множителя, что означает, что все они попадут в 1 / n-е ведра, которые имеют это значение по модулю общего множителя. Вы получаете в n раз больше столкновений, где n - общий множитель. Поскольку n не менее 2, я бы сказал, что для довольно простого варианта использования недопустимо генерировать как минимум вдвое больше столкновений, чем обычно. Если какой-то пользователь собирается разбить наш дистрибутив на сегменты, мы хотим, чтобы это было странной случайностью, а не каким-то простым предсказуемым использованием.
Очевидно, что реализации хеш-таблиц не имеют никакого контроля над помещаемыми в них элементами. Они не могут предотвратить их родство. Итак, что нужно сделать, это убедиться, что постоянный счетчик и счетчик ведра взаимно просты. Таким образом, вы не полагаетесь только на «последний» компонент для определения модуля ковша по отношению к некоторому небольшому общему коэффициенту. Насколько я знаю, они не должны быть простыми, чтобы достичь этого, просто взаимно просты.
Но если хеш-функция и хеш-таблица написаны независимо, то хеш-таблица не знает, как работает хеш-функция. Это может быть константа с небольшими коэффициентами. Если вам повезет, он может работать совершенно иначе и быть нелинейным. Если хеш достаточно хорош, то любое количество ведер вполне подойдет. Но параноидальная хеш-таблица не может предполагать хорошую хеш-функцию, поэтому следует использовать простое количество сегментов. Точно так же параноидальная хеш-функция должна использовать большую простую константу, чтобы уменьшить вероятность того, что кто-то использует несколько сегментов, которые имеют общий множитель с константой.
На практике я думаю, что довольно нормально использовать степень 2 как количество ведер. Это удобно и избавляет от необходимости искать или предварительно выбирать простое число нужной величины. Таким образом, вы полагаетесь на хеш-функцию, чтобы не использовать четные множители, что обычно является безопасным предположением. Но вы все равно можете иногда получать плохое хеш-поведение, основанное на хэш-функциях, подобных приведенной выше, и подсчет первичных сегментов может помочь в дальнейшем.
Говоря о принципе, "все должно быть простым" насколько я знаю, достаточное, но не необходимое условие для хорошего распределения по хэш-таблицам. Это позволяет каждому взаимодействовать, не предполагая, что другие следовали тому же правилу.
[Edit: есть еще одна, более специализированная причина использовать простое количество сегментов, то есть если вы обрабатываете столкновения с помощью линейного зондирования. Затем вы вычисляете шаг из хэш-кода, и если этот шаг оказывается фактором количества ведра, вы можете выполнить только (bucket_count / stride) зонды, прежде чем вернуться к тому месту, где вы начали. Разумеется, вам больше всего нужно избегать stride = 0, который должен иметь специальный регистр, но чтобы избежать также bucket_count / stride в специальном регистре, равного небольшому целому числу, вы можете просто сделать bucket_count простым и не заботиться о том, что шаг, если он не равен 0.]
"Природа математики" относительно главных модулей питания - то, что они - один стандартный блок конечное поле . Другие два стандартных блока являются дополнением и операцией умножения. Специальное свойство главных модулей - то, что они формируют конечное поле с "регулярными" операциями дополнения и умножения, просто взятыми к модулю. Это значит каждое умножение карты для различного целого числа по модулю начало, каждое дополнение - также.
Главные модули выгодны потому что:
, у Них однако есть большая оборотная сторона, они требуют целочисленного деления, которое берет многих (~ 15-40) циклы, даже на современном ЦП. Приблизительно с половиной вычисления можно удостовериться, что хеш перепутан очень хорошо. Два умножения и xorshift операции смешаются лучше, чем главный модуль. Затем мы можем использовать любой размер хеш-таблицы и хешировать сокращение, является самым быстрым, давая 7 операций всего для питания 2 размеров таблицы и приблизительно 9 операций для произвольных размеров.
я недавно посмотрел на многие из , самые быстрые реализации хеш-таблицы и большинство из них не используют главные модули.
http: // computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/
Довольно четкое объяснение, также с изображениями.
Изменить: Вкратце, простые числа используются, потому что у вас больше шансов получить уникальное значение, если умножить значения на выбранное простое число и сложить их все. Например, учитывая строку, умножение каждого буквенного значения на простое число и последующее их сложение даст вам его хэш-значение.
Лучше спросить, почему именно число 31?
Для хеш-функции важно не только минимизировать коллизии в целом, но и сделать невозможным сохранение одного и того же хеш-кода при изменении нескольких байтов.
Допустим, у вас есть уравнение:
(x + y * z)% key = x
с 0
0
Пример, где ключ не является основным примером: x = 1, z = 2 и ключ = 8 Поскольку key / z = 4 по-прежнему является натуральным числом, 4 становится решением нашего уравнения, и в этом случае (n / 2) * y = key верно для каждого n в N. Количество решений для уравнения практически удвоилось. потому что 8 не является простым числом.
Если наш злоумышленник уже знает, что 8 является возможным решением уравнения, он может изменить файл с производящего 8 на 4 и все равно получить тот же хэш.
Чтобы представить альтернативную точку зрения, есть этот сайт:
http://www.codexon.com/posts/hash-functions-the-modulo-prime-myth
Который утверждает, что вам следует использовать как можно большее количество сегментов, а не округлять до простого числа сегментов. Это кажется разумной возможностью. Интуитивно я, конечно, вижу, чем было бы лучше большее количество корзин, но я не могу привести это математическое обоснование.
Простые числа - это уникальные числа. Они есть уникальный в том, что произведение простого с любым другим номером имеет лучшее шанс быть уникальным (не таким уникальным как само прайм конечно) из-за тот факт, что штрихи используются для сочини это. Это свойство используется в функции хеширования.
Учитывая строку «Samuel», вы можете сгенерировать уникальный хеш умножением каждая из составляющих цифр или буквы с простым числом и добавлением их вверх. Вот почему используются простые числа.
Однако использование простых чисел - старая техника. Ключ здесь, чтобы понять что до тех пор, пока вы можете создать достаточно уникальный ключ, который можно переместить к другим методам хеширования. Идти здесь для получения дополнительной информации по этой теме о http://www.azillionmonkeys.com/qed/hash.html
http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers /
Это зависит от выбора хэш-функции.
Многие хеш-функции функции комбинируют различные элементы в данных, умножая их на некоторые множители по модулю степени двойки, соответствующие размеру слова машины (этот модуль освобождается, просто позволяя вычислению переполняться).
Вы не делаете ' Мне нужен какой-либо общий множитель между множителем для элемента данных и размером хеш-таблицы, потому что тогда может случиться так, что изменение элемента данных не приведет к распределению данных по всей таблице. Если вы выберете простое число для размера таблицы, такой общий множитель маловероятен.
С другой стороны, эти множители обычно состоят из нечетных простых чисел, поэтому вы также должны быть в безопасности, используя степени двойки для вашего хэша. table (например, Eclipse использует 31 при создании метода Java hashCode ()).
Первое, что вы делаете при вставке / извлечении из хэш-таблицы, - это вычисление хэш-кода для данного ключа, а затем поиск правильного сегмента, обрезая хэш-код до размера хэш-таблицы, выполнив hashCode % table_length. Вот 2 «утверждения», которые вы, скорее всего, где-то читали
И вот доказательство.
Если предположим, что ваша функция hashCode выдает следующие хэш-коды, среди прочих {x, 2x, 3x, 4x, 5x, 6x ...}, то все они будут сгруппированы всего в m сегментов, где m = длина_таблицы / GreatestCommonFactor (длина_таблицы, x). (Это тривиально проверить / вывести). Теперь вы можете сделать одно из следующих действий, чтобы избежать кластеризации
. Убедитесь, что вы не генерируете слишком много хэш-кодов, которые кратны другому хэш-коду, как в {x, 2x, 3x, 4x, 5x, 6x ...}. Но это может быть сложно, если ваша хэш-таблица должна иметь миллионы записей. Или просто сделайте m равным table_length, сделав GreatestCommonFactor (table_length, x) равным 1, то есть сделав table_length равным x. И если x может быть практически любым числом, убедитесь, что table_length - простое число.
From - http://srinvis.blogspot.com/2006/07/hash-table-lengths-and-prime -numbers.html