Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Permutations of binary number by swapping two bits (not lexicographically)

I'm looking for an algorithm which computes all permutations of a bitstring of given length (n) and amount of bits set (k). For example while n=4 and k=2 the algorithm shall output:

1100
1010
1001
0011
0101
0110

I'm aware of Gosper's Hack which generates the needed permutations in lexicographic order. But i need them to be generated in such a manner, that two consecutive permutations differ in only two (or at least a constant number of) bitpositions (like in the above example). Another bithack to do that would be awesome, but also a algorithmic description would help me alot.

like image 992
Memphisd Avatar asked Apr 06 '16 12:04

Memphisd


2 Answers

Walking bit algorithm

To generate permutations of a binary sequence by swapping exactly one set bit with an unset bit in each step (i.e. the Hamming distance between consecutive permutations equals two), you can use this "walking bit" algorithm; the way it works is similar to creating the (reverse) lexicographical order, but the set bits walk right and left alternately, and as a result some parts of the sequence are mirrored. This is probably better explained with an example:

walking-bit permutations example

Recursive implementation

A recursive algorithm would receive a sequence of n bits, with k bits set, either all on the left or all on the right. It would then keep a 1 at the end, recurse with the rest of the sequence, move the set bit and keep 01 at the end, recurse with the rest of the bits, move the set bit and keep 001 at the end, etc... until the last recursion with only set bits. As you can see, this creates alternating left-to-right and right-to-left recursions.
When the algorithm is called with a sequence with only one bit set, this is the deepest recursion level, and the set bit walks from one end to the other.

walking-bit permutations recursion


Code example 1

Here's a simple recursive JavaScript implementation:

function walkingBits(n, k) {
    var seq = [];
    for (var i = 0; i < n; i++) seq[i] = 0;
    walk (n, k, 1, 0);

    function walk(n, k, dir, pos) {
        for (var i = 1; i <= n - k + 1; i++, pos += dir) {
            seq[pos] = 1;
            if (k > 1) walk(n - i, k - 1, i%2 ? dir : -dir, pos + dir * (i%2 ? 1 : n - i))
            else document.write(seq + "<BR>");
            seq[pos] = 0;
        }
    }
}

walkingBits(7,3);

Translated into C++ that could be something like this:

#include <iostream>
#include <string>

void walkingBits(int n, int k, int dir = 1, int pos = 0, bool top = true) {
    static std::string seq;
    if (top) seq.resize(n, '0');
    for (int i = 1; i <= n - k + 1; i++, pos += dir) {
        seq[pos] = '1';
        if (k > 1) walkingBits(n - i, k - 1, i % 2 ? dir : -dir, pos + dir * (i % 2 ? 1 : n - i), false);
        else std::cout << seq << '\n';
        seq[pos] = '0';
    }
    if (top) seq.clear();
}

int main() {
    walkingBits(7, 3);
}

(See also [this C++11 version][3], written by VolkerK in response to a question about the above code.)

(Rextester seems to have been hacked, so I've pasted Volker's code below.)

#include <iostream>
#include <vector>
#include <functional>

void walkingBits(size_t n, size_t k) {
    std::vector<bool> seq(n, false);
    std::function<void(const size_t, const size_t, const int, size_t)> walk = [&](const size_t n, const size_t k, const int dir, size_t pos){
        for (size_t i = 1; i <= n - k + 1; i++, pos += dir) {
            seq[pos] = true;
            if (k > 1) {
                walk(n - i, k - 1, i % 2 ? dir : -dir, pos + dir * (i % 2 ? 1 : n - i));
            }
            else {
                for (bool v : seq) {
                    std::cout << v;
                }
                std::cout << std::endl;;
            }
            seq[pos] = false;
        }
    };
    walk(n, k, 1, 0);
}

int main() {
    walkingBits(7, 3);

    return 0;
}

Code example 2

Or, if you prefer code where elements of an array are actually being swapped:

function walkingBits(n, k) {
    var seq = [];
    for (var i = 0; i < n; i++) seq[i] = i < k ? 1 : 0;
    document.write(seq + "<BR>");
    walkRight(n, k, 0);

    function walkRight(n, k, pos) {
        if (k == 1) for (var p = pos + 1; p < pos + n; p++) swap(p - 1, p)
        else for (var i = 1; i <= n - k; i++) {
            [walkLeft, walkRight][i % 2](n - i, k - 1, pos + i);
            swap(pos + i - 1, pos + i + (i % 2 ? 0 : k - 1));
        }
    }
    function walkLeft(n, k, pos) {
        if (k == 1) for (var p = pos + n - 1; p > pos; p--) swap(p - 1, p)
        else for (var i = 1; i <= n - k; i++) {
            [walkRight, walkLeft][i % 2](n - i, k - 1, pos);
            swap(pos + n - i - (i % 2 ? 1 : k), pos + n - i);
        }
    }
    function swap(a, b) {
        var c = seq[a]; seq[a] = seq[b]; seq[b] = c;
        document.write(seq + "<BR>");
    }
}

walkingBits(7,3);

Code example 3

Here the recursion is rolled out into an iterative implementation, with each of the set bits (i.e. each of the recursion levels) represented by an object {o,d,n,p} which holds the offset from the leftmost position, the direction the set bit is moving in, the number of bits (i.e. the length of this part of the sequence), and the current position of the set bit within this part.

function walkingBits(n, k) {
    var b = 0, seq = [], bit = [{o: 0, d: 1, n: n, p: 0}];
    for (var i = 0; i < n; i++) seq.push(0);

    while (bit[0].p <= n - k) {
        seq[bit[b].o + bit[b].p * bit[b].d] = 1;
        while (++b < k) {
            bit[b] = {
                o: bit[b-1].o + bit[b-1].d * (bit[b-1].p %2 ? bit[b-1].n-1 : bit[b-1].p+1),
                d: bit[b-1].d * (bit[b-1].p %2 ? -1 : 1),
                n: bit[b-1].n - bit[b-1].p - 1,
                p: 0
            }
            seq[bit[b].o + bit[b].p * bit[b].d] = 1;
        }
        document.write(seq + "<BR>");
        b = k - 1;
        do seq[bit[b].o + bit[b].p * bit[b].d] = 0;
        while (++bit[b].p > bit[b].n + b - k && b--);
    }
}

walkingBits(7, 3); // n >= k > 0

Transforming lexicographical order into walking bit

Because the walking bit algorithm is a variation of the algorithm to generate the permutations in (reverse) lexicographical order, each permutation in the lexicographical order can be transformed into its corresponding permutation in the walking bit order, by mirroring the appropriate parts of the binary sequence.
So you can use any algorithm (e.g. Gosper's Hack) to create the permutations in lexicographical or reverse lexicographical order, and then transform each one to get the walking bit order.

lexicographical to walking bit transform

Practically, this means iterating over the binary sequence from left to right, and if you find a set bit after an odd number of zeros, reversing the rest of the sequence and iterating over it from right to left, and so on...

Code example 4

In the code below the permutations for n,k = 7,3 are generated in reverse lexicographical order, and then transformed one-by-one:

function lexi2walk(lex) {
    var seq = [], ofs = 0, pos = 0, dir = 1; 
    for (var i = 0; i < lex.length; ++i) {
        if (seq[ofs + pos * dir] = lex[i]) {
            if (pos % 2) ofs -= (dir *= -1) * (pos + lex.length - 1 - i)
            else ofs += dir * (pos + 1);
            pos = 0;
        } else ++pos;
    }
    return seq;
}

function revLexi(seq) {
    var max = true, pos = seq.length, set = 1;
    while (pos-- && (max || !seq[pos])) if (seq[pos]) ++set; else max = false;
    if (pos < 0) return false;
    seq[pos] = 0;
    while (++pos < seq.length) seq[pos] = set-- > 0 ? 1 : 0;
    return true;
}
    
var s = [1,1,1,0,0,0,0];
document.write(s + " &rarr; " + lexi2walk(s) + "<br>");
while (revLexi(s)) document.write(s + " &rarr; " + lexi2walk(s) + "<br>");

Homogeneous Gray path

The permutation order created by this algorithm is similar, but not identical, to the one created by the "homogeneous Gray path for combinations" algorithm described by D. Knuth in The Art of Computer Programming vol. 4a, sect. 7.2.1.3, formula (31) & fig. 26c.

walking bit vs. homogeneous (31)

like image 91

This is easy to achieve with recursion:

public static void nextPerm(List<Integer> list, int num, int index, int n, int k) {
    if(k == 0) {
        list.add(num);
        return;
    }

    if(index == n) return;

    int mask = 1<<index;

    nextPerm(list, num^mask, index+1, n, k-1);
    nextPerm(list, num, index+1, n, k);
}

Running this with the client:

public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<Integer>();
    nextPerm(list, 0, 0, 4, 2);
}  

Output:

0011
0101
1001
0110
1010
1100

The idea is to start with the initial number, and consider changing a bit, one index at a time, and to keep track of how many times you changed the bits. Once you changed the bits k times (when k == 0), store the number and terminate the branch.

like image 22
Maljam Avatar answered Nov 14 '22 03:11

Maljam