查找从1到n的总位数

时间:2012-03-21 20:57:22

标签: algorithm

编写一个算法来查找F(n)设置为1的位数,对于任何给定的n值,所有数字都从1到n。

复杂性应为O(log n)

例如:

1: 001
2: 010
3: 011
4: 100
5: 101
6: 110

所以

F(1) = 1,  
F(2) = F(1) + 1 = 2,
F(3) = F(2) + 2 = 4,
F(4) = F(3) + 1 = 5,
etc.

我只能设计O(n)算法。

16 个答案:

答案 0 :(得分:39)

解决这些问题的方法是写出前几个值,并寻找一个模式

Number  binary   # bits set   F(n)
1       0001     1            1
2       0010     1            2
3       0011     2            4
4       0100     1            5
5       0101     2            7
6       0110     2            9
7       0111     3            12
8       1000     1            13
9       1001     2            15
10      1010     2            17
11      1011     3            20
12      1100     2            22
13      1101     3            25
14      1110     3            28
15      1111     4            32

需要一点盯着看,但有些人认为你注意到前8个和后8个数字的二进制表示是完全相同的,除了前8个在MSB中有一个0 (最高位),而最后8位有1。因此,例如,为了计算F(12),我们可以取F(7)并在其中添加8,9,10,11和12中的设置位数。但是这与集合的数量相同-bits在0,1,2,3和4 (即F(4)中,每个数字再加一个!

#    binary
0    0 000
1    0 001
2    0 010
3    0 011
4    0 100
5    0 101
6    0 110
7    0 111

8    1 000  <--Notice that rightmost-bits repeat themselves
9    1 001     except now we have an extra '1' in every number!
10   1 010
11   1 011
12   1 100

因此,对于8 <= n <= 15F(n) = F(7) + F(n-8) + (n-7)。同样,我们可以注意到4 <= n <= 7F(n) = F(3) + F(n-4) + (n-3);以及2 <= n <= 3F(n) = F(1) + F(n-2) + (n-1)。一般来说,如果我们设置a = 2^(floor(log(n))),那么F(n) = F(a-1) + F(n-a) + (n-a+1)


这并没有给我们一个O(log n)算法;但是,这样做很容易。如果a = 2^x,请在上表中注明,对于a-1,第一位设置为a/2次,第二位设置为a/2次,第三位......一直到第x位。因此,F(a-1) = x*a/2 = x*2^(x-1)。在上面的等式中,这给了我们

F(n) = x*2x-1 + F(n-2x) + (n-2x+1)

x = floor(log(n))。计算F的每次迭代都将基本上删除MSB;因此,这是一个O(log(n))算法。

答案 1 :(得分:7)

如果n= 2^k-1, then F(n)=k*(n+1)/2

对于一般n,让m成为m = 2^k-1m<=n的最大数字。 F(n) = F(m) + F(n-m-1) + (n-m)

转弯条件:F(0)=0F(-1)=0

答案 2 :(得分:4)

考虑以下内容:

0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

如果要查找从1到14的总设置位数(1110) 几点观察:

  1. 0th位(LSB)1位每两位出现一次(参见垂直方向),因此设置位数= n/2 + 1如果n's 0th bit is 1否则0
  2. 第1位:每4位出现2个连续1位(沿所有数字垂直查看第1位) 1st位位置= (n/4 *2) + 1中的设置位数(因为1st位是一个集合,否则为0
  3. 2nd位:4连续1s出现在每个8位(这个有点棘手) 第二个位置的设置位数= (n/8*4 )+ 1(因为设置了2nd位,否则为0+ ((n%8)%(8/2)) 最后一项是包括1s位前(n/8)位组14/8 =1的数量1仅考虑4组,即。8设置位1s位。我们需要包含14-8 = 6位中找到的3rd
  4. 8位:每16位出现3rd个连续1位(与上面类似) (n/16*8)+1位置= 3rd中的设置位数(自0位开始设置,否则+ ((n%16)%(16/2))O(1)
  5. 所以我们对n的每个位进行log2(n)计算。 数字包含n位。因此,当我们对O(logn)的所有位置进行上述迭代并在每一步添加所有设置位时,我们会在{{1}}步骤中得到答案

答案 3 :(得分:3)

快速搜索序列F的值会导致此整数序列 http://oeis.org/A000788

在那里我发现了一个公式: a(0)= 0,a(2n)= a(n)+ a(n-1)+ n,a(2n + 1)= 2a(n)+ n + 1(a与F相同,因为我只需从oeis复制公式)

可用于计算log(n)中的(n)。

这是我的示例C ++代码:

memset(cache, -1, sizeof(cache))
cache[0] = 0

int f(int n)
    if cache[n] != -1 return cache[n];
    cache[n] = n % 2 ? (2 * f(n / 2) + n / 2 + 1) : (f(n / 2) + f(n / 2 - 1) + n / 2)

答案 4 :(得分:3)

这是我的解决方案。时间复杂度:O(Log n)

public int countSetBits(int n){
    int count=0;
    while(n>0){
        int i= (int)(Math.log10(n)/Math.log10(2));
        count+= Math.pow(2, i-1)*i;
        count+= n-Math.pow(2, i)+1;
        n-= Math.pow(2, i);
    }
    return count;
}

答案 5 :(得分:2)

kn所需的位数。

对于0,...,2^(k-1)-1,每个位的数字正好是一半,所以到目前为止我们有(k-1)*2^(k-1)/2 = (k-1)*2^(k-2)位。我们只需要查看大于2^(k-1)-1的数字的内容
对于MSB,我们也有n-2^(k-1)-1位“up”。

所以我们可以推导出递归函数:

f(n) = (k-1)*2^(k-2) + n-(2^(k-1)-1) + f(n-(2^(k-1)))
           ^               ^            ^
         first            MSBs        recursive call for 
       2^(k-1)-1                      n-2^(k-1) highest numbers
        numbers

其中base为f(0) = 0f(2^k) = k*2^(k-1) + 1 [正如我们之前看到的那样,我们确切知道2^(k-1)-1的位数是多少,我们只需要为MSB添加1 - 2^k]

由于发送到f的值在每次迭代时减少了至少一半,因此总计O(logn)

答案 6 :(得分:2)

使用带位掩码的DP可以解决此问题。

自下而上方法背后的基本直觉是,我们将直接访问具有值current_number / 2的数字中的设置位数,并且我们还将检查是否仅在此current_number中设置了最后一位用1.进行操作。

current_number / 2或current_number >> 1基本上删除了此current_number的最后一位,因此为了将该位包括在我们的计数中,我们必须使用&操作手动检查此数字的最后一位。

这是用于计算数字i中的设置位数的表达式 dp [i] = dp [i >> 1] +(i&1)

如果您在解决此问题时仍然感到困惑,则可以参考以下视频以获得更好的解释:-

视频链接: https://youtu.be/fCvfud4p6No

答案 7 :(得分:1)

简短又甜蜜!

 public static int countbits(int num){
    int count=0, n;
    while(num > 0){
        n=0;
        while(num >= 1<<(n+1))
            n++;
        num -= 1<<n;
        count += (num + 1 + (1<<(n-1))*n);
    }
    return count;
}//countbis

答案 8 :(得分:0)

这是java函数

private static int[] findx(int i) {
    //find the biggest power of two number that is smaller than i
    int c = 0;
    int pow2 = 1;
    while((pow2<< 1) <= i) {
        c++;
        pow2 = pow2 << 1;
    }
    return new int[] {c, pow2};
}

public static int TotalBits2(int number) {
    if(number == 0) {
        return 0;
    }
    int[] xAndPow = findx(number);
    int x = xAndPow[0];
    return x*(xAndPow[1] >> 1) + TotalBits2(number - xAndPow[1]) + number - xAndPow[1] + 1;
}

答案 9 :(得分:0)

这是用java编码的...
逻辑:说数字是34,二进制等于蚂蚁是10010,可以写成10000 + 10。 10000有4个零,因此在此数字之前的所有1的计数是2 ^ 4(原因如下)。所以计数是2 ^ 4 + 2 ^ 1 + 1(自己编号)。所以答案是35 *对于二进制数10000.填充4个位置的总组合为2 * 2 * 2 * 2x2(一或零)。因此,1的总组合是2 * 2 * 2 * 2.

public static int getOnesCount(int number) {
    String binary = Integer.toBinaryString(number);
    return getOnesCount(binary);
}

private static int getOnesCount(String binary) {
    int i = binary.length();

    if (i>0 && binary.charAt(0) == '1') {
        return gePowerof2(i) + getOnesCount(binary.substring(1));
    }else if(i==0)
        return 1;
    else
        return getOnesCount(binary.substring(1));

}
//can be replaced with any default power function
private static int gePowerof2(int i){
    int count = 1;
    while(i>1){
        count*=2;
        i--;
    }
    return count;
}

答案 10 :(得分:0)

顺便说一句,这个问题也可以通过查找表的方法来完成。预先计算0-255的设置位数并存储它。发布,我们可以通过将给定数字分成两部分,每部分8位来计算任何数字中的设置位数。对于每个部分,我们可以在第一步中形成的count数组中查找。例如,如果有一个16位数字,如

x = 1100110001011100,这里,设置位数=第一个字节中的设置位数+第二个字节中的设置位数。因此,为获得第一个字节,

y = (x & 0xff) z = (x >> 8) & (0xff) total set bits = count[y] + count[z]

此方法也将在O(n)中运行。

答案 11 :(得分:0)

不确定是否迟到了回复,但这是我的调查结果。

尝试用以下方法解决问题,对于数字N每个bitno(从LSB到MSB,比如LSB以bitno 1开始并且用下一个比特值递增)设置的位数可以被计算为,(N /(2 topower) bitno)*(2 topower bitno-1)+ {(N%(2 topower bitno)) - [(2 topower bitno-1) - 1]}

为它编写递归函数C / C ++请检查。我不确定,但我认为它的复杂性是log(N)。传递函数2参数,我们想要计算位数的数字(no)和LSB的第二个开始计数,值为1.

int recursiveBitsCal(int no, int bitno){
int res = int(no/pow(2,bitno))*int(pow(2,bitno-1));
int rem1 = int(pow(2,bitno-1)) -1;
int rem = no % int(pow(2,bitno));
if (rem1 < rem) res += rem -rem1;
if ( res <= 0 )
    return 0;
else
    return res + recursiveBitsCal(no, bitno+1);
}

答案 12 :(得分:0)

for i in range(int(input())):
    n=int(input())
    c=0
    m=13

    if n==0:
        print(c)
    while n%8!=0 or n!=0:
        t=bin(n)[2:]
        c+=t.count('1')
        n=n-1
    if n!=0:
        j=n//8
        if j==1:
            c+=m
        else:
            c+=m+((j-1)*7)
    print(c)        

答案 13 :(得分:0)

**// 1. Check database version:**

private Boolean checkDataBaseVersion() {
    Boolean Upgrade = false;

    SQLiteDatabase db = mContext.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
    Integer CurrentdbVersion = db.getVersion();

    if (CurrentdbVersion < DATABSE_VERSION) {
        Upgrade = true;
    } else {
        Upgrade = false;
    }

    Log.w("POSITIVE ...", "checkDataBase - Database Upgrade = " + Upgrade + " ... CurrentdbVersion: "
    + CurrentdbVersion + " DATABSE_VERSION: " + DATABSE_VERSION);

    db.close();
    return Upgrade;
}



**// 2. change copy database code:**
    public void copyDataBase() {
        if (!checkDataBase()) {
            this.getReadableDatabase();
            this.close();
            try {
                copyDBFile();
                Log.w("POSITIVE ...", "copyDataBase - Database does not exists ... db copied");
            } catch (IOException mIOException) {
                throw new Error("حدث خطأ في نسخ قاعدة البانات من الملف");
            }
        } else if (checkDataBaseVersion()) {
            //this.getReadableDatabase();
            //this.close();
            try {
                copyDBFile();
                Log.w("POSITIVE ...", "copyDataBase - Database exists - new version exists ... db copied ");
            } catch (IOException mIOException) {
                throw new Error("حدث خطأ في نسخ قاعدة البانات من الملف");
            }
        } else {
            Log.w("POSITIVE ...", "copyDataBase - Database exists - no new version ... nothing done ");
        }
    }

    private boolean checkDataBase() {
        File dbFile = new File(DATABASE_LOCATION + DATABASE_NAME);
        Log.w("POSITIVE ...", "checkDataBase - Database exists = " + dbFile.exists());

        close(); /// NEW
        return dbFile.exists();
    }


**// 3. remove copy database code from public QuizDbHelper(Context context) and place it where it is needed:**

  public QuizDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABSE_VERSION);

        if (android.os.Build.VERSION.SDK_INT >= 17)
            DATABASE_LOCATION = context.getApplicationInfo().dataDir + "/databases/";
        else
            DATABASE_LOCATION = "/data/data/" + context.getPackageName() + "/databases/";

        this.mContext = context;
}

请查看我在geeksforgeeks.org上的文章,以获取详细说明。 以下是文章的链接 https://www.geeksforgeeks.org/count-total-set-bits-in-all-numbers-from-1-to-n-set-2/

答案 14 :(得分:0)

我知道这则帖子迟到了,请在下面找到logn解决方案:

static int countSetBitWithinRange(int n)
{
    int x = n + 1, index = 0, count = 0;
    int numberOfOnesInAGroup = (int)Math.pow(2, index);
    while(x >= numberOfOnesInAGroup)
    {
        int countOfZeroOnePairs = (x / numberOfOnesInAGroup);
        int numberOfPairsOfZerosAndOnes = countOfZeroOnePairs / 2;
        int numberOfSetBits = numberOfPairsOfZerosAndOnes * numberOfOnesInAGroup;
        //If countOfZeroOnePairs is even then the pairs are complete else there will be ones that do not have a corresponding zeros pair
        int remainder = (countOfZeroOnePairs % 2 == 1) ? (x % numberOfOnesInAGroup) : 0;
        count = count + numberOfSetBits + remainder;
        numberOfOnesInAGroup = 1 << ++index;
    }
    return count;
}

答案 15 :(得分:-1)

x = int(input("Any number:\n"))
y = (bin(x))
print(y)
v = len(y) -2
print(v)