如何在php中最快地计算设置位数?

如何在php中最快地计算设置位数?,php,algorithm,set,bit,Php,Algorithm,Set,Bit,我只是想在php中找到一些最快的设置位计数函数 例如,0010101=>30011110=>4 我看到有一个很好的算法可以在C++中实现。 是否有任何php内置函数或最快的用户定义函数?您可以尝试应用带有二进制和的掩码,并使用shift逐个测试位,使用一个将迭代32次的循环 function getBitCount($value) { $count = 0; while($value) { $count += ($value & 1);

我只是想在php中找到一些最快的设置位计数函数

例如,0010101=>30011110=>4

<>我看到有一个很好的算法可以在C++中实现。


是否有任何php内置函数或最快的用户定义函数?

您可以尝试应用带有二进制和的掩码,并使用shift逐个测试位,使用一个将迭代32次的循环

function getBitCount($value) {

    $count = 0;
    while($value)
    {
        $count += ($value & 1);
        $value = $value >> 1;
    }

    return $count;
}
您还可以轻松地将函数转换为PHP样式

function NumberOfSetBits($v)
{
    $c = $v - (($v >> 1) & 0x55555555);
    $c = (($c >> 2) & 0x33333333) + ($c & 0x33333333);
    $c = (($c >> 4) + $c) & 0x0F0F0F0F;
    $c = (($c >> 8) + $c) & 0x00FF00FF;
    $c = (($c >> 16) + $c) & 0x0000FFFF;
    return $c;
}

我可以想出一些方法,但不确定哪种方法最快:

  • 使用substr_count()
  • 将所有非“1”字符替换为“”,然后使用strlen()
  • 使用preg_match_all()
PS:如果您从一个整数开始,这些示例将首先涉及使用decbin()。

我的基准测试代码

start_benchmark();
for ($i = 0; $i < 1000000; $i++) {
    getBitCount($i);
}
end_benchmark();

start_benchmark();
for ($i = 0; $i < 1000000; $i++) {
    NumberOfSetBits($i);
}
end_benchmark();
start_benchmark();
for ($i = 0; $i < 1000000; $i++) {
    substr_count(decbin($i), '1');
}
end_benchmark();
start_benchmark();
对于($i=0;$i<1000000;$i++){
getBitCount(一美元);
}
end_benchmark();
启动_benchmark();
对于($i=0;$i<1000000;$i++){
数目(i元);;
}
end_benchmark();
启动_benchmark();
对于($i=0;$i<1000000;$i++){
子项计数(decbin($i),“1”);
}
end_benchmark();
基准测试结果:

基准(NumberOfSetBits()):1.429042千秒

基准(substr_count()):1.672635千秒

基准测试(getBitCount()):10.464981千秒

我认为NumberOfSetBits()和substr_count()是最好的。
谢谢。

还有很多其他的方法;但对于十进制32位整数,
NumberOfSetBits
绝对是最快的

我最近无意中发现了,它有
O(log(n))
,而其他大多数都有
O(n)
。我不知道为什么它在这里出现的不是那么快;但与所有其他非专业功能相比,它仍然具有可测量的优势

当然,没有什么能用
O(1)
打败
numberofsetbit

我的基准:

函数getBitCount($value){$count=0;而($value){$count+=($value&1);$value=$value>>1;}返回$count;}
函数getBitCount2($value){$count=0;而($value){if($value&1)$count++;$value>>=1;}返回$count;}
//if()而不是+=;>>=1代替作业:有时慢,有时快
函数getBitCount2a($value){for($count=0;$value;$value>>=1)如果($value&1)$count++;返回$count;}
//而不是暂时:有时慢,有时快
函数getBitCount3($value){for($i=1,$count=0;$i;$i2)&0x33333)+($c&0x33333);
$c=($c>>4)+$c)&0x0F0F;$c=($c>>8)+$c)&0x00FF00FF;
$c=($c>>16)+$c)&0x0000FFFF;返回$c;
}
函数bitsByPregReplace($n){return strlen(preg_replace(''u 0','',decbin($n));}
函数bitsByNegPregReplace($n){返回strlen(preg_replace('/[^1]/','',decbin($n));}
函数bitsByPregMatchAll($n){return preg_match_all('/1/',decbin($n));}
函数bitsBySubstr($i){返回substr_计数(decbin($i),'1');}
函数bitsBySubstrInt($i){返回substr_计数(decbin($i),1);}
//最短(以源代码字节为单位)
函数bitsByCountChars($n){return count_chars(decbin($n))[49];}
//最慢的
函数bitsByCountChars1($n){返回count_chars(decbin($n),1)[49];}
//抛出$n=0的通知
函数Kernighan($n){for(;$n;$c++)$n&=$n-1;返回$c;}
//布赖恩·克尼汉算法
函数基准($function)
{
gc_collect_cycles();
$t0=微时间();
对于($i=1e6;$i--;)$function($i);
$t1=微时间();
$t0=爆炸(“”,$t0);$t1=爆炸(“”,$t1);
echo($t1[0]-$t0[0])+($t1[1]-$t0[1]),“s\t$function\n”;
}
基准(“getBitCount”);
基准(“getBitCount2”);
基准(“getBitCount2a”);
基准(“getBitCount3”);
基准(“getBitCount3a”);
基准(NumberOfSetBits);
基准(“bitsBySubstr”);
基准(“BitsBySubString”);
基准(“bitsByPregReplace”);
基准(“bitsByPregMatchAll”);
基准(“bitsByCountChars”);
基准(“bitsByCountChars1”);
基准(“decbin”);
banchmark结果(已排序)

这些是我的一次跑步(使用PHP7.0.22)中的数字;其他人在3.5秒组内表现出不同的顺序。我可以这么说——在我的机器上——这五个元素中有四个是相当相等的,
bitsBySubstrInt
由于类型转换的缘故总是稍微慢一点

大多数其他方法都需要decbin(这通常比实际计数时间长;我在基准测试结果中用
*
标记了它们);只有
BitsBySubstr
在没有那条野马腿的情况下才能接近获胜者

我发现很明显,通过将
count\u chars
限制为仅现有的chars,您可以将其速度提高3倍。似乎数组索引需要相当长的时间


编辑:

  • 添加了另一个
    preg\u replace
    版本
  • 修复了
    preg\u match\u all
    version
  • 增加了Kernighan算法(任意大小整数的最快算法)
  • 在基准测试函数中添加了垃圾收集
  • 重新运行基准

此选项比NumberOfSetBits($v)快一点


我想“00110101”不是一个字符串,而是整数的二进制表示形式。只要用decbin()将它转换成字符串即可。这几乎肯定会比运行循环快。@cleong不确定,因为字符串搜索可以loop@MatRt内置循环。需要基准。我更关心双函数调用
函数countSetBits($int){return substr_count(decbin($int),'1');}
事实上,这是一个二进制函数,numberOfSetBits()函数比getBitCount()快十倍,但是如果$i=1025,那么它返回258,你能解决这个问题吗?我的输入限制是2000。事实上,这似乎是一个错误,在这个算法,检查我的更新。小心第一种方法。任何大于0x7FFFFFFF(负值)的数字都可能导致无限循环(至少在32位PHP上)
> php count-bits.php
 2.286831 s     decbin

 1.364934 s     NumberOfSetBits
 3.241821 s     Kernighan

 3.498779 s     bitsBySubstr*
 3.582412 s     getBitCount2a
 3.614841 s     getBitCount2
 3.751102 s     getBitCount
 3.769621 s     bitsBySubstrInt*

 5.806785 s     bitsByPregMatchAll*
 5.748319 s     bitsByCountChars1*
 6.350801 s     bitsByNegPregReplace*
 6.615289 s     bitsByPregReplace*

13.863838 s     getBitCount3
16.39626 s      getBitCount3a
19.304038 s     bitsByCountChars*
function bitsCount(int $integer)
{
    $count = $integer - (($integer >> 1) & 0x55555555);
    $count = (($count >> 2) & 0x33333333) + ($count & 0x33333333);
    $count = ((((($count >> 4) + $count) & 0x0F0F0F0F) * 0x01010101) >> 24) & 0xFF;

    return $count;
}