假设我有一个整数I,并希望以二进制形式得到1的计数。
我目前正在使用以下代码。
--init-path
有更快的方法吗?我正在考虑使用按位运算符。有什么想法吗?
注意: Number(i.toString(2).split("").sort().join("")).toString().length;
在32位限制范围内。
答案 0 :(得分:13)
您可以使用此Bit Twiddling Hacks集合中的策略:
function bitCount (n) {
n = n - ((n >> 1) & 0x55555555)
n = (n & 0x33333333) + ((n >> 2) & 0x33333333)
return ((n + (n >> 4) & 0xF0F0F0F) * 0x1010101) >> 24
}
console.log(bitCount(0xFF)) //=> 8

请注意,上述策略仅适用于32位整数(JavaScript中按位运算符的限制)。
对于更大整数的更一般方法将涉及单独计算32位块(感谢harold的灵感):
function bitCount (n) {
var bits = 0
while (n !== 0) {
bits += bitCount32(n | 0)
n /= 0x100000000
}
return bits
}
function bitCount32 (n) {
n = n - ((n >> 1) & 0x55555555)
n = (n & 0x33333333) + ((n >> 2) & 0x33333333)
return ((n + (n >> 4) & 0xF0F0F0F) * 0x1010101) >> 24
}
console.log(bitCount(Math.pow(2, 53) - 1)) //=> 53

您还可以使用正则表达式:
function bitCount (n) {
return n.toString(2).match(/1/g).length
}
console.log(bitCount(0xFF)) //=> 8

答案 1 :(得分:2)
一种非常好但慢的递归方式:
function count1(n, accumulator=0) {
if (n === 0) {
return accumulator
}
return count1(n/2, accumulator+(n&1))
}
console.log(count1(Number.MAX_SAFE_INTEGER));
但是,如果您想要一个非常快速的命令(比T.J. Crowder的回答更快):
count1s=(n)=>n.toString(2).replace(/0/g,"").length
console.log(count1s(Number.MAX_SAFE_INTEGER));
注意:其他一些解决方案不适用于位整数(> 32位) 这两个做到了!
现在,如果我们仅考虑32位数字,则最快的方式是这样的:
function count1s32(i) {
var count = 0;
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
i = (i + (i >> 4)) & 0x0f0f0f0f;
i = i + (i >> 8);
i = i + (i >> 16);
count += i & 0x3f;
return count;
}
console.log(count1s32(0xffffffff));
53位比较:
32位比较:
这里是基准! (因为jsperf经常崩溃)。
function log(data) {
document.getElementById("log").textContent += data + "\n";
}
benchmark = (() => {
time_function = function(ms, f, num) {
var z;
var t = new Date().getTime();
for (z = 0;
((new Date().getTime() - t) < ms); z++) f(num);
return (z / ms)
} // returns how many times the function was run in "ms" milliseconds.
// two sequential loops
count1s = (n) => n.toString(2).replace(/0/g, "").length
// three loops and a function.
count1j = (n) => n.toString(2).split('').filter(v => +v).length
/* Excluded from test because it's too slow :D
function count1(n, accumulator=0) {
if (n === 0) {
return accumulator
}
return count1(n / 2, accumulator + (n & 1))
}
*/
function countOnes(i) {
var str = i.toString(2);
var n;
var count = 0;
for (n = 0; n < str.length; ++n) {
if (str[n] === "1") {
++count;
}
}
return count;
} // two sequential loops ( one is the toString(2) )
function count1sb(num) {
i = Math.floor(num / 0x100000000);
// if (i > 0) {
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
i = (i + (i >> 4)) & 0x0f0f0f0f;
i = i + (i >> 8);
i = i + (i >> 16);
count = i & 0x3f;
i = num & 0xffffffff;
// }
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
i = (i + (i >> 4)) & 0x0f0f0f0f;
i = i + (i >> 8);
i = i + (i >> 16);
count += i & 0x3f;
return count;
}
function benchmark() {
function compare(a, b) {
if (a[1] > b[1]) {
return -1;
}
if (a[1] < b[1]) {
return 1;
}
return 0;
}
funcs = [
[count1s, 0],
[count1j, 0],
[count1sb, 0],
[countOnes, 0]
];
funcs.forEach((ff) => {
console.log("Benchmarking: " + ff[0].name);
ff[1] = time_function(2500, ff[0], Number.MAX_SAFE_INTEGER);
console.log("Score: " + ff[1]);
})
return funcs.sort(compare);
}
return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();
console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea cols=80 rows=30 id="log"></textarea>
基准测试将持续10秒。
答案 2 :(得分:1)
以下任何数字都可以正常使用:
var i=8823475632,count=0;while (i=Math.floor(i)) i&1?count++:0,i/=2
console.log(count); //17
将i更改为您想要的值或将其包装为函数
如果整数在32位以内,则工作
var i=10,count=0;while (i) i&1?count++:0,i>>=1
答案 3 :(得分:1)
您可以跳过Number
,sort
和第二个toString
。使用filter
仅考虑数组中的1
s(真值),然后检索length
获得的数量。
i.toString(2).split('').filter(v => +v).length
答案 4 :(得分:1)
执行n = n & (n - 1)
删除号码中的最后1位。
根据这个,您可以使用以下算法:
function getBitCount(n) {
var tmp = n;
var count = 0;
while (tmp > 0) {
tmp = tmp & (tmp - 1);
count++;
}
return count;
}
console.log(getBitCount(Math.pow(2, 10) -1));
&#13;
答案 5 :(得分:1)
如果您想使用一种绝对的衬管解决方案,则可以查看一下。
countBits = n => n.toString(2).split('0').join('').length;
1。此处n.toString(2)将n转换为二进制字符串
2.split('0')使数组仅在以下位置由二进制字符串拆分 0,因此返回n的二进制数中只有1的数组
3.join('')将所有字符连接起来并形成1s字符串
4.length查找实际上计算n中的1的字符串的长度。
答案 6 :(得分:0)
鉴于你正在创建,排序和加入一个数组,如果它真的是更快你想要的话,你可能最好不要那么无聊:
console.log(countOnes(8823475632));
function countOnes(i) {
var str = i.toString(2);
var n;
var count = 0;
for (n = 0; n < str.length; ++n) {
if (str[n] === "1") {
++count;
}
}
return count;
}
(如果您需要支持过时的浏览器,请使用str.charAt(n)
代替str[n]
。)
这不是l33t或简洁,但我打赌它更快 it's much faster:
...同样适用于Firefox,IE11(IE11程度较低)。
答案 7 :(得分:0)
n.toString(2).match(/1/g).length
答案 8 :(得分:0)
更多“有趣”的1层衬垫:
递归:递归计数每个位,直到不再设置位
let f = x => !x ? 0 : (x & 1) + f(x >>= 1);
功能性:拆分x的基数2字符串并返回设置的累积位长度
g = x => x.toString(2).split('0').map(bits => bits.length).reduce((a, b) => a + b);
答案 9 :(得分:0)
继续检查最后一位是否为 1,然后将其删除。如果它发现最后一位是 1,则将其添加到结果中。
Math.popcount = function (n) {
let result = 0;
while (n) {
result += n % 2;
n = n >>> 1;
};
return result;
};
console.log(Math.popcount(0b1010));
对于64位,你可以用两个整数来表示数字,第一个是前32位,第二个是后32位。要统计64位的个数,你可以把它们分成2位,32位整数,并添加第一个和第二个的popcount。