在matlab中有效地计算汉明重量

时间:2009-06-21 22:40:05

标签: matlab bit-manipulation bitstring hammingweight

鉴于要将MATLAB uint32解释为位串,什么是计算字符串中有多少非零位的有效且简洁的方法?

我有一种工作,天真的方法可以循环使用,但这对我的需求来说太慢了。 (使用std :: bitset count()的C ++实现几乎立即运行。)

我找到了一个非常好的页面列出了各种位计数技术,但我希望有一种简单的MATLAB方式。

http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetNaive


更新#1

刚刚实施了Brian Kernighan算法,如下所示:

w = 0;
while ( bits > 0 )
    bits = bitand( bits, bits-1 );
    w = w + 1;
end

性能仍然很糟糕,超过10秒钟只计算4096 ^ 2重量计算。使用std :: bitset中的count()的我的C ++代码在亚秒时间内执行此操作。


更新#2

这是我迄今为止尝试过的技术的运行时间表。我会在获得更多想法/建议时更新它。

Vectorized Scheiner algorithm                =>    2.243511 sec
Vectorized Naive bitget loop                 =>    7.553345 sec
Kernighan algorithm                          =>   17.154692 sec
length( find( bitget( val, 1:32 ) ) )        =>   67.368278 sec
nnz( bitget( val, 1:32 ) )                   =>  349.620259 sec
Justin Scheiner's algorithm, unrolled loops  =>  370.846031 sec
Justin Scheiner's algorithm                  =>  398.786320 sec
Naive bitget loop                            =>  456.016731 sec
sum(dec2bin(val) == '1')                     => 1069.851993 sec


评论:MATLAB中的dec2bin()函数似乎执行得很差。它运行得非常慢。

评论:“Naive bitget loop”算法实现如下:

w=0;
for i=1:32
   if bitget( val, i ) == 1
       w = w + 1;
   end
end

注释的: Scheiner算法的循环展开版本如下所示:

function w=computeWeight( val )
w = val;
w = bitand(bitshift(w, -1), uint32(1431655765)) + ...
    bitand(w, uint32(1431655765));

w = bitand(bitshift(w, -2), uint32(858993459)) + ...
    bitand(w, uint32(858993459));

w = bitand(bitshift(w, -4), uint32(252645135)) + ...
    bitand(w, uint32(252645135));

w = bitand(bitshift(w, -8), uint32(16711935)) + ...
    bitand(w, uint32(16711935));

w = bitand(bitshift(w, -16), uint32(65535)) + ...
    bitand(w, uint32(65535));

9 个答案:

答案 0 :(得分:9)

我很想知道这个解决方案的速度有多快:

function r = count_bits(n)

shifts = [-1, -2, -4, -8, -16];
masks = [1431655765, 858993459, 252645135, 16711935, 65535];

r = n;
for i=1:5
   r = bitand(bitshift(r, shifts(i)), masks(i)) + ...
      bitand(r, masks(i));
end

回过头来看,我看到这是bithacks页面上给出的'并行'解决方案。

答案 1 :(得分:5)

编辑:新解决方案

似乎您要为4096 x 4096 UINT32值数组中的每个元素重复计算。如果你正在做的事情,我认为在MATLAB中最快的方法是使用BITGET设计用于操作值矩阵的事实。代码如下所示:

numArray = ...your 4096-by-4096 matrix of uint32 values...
w = zeros(4096,4096,'uint32');
for iBit = 1:32,
  w = w+bitget(numArray,iBit);
end

如果你想制作一些其他算法的矢量化版本,我相信BITAND也可以在矩阵上运行。


旧解决方案......

我能想到的最简单的方法是使用DEC2BIN函数,它为您提供非负整数的二进制表示(作为字符串):

w = sum(dec2bin(num) == '1');  % Sums up the ones in the string

这很慢,但很容易。 =)

答案 2 :(得分:5)

除非这是一个MATLAB实现练习,否则您可能只想采用快速C ++实现并将其编译为mex函数,每个目标平台一次。

答案 3 :(得分:5)

从顶部的斯坦福链接实现了“最佳32位算法”。 改进的算法将处理时间缩短了6%。 还优化了分段大小,发现32K稳定,比4K时间缩短了15%。 预计4Kx4K时间为矢量化Scheiner算法的40%。

function w = Ham(w)
% Input uint32
% Output vector of Ham wts
 for i=1:32768:length(w)
  w(i:i+32767)=Ham_seg(w(i:i+32767));
 end
end

% Segmentation gave reduced time by 50%

function w=Ham_seg(w)
 %speed
 b1=uint32(1431655765); 
 b2=uint32(858993459);
 b3=uint32(252645135);
 b7=uint32(63); % working orig binary mask

 w = bitand(bitshift(w, -1), b1) + bitand(w, b1);
 w = bitand(bitshift(w, -2), b2) + bitand(w, b2);
 w =bitand(w+bitshift(w, -4),b3);
 w =bitand(bitshift(w,-24)+bitshift(w,-16)+bitshift(w,-8)+w,b7);

end

答案 4 :(得分:1)

在Matlab Cody上进行了一些时序比较。 确定分段修改的矢量化Scheiner可以提供最佳性能。

对于L = 4096 * 4096向量,基于Cody 1.30秒到0.60秒的变化有> 50%的时间减少。

function w = Ham(w)
% Input uint32
% Output vector of Ham wts

 b1=uint32(1431655765); % evaluating saves 15% of time 1.30 to 1.1 sec
 b2=uint32(858993459);
 b3=uint32(252645135);
 b4=uint32(16711935);
 b5=uint32(65535);

 for i=1:4096:length(w)
  w(i:i+4095)=Ham_seg(w(i:i+4095),b1,b2,b3,b4,b5);
 end
end

% Segmentation reduced time by 50%

function w=Ham_seg(w,b1,b2,b3,b4,b5)
 % Passing variables or could evaluate b1:b5 here


 w = bitand(bitshift(w, -1), b1) + bitand(w, b1);
 w = bitand(bitshift(w, -2), b2) + bitand(w, b2);
 w = bitand(bitshift(w, -4), b3) + bitand(w, b3);
 w = bitand(bitshift(w, -8), b4) + bitand(w, b4);
 w = bitand(bitshift(w, -16), b5) + bitand(w, b5);

end





vt=randi(2^32,[4096*4096,1])-1;
% for vt being uint32 the floor function gives unexpected values
tic
v=num_ones(mod(vt,65536)+1)+num_ones(floor(vt/65536)+1); % 0.85 sec
toc
% a corrected method is
v=num_ones(mod(vt,65536)+1)+num_ones(floor(double(vt)/65536)+1);
toc

答案 5 :(得分:1)

快速方法是使用查找表计算每个字节中的位,然后对这些值求和;实际上,这是问题中给出的网页上建议的方法之一。这种方法的好处是查找和求和都是MATLAB中的可矢量化操作,因此您可以对此方法进行矢量化并同时非常快速地计算汉明重量/大量位串的设置位数。这种方法在MATLAB文件交换的bitcount提交中实现。

答案 6 :(得分:0)

尝试将作业拆分为较小的部分。我的猜测是,如果你想一次处理所有数据,matlab会尝试在所有整数上执行每个操作,然后再执行连续的步骤,并且每个步骤都会使处理器的缓存无效。

for i=1:4096,
    «process bits(i,:)»
end

答案 7 :(得分:0)

我正在恢复一个旧线程,但我遇到了这个问题,我为此编写了一些代码:

distance = sum(bitget(bits, 1:32));

看起来非常简洁,但我很害怕bitget在O(n)bitshift操作中实现。该代码适用于我要去的地方,但我的问题集不依赖于汉明重量。

答案 8 :(得分:0)

num_ones=uint8(zeros(intmax('uint32')/2^6,1));
% one time load of array not implemented here
tic
for i=1:4096*4096
 %v=num_ones(rem(i,64)+1)+num_ones(floor(i/64)+1); % 1.24 sec
 v=num_ones(mod(i,64)+1)+num_ones(floor(i/64)+1); % 1.20 sec
end
toc
tic
num_ones=uint8(zeros(65536,1));
for i=0:65535
 num_ones(i+1)=length( find( bitget( i, 1:32 ) ) ) ;
end
toc
% 0.43 sec to load
% smaller array to initialize
% one time load of array
tic
for i=1:4096*4096
 v=num_ones(mod(i,65536)+1)+num_ones(floor(i/65536)+1); %  0.95 sec
 %v=num_ones(mod(i,65536)+1)+num_ones(bitshift(i,-16)+1); % 16 sec for 4K*1K
end
toc
%vectorized
tic
num_ones=uint8(zeros(65536,1));
for i=0:65535
 num_ones(i+1)=length( find( bitget( i, 1:32 ) ) ) ;
end % 0.43 sec
toc
vt=randi(2^32,[4096*4096,1])-1;
tic
v=num_ones(mod(vt,65536)+1)+num_ones(floor(vt/65536)+1); % 0.85 sec
toc