Число 1s в двух дополнительных двоичных представлениях целых чисел в диапазоне
Эта проблема относится к 2011 Codesprint (http://csfall11.interviewstreet.com/):
Одна из основ компьютерной науки - знание того, как числа представлены в 2 дополнениях. Представьте, что вы записываете все числа между A и B включительно в 2 дополнениях с использованием 32 бит. Сколько всего вы запишете?
Входные данные:
Первая строка содержит количество тестовых примеров T (< 1000). Каждая из следующих T строк содержит два целых числа A и B.
Вывод:
Выходные линии T, соответствующие каждому тестовому примеру.
Ограничения:
-2 ^ 31 <= A <= B <= 2 ^ 31 - 1
Пример ввода:
3
-2 0
-3 4
-1 4
Результат выборки:
63
99
37
Объяснение:
В первом случае -2 содержит 31 1, за которым следует 0, -1 содержит 32 1 и 0 содержит 0 1. Таким образом, общее число составляет 63.
Для второго случая ответ равен 31 + 31 + 32 + 0 + 1 + 1 + 2 + 1 = 99
Я понимаю, что вы можете использовать тот факт, что число 1s в -X равно числу 0s в дополнении (-X) = X-1, чтобы ускорить поиск. В решении утверждается, что для генерации ответа существует рекуррентное отношение O (log X), но я его не понимаю. Код решения можно посмотреть здесь: https://gist.github.com/1285119
Я был бы признателен, если бы кто-нибудь мог объяснить, как это отношение получается!
Ответы
Ответ 1
Ну, это не так сложно...
Функция с одним аргументом solve(int a)
является ключом. Он короткий, поэтому я разрежу его и вставьте сюда:
long long solve(int a)
{
if(a == 0) return 0 ;
if(a % 2 == 0) return solve(a - 1) + __builtin_popcount(a) ;
return ((long long)a + 1) / 2 + 2 * solve(a / 2) ;
}
Он работает только для неотрицательных a и подсчитывает количество 1 бита во всех целых числах от 0 до a
включительно.
Функция имеет три случая:
a == 0
→ возвращает 0. Очевидно.
a
even → возвращает число 1 бит в a
плюс solve(a-1)
. Также довольно очевидно.
Последний случай интересный. Итак, как мы подсчитываем количество 1 бит от 0 до нечетного числа a
?
Рассмотрим все целые числа от 0 до a
и разделим их на две группы: эвенов и коэффициентов. Например, если a
равно 5, у вас есть две группы (в двоичном формате):
000 (aka. 0)
010 (aka. 2)
100 (aka. 4)
и
001 (aka 1)
011 (aka 3)
101 (aka 5)
Обратите внимание, что эти две группы должны иметь одинаковый размер (поскольку a
нечетно, а диапазон включен). Чтобы подсчитать, сколько 1 бит есть в каждой группе, сначала подсчитайте все, кроме последних бит, затем подсчитайте последние бит.
Все, кроме последних бит, выглядят следующим образом:
00
01
10
... и для обеих групп это выглядит так. Число 1 бит здесь равно solve(a/2)
. (В этом примере это число из 1 бита от 0 до 2. Также напомните, что целочисленное деление в C/С++ округляется вниз.)
Последний бит равен нулю для каждого номера в первой группе и по одному для каждого числа во второй группе, поэтому последние бит вносят (a+1)/2
один бит в итоговый.
Итак, третий случай рекурсии (a+1)/2 + 2*solve(a/2)
, с соответствующими нажатиями на long long
для обработки случая, когда a
является INT_MAX
(и, следовательно, a+1
переполнения).
Это решение O (log N). Чтобы обобщить его на solve(a,b)
, вы просто вычислите solve(b) - solve(a)
, а также соответствующую логику для беспокойства по поводу отрицательных чисел. Это то, что делает два аргумента solve(int a, int b)
.
Ответ 2
Переместите массив в ряд целых чисел. Тогда для каждого целого числа do:
int NumberOfSetBits(int i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
Также это переносимо, в отличие от __builtin_popcount
Смотрите здесь: Как подсчитать количество битов в 32-битовом целое?
Ответ 3
когда a положительно, лучшее объяснение уже было опубликовано.
Если a отрицательно, то в 32-битной системе каждое отрицательное число между а и нолем будет иметь на 32 1 бит меньше числа бит в диапазоне от 0 до двоичного представления положительного а.
Итак, лучше,
long long solve(int a) {
if (a >= 0){
if (a == 0) return 0;
else if ((a %2) == 0) return solve(a - 1) + noOfSetBits(a);
else return (2 * solve( a / 2)) + ((long long)a + 1) / 2;
}else {
a++;
return ((long long)(-a) + 1) * 32 - solve(-a);
}
}
Ответ 4
В следующем коде битум x определяется как счетчик 1 бит в двух дополнительных представлениях чисел от 0 до x (включительно), где Integer.MIN_VALUE <= x <= Integer.MAX_VALUE.
Например:
bitsum(0) is 0
bitsum(1) is 1
bitsum(2) is 1
bitsum(3) is 4
.. и т.д.
10987654321098765432109876543210 i % 10 for 0 <= i <= 31
00000000000000000000000000000000 0
00000000000000000000000000000001 1
00000000000000000000000000000010 2
00000000000000000000000000000011 3
00000000000000000000000000000100 4
00000000000000000000000000000101 ...
00000000000000000000000000000110
00000000000000000000000000000111 (2^i)-1
00000000000000000000000000001000 2^i
00000000000000000000000000001001 (2^i)+1
00000000000000000000000000001010 ...
00000000000000000000000000001011 x, 011 = x & (2^i)-1 = 3
00000000000000000000000000001100
00000000000000000000000000001101
00000000000000000000000000001110
00000000000000000000000000001111
00000000000000000000000000010000
00000000000000000000000000010001
00000000000000000000000000010010 18
...
01111111111111111111111111111111 Integer.MAX_VALUE
Формула битума:
bitsum(x) = bitsum((2^i)-1) + 1 + x - 2^i + bitsum(x & (2^i)-1 )
Заметим, что x - 2 ^ я = x и (2 ^ i) -1
Отрицательные числа обрабатываются несколько иначе, чем положительные числа. В этом случае количество нулей вычитается из общего количества бит:
Integer.MIN_VALUE <= x < -1
Total number of bits: 32 * -x.
Число нулей в отрицательном числе x равно числу единиц в -x - 1.
public class TwosComplement {
//t[i] is the bitsum of (2^i)-1 for i in 0 to 31.
private static long[] t = new long[32];
static {
t[0] = 0;
t[1] = 1;
int p = 2;
for (int i = 2; i < 32; i++) {
t[i] = 2*t[i-1] + p;
p = p << 1;
}
}
//count the bits between x and y inclusive
public static long bitsum(int x, int y) {
if (y > x && x > 0) {
return bitsum(y) - bitsum(x-1);
}
else if (y >= 0 && x == 0) {
return bitsum(y);
}
else if (y == x) {
return Integer.bitCount(y);
}
else if (x < 0 && y == 0) {
return bitsum(x);
} else if (x < 0 && x < y && y < 0 ) {
return bitsum(x) - bitsum(y+1);
} else if (x < 0 && x < y && 0 < y) {
return bitsum(x) + bitsum(y);
}
throw new RuntimeException(x + " " + y);
}
//count the bits between 0 and x
public static long bitsum(int x) {
if (x == 0) return 0;
if (x < 0) {
if (x == -1) {
return 32;
} else {
long y = -(long)x;
return 32 * y - bitsum((int)(y - 1));
}
} else {
int n = x;
int sum = 0; //x & (2^i)-1
int j = 0;
int i = 1; //i = 2^j
int lsb = n & 1; //least significant bit
n = n >>> 1;
while (n != 0) {
sum += lsb * i;
lsb = n & 1;
n = n >>> 1;
i = i << 1;
j++;
}
long tot = t[j] + 1 + sum + bitsum(sum);
return tot;
}
}
}