在JavaScript中实现位数组的最佳方法是什么?
答案 0 :(得分:16)
这是我掀起的一个:
UPDATE - 关于这个类的一些事情一直困扰着我 - 它不是基于大小的 - 创建一个带有N个插槽/位的BitArray是一个两步操作 - 实例化,调整大小。使用可选的第二个参数更新了类的大小,以便使用数组值或基数为10的数值填充基于大小的实例。
(摆弄它here)
/* BitArray DataType */
// Constructor
function BitArray(size, bits) {
// Private field - array for our bits
this.m_bits = new Array();
//.ctor - initialize as a copy of an array of true/false or from a numeric value
if (bits && bits.length) {
for (var i = 0; i < bits.length; i++)
this.m_bits.push(bits[i] ? BitArray._ON : BitArray._OFF);
} else if (!isNaN(bits)) {
this.m_bits = BitArray.shred(bits).m_bits;
}
if (size && this.m_bits.length != size) {
if (this.m_bits.length < size) {
for (var i = this.m_bits.length; i < size; i++) {
this.m_bits.push(BitArray._OFF);
}
} else {
for(var i = size; i > this.m_bits.length; i--){
this.m_bits.pop();
}
}
}
}
/* BitArray PUBLIC INSTANCE METHODS */
// read-only property - number of bits
BitArray.prototype.getLength = function () { return this.m_bits.length; };
// accessor - get bit at index
BitArray.prototype.getAt = function (index) {
if (index < this.m_bits.length) {
return this.m_bits[index];
}
return null;
};
// accessor - set bit at index
BitArray.prototype.setAt = function (index, value) {
if (index < this.m_bits.length) {
this.m_bits[index] = value ? BitArray._ON : BitArray._OFF;
}
};
// resize the bit array (append new false/0 indexes)
BitArray.prototype.resize = function (newSize) {
var tmp = new Array();
for (var i = 0; i < newSize; i++) {
if (i < this.m_bits.length) {
tmp.push(this.m_bits[i]);
} else {
tmp.push(BitArray._OFF);
}
}
this.m_bits = tmp;
};
// Get the complimentary bit array (i.e., 01 compliments 10)
BitArray.prototype.getCompliment = function () {
var result = new BitArray(this.m_bits.length);
for (var i = 0; i < this.m_bits.length; i++) {
result.setAt(i, this.m_bits[i] ? BitArray._OFF : BitArray._ON);
}
return result;
};
// Get the string representation ("101010")
BitArray.prototype.toString = function () {
var s = new String();
for (var i = 0; i < this.m_bits.length; i++) {
s = s.concat(this.m_bits[i] === BitArray._ON ? "1" : "0");
}
return s;
};
// Get the numeric value
BitArray.prototype.toNumber = function () {
var pow = 0;
var n = 0;
for (var i = this.m_bits.length - 1; i >= 0; i--) {
if (this.m_bits[i] === BitArray._ON) {
n += Math.pow(2, pow);
}
pow++;
}
return n;
};
/* STATIC METHODS */
// Get the union of two bit arrays
BitArray.getUnion = function (bitArray1, bitArray2) {
var len = BitArray._getLen(bitArray1, bitArray2, true);
var result = new BitArray(len);
for (var i = 0; i < len; i++) {
result.setAt(i, BitArray._union(bitArray1.getAt(i), bitArray2.getAt(i)));
}
return result;
};
// Get the intersection of two bit arrays
BitArray.getIntersection = function (bitArray1, bitArray2) {
var len = BitArray._getLen(bitArray1, bitArray2, true);
var result = new BitArray(len);
for (var i = 0; i < len; i++) {
result.setAt(i, BitArray._intersect(bitArray1.getAt(i), bitArray2.getAt(i)));
}
return result;
};
// Get the difference between to bit arrays
BitArray.getDifference = function (bitArray1, bitArray2) {
var len = BitArray._getLen(bitArray1, bitArray2, true);
var result = new BitArray(len);
for (var i = 0; i < len; i++) {
result.setAt(i, BitArray._difference(bitArray1.getAt(i), bitArray2.getAt(i)));
}
return result;
};
// Convert a number into a bit array
BitArray.shred = function (number) {
var bits = new Array();
var q = number;
do {
bits.push(q % 2);
q = Math.floor(q / 2);
} while (q > 0);
return new BitArray(bits.length, bits.reverse());
};
/* BitArray PRIVATE STATIC CONSTANTS */
BitArray._ON = 1;
BitArray._OFF = 0;
/* BitArray PRIVATE STATIC METHODS */
// Calculate the intersection of two bits
BitArray._intersect = function (bit1, bit2) {
return bit1 === BitArray._ON && bit2 === BitArray._ON ? BitArray._ON : BitArray._OFF;
};
// Calculate the union of two bits
BitArray._union = function (bit1, bit2) {
return bit1 === BitArray._ON || bit2 === BitArray._ON ? BitArray._ON : BitArray._OFF;
};
// Calculate the difference of two bits
BitArray._difference = function (bit1, bit2) {
return bit1 === BitArray._ON && bit2 !== BitArray._ON ? BitArray._ON : BitArray._OFF;
};
// Get the longest or shortest (smallest) length of the two bit arrays
BitArray._getLen = function (bitArray1, bitArray2, smallest) {
var l1 = bitArray1.getLength();
var l2 = bitArray2.getLength();
return l1 > l2 ? smallest ? l2 : l1 : smallest ? l2 : l1;
};
向@Daniel Baulig致信,要求重构从快速,肮脏到原型。
答案 1 :(得分:12)
我不知道位数组,但是你可以使用新功能轻松实现字节数组。
查找typed arrays。我在Chrome和Firefox中都使用过它们。重要的是Uint8Array。
创建512个未初始化字节的数组:
var arr = new UintArray(512);
访问它(第六个字节):
var byte = arr[5];
对于node.js,请使用Buffer(服务器端)。
编辑:
要访问各个位,请使用位掩码。
要获得一个位置的位,请执行num & 0x1
答案 2 :(得分:5)
Stanford Javascript Crypto Library (SJCL)提供了一个位数组实现,可以将不同的输入(十六进制字符串,字节数组等)转换为位数组。
他们的代码在GitHub上公开:bitwiseshiftleft/sjcl。因此,如果查找bitArray.js,您可以找到它们的位数组实现。
可以找到从字节到位的转换here。
答案 3 :(得分:2)
这样的事情就像我能想到的那样接近。将位数组保存为32位数,并有一个标准数组支持它来处理更大的数组。
class bitArray {
constructor(length) {
this.backingArray = Array.from({length: Math.ceil(length/32)}, ()=>0)
this.length = length
}
get(n) {
return (this.backingArray[n/32|0] & 1 << n % 32) > 0
}
on(n) {
this.backingArray[n/32|0] |= 1 << n % 32
}
off(n) {
this.backingArray[n/32|0] &= ~(1 << n % 32)
}
toggle(n) {
this.backingArray[n/32|0] ^= 1 << n % 32
}
forEach(callback) {
this.backingArray.forEach((number, container)=>{
const max = container == this.backingArray.length-1 ? this.length%32 : 32
for(let x=0; x<max; x++) {
callback((number & 1<<x)>0, 32*container+x)
}
})
}
}
let bits = new bitArray(10)
bits.get(2) //false
bits.on(2)
bits.get(2) //true
bits.forEach(console.log)
/* outputs:
false
false
true
false
false
false
false
false
false
false
*/
bits.toggle(2)
bits.forEach(console.log)
/* outputs:
false
false
false
false
false
false
false
false
false
false
*/
bits.toggle(0)
bits.toggle(1)
bits.toggle(2)
bits.off(2)
bits.off(3)
bits.forEach(console.log)
/* outputs:
true
true
false
false
false
false
false
false
false
false
*/
答案 4 :(得分:0)
可能(绝对)不是执行此操作的最有效方法,但是可以将一串零和一解析为以2为底的数字,然后转换为十六进制数,最后转换为缓冲区。
const bufferFromBinaryString = (binaryRepresentation = '01010101') =>
Buffer.from(
parseInt(binaryRepresentation, 2).toString(16), 'hex');
再次,效率不高;但由于相对简单,我喜欢这种方法。
答案 5 :(得分:0)
感谢一个非常简单的类,它可以满足我的需求。
在测试过程中,我确实发现了一些边缘情况的错误:
get(n) {
return (this.backingArray[n/32|0] & 1 << n % 32) != 0
// test of > 0 fails for bit 31
}
forEach(callback) {
this.backingArray.forEach((number, container)=>{
const max = container == this.backingArray.length-1 && this.length%32
? this.length%32 : 32;
// tricky edge-case: at length-1 when length%32 == 0,
// need full 32 bits not 0 bits
for(let x=0; x<max; x++) {
callback((number & 1<<x)!=0, 32*container+x) // see fix in get()
}
})
我的最终实现修复了上述错误,并将backArray更改为Uint8Array而不是Array,从而避免了有符号的int错误。
答案 6 :(得分:0)
您可以使用按位运算符轻松地做到这一点。这很简单。 让我们尝试使用数字75。
它的二进制表示形式是1001011。那么,如何从数字中获取每一位呢? 您可以使用AND“&”运算符来选择一位并将其其余部分设置为0。然后使用Shift运算符,删除此刻无关紧要的其余0。
Let's do an AND operation with 4 (000 0010)
0100 1011 & 0000 0010 => 0000 0010
现在,我们需要过滤选定的位,在这种情况下,第二位是从右到左读取的。
0000 0010 >> 1 => 1
左边的零没有代表。因此输出将是我们选择的位,在本例中为第二位。
var word=75;
var res=[];
for(var x=7; x>=0; x--){
res.push((word&Math.pow(2,x))>>x);
}
console.log(res);
如果您需要的不仅仅是一个简单的数字,则可以对一个字节应用相同的功能。假设您有一个包含多个字节的文件。因此,您可以在ByteArray中分解该文件,然后在BitArray中分解数组中的每个字节。
祝你好运!
答案 7 :(得分:0)
@Commi的实现是我最终使用的。
我相信此实现中存在错误。每31个边界上的位给出错误的结果。 (即,当索引为(32 * index - 1)
时,则为31、63、95等
我通过将> 0
替换为!= 0
在get()方法中修复了它。
get(n) {
return (this.backingArray[n/32|0] & 1 << n % 32) != 0
}
该错误的原因是int是32位带符号的。将1左移31可得到一个负数。由于检查是针对>0
的,因此当它为true时将为false。
我编写了一个程序来证明之前的错误以及之后的修复。将其发布空间用完。
for (var i=0; i < 100; i++) {
var ar = new bitArray(1000);
ar.on(i);
for(var j=0;j<1000;j++) {
// we should have TRUE only at one position and that is "i".
// if something is true when it should be false or false when it should be true, then report it.
if(ar.get(j)) {
if (j != i) console.log('we got a bug at ' + i);
}
if (!ar.get(j)) {
if (j == i) console.log('we got a bug at ' + i);
}
}
}