鉴于要将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));
答案 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