Подтвердить что ты не робот

Что такое 4/16 в хешах?

if (%hash){
     print "That was a true value!\n";
}

Это будет верно, если (и только если) хэш имеет хотя бы один   ключ-значение   пара.

     

Фактический результат - внутренняя строка отладки, полезная для   люди   которые поддерживают Perl. Он выглядит примерно так: "4/16" , но значение   гарантируется, когда хеш не пуст, а false, когда   он пуст. --Ллама книга

Что это за 4/16? Может ли кто-нибудь показать мне небольшую программу, откуда я вижу, что результат 4/16?

4b9b3361

Ответ 1

Из perldoc perldata:

Если вы оцениваете хэш в скалярном контексте, он возвращает false, если хэш пусто. Если есть пары ключ/значение, он возвращает true; Больше точно, возвращаемое значение представляет собой строку, состоящую из числа использованные ковши и количество выделенных ведер, разделенных слэш. Это очень полезно, только чтобы узнать, является ли Perl внутренний алгоритм хэширования плохо работает в вашем наборе данных. Для Например, вы вставляете 10 000 вещей в хэш, но оцениваете% HASH в скалярный контекст показывает "1/16", что означает только один из шестнадцати ковши были затронуты и предположительно содержат все 10 000 ваших элементы.

поэтому 4/16 будет подсчитывать используемые ведра/выделенные столбцы, и что-то вроде следующего будет отображать это значение:

%hash = (1, 2);
print scalar(%hash); #prints 1/8 here

Ответ 2

Хеш - это массив связанных списков. Функция хэширования преобразует ключ в число, которое используется как индекс элемента массива ( "ведро" ), в который будет храниться значение. Более одного ключа могут хешировать к одному и тому же индексу ( "столкновение" ), ситуация, с которой связаны связанные списки.

Знаменатель дроби - это общее количество ведер.

Числитель дроби - это количество ведер, имеющих один или несколько элементов.

Для хэшей с одинаковым количеством элементов, чем больше число, тем лучше. Тот, который возвращает 6/8, имеет меньше коллизий, чем тот, который возвращает 4/8.

Ответ 3

Это слегка измененная версия письма, отправленного мне в список рассылки Perl Beginners, отвечая на этот же вопрос.

Говоря

my $hash_info = %hash;

Вы получите либо 0 (если хэш пуст), либо отношение используемого к общее количество ковшей. Эта информация почти, но не полностью, бесполезный для вас. Чтобы понять, что это значит, вы должны сначала понять, как работает хеширование.

Позволяет реализовать хеш с использованием Perl 5. Прежде всего нам нужно хеширование. Хеширующие функции превращают строки, надеюсь, уникальные номера. Примерами реальных сильных хэширующих функций являются MD5 или SHA1, но они, как правило, слишком медленны для общего использования, поэтому люди склонны использовать более слабые (т.е. те, которые производят менее уникальный выход) функции для хэш-таблиц. Perl 5 использует Боба Дженкинса [один на время] алгоритм, который имеет хороший компромисс уникальности к скорости. Для нашего Например, я буду использовать очень слабую хэширующую функцию:

#!/usr/bin/perl

use strict;
use warnings;

sub weak_hash {
       my $key  = shift;
       my $hash = 1;
       #multiply every character in the string ASCII/Unicode value together
       for my $character (split //, $key) {
               $hash *= ord $character;
       }
       return $hash;
}

for my $string (qw/cat dog hat/) {
       print "$string hashes to ", weak_hash($string), "\n";
}

Поскольку функции хэширования имеют тенденцию возвращать числа, которые находятся в диапазоне больше, чем мы хотим, вы обычно используете modulo, чтобы уменьшить диапазон чисел, которые он дает обратно:

#!/usr/bin/perl

use strict;
use warnings;

sub weak_hash {
       my $key  = shift;
       my $hash = 1;
       #multiply every character in the string ASCII/Unicode value together
       for my $character (split //, $key) {
               $hash *= ord $character;
       }
       return $hash;
}

for my $string (qw/cat dog hat/) {
       # the % operator is constraining the number
       # weak_hash returns to 0 - 10
       print "$string hashes to ", weak_hash($string) % 11, "\n";
}

Теперь, когда у нас есть хеширующая функция, нам нужно где-то сохранить ключ и значение. Это называется хэш-таблицей. Хэш-таблица часто является массив, элементы которого называются ковшиками (это ковши, которые это соотношение). Ведро будет удерживать все ключи/значение пары, которые имеют хеш, к одному и тому же числу:

#!/usr/bin/perl

use strict;
use warnings;

sub weak_hash {
       my $key  = shift;
       my $hash = 1;
       for my $character (split //, $key) {
               $hash *= ord $character;
       }
       return $hash;
}

sub create {
       my ($size) = @_;

       my @hash_table;

       #set the size of the array
       $#hash_table = $size - 1;

       return \@hash_table;
}


sub store {
       my ($hash_table, $key, $value) = @_;

       #create an index into $hash_table
       #constrain it to the size of the hash_table
       my $hash_table_size = @$hash_table;
       my $index           = weak_hash($key) % $hash_table_size;

       #push the key/value pair onto the bucket at the index
       push @{$hash_table->[$index]}, {
               key   => $key,
               value => $value
       };

       return $value;
}

sub retrieve {
       my ($hash_table, $key) = @_;

       #create an index into $hash_table
       #constrain it to the size of the hash_table
       my $hash_table_size = @$hash_table;
       my $index           = weak_hash($key) % $hash_table_size;

       #get the bucket for this key/value pair
       my $bucket = $hash_table->[$index];

       #find the key/value pair in the bucket
       for my $pair (@$bucket) {
               return $pair->{value} if $pair->{key} eq $key;
       }

       #if key isn't in the bucket:
       return undef;
}

sub list_keys {
       my ($hash_table) = @_;

       my @keys;

       for my $bucket (@$hash_table) {
               for my $pair (@$bucket) {
                       push @keys, $pair->{key};
               }
       }

       return @keys;
}

sub print_hash_table {
       my ($hash_table) = @_;

       for my $i (0 .. $#$hash_table) {
               print "in bucket $i:\n";
               for my $pair (@{$hash_table->[$i]}) {
                       print "$pair->{key} => $pair->{value}\n";
               }
       }
}

my $hash_table = create(3);

my $i = 0;
for my $key (qw/a b c d g j/) {
       store($hash_table, $key, $i++);
}
print_hash_table($hash_table);

print "the a key holds: ", retrieve($hash_table, "a"), "\n";

Как видно из этого примера, возможно, что один ковш имеет больше пар ключ/значение, чем другие. Это плохая ситуация, чтобы быть in. Это заставляет хэш быть медленным для этого ведра. Это один из использование отношения используемых к общему количеству ведер, возвращаемых хешами в скалярный контекст. Если хэш говорит, что всего несколько ведер но у них много ключей в хэше, тогда вы знаете, что у вас есть проблема.

Чтобы узнать больше о хэшах, задайте здесь вопросы о том, что я сказал, или прочитать о них.

Ответ 4

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

Другим подходом к пониманию того, что означает "4/16", является использование модуля Hash::Esoteric (предупреждающий альфа-код качества). Я написал это, чтобы лучше понять, что происходит внутри хеша, поэтому я мог бы попытаться понять проблему производительности , которую, похоже, имеют большие хэши. Функция keys_by_bucket из Hash::Esoteric вернет все ключи из хеша, но вместо того, чтобы возвращать их как список, например keys, он возвращает их как AoA, где верхний уровень представляет собой ведра, а внутри arrayref содержит ключи в этом ковше.

#!/user/bin/env perl

use strict;
use warnings;

use Hash::Esoteric qw/keys_by_bucket/;

my %hash = map { $_ => undef } "a" .. "g";
my $buckets = keys_by_bucket \%hash;

my $used;
for my $i (0 .. $#$buckets) {
    if (@{$buckets->[$i]}) {
        $used++;
    }
    print "bucket $i\n";
    for my $key (@{$buckets->[$i]}) {
        print "\t$key\n";
    }
}

print "scalar %hash: ", scalar %hash, "\n",
      "used/total buckets: $used/", scalar @$buckets, "\n";

Приведенный выше код выдает что-то вроде (фактические данные зависят от версии Perl):

bucket 0
    e
bucket 1
    c
bucket 2
    a
bucket 3
    g
    b
bucket 4
bucket 5
    d
bucket 6
    f
bucket 7
scalar %hash: 6/8
used/total buckets: 6/8

Ответ 5

Фракция заполняющая скорость хеша: используемые ведра против выделенных ковшей. Также иногда называют коэффициент загрузки.

Чтобы получить "4/16", вам понадобятся некоторые трюки. 4 клавиши приведут к 8 ведрам. Таким образом вам нужно как минимум 9 ключей, а затем удалить 5.

$ perl -le'%h=(0..16); print scalar %h; delete $h{$_} for 0..8; print scalar %h'
9/16
4/16

Обратите внимание, что ваши номера будут отличаться, поскольку семя рандомизировано, и вы не сможете предсказать точные столкновения.

Частота заполнения - критическая хеш-информация, когда нужно перефразировать. Perl 5 переигрывает со скоростью заполнения 100%, см. Макрос DO_HSPLIT в hv.c. Таким образом, он обрабатывает память только для чтения. Нормальная скорость заполнения будет составлять от 80 до 95%. Вы всегда оставляете дыры, чтобы сохранить некоторые столкновения. Более низкие скорости заполнения приводят к более быстрому доступу (меньше столкновений), но к более большому числу повторных вызовов.

Вы не видите сразу количество столкновений с дробью. Вам также нужно keys %hash, чтобы сравнить с числителем дроби, используемым числом ковшей.

Таким образом, одна часть качества столкновения - это ключи/используемые ведра:

my ($used, $max) = split '/',scalar(%hash);
keys %hash / $used;

Но на самом деле вам нужно знать сумму длин всех связанных списков в ведрах. Вы можете получить доступ к этому качеству с помощью Hash::Util::bucket_info

($keys, $buckets, $used, @length_count)= Hash::Util::bucket_info(\%hash)

В то время как доступ к хэшу обычно равен O (1), с длинной длиной это только O (n/2), особенно. для перекрытых ведер. На https://github.com/rurban/perl-hash-stats Предоставляю статистическую информацию о характеристиках столкновения для различных хеш-функций для данных тестового набора perl5. Я еще не тестировал компромиссы для разных значений заполнения, так как полностью переписываю текущие хеш-таблицы.

Обновление: для perl5 более высокая скорость заполнения, чем 100%, будет равна 90%, как было проверено недавно. Но это зависит от используемой хэш-функции. Я использовал плохой и быстрый: FNV1A. С более эффективными более медленными функциями хеширования вы можете использовать более высокие скорости заполнения. Текущий OOAT_HARD по умолчанию плохой и медленный, поэтому его следует избегать.

Ответ 6

То, что (%hash) оценивает хэш в скалярном контексте.

Здесь пустой хеш:

command_line_prompt> perl -le '%hash=(); print scalar %hash;'

Результат равен 0.

Здесь непустой хеш:

command_line_prompt> perl -le '%hash=(foo=>'bar'); print scalar %hash;'

В результате получается строка "1/8".