Shifting a Java BitSet

后端 未结 8 1535
太阳男子
太阳男子 2020-12-31 00:11

I am using a java.util.BitSet to store a dense vector of bits.

I want to implement an operation that shifts the bits right by 1, analogous to >

相关标签:
8条回答
  • 2020-12-31 00:43

    In order to achieve better performance you can extend java.util.BitSet implementation and avoid unnecessary array copying. Here is implementation (I've basically reused Jeff Piersol implementation):

    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);
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-31 00:46

    An alternative which is probably more efficient would be to work with the underlying long[].

    Use bitset.toLongArray() to get the underlying data. 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.

    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.

    EDIT: Based on Louis Wasserman's comment. This is only available in Java 1.7 API. Didn't realize that when I wrote it.

    0 讨论(0)
  • 2020-12-31 00:46

    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);
    }
    
    0 讨论(0)
  • 2020-12-31 00:46

    You can look at the BitSet toLongArray and the valueOf(long[]).
    Basically get the long array, shift the longs and construct a new BitSet from the shifted array.

    0 讨论(0)
  • 2020-12-31 00:54

    Please find this code block where BitSet is "left-shifted"

    /**
     * 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);
    }
    
    0 讨论(0)
  • 2020-12-31 00:57

    You can use BigInteger instead of BitSet. BigInteger already has ShiftRight and ShiftLeft.

    0 讨论(0)
提交回复
热议问题