Побитовый сдвиг для создания всех возможных перестановок в C

Возможный дубликат:
Создание несколько чисел с определенным количеством битов

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

Например, я хотел найти все возможные комбинации из 3 бит (где максимальная цифра может быть 6), массив должен содержать:

000111
001011
001101
001110
010011
010101
010110
011001
011010
011100
100011

И так далее...

Из того, что я интерпретировал, когда последний бит позиции равен 1, мы сдвигаем число на 1 (x >> 1) и добавляем 1 в начале. Однако я не уверен, как кодировать остальные. Я использую C, чтобы написать это.

Кроме того, насколько я могу судить, это последовательность колекса, однако я все уши, если есть другая последовательность, которая даст мне тот же конечный результат (массив со всеми возможными комбинациями k-битов с ограничением N) .


person hungrii    schedule 17.09.2011    source источник


Ответы (3)


Вы можете решить эту проблему, рекурсивно генерируя последовательности.

Давайте определим рекурсивную функцию f(int index, int bits, int number), которая будет принимать текущие index бита и количество bits, оставшихся для размещения, и number, сгенерированных на данный момент. Затем у вас есть возможность установить для текущего бита значение 1 или 0 и выполнить рекурсию оттуда.

В целом, временная сложность должна быть O (количество последовательностей) или O (N выбирает B), где N — количество цифр, а B — количество битов, установленных на 1.

Функция выглядит примерно так:

void f(int index, int bits, int number) {
    if (index == 0) {
        if (bits == 0) {   // all required bits have been used
            emit_answer(number); // chuck number into an array, print it, whatever.
        }   
        return;
    }   

    if (index-1 >= bits) {  // If we can afford to put a 0 here
        f(index-1, bits, number);
    }   

    if (bits > 0) {  // If we have any 1s left to place
        f(index-1, bits-1, number | (1 << (index-1)));
    }   
}

// to call:
f(6, 3, 0); 

Для N,B = 6,3 выходные данные совпадают с вашими и отсортированы. Ссылка на рабочий пример: http://codepad.org/qgd689ZM

person evgeny    schedule 17.09.2011
comment
Спасибо @evgeny. Поиграл с этим и узнал несколько вещей. Просто по поводу дюпа - для тех, кому интересно, перейдите по ссылке дюпа и почитайте Стэнфордский ресурс. Мне сильно помог. - person hungrii; 17.09.2011

Вероятно, есть более эффективный способ, но вы могли бы просто перебрать числа и отклонить числа, у которых битовое число не равно 3? См. этот ответ для подсчета битов.

person dantswain    schedule 17.09.2011
comment
Затем вам нужно пройти через 2 ^ N комбинаций, что может стать довольно медленным. Опять же, ему это нужно только для 6 бит, так что все должно быть в порядке. - person flight; 17.09.2011
comment
Это довольно медленно, O (2 ^ N), как указал квазивселенная. Мое решение должно генерировать все допустимые последовательности без каких-либо потерь. - person evgeny; 17.09.2011
comment
Да конечно. Это О (2 ^ 6). Хотя я бы сказал, что если скорость действительно проблема, он мог бы рассчитать числа заранее и сохранить их. Тогда это будет O(1) :-p Не говоря уже о читабельности этого решения по сравнению с некоторыми другими. пожал плечами - person dantswain; 17.09.2011

Нет необходимости в какой-либо причудливой рекурсии. Достаточно простой математики (требуется деление на значение, которое всегда будет степенью двойки).

    Function nextBits(ByVal prevVal As Integer)
        Dim lsOne As Integer = ((prevVal - 1) And Not prevVal) + 1
        Dim nextZero As Integer = (prevVal + lsOne) And Not prevVal
        Dim lowBits As Integer = ((nextZero \ lsOne \ 2) - 1)
        Return prevVal + lsOne + lowBits
    End Function

Легко и приятно.

person supercat    schedule 17.09.2011
comment
Эээ... мой мозг болит от кода. Кроме того, что такое оператор \? И, возможно, объяснение того, почему это работает, поможет. - person flight; 17.09.2011
comment
Пожалуйста, добавьте больше пояснений к коду. OP, кажется, интересуется кодом C, и это скорее похоже на VB-ish. - person Hritik; 20.10.2018
comment
Прости. Я протестировал алгоритм в VB.NET и скопировал/вставил код. Тот же алгоритм должен легко адаптироваться к любому другому общему языку. Я забыл об одном аспекте VB.NET, о котором люди не знали, что он использует другой оператор для дробного деления и целочисленного деления, поэтому 15 / 4 дает 3,75, а 15 \ 4 дает 3. Эквивалентом Python будет оператор //, и эквивалентом C является оператор / с целочисленными операндами (требующий явного приведения или принуждения, если кто-то хочет разделить два целых числа, чтобы получить дробный результат). - person supercat; 20.10.2018
comment
@Hritik: Я подумал, что вопрос может показаться домашним заданием, поэтому я решил, что хочу направить человека в правильном направлении, не давая решения, которое он мог бы использовать, не задумываясь или не понимая. Попробовав несколько разных значений и увидев, что каждая переменная оказывается в двоичном виде, вы должны понять, что происходит. - person supercat; 20.10.2018