Смещение битового набора Java
Я использую java.util.BitSet
для хранения плотного вектора битов.
Я хочу реализовать операцию, которая сдвигает биты прямо на 1, аналогично >>>
on ints.
есть ли функция библиотеки, которая сдвигает BitSet
s?
если нет, есть ли лучший способ, чем ниже?
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...
}
}
8 ответов
это должно сделать трюк:
BitSet shifted = bs.get(1, bs.length());
это даст вам битовый набор, равный первоначальному, но без самого нижнего бита.
EDIT:
обобщить это n
бит
BitSet shifted = bs.get(n, Math.max(n, bs.length()));
альтернативой, которая, вероятно, более эффективна, была бы работа с базовым long[].
использовать bitset.toLongArray()
для получения базовых данных. Сдвиньте эти длины соответственно, а затем создайте новый битовый набор через BitSet.valueOf(long[])
вам нужно будет очень осторожно сдвигать базовые лонги, так как вам придется взять бит низкого порядка и переместить его в бит высокого порядка на следующий лонг в массиве.
этой должны позвольте вам использовать собственные операции сдвига битов ваш процессор для перемещения 64 бит за раз, в отличие от итерации через каждый отдельно.
EDIT: на основе комментария Луиса Вассермана. Это доступно только в Java 1.7 API. Не понимал этого, когда писал.
пожалуйста, найдите этот блок кода, где битовый набор "сдвинут влево"
/**
* 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);
}
эти функции имитируют операторы > > соответственно.
/**
* 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);
}
вы можете посмотреть на BitSet toLongArray
и valueOf(long[])
.
В основном получить long
массив, смещение long
S и построить новый BitSet
из сдвинутого массива.
для достижения лучшей производительности вы можете расширить java.утиль.BitSet реализация и избежать ненужного копирования массива. Вот реализация (я в основном повторно использовал реализацию Джеффа Пирсола):
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);
}
}
}
}
С java SE8, это может быть достигнуто более сжатым способом:
BitSet b = new BitSet();
b.set(1, 3);
BitSet shifted = BitSet.valueOf(Arrays.stream(
b.toLongArray()).map(v -> v << 1).toArray());
Я пытался выяснить, как использовать LongBuffer, чтобы сделать это, но не совсем получилось. Надеюсь, кто-то, кто знаком с низкоуровневым программированием, может указать на решение.
спасибо заранее!!!