简体   繁体   English

转移Java BitSet

[英]Shifting a Java BitSet

I am using a java.util.BitSet to store a dense vector of bits. 我使用java.util.BitSet来存储密集的位向量。

I want to implement an operation that shifts the bits right by 1, analogous to >>> on ints. 我想实现一个将位向右移1的操作,类似于>>> on int。

Is there a library function that shifts BitSet s? 是否有一个库函数可以移动BitSet

If not, is there a better way than the below? 如果没有,有没有比下面更好的方法?

public static void logicalRightShift(BitSet bs) {
  for (int i = 0; (i = bs.nextSetBit(i)) >= 0;) {
    // i is the first bit in a run of set bits.

    // Set any bit to the left of the run.
    if (i != 0) { bs.set(i - 1); }

    // Now i is the index of the bit after the end of the run.
    i = bs.nextClearBit(i);  // nextClearBit never returns -1.
    // Clear the last bit of the run.
    bs.clear(i - 1);

    // 0000111100000...
    //     a   b
    // i starts off the loop at a, and ends the loop at b.
    // The mutations change the run to
    // 0001111000000...
  }
}

That should do the trick: 这应该够了吧:

BitSet shifted = bs.get(1, bs.length());

It will give you a bitset equal to the orginial one, but without the lower-most bit. 它会给你一个等于orginial的bitset,但没有最低位。

EDIT: 编辑:

To generalize this to n bits, 将此概括为n位,

BitSet shifted = bs.get(n, Math.max(n, bs.length()));

Please find this code block where BitSet is "left-shifted" 请找到BitSet“左移”的代码块

/**
 * Shift the BitSet to left.<br>
 * For example : 0b10010 (=18) => 0b100100 (=36) (equivalent to multiplicate by 2)
 * @param bitSet
 * @return shifted bitSet
 */
public static BitSet leftShiftBitSet(BitSet bitSet) {
    final long maskOfCarry = 0x8000000000000000L;
    long[] aLong = bitSet.toLongArray();

    boolean carry = false;
    for (int i = 0; i < aLong.length; ++i) {
        if (carry) {
            carry = ((aLong[i] & maskOfCarry) != 0);
            aLong[i] <<= 1;
            ++aLong[i];
        } else {
            carry = ((aLong[i] & maskOfCarry) != 0);
            aLong[i] <<= 1;
        }
    }

    if (carry) {
        long[] tmp = new long[aLong.length + 1];
        System.arraycopy(aLong, 0, tmp, 0, aLong.length);
        ++tmp[aLong.length];
        aLong = tmp;
    }

    return BitSet.valueOf(aLong);
}

An alternative which is probably more efficient would be to work with the underlying long[]. 可能更有效的替代方案是使用底层的long []。

Use bitset.toLongArray() to get the underlying data. 使用bitset.toLongArray()获取基础数据。 Shift those longs accordingly, then create a new BitSet via BitSet.valueOf(long[]) You'll have to be very careful shifting the underlying longs, as you will have to take the low order bit and shift it into the high order bit on the next long in the array. 相应地移动那些长点,然后通过BitSet.valueOf(long[])创建一个新的BitSet你必须非常小心地移动底层长点,因为你必须取低位并将其转换为高位在数组的下一个长。

This should let you use the bit shift operations native on your processor to move 64 bits at a time, as opposed to iterating through each one separately. 应该允许您使用处理器本机的位移操作一次移动64位,而不是分别迭代每一位。

EDIT: Based on Louis Wasserman's comment. 编辑:根据Louis Wasserman的评论。 This is only available in Java 1.7 API. 这仅适用于Java 1.7 API。 Didn't realize that when I wrote it. 当我写它时没有意识到。

These functions mimic the << and >>> operators, respectively. 这些函数分别模仿<<和>>>运算符。

/**
 * Shifts a BitSet n digits to the left. For example, 0b0110101 with n=2 becomes 0b10101.
 *
 * @param bits
 * @param n the shift distance.
 * @return
 */
public static BitSet shiftLeft(BitSet bits, int n) {
    if (n < 0)
        throw new IllegalArgumentException("'n' must be >= 0");
    if (n >= 64)
        throw new IllegalArgumentException("'n' must be < 64");

    long[] words = bits.toLongArray();

    // Do the shift
    for (int i = 0; i < words.length - 1; i++) {
        words[i] >>>= n; // Shift current word
        words[i] |= words[i + 1] << (64 - n); // Do the carry
    }
    words[words.length - 1] >>>= n; // shift [words.length-1] separately, since no carry

    return BitSet.valueOf(words);
}

/**
 * Shifts a BitSet n digits to the right. For example, 0b0110101 with n=2 becomes 0b000110101.
 *
 * @param bits
 * @param n the shift distance.
 * @return
 */
public static BitSet shiftRight(BitSet bits, int n) {
    if (n < 0)
        throw new IllegalArgumentException("'n' must be >= 0");
    if (n >= 64)
        throw new IllegalArgumentException("'n' must be < 64");

    long[] words = bits.toLongArray();

    // Expand array if there will be carry bits
    if (words[words.length - 1] >>> (64 - n) > 0) {
        long[] tmp = new long[words.length + 1];
        System.arraycopy(words, 0, tmp, 0, words.length);
        words = tmp;
    }

    // Do the shift
    for (int i = words.length - 1; i > 0; i--) {
        words[i] <<= n; // Shift current word
        words[i] |= words[i - 1] >>> (64 - n); // Do the carry
    }
    words[0] <<= n; // shift [0] separately, since no carry

    return BitSet.valueOf(words);
}

You can use BigInteger instead of BitSet . 您可以使用BigInteger而不是BitSet BigInteger already has ShiftRight and ShiftLeft. BigInteger已经有ShiftRight和ShiftLeft。

You can look at the BitSet toLongArray and the valueOf(long[]) . 您可以查看BitSet toLongArrayvalueOf(long[])
Basically get the long array, shift the long s and construct a new BitSet from the shifted array. 基本上得到long数组,移位long s并从移位数组构造一个新的BitSet

In order to achieve better performance you can extend java.util.BitSet implementation and avoid unnecessary array copying. 为了获得更好的性能,您可以扩展java.util.BitSet实现并避免不必要的数组复制。 Here is implementation (I've basically reused Jeff Piersol implementation): 这是实现(我基本上重用了Jeff Piersol实现):

package first.specific.structure;

import java.lang.reflect.Field;
import java.util.BitSet;

public class BitSetMut extends BitSet {

    private long[] words;
    private static Field wordsField;

    static {
        try {
            wordsField = BitSet.class.getDeclaredField("words");
            wordsField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            throw new IllegalStateException(e);
        }
    }

    public BitSetMut(final int regLength) {
        super(regLength);
        try {
            words = (long[]) wordsField.get(this);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    public void shiftRight(int n) {
        if (n < 0)
            throw new IllegalArgumentException("'n' must be >= 0");
        if (n >= 64)
            throw new IllegalArgumentException("'n' must be < 64");

        if (words.length > 0) {
            ensureCapacity(n);

            // Do the shift
            for (int i = words.length - 1; i > 0; i--) {
                words[i] <<= n; // Shift current word
                words[i] |= words[i - 1] >>> (64 - n); // Do the carry
            }
            words[0] <<= n; // shift [0] separately, since no carry
            // recalculateWordInUse() is unnecessary
        }
    }

    private void ensureCapacity(final int n) {
        if (words[words.length - 1] >>> n > 0) {
            long[] tmp = new long[words.length + 3];
            System.arraycopy(words, 0, tmp, 0, words.length);
            words = tmp;
            try {
                wordsField.set(this, tmp);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
    }
}

With java SE8, it can be achieved more concise way: 使用java SE8,它可以实现更简洁的方式:

BitSet b = new BitSet();
b.set(1, 3);
BitSet shifted = BitSet.valueOf(Arrays.stream(
       b.toLongArray()).map(v -> v << 1).toArray());

I was trying to figure out how to use LongBuffer to do so but not quite got it to work. 我试图弄清楚如何使用LongBuffer来做到这一点,但并没有让它发挥作用。 Hopefully, someone who is familiar with low level programming can point out a solution. 希望熟悉低级编程的人可以指出解决方案。

Thanks in advance!!! 提前致谢!!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM