如何在Java的BitSet中将位向左和向右移动?

huangapple go评论65阅读模式
英文:

How to shift bits to left and right in BitSet JAVA?

问题

我有一个有 100000 位的位集。我希望尽可能高效地将其向右移和向左移。我猜 BitSet 类中没有移位位的函数,所以我尝试使用 toLongArray() 将它们转换为长整数数组,但预期会溢出。

我找到的一个临时解决方案是将位集转换为 BigInteger,然后移位 BigInteger,然后将 BigInteger 转换回位集,但速度非常慢。

所以我的问题是:

  1. 是否有更好的方法来移位位集(移位指左移和右移)?
  2. 当我将位数大于 64 的位集转换为长整数数组时,数组中会出现负数。为什么一开始就有一个 toLongArray() 函数,当 BitSet 只能表示 1 个数字的位?如果我有错误,请纠正我。

以下是使用 BigInteger 的代码。

public static BitSet toBitSet(BigInteger val) {
  if(val.signum() < 0)
  throw new IllegalArgumentException("负值:" + val);
  return BitSet.valueOf(reverse(val.toByteArray()));
}
static byte[] reverse(byte[] bytes) {
  for(int i = 0; i < bytes.length/2; i++) {
    byte temp = bytes[i];
    bytes[i] = bytes[bytes.length-i-1];
    bytes[bytes.length-i-1] = temp;
  }
  return bytes;
}
public static BigInteger toBigInteger(BitSet val) {
  return new BigInteger(1, reverse(val.toByteArray()));
}
public static BitSet shiftLeft(BitSet bits, int n) {
  BigInteger temp= toBigInteger(bits);
  temp= temp.shiftLeft(n);
  return toBitSet(temp);
}
英文:

I have a bitset with 100000 number of bits. I want to shift it to the right and to the left as efficiently as possible. I guess there is no function in BitSet class to shift bits, so I tried converting them into long array using toLongArray() but as expected it gives overflow.

A temporary solution which I found was to convert bitset into BigInteger and then shift the BigInteger and then convert BigInteger back to bitset, but that is very slow.

So my questions are:

  1. Is there any better way to shift a bitset (by shifting I mean both left and right shift)
  2. When I convert a bitset with number of bits greater than 64, into long array, I get negatives in the array. Why is there a function toLongArray() in the first place when BitSet can only represent bits of 1 number. Please correct me if I am wrong.

Following is the code to using BigInteger.

public static BitSet toBitSet(BigInteger val) {
  if(val.signum() &lt; 0)
  throw new IllegalArgumentException(&quot;Negative value: &quot; + val);
  return BitSet.valueOf(reverse(val.toByteArray()));
}
static byte[] reverse(byte[] bytes) {
  for(int i = 0; i &lt; bytes.length/2; i++) {
    byte temp = bytes[i];
    bytes[i] = bytes[bytes.length-i-1];
    bytes[bytes.length-i-1] = temp;
  }
  return bytes;
}
public static BigInteger toBigInteger(BitSet val) {
  return new BigInteger(1, reverse(val.toByteArray()));
}
public static BitSet shiftLeft(BitSet bits, int n) {
  BigInteger temp= toBigInteger(bits);
  temp= temp.shiftLeft(n);
  return toBitSet(temp);
}

PS: All answers that I found were for number of bits <= 64

答案1

得分: 2

当我将比特位数大于64的位集转换为长整型数组时,数组中会出现负数。

负数元素是完全可以的,这只是使用long的所有64位的结果,其中包括了符号位。只要你预先考虑到这一点,这并不重要。基本上,只有在使用有符号解释打印时才会显得奇怪,否则这不是一件坏事。

因此,我尝试使用toLongArray()将它们转换为长整型数组,但预期会出现溢出。

虽然这是一个合理的实现策略,让我们来修复它。我猜你在转换每个元素时单独进行了移位,但没有处理“进位”。左移的操作是,一个元素的最高位被放入下一个元素的最低位。右移则相反。例如,对于n 值小于等于63时(未经测试):

static BitSet shiftRight(BitSet bits, int n) {
    long[] raw = bits.toLongArray();
    long carry = 0;
    for (int i = raw.length - 1; i >= 0; i--) {
        long newcarry = raw[i] << -n;
        raw[i] = (raw[i] >>> n) | carry;
        carry = newcarry;
    }
    return BitSet.valueOf(raw);
}

如果n 可以是64或更大,还有一个额外的问题,涉及将整个元素移动到不同的位置。

左移的额外问题是,结果集的大小可能会更大(不是指具有更多设置位,而是在物理上更大,需要更长的数组),与输入相比。

英文:

> When I convert a bitset with number of bits greater than 64, into long array, I get negatives in the array.

Negative elements are perfectly fine, that's just a consequence of using all 64 bits of a long, thereby including the sign bit. That doesn't matter as long as you anticipate it. Basically, it only looks strange when printing it with a signed interpretation, otherwise it's not a bad thing.

> so I tried converting them into long array using toLongArray() but as expected it gives overflow.

That is a reasonable implementation strategy though, so let's fix it. I guess you shifted each element individually without handling the "carry". What needs to happen for a left shift, is that the top bit(s) of an element is put into the bottom bit(s) of the next element. For a right shift it's the opposite of that. For example, for n up to 63, (not tested)

static BitSet shiftRight(BitSet bits, int n) {
    long[] raw = bits.toLongArray();
    long carry = 0;
    for (int i = raw.length - 1; i &gt;= 0; i--) {
        long newcarry = raw[i] &lt;&lt; -n;
        raw[i] = (raw[i] &gt;&gt;&gt; n) | carry;
        carry = newcarry;
    }
    return BitSet.valueOf(raw);
}

If n can be 64 or larger, there is an extra complication of moving whole elements to different positions.

Shifting left has the additional complication that the resulting set may be larger (not with more set bits, but physically larger, requiring a longer array) than the input.

huangapple
  • 本文由 发表于 2020年10月11日 20:43:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/64304146.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定