Каков наиболее эффективный способ найти индекс самого левого/правого неустановленного бита в Java?

Предположим, что у нас есть int x = 371, то есть в двоичном формате 101110011. Я хочу найти индекс самого левого неустановленного бита (в данном случае 7) и индекс самого правого неустановленного бита (в данном случае 2). Каков наиболее эффективный способ сделать это?

Вот что у меня есть:

public class BitOperatons {

    public static int setBit(int x, int i) {
        int y = x | (1 << i);
        return y;
    }

    public static boolean isBitSet(int x, int i) {
        int y = setBit(0, i);
        return y == (x & y);
    }    

    public static int findLeftMostSetBit(int x) {
        for (int i = 31; i >= 0; i--) {
            if (isBitSet(x, i))
                return i;
        }
        return -1;
    }

    public static int findRightMostUnsetBit(int x) {
        for (int i = 0; i <= 31; i++) {
            if (! isBitSet(x, i))
                return i;
        }
        return -1;
    }

    public static int findLeftMostUnsetBit(int x) {
        int k = findLeftMostSetBit(x);
        for (int i = k; i >= 0; i--) {
            if (! isBitSet(x, i))
                return i;
        }
        return -1;
    }

    public static1+ void main(String[] args) {
        int x = 
            (1 << 0) |
            (1 << 1) |
            (1 << 4) |
            (1 << 5) |
            (1 << 6) |
            (1 << 8);
        System.out.println(findLeftMostUnsetBit(x));
        System.out.println(findRightMostUnsetBit(x));
    }

}

Если я не ошибаюсь, моя текущая реализация требует линейного времени. Можем ли мы сделать лучше?


person MarcoS    schedule 27.06.2011    source источник
comment
Невозможно сделать лучше, чем линейный.   -  person toto2    schedule 27.06.2011
comment
@toto, это неправда, забыл, посмотрите на Hacker's Delight 5-3 (например) (ведущие нули, которые являются частью Integer.class)   -  person bestsss    schedule 27.06.2011
comment
@bestsss, хорошо, если вы имеете в виду машинные инструкции, предложенные flolo. Если вы знаете о сублинейном алгоритме, я хотел бы знать.   -  person toto2    schedule 27.06.2011
comment
@toto, нет, это не инструкция, предоставленная ассемблером или процессором. это бинарный поиск вместо линейного :). Серьезно попытайтесь найти экземпляр Hacker's Delight (книги) и проверить его. Я опубликую, как сделать ведущую часть с нулями. Я уверен, что даже у SO будет кое-что.   -  person bestsss    schedule 27.06.2011
comment
@bestsss Да, я думал об этом прямо сейчас. Я хотел написать это как ответ. Спасибо. Еще лучше использовать Java API. (Редактировать: только что проверил код Integer.numberOfTrailingZeros, и это двоичный поиск.)   -  person toto2    schedule 27.06.2011
comment
graphics.stanford.edu/~seander/bithacks.html — интересная статья о этот предмет. Однако не все представленные алгоритмы подходят для Java.   -  person Paul Cager    schedule 27.06.2011


Ответы (2)


В классе Integer доступны методы.

Integer.numberOfTrailingZeros(Integer.lowestOneBit(~yourValue)) сделал бы это для самого младшего неустановленного бита, для самого старшего это немного сложнее, так как мы сначала должны определить старший установленный бит.

int leadingZeroBits = Integer.numberOfLeadingZeros(Integer.highestOneBit(yourValue));
result = Integer.
       numberOfTrailingZeros(Integer.highestOneBit((~yourValue)<<leadingZeroBits)
      -leadingZeroBits;`

Следует сделать это для старшего неустановленного бита.

И это может быть быстрее, чем линейное время, поскольку процессоры часто имеют машинные инструкции для быстрого определения начального/конечного нулевого бита (но не уверен, что виртуальная машина использует их РЕДАКТИРОВАТЬ: теперь я уверен ;-).

EDIT: Кажется, они добавили использование asm встроенные функции для начальных/конечных нулей в 1.6.0_18, идентификатор 6823354

person flolo    schedule 27.06.2011
comment
хм, действительно, встроенные функции там, где это возможно, выглядят как инструкция процессора. - person bestsss; 27.06.2011

Ниже приведен исходный код Integer.numberOfLeadingZeros. Как указано, это взято из HD (Hacker's Delight Генри С. Уоррена-младшего)

Основная идея заключается в использовании бинарного поиска вместо перебора битов один за другим. Пожалуйста, загляните в книгу, если вы заинтересованы в твиддлинге. Это прекрасное произведение искусства.

public static int numberOfLeadingZeros(int i) {
    // HD, Figure 5-6
    if (i == 0)
        return 32;
    int n = 1;
    if (i >>> 16 == 0) { n += 16; i <<= 16; }
    if (i >>> 24 == 0) { n +=  8; i <<=  8; }
    if (i >>> 28 == 0) { n +=  4; i <<=  4; }
    if (i >>> 30 == 0) { n +=  2; i <<=  2; }
    n -= i >>> 31;
    return n;
}
person Community    schedule 27.06.2011
comment
Код, реализованный в библиотеке Sun (Oracle), немного отличается, но эквивалентен. - person toto2; 27.06.2011
comment
@bestsss: numberOfLeadingZeros — более эффективная реализация моего findLeftMostSetBit. Однако я не понимаю, как использовать его для ответа на мой вопрос, то есть найти индекс самого левого/правого неустановленного бита. Если у нас есть 0000000000000000000000101110011, то я хочу найти индекс самого левого неустановленного бита, который равен 7, и индекс самого правого неустановленного бита, который равен 2. Как бы вы это сделали? (спасибо за рекомендацию Hacker's Delight Генри С. Уоррена-младшего: это очень интересная книга, и я внесу ее в свой список пожеланий :) ) - person MarcoS; 28.06.2011
comment
@MarcoS, посмотрите на ответ Флоло, чтобы понять, этот ответ в основном был нацелен на замечание Тото в комментариях. Комментарий Пола Кейджера также содержит очень полезную коллекцию C (также может быть перенесена на java) немного крутящихся хаков. - person bestsss; 28.06.2011
comment
@bestsss: Понятно! ... поэтому я должен выбрать в качестве ответов и ваш, и пост Флоло, но я не могу. Итак, я выберу flolo в качестве ответа, но все же проголосую за ваш. - person MarcoS; 28.06.2011
comment
@MarcoS, я назвал это вики сообщества не просто так. Тем не менее, что касается HD, если вы заинтересованы в том, чтобы немного покрутить его, это главный источник идей; танец битов очаровывал меня, когда я был ребенком (занимался ассемблером) - person bestsss; 28.06.2011