移动 Java 位集

发布于 2024-12-29 03:21:33 字数 766 浏览 0 评论 0原文

我正在使用 java.util.BitSet 来存储密集的位向量。

我想实现一个将位右移 1 的操作,类似于整数上的 >>>

是否有库函数可以转换 BitSet?

如果没有,还有比下面更好的方法吗?

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...
  }
}

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 >>> on ints.

Is there a library function that shifts BitSets?

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...
  }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

草莓酥 2025-01-05 03:21:34

为了获得更好的性能,您可以扩展java.util.BitSet实现并避免不必要的数组复制。这是实现(我基本上重用了 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);
            }
        }
    }
}

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);
            }
        }
    }
}
笙痞 2025-01-05 03:21:34

使用 java SE8,可以实现更简洁的方式:

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

我试图弄清楚如何使用 LongBuffer 来做到这一点,但还没有完全实现它。希望熟悉底层编程的人能够指出解决方案。

提前致谢!!!

With java SE8, it can be achieved more concise way:

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. Hopefully, someone who is familiar with low level programming can point out a solution.

Thanks in advance!!!

左耳近心 2025-01-05 03:21:33

这应该可以解决问题:

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

它会给你一个等于原始位的位集,但没有最低位。

编辑:

要将其概括为 n 位,

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

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.

EDIT:

To generalize this to n bits,

BitSet shifted = bs.get(n, Math.max(n, bs.length()));
九歌凝 2025-01-05 03:21:33

另一种可能更有效的替代方案是使用底层的 long[]。

使用bitset.toLongArray()来获取底层数据。相应地移动这些长整型,然后通过 BitSet.valueOf(long[]) 创建一个新的 BitSet 您必须非常小心地移动基础长整型,因为您必须采用低位并将其移入数组中下一个 long 的高位。

应该允许您使用处理器上本机的位移操作一次移动 64 位,而不是单独迭代每个位。

编辑:基于路易斯·沃瑟曼的评论。这仅在 Java 1.7 API 中可用。我写的时候没意识到这一点。

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.

梅倚清风 2025-01-05 03:21:33

请找到 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);
}

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);
}
别再吹冷风 2025-01-05 03:21:33

您可以使用 BigInteger 代替 BitSet。 BigInteger 已经有 ShiftRight 和 ShiftLeft。

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

薄凉少年不暖心 2025-01-05 03:21:33

这些函数模仿了<<和>>分别是运营商。

/**
 * 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);
}

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);
}
风和你 2025-01-05 03:21:33

您可以查看 BitSet toLongArrayvalueOf(long[])
基本上获取long数组,移位long并从移位后的数组构造一个新的BitSet

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文