Подсчет количества установленных бит с минимальным N - PullRequest
0 голосов
/ 06 января 2019

Я пытаюсь решить эту проблему:

Учитывая целое число k , найдите наименьшее целое число n , такое, что общее число единиц в двоичных представлениях {1, 2,. , ., n } не менее k .

Например, учитывая k = 4, мы хотим n = 3, потому что двоичные представления 1, 2 и 3 содержат 1, 1 и 2 единицы (соответственно ) и 1 + 1 + 2 & ge; 4.

Я попытался использовать подсчет установленных битов от (1 до n) в журнале (n), невозможно найти минимальное n эффективным способом.

Редактировать:

Код: Расчет нет. битов от 1 до n ( Reference ), но проблема в нахождении минимального n. Есть ли способ найти какое-то решение вокруг этого?

int getSetBitsFromOneToN(int N){ 
int two = 2,ans = 0; 
int n = N; 
while(n){ 
    ans += (N/two)*(two>>1); 
    if((N&(two-1)) > (two>>1)-1) ans += (N&(two-1)) - (two>>1)+1; 
    two <<= 1; 
    n >>= 1; 
} 
return ans; 
} 

1 Ответ

0 голосов
/ 06 января 2019

Алгоритм относительно прост. Вместо того, чтобы идти по одному числу за раз и накапливать число единиц, которое имеет его двоичное представление, мы будем пропускать все ближе и ближе к цели, используя ряд функций {a (m), b (m), c (m) .. .} чтобы приблизиться к цели и оставить в конце не более пары цифр для добавления вручную. Каждая функция будет иметь формат image, где x - номер функции (для a (m) x = 0, для b (m) x = 1 ...).

Эти функции основаны на признаке двоичных чисел: во всех числах от 1 до image вы можете узнать количество единиц в его двоичных представлениях {1,2 ... n}.

Давайте посмотрим на число image,, которое находится в двоичном 1111. Вы можете узнать количество единиц во всех числах от 1 (0001) до 15 (1111) - это подсчет, сколько способов вы можно поставить 1 в 4 местах (4) раза 1, плюс сколько раз вы можете положить 2 в 4 места (6) раз 2, плюс сколько раз вы можете положить 3 в 4 места (4) раз 3 плюс сколько способов вы можно поставить 4 в 4 местах (1) раз 4. Таким образом, общее число равно 32, что также image. вы заметите, что для любого такого числа n = image, накопленное число из 1 - это image. Давайте назовем эту функцию a (m), как было решено выше (здесь x = 0, поэтому нет необходимости в добавлении элемента в этом). Например:

  • 1 = a (1) = image = image = image = 1.
  • 3 = a (2) = image = image = image = 4.
  • 7 = a (3) = image = image = image = 12.
  • 15 = a (4) = image = image = image = 32.
  • 31 = a (5) = image = image = image = 80.

и так далее. поэтому для числа 15, которое является image,, мы вычисляем a (4) и получаем 32 накопленных единицы. Мы также заметим, что число image содержит ровно m 1 (все цифры установлены в 1).

Зная, что вы берете свое число k, находите ближайшее a (m), которое меньше k, тогда как a (m + 1) будет больше k. Если a (m + 1) только на m + 1 больше, чем k, возьмите a (m + 1) в качестве ответа и завершите алгоритм. Поскольку a (m + 1) имеет по крайней мере m + 2, это означает, что вы не можете накопить все k 1, которые вам нужны, без него.

Если k больше, чем m + 1, больше, чем (m + 1), но больше, чем (m), вам потребуется приближение второго шага путем определения второй функции - назовем ее b (m). Мы определим b (m) = image. Это число будет эквивалентно image точно (не image, как это было для функции a) Например:

  • 2 = b (1) = image = image = image = 1 + 2 = 3.
  • 4 = b (2) = image = image = image = 4 + 4 = 8.
  • 8 = b (3) = image = image = image = 12 + 8 = 20.
  • 16 = b (4) = image = image = image = 32 + 16 = 48.
  • 32 = b (5) = image = image = image = 80 + 32 = 112.

Причина, по которой мы определили b, заключается в том, чтобы описать уникальную разницу в накоплении единиц между первой image серией чисел и второй следующей image серией чисел - добавление другого наиболее значимого 1 для них каждого из чисел во второй партии. Вот почему мы теперь смотрим на image, а не на image.

Добавив две функции, мы можем получить наш номер n. Если после двух приближений нам все еще не хватает конечного k, мы можем накапливать одно за другим числа, пока не достигнем k.

Давайте предположим, что k = 50. Мы знаем, что a (4) - самое близкое, что мы можем получить, будучи наибольшим числом, которое все еще ниже 50. a (5) приведет нас к 80, как мы видели выше. Таким образом, (4) является первой половиной решения, которое составляет 15.

Остальные 1 - 50-32 = 18. Нам нужно увидеть, сколько чисел нам нужно обработать за 15, чтобы накопить как минимум еще 18 единиц. вычисляя функцию b, мы видим, что b (2) приближает нас, а b (3) на 2 больше. Так как мы знаем, что число, представленное b (3), содержит по крайней мере 4 1, мы знаем, что это число, которое нам нужно - любое число ниже него будет иметь только 16 1, и нам нужно 18. Итак, мы идем с b (3 ), который равен image или 8. Результат равен 15 + 8 = 23, и это наименьшее число, которое имеет не менее 50 накопленных единиц во всех двоичных {1,2..23} представления.

Если нам нужно пройти еще одну итерацию, мы можем определить image, и это сделает нас еще ближе. Например, для k = 120 мы получим (5) + b (3) = 100 и добавление c (2) добавит нас еще 12 к 112. Мы можем вручную добавить недостающие 8 чисел или решить добавить image, что приведет нас к 119, добавив (5) + b ( 3) + с (2) + d (1). Это означает, что следующее число должно быть наименьшим n, у которого накоплено k или более единиц. a (5) = 31, b (3) = 8, c (2) = 4 и d (1) = 2, поэтому n = 46 - следующее число по сравнению с 119 1, собранными на 45.

Сложность O (log (k)) - у нас есть log (k) шагов, чтобы получить a (m) и еще не более log (k) накоплений, чтобы получить b (m) и наше возможное n.

КОД

//This represents the function a(m), b(m)... etc.
public int getFuncResult(int funcNum, int arg) {
    Double result =  Math.pow(2,arg-1)*arg+funcNum*Math.pow(2,arg);
    return result.intValue();
}

//This is the iterative algorithm described: add a(m)+b(m)... until k
public int countOnesToKIter(int k) {
    int funcNum = 0;
    int counter = 0;
    int retVal = 0;
    int exponent = 0;
    while (k > 0) {
        //for the current function, find the appropriate m
        while (k > counter) {
            exponent++;
            counter = getFuncResult(funcNum, exponent);
        }

        //if the last number contains more 1's than the difference, use it.
        if (counter-k < exponent) {
            counter=getFuncResult(funcNum, exponent-2);
            retVal+=Math.pow(2,exponent-2);
        } else {
            counter = getFuncResult(funcNum, exponent-1);
            retVal+=Math.pow(2,exponent-1);
        }
        funcNum ++;
        exponent=0;
        k = k-counter;
        counter = 0;
    }

    return retVal;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...