• 数字之魅笔试题


    1、整型数V的二进制中1的个数

    //普通解法:除以2,看余数
    int Count(int v)
    {
        int num = 0;
        while(v)
        {
            if(v % 2 == 1)
                ++ num;
            v /= 2;
        }
        return num;
    }
    
    //使用位移
    int Count(int v)
    {
        int num = 0;
        while(v)
        {
            num += v & 1;
            v >>= 1;
        }
        return num;
    }

    位移的思路,当输入i是正数时没有问题,但当输入的i是一个负数时,不但不能得到正确的1的个数,还将导致死循环。以负数0x80000000为例,右移一位的时候,并不是简单地把最高位的1移到第二位变成0x40000000,而是0xC0000000。这是因为移位前是个负数,仍然要保证移位后是个负数,因此移位后的最高位会设为1。如果一直做右移运算,最终这个数字就会变成0xFFFFFFFF而陷入死循环。
    为了避免死循环,我们可以不右移输入的数字i。首先i和1做与运算,判断i的最低位是不是为1。接着把1左移一位得到2,再和i做与运算,就能判断i的次高位是不是1……这样反复左移,每次都能判断i的其中一位是不是1。基于此,我们得到如下代码:

    int Count1(int v)
    {
        int num = 0;
        unsigned int flag = 1;
        while(flag)
        {
            if(v & flag)
                ++ num;
            flag <<= 1;
        }
        return num;
    }

    整数A 和B 的二进制表示中有多少位是不同的?
    把两个整数 A, B 异或, 然后又回归到判断 1 的个数

    int Count(int a, int b)
    {
        int num = 0;
        int v = a ^ b;
        while(v)
        {
           v &= (v-1);
           num++;
        }
        return num;
    }

    整数n,判断它是否为2的方幂(即二进制中只有一个1)
    解答:n>0 && (n&(n-1)==0)
    原理:由于2的N次方的数二进制表示是第1位为1,其余为0,而x-1(假如x为2的N次方)得到的数的二进制表示恰恰是第1位为0,其余为1,两者相与,得到的结果就为0,否则结果肯定不为0

    2、N的阶乘中末尾有几个0

    如果N!= K×10M,且K不能被10整除,那么N!末尾有M个0。再考虑对N!进行质因数分解,N!=(2^x)×(3^y)×(5^z)…,由于10 = 2×5,所以M只跟X和Z相关,每一对2和5相乘可以得到一个10,于是M = min(X, Z)。不难看出X大于等于Z,因为能被2整除的数出现的频率比能被5整除的数高得多,所以把公式简化为M = Z。问题相当于求N!中有质因数5的个数

    //直接计算i(i=1,2,...,N)的因式分解中5的指数
    int numberOfFive(int N)
    {
        int ret = 0;
        for(int i = 1; i <= N; i++)
        {
            int j = i;
            while(j%5 == 0)
            {
                ret++;
                j /= 5;
            }
        }
        return ret;
    }
    
    // N!中含有质因数5的个数: Z = [N/5] + [N/5^2] + [N/5^3]+...
    int numberOfFive2(int N)
    {
        int ret = 0;
        while(N)
        {
            ret += N / 5;
            N /= 5;
        }
        return ret;
    }

    N!的二进制表示中最低位1的位置。给定一个整数N,求N!二进制表示的最低位1在第几位?例如:给定N=3,N!=6,那么N!的二进制表示(110)的最低位1在第二位。
    这个问题实际上等同于求N!含有质因数2的个数。即答案等于N!含有质因数2的个数加1。
    N!中含有质因数2的个数,等于 N/2 + N/4 + N/8 + N/16 + …

    int lowestOne(int N)
    {
        int ret = 0;
        while(N)
        {
            N >>= 1;
            ret += N;
        }
        return ret;
    }

    3、1的个数

    给定一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
    例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。

    //统计一个整数的十进制表示中1出现的次数   
    unsigned int CountOneInAInteger(unsigned int n)  
    {  
        unsigned int count = 0;  
        while(n)  
        {  
            if(n % 10 == 1)  
                count++;  
            n /= 10;  
        }  
        return count;  
    }  
    //统计从1到n这n个整数的十进制表示中1出现的次数  
    unsigned int CountOneBitween1AndN_Solution1(unsigned int n)  
    {  
        unsigned int count = 0;  
        for(unsigned int i = 1; i <= n; i++)  
            count += CountOneInAInteger(i); //穷举法   
        return count;  
    }  
    
    //优化的算法,来自《编程之美》,比较麻烦,只能看明白   
    unsigned int CountOneBitween1AndN_Solution2(unsigned int n)  
    {  
        unsigned int count = 0;  
        unsigned int factor = 1; //计算每一位上出现1的次数,从个位开始   
        //每一位上出现1的次数与它的高位数字、低位数字、以及自身都有关系   
        //比如12345,如果计算百位上出现1的次数,这要考虑高位数字12,以及低位数字45有关   
        unsigned int lowNum = 0;   //低位数字   
        unsigned int highNum = 0;  //高位数字   
        unsigned int curNum = 0;   //当前位   
        while(n / factor != 0)  
        {  
            lowNum = n - (n / factor) * factor;  
            highNum = n / (factor * 10);  
            curNum = (n / factor) % 10;     
            switch(curNum)  
            {  
            case 0: //当前位的数字为 0   
                count += highNum * factor; //只与高位有关    
                break;  
            case 1:  
                count += highNum * factor + lowNum + 1; //与高位低位都有关   
                break;  
            default:  
                count += (highNum + 1) * factor; //与高位有关   
                break;  
            }  
            factor *= 10; //计算下一位   
        }  
        return count;  
    }  

    4、最大公约数

    //辗转相除法:f(x, y)= f(y, x % y)(y>0)
    int gcd(int x, int y)
    {
        if(y == 0)
            return x;
        else return gcd(y, x%y);
    }
    
    //辗转相减法:f(x, y)= f(x-y, y)(x>y)
    int gcd(int x, int y)
    {
        if(x < y)
            return gcd(y, x);
        if(y == 0)
            return x;
        else
            return gcd(x - y, y);
    }
    
    /*
    x is even, y is even, f(x, y) = 2 * f(x>>1, y>>1)
    x is even, y is odd, f(x,y) = f(x>>1, y)
    x is odd, y is even, f(x,y) = f(x, y>>1)
    x is odd, y is odd, f(x,y) = f(y, x-y)
    */
    int gcd(int x, int y)
    {
        if(x < y) 
            return gcd(y, x);
        if(y == 0)
            return x;
        else
        {
            if(IsEven(x))
            {
                if(IsEven(y))
                    return (gcd(x >> 1, y >> 1) << 1);
                else
                    return gcd(x >> 1, y);
            }
            else
            {
                if(IsEven(y))
                    return gcd(x, y >> 1);
                else
                    return gcd(y, x - y);
            }
        }
    }

    5、判断一个自然数N是否是某个数的平方。当然不能使用开方sqrt()函数运算。

    方法1:遍历从1到N的数字,求取平方并和N进行比较。如果平方小于N,则继续遍历;如果等于N,则成功退出;如果大于N,则失败退出。时间复杂度为O(n)。

    方法2:使用二分查找法,对1到N之间的数字进行判断。复杂度为O(logN)。

    unsigned int sqrt(unsigned int x) 
    {
        // x的最大值不超过max*max
        unsigned int max = (1 << (sizeof(x)/2*8))-1; //65535
        if (max*max < x) return max;
        
        unsigned int low = 0;
        unsigned int high = max-1;
        
        unsigned int mid = 0;
        while(1) 
        {
            mid = (low+high)/2;
            if (x < mid * mid)
                high = mid-1;
            else if((mid+1)*(mid+1) <= x)
                low = mid+1;
            else //if(mid * mid <= x && x < (mid+1)*(mid+1))
                break;
        }
        
        return  mid;
    }

    方法3:由于(n+1)^2 = n^2 + 2n + 1 = ...= 1 + (2*1 + 1) + (2*2 + 1) + ... + (2*n + 1)
    注意到这些项构成了等差数列(每项之间相差2),所以我们可以比较 N-1, N - 1 - 3, N - 1 - 3 - 5 ... 和0的关系。如果大于0,则继续减;如果等于0,则成功退出;如果小于 0,则失败退出。
    复杂度为O(n)。不过方法3中利用加减法替换掉了方法1中的乘法,所以速度会更快些。

    int square(int n)  
    {  
        int i = 1;  
        n = n - i;  
        while(n > 0)  
        {  
            i += 2;  
            n -= i;  
        }  
        if(n == 0)        //是某个数的平方   
            return 1;  
        else                //不是某个数的平方   
            return 0;  
    } 

    6、怎么能随即生成m个数,让其和等于n?

    题目:假设生成26个非负随机数,要求其和是301,求程序生成此列数字
     
    解法一:关于此种算法原理,可以假想是一根长301单位的绳子,然后随即在其上面截25个点,于是得到26根子绳,这26根子绳之和恰好就是绳子总长301。
    于是可以:
    1 初始化27的数组
    2 该数组0位和26位分别为0和301,其他位置填充0到301之间的随机数字
    3 对数组排序
    4 数组相邻数字相减,所得的26个差值即为所求数列。

    解法二:这种方案利用了非负这个不显眼的条件,思路非常简单,就是将1随机加到一个26位数组中,随机301次,有点剑走偏锋,另辟蹊径。

  • 相关阅读:
    pytorch bug: for step,data in enumerate(loader)+Connection reset by peer
    pytorch bug
    ImportError: No module named '_tkinter', please install the python3-tk package
    nvidia-docker+cuda8.0+ubuntu16.04
    召回率,精确率,mAP如何计算
    tensorflow 迭代周期长,每个epoch时间变慢
    yolov3中 预测的bbox如何从特征图映射到原图?
    知乎问题:目标检测领域还有什么可以做的?
    目标检测数据增强,旋转方法
    OpenBLAS(1)----安装OpenBLAS
  • 原文地址:https://www.cnblogs.com/luxiaoxun/p/2779339.html
Copyright © 2020-2023  润新知