Что лучший способ состоит в том, чтобы сделать base36 арифметику в Perl?
Чтобы быть точнее, я должен смочь сделать следующее:
Воздействуйте на положительные N-разрядные числа в основе 36 (например, цифры являются 0-9 A-Z),
N конечен, скажите 9
Обеспечьте основную арифметику, по крайней мере следующие 3:
Дополнение (A+B)
Вычитание (A-B)
Целое подразделение, например, пол (A/B).
Строго говоря мне действительно не нужна base10 способность к преобразованию - числа будут 100% времени быть в base36. Таким образом, я довольно в порядке, если решение НЕ реализует преобразование из base36 назад к base10 и наоборот.
Я не очень забочусь, "в лоб" ли решение, "преобразовывают в основу 10 и назад" или преобразовывающий в двоичный файл или некоторый более изящный подход, "исходно" выполняющий операции бассейна (как указано выше, к/от base10 преобразованию не требование). Мои только 3 соображения:
Это соответствует минимальным спецификациям выше
Это "стандартно". В настоящее время мы используем и старый модуль собственной разработки на основе base10 преобразования, сделанного вручную, который является багги и сосет.
Я очень заменил бы это некоторым наиболее часто используемым решением CPAN вместо того, чтобы переписать мой собственный велосипед с нуля, но я совершенно способен к созданию его, если никакая лучшая стандартная возможность не существует.
Это должен быть быстрый выход (хотя не молния быстро). Что-то, что занимает 1 секунду для подведения 2 9-разрядных base36 чисел, хуже, чем что-нибудь, что я могу прокрутить самостоятельно :)
P.S. Только для обеспечения некоторого контекста в случае, если люди решают решить мою проблему XY для меня в дополнение к ответу на технический вопрос выше :)
У нас есть довольно большое дерево (сохраненный в DB как набор краев), и мы должны наложить порядок на подмножество того дерева. Дерево dimentions является большим и глубина - и ширина - мудрый. Дерево ОЧЕНЬ активно обновляется (вставляет и удаляет, и перейдите перемещения).
Это в настоящее время делается при наличии второй таблицы с 3 столбцами: parent_vertex, child_vertex, local_order
, где local_order
9 символьных строк, созданных из A-Z0-9 (например, основывайте 36 чисел).
Дополнительные соображения:
Требуется, что локальный порядок уникален на ребенка (и очевидно уникален на родителя),
Любое полное переупорядочение родителя является несколько дорогим, и таким образом реализация должна попытаться присвоиться - для родителя с X детьми - заказы, которые несколько равномерно распределяются между 0 и 36 ** 10-1, так, чтобы почти никакое дерево не вставляло результат в полное переупорядочение.
Я предполагаю, что основные модули Perl в порядке?
Как насчет использования собственной (двоичной) целочисленной математики и преобразования из результата с основанием 36 с использованием POSIX :: strtol ()
Существует ОГРОМНАЯ скорость разницы в скорости в различных методах преобразования в / из базы 36. Strtol в 80 раз быстрее, чем, например, Math :: Base36: decode_base36, а подсистемы преобразования, которые у меня есть в листинге, в 2–4 раза быстрее, чем Math :: Base36. Они также поддерживают любую целочисленную базу до 62. (легко расширяется путем добавления символов в массив nums.)
Вот быстрый тест:
#!/usr/bin/perl
use POSIX;
use Math::BaseCnv;
use Math::Base36 ':all';
use Benchmark;
{
my @nums = (0..9,'a'..'z','A'..'Z');
$chr=join('',@nums);
my %nums = map { $nums[$_] => $_ } 0..$#nums;
sub to_base
{
my ($base, $n) = @_;
return $nums[0] if $n == 0;
return $nums[0] if $base > $#nums;
my $str = '';
while( $n > 0 )
{
$str = $nums[$n % $base] . $str;
$n = int( $n / $base );
}
return $str;
}
sub fr_base
{
my ($base,$str) = @_;
my $n = 0;
return 0 if $str=~/[^$chr]/;
foreach ($str =~ /[$chr]/g)
{
$n *= $base;
$n += $nums{$_};
}
return $n;
}
}
$base=36;
$term=fr_base($base,"zzz");
for(0..$term) { push @numlist, to_base($base,$_); }
timethese(-10, {
'to_base' => sub { for(0..$#numlist){ to_base($base,$_); } },
'encode_base36' => sub { for(0..$#numlist){ encode_base36($_); } },
'cnv->to 36' => sub { for(0..$#numlist){ cnv($_); } },
'decode_base36' => sub { foreach(@numlist){ decode_base36($_); } },
'fr_base' => sub { foreach(@numlist){ fr_base($base,$_); } },
'cnv->to decimal' => sub { foreach(@numlist){ cnv($_,$base,10); } },
'POSIX' => sub { foreach(@numlist){ POSIX::strtol($_,$base);}},
} );
Я бы поставил свои деньги на преобразование в base10 и обратно.
Если вам не нужно делать это очень часто и количество не очень велико, это самый простой (и, следовательно, наименее сложный => наименьшее количество ошибок) способ сделать это.
Конечно, есть еще один способ сделать это - также сохранить число base10 только для целей вычислений, однако я не уверен, возможно ли это или имеет ли это какое-либо преимущество в вашем случае