Search code examples
phpregexbinarynegation

How to effectively convert positive number in base -2 to a negative one in base - 2?


Old question name: How to effectively split a binary string in a groups of 10, 0, 11?

I have some strings as an input, which are binary representation of a number. For example:

10011
100111
0111111
11111011101

I need to split these strings (or arrays) into groups of 10, 0, and 11 in order to replace them.

10 => 11
0 => 0
11 => 10

How to do it? I have tried these options but don't work.

preg_match('/([10]{2})(0{1})([11]{2})/', $S, $matches);

It should be [10] [0], [11] for 10011 input. And it should be 11010 when replaced.

UPD1.

Actually, I'm trying to do a negation algorithm for converting a positive number in a base -2 to a negative one in a base -2. It could be done with an algorithm from Wikipedia with a loop. But byte groups replacing is a much faster. I have implemented it already and just trying to optimize it.

For this case 0111111 it's possible to add 0 in the end. Then rules will be applied. And we could remove leading zeros in a result. The output will be 101010.

UPD2.

@Wiktor Stribiżew proposed an idea how to do a replace immediately, without splitting bytes into groups first. But I have a faster solution already.

$S = strtr($S, $rules);

The meaning of this question isn't do a replacement, but get an array of desired groups [11] [0] [10].

UPD3.

This is a solution which I reached with an idea of converting binary groups. It's faster than one with a loop.

function solution2($A)
{
    $S = implode('', $A);

    //we could add leading 0
    if (substr($S, strlen($S) - 1, 1) == 1) {
        $S .= '0';
    }

    $rules = [
        '10' => '11',
        '0'  => '0',
        '11' => '10',
    ];

    $S = strtr($S, $rules);

    $arr = str_split($S);

    //remove leading 0
    while ($arr[count($arr) - 1] == 0) {
        array_pop($arr);
    }

    return $arr;
}

But the solution in @Alex Blex answer is faster.


Solution

  • Answering the question

    algorithm for converting a positive number in a base -2 to a negative one in a base -2

    I believe following function is more efficient than a regex:

    function negate($negabin)
    {
        $mask = 0xAAAAAAAAAAAAAAA;
        return decbin((($mask<<1)-($mask^bindec($negabin)))^$mask);     
    }
    

    Parameter is a positive int60 in a base -2 notation, e.g. 11111011101.

    The function converts the parameter to base 10, negate it, and convert it back to base -2 as described in the wiki: https://en.wikipedia.org/wiki/Negative_base#To_negabinary

    Works on 64bit system, but can easily adopted to work on 32bit.