LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
楼主: wes109

[算法]Google的面试题

[复制链接]
发表于 2008-1-19 16:26:36 | 显示全部楼层
Another algorithm in C. Someone else might have already thought similar. The main idea is to use memorization technology to keep track of all previous found 1's and calc how many 1's the current number carries. Algorithm to calc the current 1's are simply. O(kn) time where k is the digits of the number.
  1. #define UL unsigned long
  2. UL MAX=1111111112;
  3. int main() {
  4.   UL i, k, p=0;
  5.   for(i = 1; i <= MAX; i++) {
  6.     k = i;
  7.     while(1) {
  8.       if(k > 0 && k < 10) {
  9.         if(k == 1) p++;
  10.         break;
  11.       } else if(k >= 10) {
  12.         if(k%10 == 1) p++;
  13.         k /= 10;
  14.       }
  15.     }
  16.     if(p == i) printf("%d\n", p);
  17.   }
  18. }
复制代码
./counting_number_2  327.36s user 0.49s system 99% cpu 5:29.01 total
回复 支持 反对

使用道具 举报

发表于 2008-1-19 16:27:39 | 显示全部楼层
Another algorithm in C. Someone else might have already thought similar. The main idea is to use memorization technology to keep track of all previous found 1's and calc how many 1's the current number carries. Algorithm to calc the current 1's are simply. O(kn) time where k is the digits of the number.


  1. #define UL unsigned long
  2. UL MAX=1111111112;

  3. int main() {
  4.   UL i, k, p=0;
  5.   for(i = 1; i <= MAX; i++) {
  6.     k = i;
  7.     while(1) {
  8.       if(k > 0 && k < 10) {
  9.         if(k == 1) p++;
  10.         break;
  11.       } else if(k >= 10) {
  12.         if(k%10 == 1) p++;
  13.         k /= 10;
  14.       }
  15.     }
  16.     if(p == i) printf("%d\n", p);
  17.   }
  18. }
复制代码

./counting_number_2  327.36s user 0.49s system 99% cpu 5:29.01 total
回复 支持 反对

使用道具 举报

发表于 2008-2-20 12:03:36 | 显示全部楼层
今天看了一下大家的思路,好像方向不对阿,要提高这算法的效率,关键在于搜索的步长,而不是讨论怎么计算1的个数,大家在这里都只是想方设法提高计算1个数的效率而已。计算1个数的公式pointer 已经得出,这已经是最高效率的了。而且也证明了n的范围,这点很重要(虽然证明过程有些笔误)。
不过还有最关键的问题是搜索的步长,在这点上pointer的算法并非最好,不过也比普遍的递进好太多了。我也试着做了一些改进:

一、更改一下n的下界,
首先可以证明除1外最小n>5:
由pointer的公式有
f(an,an-1.....a0)=(an-1...a0)+1+f(10^n-1) + f(an-1,...,0) ,  an=1

f(an,an-1.....a0) = (an...a0)

(an...a0)- (an-1...a0) = 1+f(10^n-1) + f(an-1,...,0)
-->
10^n = 1+n*10^(n-1) + f(an-1,...,0) <= 2( 1+ n*10^(n-1))   ( 因 f(an-1,...,0)<=f(an,...,0), 且 (an-1...a0)可以为 0)
于是有 n> 5,编程可以直接跳到 n=1e5进行搜索。

二、提高搜索步长:
因假设一个数n长为 L,d = n- f(n) 。
这里仅仅讨论d>0的情况。如果n增加能使d减少,则n至少固定含有一个'1'。并且在仅有一个1不变的情况下n每增加a,0<d-a<=d。基于这一思想,对于可能出现 L 个 '1',pointer的算法保守得使用 d/L 作为增加的步长,但实际上可以动态设置。比较简单得就是原有的'1'的个数加上 d 的长度作为除数即可。

三、连续打印连续的10个数:
可以从公式证明在如果 n 是所求,且除个,十位仅有1个'1',则连续的个十位由01-10都是所求。

程序:
  1. #include<stdio.h>
  2. #define __USE_ISOC99
  3. #include<stdlib.h>
  4. #define MAXNL  10 // It has been proofed that n<=8 and n_max = 1111111110
  5. #define MAN_N 1111111111
  6. long long tenpowers[MAXNL]= {1};// 10^i
  7. long long rempowers[MAXNL]= {0};// remainder power: i*10^(i-1)
  8. int remainders[MAXNL]={0};        //  remainders
  9. // Suppose that n= a* 10^i+b, where i >0 and b<10^i.
  10. // Then b_i = n- a*10^i, and b_num stores b_i.
  11. long long b_num[MAXNL]={0};
  12. int len=0; // length of n
  13. long long countOnes(long long n)
  14. {       
  15.         int  i, j;
  16.         long long ones_num=0;
  17.         lldiv_t qr;       
  18.         // initial
  19.         len =0;
  20.         qr.quot=n;
  21.         // count len and set b_num
  22.         while(qr.quot)        {
  23.                 qr=lldiv(qr.quot, 10L);
  24.                 remainders[len]=qr.rem;
  25.                 if (len>0) { // set b_num
  26.                         b_num[len]=b_num[len-1]+qr.rem*tenpowers[len];
  27.                 }
  28.                 else{
  29.                         b_num[len]=qr.rem;
  30.                 }
  31.                 len++;
  32.         }
  33.         // count ones
  34.         for(i=len-1; i>0; --i)        {
  35.                 if(remainders[i]>1) {
  36.                         ones_num+=tenpowers[i]+remainders[i]*rempowers[i];
  37.                 }
  38.                 else if(remainders[i]==1){
  39.                         ones_num+=rempowers[i]+b_num[i-1]+1;
  40.                 }
  41.         }
  42.         ones_num+=(remainders[i]>0)?1:0;
  43.         return ones_num;
  44. }
  45. // count the number of one in remainders
  46. int numOfOne() {
  47.         int count=0,i;
  48.         for (i=1;i<len;i++) { // ignore the first remainder
  49.                 if ( 1 == remainders[i] ) {
  50.                         count++;
  51.                 }
  52.         }
  53.         return count;
  54. }
  55. // Print the adjoining ten number, each of who has only one '1' except the first one .
  56. int printAdjTen( long long n, int n_num) {       
  57.         int i;
  58.         if (numOfOne() ==1) {                                       
  59.                 for (i=0; i<9; i++) {       
  60.                         printf("%4d: %lld\n", n_num++ ,++n);
  61.                 }       
  62.                 return 1;                               
  63.         }
  64.         return 0;       
  65. }
  66. // Get the length of one number
  67. int getLen(long long n) {
  68.         int len =0 ;
  69.         for (len=0;len<MAXNL;len++) {
  70.                 if ( n < tenpowers[len]) {
  71.                         break;
  72.                 }
  73.         }               
  74.         return len;
  75. }
  76. int main(int argc, char * argv[])
  77. {
  78.         long long ones_num,  diff; // numbers of '1' ; difference between n and ones_num
  79.         int  i,n_num=1;
  80.         int diff_len,step;
  81.         long long  n=1;
  82.         for(i=1;i<MAXNL;++i)         {
  83.                 tenpowers[i]=tenpowers[i-1]*10L;
  84.                 rempowers[i]=tenpowers[i-1]*i;
  85.         }
  86.         printf("%4d: %lld\n", n_num++ ,n);        // print '1'
  87.         n = 1e5; // It has been proved that next n must be larger than 1e5
  88.         while(n<MAN_N) { // searching
  89.                 ones_num=countOnes(n);
  90.                 diff=n-ones_num;
  91.                 if(diff>len)        {
  92.                         // set search step.
  93.                         step=numOfOne();
  94.                         if (step==0) {
  95.                                 step = len-1;
  96.                         }else {
  97.                                 diff_len = getLen(diff); // get length of diff
  98.                                 step = step+diff_len;
  99.                                 if (step==len) {
  100.                                         step--;
  101.                                 }
  102.                         }
  103.                         n+=diff/step+1;
  104.                 }else if(diff<0) {
  105.                         n+=(-diff);
  106.                 }else {
  107.                         if(diff==0){
  108.                                 printf("%4d: %lld\n", n_num++ ,n);                                                               
  109.                                 if ( printAdjTen( n, n_num) ) {
  110.                                         n+=9;
  111.                                         n_num+=9;
  112.                                 }
  113.                         }                       
  114.                         ++n;
  115.                 }
  116.          }
  117.         return 0;
  118. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2008-2-20 12:16:23 | 显示全部楼层
  1.    1: 1
  2.    2: 199981
  3.    3: 199982
  4.    4: 199983
  5.    5: 199984
  6.    6: 199985
  7.    7: 199986
  8.    8: 199987
  9.    9: 199988
  10.   10: 199989
  11.   11: 199990
  12.   12: 200000
  13.   13: 200001
  14.   14: 1599981
  15.   15: 1599982
  16.   16: 1599983
  17.   17: 1599984
  18.   18: 1599985
  19.   19: 1599986
  20.   20: 1599987
  21.   21: 1599988
  22.   22: 1599989
  23.   23: 1599990
  24.   24: 2600000
  25.   25: 2600001
  26.   26: 13199998
  27.   27: 35000000
  28.   28: 35000001
  29.   29: 35199981
  30.   30: 35199982
  31.   31: 35199983
  32.   32: 35199984
  33.   33: 35199985
  34.   34: 35199986
  35.   35: 35199987
  36.   36: 35199988
  37.   37: 35199989
  38.   38: 35199990
  39.   39: 35200000
  40.   40: 35200001
  41.   41: 117463825
  42.   42: 500000000
  43.   43: 500000001
  44.   44: 500199981
  45.   45: 500199982
  46.   46: 500199983
  47.   47: 500199984
  48.   48: 500199985
  49.   49: 500199986
  50.   50: 500199987
  51.   51: 500199988
  52.   52: 500199989
  53.   53: 500199990
  54.   54: 500200000
  55.   55: 500200001
  56.   56: 501599981
  57.   57: 501599982
  58.   58: 501599983
  59.   59: 501599984
  60.   60: 501599985
  61.   61: 501599986
  62.   62: 501599987
  63.   63: 501599988
  64.   64: 501599989
  65.   65: 501599990
  66.   66: 502600000
  67.   67: 502600001
  68.   68: 513199998
  69.   69: 535000000
  70.   70: 535000001
  71.   71: 535199981
  72.   72: 535199982
  73.   73: 535199983
  74.   74: 535199984
  75.   75: 535199985
  76.   76: 535199986
  77.   77: 535199987
  78.   78: 535199988
  79.   79: 535199989
  80.   80: 535199990
  81.   81: 535200000
  82.   82: 535200001
  83.   83: 1111111110
  84. real    0m0.006s
  85. user    0m0.002s
  86. sys     0m0.000s
  87. model name      : Intel(R) Pentium(R) M processor 1.73GHz
复制代码
回复 支持 反对

使用道具 举报

发表于 2008-2-20 12:28:36 | 显示全部楼层
比较pointer的算法,大概节省25%左右时间,以下是屏蔽输出各运行5000次的时间:
pointer:
real    0m24.951s
user    0m24.114s
sys     0m0.014s

my:
real    0m17.906s
user    0m17.316s
sys     0m0.008s
回复 支持 反对

使用道具 举报

发表于 2008-4-26 18:37:33 | 显示全部楼层

我花了半天作出来的

这个题我花了半天时间,就是一个排列组合的问题,用了一个递归做出来了,运行速度很快,几乎不用时间。

#include <stdio.h>

long fn(long);
int main(int argc, char *argv[])
{
        long num = atoi(argv[1]);
        long n = fn(num);
       
        printf("{0, ..., %ld}中共有%ld个1\n", num, n);
       
        return 0;
}

long fn(long num)
{
        long height_bit;        // 用存储最高位
        long low_bits;        // 存储最高位除外的低几位
        int bits;                // 用于存储num的位数
        long power;
        long t;
       
        if (num == 0)
                return 0;
        if (num < 10)
                return 1;
       
        height_bit = num;
        low_bits = 0;
        bits = 1;
        power = 1;
        t = 0;
        while (height_bit / 10) {
                low_bits += height_bit % 10 * power;
                height_bit /= 10;
                t = t * 10 + 9;
                power *= 10;
                bits++;
        }
       
        if (low_bits == t) {
                return (power + (height_bit + 1) * (power / 10) * (bits - 1));
        }
       
        return (fn((height_bit - 1) * power + t) + fn(low_bits) +
            low_bits+1);
}
回复 支持 反对

使用道具 举报

发表于 2008-4-26 19:06:06 | 显示全部楼层
Post by senmole;1842868
这个题我花了半天时间,就是一个排列组合的问题,用了一个递归做出来了,运行速度很快,几乎不用时间。

#include <stdio.h>

long fn(long);
int main(int argc, char *argv[])
{
        long num = atoi(argv[1]);
        long n = fn(num);
       
        printf("{0, ..., %ld}中共有%ld个1\n", num, n);
       
        return 0;
}

long fn(long num)
{
        long height_bit;        // 用存储最高位
        long low_bits;        // 存储最高位除外的低几位
        int bits;                // 用于存储num的位数
        long power;
        long t;
       
        if (num == 0)
                return 0;
        if (num < 10)
                return 1;
       
        height_bit = num;
        low_bits = 0;
        bits = 1;
        power = 1;
        t = 0;
        while (height_bit / 10) {
                low_bits += height_bit % 10 * power;
                height_bit /= 10;
                t = t * 10 + 9;
                power *= 10;
                bits++;
        }
       
        if (low_bits == t) {
                return (power + (height_bit + 1) * (power / 10) * (bits - 1));
        }
       
        return (fn((height_bit - 1) * power + t) + fn(low_bits) +
            low_bits+1);
}
修正:
上面有个错误,最后一句返回语句需要改成:
if (height_bit == 0) {
    return (fn((height_bit - 1) * power + t) + fn(low_bits) + low_bits + 1);
}
else {
    return (fn((height_bit - 1) * power + t) + fn(low_bits));
}
回复 支持 反对

使用道具 举报

发表于 2008-4-27 20:30:45 | 显示全部楼层
都是夹高手啊
回复 支持 反对

使用道具 举报

发表于 2008-5-22 09:39:26 | 显示全部楼层
/*这是一个计算从1数到n,1出现次数的程序,我的解法,从最高位开算,只算最高位能包含多少个
1,然后砍掉最高算次高,一直砍到最低位一切OK,不过需注意的是,出现某个位上有1和0的时候
计算1个数的方法和高于1有数不一样。因为只是计算其权位上的数,所以循环次数只是有几位数就循环几次,我感觉还是比较快的,不知道我这个新手怎么样,希望高手能指点*/


#include <iostream>
#include  <ctime>
using namespace std;

const int ten=10;

int  quan(long);                                        //计算一个数的位数,也就是这个数的最高权
int bit(long);                                                //计算一个数最高权上的数
long  sumhigh(long);                                        //把一个数的最高位砍掉(去掉最高位)
long pow(int x,int y);                                //计算一个数的正数次幂



int main()
{
               

        long a,b,c,high,d;
        int x,y,z;
       
        cout <<"请输入一个整数,我们将判断它包含的数中有多少个1:\n";
        cin >>a;
       
        d=a;                                                //d,用来存储a的值,以便下面运算的时候不受a的变化影响
        b=0;
        high=0;                                        //high用来存储一个数的绝对最高位
        for (long i=quan(a);i>0;--i)
                {
               
               
                if  (quan(a)<i)        y = 0 ;        //如果把a取权之后小于i,证明当前位置为零。
               
                else y= bit(a);                       
               
               
                if (y==1)
                        {
                        c=high*pow(ten,i-1)+sumhigh(a)+1;               
                        b=b+c;
                        }
               
               
                else if (y>1)
                        {
                        c=high*pow(ten,i-1)+pow(ten,i-1);
                        b=b+c;
                        }
                else if (y==0&&i!=1)
                        {
                        c=high*pow(ten,i-1);
                        b=b+c;
                        }
                else if(y==0&&i==1)
                        {
                        c=high;
                        b=b+c;
                        }
               
                high=d/pow(ten,i-1);
               
                if (y!=0)        a=sumhigh(a);
               
                }
       
        cout <<"包含1的个数有:"<<b<<"个\n";
       
}


int quan(long x)
{
        bool a=true;
        int b=0;
        do
        {
        if (x<1)
                {
                a=false;
                }
                else
                {
                x=x/ten;
                b++;
                }
        }while(a);
return b;
}


int bit(long x)
{
        int a,b=1;
        do
        {
        if (x>=ten)
                {       
                x=x/ten;
                }
        else b=0;
        }while (b);
return x;
}


long sumhigh(long x)
{
        long a;
        a= x-bit(x)*pow(ten,quan(x)-1);
return a;
}


long pow(int x,int y)
{
        long a=1;
        if (y>0)
                {
                for (int i=0;i<y;++i)
                        {
                        a=a*x;
                        }
                }
        else if (y==0) a=1;
       
return a;
}
回复 支持 反对

使用道具 举报

发表于 2008-5-22 21:50:28 | 显示全部楼层
嗯, 很好----发现大部分人理解错了题, 你是少有的理解了的人.

Hand
Post by Mason_hou;1818060
今天看了一下大家的思路,好像方向不对阿,要提高这算法的效率,关键在于搜索的步长,而不是讨论怎么计算1的个数,大家在这里都只是想方设法提高计算1个数的效率而已。计算1个数的公式pointer 已经得出,这已经是最高效率的了。而且也证明了n的范围,这点很重要(虽然证明过程有些笔误)。
不过还有最关键的问题是搜索的步长,在这点上pointer的算法并非最好,不过也比普遍的递进好太多了。我也试着做了一些改进:

一、更改一下n的下界,
首先可以证明除1外最小n>5:
由pointer的公式有
f(an,an-1.....a0)=(an-1...a0)+1+f(10^n-1) + f(an-1,...,0) ,  an=1

f(an,an-1.....a0) = (an...a0)

(an...a0)- (an-1...a0) = 1+f(10^n-1) + f(an-1,...,0)
-->
10^n = 1+n*10^(n-1) + f(an-1,...,0) <= 2( 1+ n*10^(n-1))   ( 因 f(an-1,...,0)<=f(an,...,0), 且 (an-1...a0)可以为 0)
于是有 n> 5,编程可以直接跳到 n=1e5进行搜索。

二、提高搜索步长:
因假设一个数n长为 L,d = n- f(n) 。
这里仅仅讨论d>0的情况。如果n增加能使d减少,则n至少固定含有一个'1'。并且在仅有一个1不变的情况下n每增加a,0<d-a<=d。基于这一思想,对于可能出现 L 个 '1',pointer的算法保守得使用 d/L 作为增加的步长,但实际上可以动态设置。比较简单得就是原有的'1'的个数加上 d 的长度作为除数即可。

三、连续打印连续的10个数:
可以从公式证明在如果 n 是所求,且除个,十位仅有1个'1',则连续的个十位由01-10都是所求。

程序:

  1. #include<stdio.h>
  2. #define __USE_ISOC99
  3. #include<stdlib.h>
  4. #define MAXNL  10 // It has been proofed that n<=8 and n_max = 1111111110
  5. #define MAN_N 1111111111
  6. long long tenpowers[MAXNL]= {1};// 10^i
  7. long long rempowers[MAXNL]= {0};// remainder power: i*10^(i-1)
  8. int remainders[MAXNL]={0};        //  remainders

  9. // Suppose that n= a* 10^i+b, where i >0 and b<10^i.
  10. // Then b_i = n- a*10^i, and b_num stores b_i.
  11. long long b_num[MAXNL]={0};

  12. int len=0; // length of n

  13. long long countOnes(long long n)
  14. {       
  15.         int  i, j;
  16.         long long ones_num=0;
  17.         lldiv_t qr;       
  18.         // initial
  19.         len =0;
  20.         qr.quot=n;
  21.         // count len and set b_num
  22.         while(qr.quot)        {
  23.                 qr=lldiv(qr.quot, 10L);
  24.                 remainders[len]=qr.rem;
  25.                 if (len>0) { // set b_num
  26.                         b_num[len]=b_num[len-1]+qr.rem*tenpowers[len];
  27.                 }
  28.                 else{
  29.                         b_num[len]=qr.rem;
  30.                 }
  31.                 len++;
  32.         }
  33.         // count ones
  34.         for(i=len-1; i>0; --i)        {
  35.                 if(remainders[i]>1) {
  36.                         ones_num+=tenpowers[i]+remainders[i]*rempowers[i];
  37.                 }
  38.                 else if(remainders[i]==1){
  39.                         ones_num+=rempowers[i]+b_num[i-1]+1;
  40.                 }
  41.         }
  42.         ones_num+=(remainders[i]>0)?1:0;

  43.         return ones_num;
  44. }

  45. // count the number of one in remainders
  46. int numOfOne() {
  47.         int count=0,i;
  48.         for (i=1;i<len;i++) { // ignore the first remainder
  49.                 if ( 1 == remainders[i] ) {
  50.                         count++;
  51.                 }
  52.         }
  53.         return count;
  54. }

  55. // Print the adjoining ten number, each of who has only one '1' except the first one .
  56. int printAdjTen( long long n, int n_num) {       
  57.         int i;
  58.         if (numOfOne() ==1) {                                       
  59.                 for (i=0; i<9; i++) {       
  60.                         printf("%4d: %lld\n", n_num++ ,++n);
  61.                 }       
  62.                 return 1;                               
  63.         }
  64.         return 0;       
  65. }

  66. // Get the length of one number
  67. int getLen(long long n) {
  68.         int len =0 ;
  69.         for (len=0;len<MAXNL;len++) {
  70.                 if ( n < tenpowers[len]) {
  71.                         break;
  72.                 }
  73.         }               
  74.         return len;
  75. }

  76. int main(int argc, char * argv[])
  77. {
  78.         long long ones_num,  diff; // numbers of '1' ; difference between n and ones_num
  79.         int  i,n_num=1;
  80.         int diff_len,step;
  81.         long long  n=1;
  82.         for(i=1;i<MAXNL;++i)         {
  83.                 tenpowers[i]=tenpowers[i-1]*10L;
  84.                 rempowers[i]=tenpowers[i-1]*i;
  85.         }
  86.         printf("%4d: %lld\n", n_num++ ,n);        // print '1'
  87.         n = 1e5; // It has been proved that next n must be larger than 1e5
  88.         while(n<MAN_N) { // searching
  89.                 ones_num=countOnes(n);
  90.                 diff=n-ones_num;
  91.                 if(diff>len)        {
  92.                         // set search step.
  93.                         step=numOfOne();
  94.                         if (step==0) {
  95.                                 step = len-1;
  96.                         }else {
  97.                                 diff_len = getLen(diff); // get length of diff
  98.                                 step = step+diff_len;
  99.                                 if (step==len) {
  100.                                         step--;
  101.                                 }
  102.                         }
  103.                         n+=diff/step+1;
  104.                 }else if(diff<0) {
  105.                         n+=(-diff);
  106.                 }else {
  107.                         if(diff==0){
  108.                                 printf("%4d: %lld\n", n_num++ ,n);                                                               
  109.                                 if ( printAdjTen( n, n_num) ) {
  110.                                         n+=9;
  111.                                         n_num+=9;
  112.                                 }
  113.                         }                       
  114.                         ++n;
  115.                 }
  116.          }
  117.         return 0;
  118. }
复制代码
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表