Старое название вопроса: Как эффективно разбить двоичную строку на группы по 10, 0, 11?
У меня есть несколько строк в качестве входных данных, которые представляют собой двоичное представление числа.
Например:
10011
100111
0111111
11111011101
Мне нужно разбить эти строки (или массивы) на группы по 10, 0 и 11, чтобы заменить их.
10 => 11
0 => 0
11 => 10
Как это сделать? Я пробовал эти варианты, но не работает.
preg_match('/([10]{2})(0{1})([11]{2})/', $S, $matches);
Должно быть [10] [0], [11] для ввода 10011.
И это должно быть 11010 при замене.
UPD1.
На самом деле, я пытаюсь сделать алгоритм отрицания для преобразования положительного числа в базе -2 в отрицательное в базе -2.
Это можно сделать с помощью алгоритма из Википедии с циклом. Но замена групп байтов происходит намного быстрее. Я уже реализовал это и просто пытаюсь оптимизировать это.
Для этого случая 0111111
возможно добавить 0 в конце. Тогда правила будут применяться. И мы могли бы удалить ведущие нули в результате. Выход будет 101010.
UPD2.
@Wiktor Stribiżew предложил идею, как сделать замену немедленно, не разбивая байты на группы.
Но у меня уже есть более быстрое решение.
$S = strtr($S, $rules);
Смысл этого вопроса состоит не в замене, а в получении массива желаемых групп [11] [0] [10].
UPD3.
Это решение, которое я достиг с идеей преобразования бинарных групп. Это быстрее, чем один с петлей.
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;
}
Но решение в ответе @Alex Blex быстрее.
Отвечая на вопрос
алгоритм для преобразования положительного числа в базе -2 в отрицательное в базе -2
Я считаю, что следующая функция более эффективна, чем регулярное выражение:
function negate($negabin)
{
$mask = 0xAAAAAAAAAAAAAAA;
return decbin((($mask<<1)-($mask^bindec($negabin)))^$mask);
}
Параметр является положительным значением int60 в нотации с основанием -2, например, 11111011101.
Функция преобразует параметр в базу 10, отрицает его и преобразует обратно в базу -2, как описано в вики: https://en.wikipedia.org/wiki/Negative_base#To_negabinary
Работает на 64-битной системе, но может быть легко адаптирован для работы на 32-битной.
Вы можете использовать простой /11|10/
регулярное выражение с preg_replace_callback
:
$s = '10011';
echo preg_replace_callback("/11|10/", function($m) {
return $m[0] == "11" ? "10" : "11"; // if 11 is matched, replace with 10 or vice versa
}, $s);
// => 11010
Увидеть онлайн PHP демо.