perl 从四进制快速切换到十进制

Posted

技术标签:

【中文标题】perl 从四进制快速切换到十进制【英文标题】:perl quick switch from quaternary to decimal 【发布时间】:2012-09-20 20:30:17 【问题描述】:

我将核苷酸 A、C、G、T 表示为 0、1、2、3,然后我需要将表示为四进制的序列转换为十进制。有没有办法在 perl 中实现这一点?我不确定打包/解包是否可以做到这一点。

【问题讨论】:

请更清楚地描述您想要什么,并向我们展示您的尝试! 所以你想要来自3031205 您是否计划输入长度超过 8 位? 所以,对于DNA序列,比如“ACGTTTCGA”,我要像$dna =~ tr/ACGT/0123/这样转换,转换后的序列是一串数字,但是每个数字只能是0-3,不像十进制可以是0-9。我想把这串数字转换成十进制整数。这就像二进制和十进制之间的关系。就这么简单 想问你为什么要这样做? 【参考方案1】:

Base 4 正好需要 2 位,因此很容易高效处理。

my $uvsize = length(pack('J>', 0)) * 8;
my %base4to2 = map  $_ => sprintf('%2b', $_)  0..3;

sub base4to10 
   my ($s) = @_;
   $s =~ s/(.)/$base4to2$1/sg;
   $s = substr(("0" x $uvsize) . $s, -$uvsize);
   return unpack('J>', pack('B*', $s));

这允许在支持 32 位整数的版本上输入 16 位数字,在支持 64 位整数的版本上允许输入 32 位数字。

使用浮点数支持稍大的数字是可能的:使用 IEEE 双精度数的构建为 26,使用 IEEE 四元数的构建为 56。这将需要不同的实现。

如果比这更大,则需要一个诸如 Math::BigInt 之类的模块,以便 Perl 存储它们。


更快更简单:

my %base4to16 = (
   '0' => '0',   '00' => '0',   '20' => '8',
   '1' => '1',   '01' => '1',   '21' => '9',
   '2' => '2',   '02' => '2',   '22' => 'A',
   '3' => '3',   '03' => '3',   '23' => 'B',
                 '10' => '4',   '30' => 'C',
                 '11' => '5',   '31' => 'D',
                 '12' => '6',   '32' => 'E',
                 '13' => '7',   '33' => 'F',
);

sub base4to10 
   (my $s = $_[0]) =~ s/(..?)/$base4to16$1/sg;
   return hex($s);

【讨论】:

注意:此解决方案会丢失有关存在多少前导零的信息(但 OP 说没关系)。 添加了更快更简单的解决方案。 看来函数 hex($s) 不能处理大于 16 的长度,对吧? @lolibility, hex 无法处理超出您的 Perl 支持的数字,我的帖子已经详细说明了您的 Perl 可以支持的数字。在我发布答案前一小时,我问你需要处理多大的数字,而你仍然没有回答......请回答。 抱歉,没注意到,我可能要处理由 32 个核苷酸组成的序列。还有我的 perl 和操作系统信息“这是 perl,为 x86_64-linux-thread-multi 构建的 v5.8.8”【参考方案2】:

我从未使用过它,但它看起来像 Convert::BaseN 模块将是一个不错的选择。 Convert::BaseN - encoding and decoding of base2,4,8,16,32,64 strings

【讨论】:

你不太明白我的意思。由于翻译部分很简单,请使用 $mysequence =~ tr/ACGT/0123/ 之类的代码;但翻译后,序列由一串数字表示。但我想将它们转换成一个整数,因为那串数字是四进制的,每个数字只能是 0-3,不像十进制,每个数字可以是 0-9。就像二进制和十进制的关系 打包字符串,但是你必须将它们解包为十进制,所以这只是答案的一半。【参考方案3】:

通过处理循环中的每个数字来将base-4字符串计算为十进制非常简单

请注意,在 32 位机器上,您将无法表示长度超过 16 个碱基的序列

这段代码说明了这个想法

use strict;
use warnings;

print seq2dec('ACGTACGTACGTACGT');

sub seq2dec
  my ($sequence) = @_;
  my $n = 0;
  for (map index 'ACGT', $_ split //, $sequence) 
    $n = $n * 4 + $_;
  
  return $n;

输出

454761243

【讨论】:

如果我的机器是64位的,那么我可以表示一个序列多长时间? 在 64 位平台上,您可以表示 32 个碱基,但您需要安装 64 位 Perl。如果将其保留为字符串,则可以存储不定长度的序列,但也可以将其编码为 ACGT 字符 这里有一个问题,就是没有办法保持原始序列的长度。无论序列中有多少个碱基,AAAAAAAAAA 在十进制中都只是零 这对我来说没关系,我的一个程序运行的所有 DNA 序列都是相同的长度,所以,不用担心 A、AA、AAA 或 A...A 的相同整数表示 我关心的是这个速度,不知道如果我使用循环来做加法和电源是否有效

以上是关于perl 从四进制快速切换到十进制的主要内容,如果未能解决你的问题,请参考以下文章

Perl - 将数字转换为 2 个十进制

在 bash/perl 中将科学记数法转换为十进制(不是整数)

如何在 Perl 中转换科学记数法和十进制记数法?

Perl:创建二进制数并将其转换为十六进制

perl程序如何编译成二进制文件并使用

Perl程序将二进制转换为ascii