• Task 10 统计从1到某个整数之间出现的1的次数


         任务:给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数。

         要求: 写一个函数 f(N) ,返回1 到 N 之间出现的 “1”的个数。例如 f(12) = 5。

                   在32位整数范围内,满足条件的“f(N) =N”的最大的N是多少。

    1.设计思想:因为上课很多同学都给出了一个一个数地求出所出现的1,最多每个数也就求5、6次,但是所给的整数很大的时候计算机会一下循环递归N次来计算1的次数,这样会导致效率非常低。我们都知道每个位数上都有一定的规律,每一位上出现1的次数都和其前一位和后一位以及当前位上的数字有关系,所以得通过大量的数据一位一位的进行分析,进而找到每个数的规律。

    通过对1位数、2位数、3位数,,,进行分析统计,发现如果当前位上的数字为0,1,大于等于1时有不同的情况;则此位上出现的1的次数分别会由更高位数上、更低位或者当前位的数字决定,具体如下:

    假设一个数为abcde

    如果百位上数字c为0,百位上可能出现1的次数由更高位决定。比如:33033,则可以知道百位出现1的情况可能是:100~199,1100~1199,2100~2199,,.........,32100~32199,一共3300个。可以看出是由更高位数字(12)决定,并且等于更高位数字(ab)乘以 当前位数(100)。

    如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:33133,则可以知道百位受高位影响出现的情况是:100~199,1100~1199,2100~2199,,.........,32100~32199,一共3300个。和上面情况一样,并且等于更高位数字(ab)乘以 当前位数(100)。但同时它还受低位影响,百位出现1的情况是:33100~33133,一共134个,等于低位数字(cde)+1。

    如果百位上数字大于1(2~9),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100~199,1100~1199,2100~2199,...........,11100~11199,12100~12199,一共有1300个,并且等于更高位数字+1(ab+1)乘以当前位数(100)。

    除了百位上其他位数也都符合这个规律,所以只需要循环整数的位数次就可以求出最终的结果。

    然后第二步实现的时候,开始以为只需要一个循环就行了,让计算机循环。不过由于基数太大所以会用很长时间

    将要计算的范围划分为几个区间,然后对每个区间进行计算。比如说:

    1到999,先将这些数划分为10个区间:1-99、100-199 … 900-999

    由f(999) = 300可知,300以后的区间段可以不计算。当计算200时,可以先计算299,由于f(299)=160<200,200-299的区间可以都不必计算。对要计算的区间,再将它划分为10个区间,重复进行。这样划分的另一个好处是利用公式:f(10^n-1) = n * 10^(n-1),保存上次算得的f(n)直接计算下个数的f(n)。

    2.源代码:

    #include<iostream>
    using namespace std;
    
    int Count(int n){
        
        int count = 0;//1的个数
        
        int CurrentPosition = 1;//当前位
        
        int LowerNum = 0;//低位数字
        
        int CurrNum = 0;//当前位数字
        
        int HigherNum = 0;//高位数字
    
        while(n / CurrentPosition != 0)
        {
            LowerNum = n - (n / CurrentPosition) * CurrentPosition;//低位数字
            
            CurrNum = (n / CurrentPosition) % 10;//当前位数字
            
            HigherNum = n / (CurrentPosition * 10);//高位数字
            
            if(CurrNum == 0)//如果为0,出现1的次数由高位决定
            {
                count += HigherNum * CurrentPosition;//等于高位数字 * 当前位数
            }
            
            else if(CurrNum == 1)//如果为1,出现1的次数由高位和低位决定
            {
                count += HigherNum * CurrentPosition + LowerNum + 1;//高位数字 * 当前位数 + 低位数字 + 1
            }
            
            else//如果大于1,出现1的次数由高位决定
            {
                count += (HigherNum + 1) * CurrentPosition;//(高位数字+1)* 当前位数
            }
            
            CurrentPosition *= 10;//前移一位
        }
        return count;
    }
    
    void main()
    {
        int a;
        cout << "请输入一个正整数:";
        cin >> a;
        cout << a;
        cout << "从1到该数字出现的1的次数为:" << Count(a) << endl;
        for (int i = 0; i < 4294967295 ; i++)
        {
            if( Count(i) == i)
            {
                cout << i << "  ";
            }
    
        }
    }

    改进之后:

    #include<iostream>
    using namespace std;
    
    int Count(int n){
        
        int count = 0;//1的个数
        
        int CurrentPosition = 1;//当前位
        
        int LowerNum = 0;//低位数字
        
        int CurrNum = 0;//当前位数字
        
        int HigherNum = 0;//高位数字
    
        while(n / CurrentPosition != 0)
        {
            LowerNum = n - (n / CurrentPosition) * CurrentPosition;//低位数字
            
            CurrNum = (n / CurrentPosition) % 10;//当前位数字
            
            HigherNum = n / (CurrentPosition * 10);//高位数字
            
            if(CurrNum == 0)//如果为0,出现1的次数由高位决定
            {
                count += HigherNum * CurrentPosition;//等于高位数字 * 当前位数
            }
            
            else if(CurrNum == 1)//如果为1,出现1的次数由高位和低位决定
            {
                count += HigherNum * CurrentPosition + LowerNum + 1;//高位数字 * 当前位数 + 低位数字 + 1
            }
            
            else//如果大于1,出现1的次数由高位决定
            {
                count += (HigherNum + 1) * CurrentPosition;//(高位数字+1)* 当前位数
            }
            
            CurrentPosition *= 10;//前移一位
        }
        return count;
    }
    
    inline unsigned count_digits(unsigned long long num)
    {
        unsigned long long n = 1;
        unsigned ret = 0;
        while (n <= num) { n *= 10; ++ret; }
        return ret;
    }
    
    void get_nums()
    {
    
        unsigned long long x = 1e11 - 1, y;
        unsigned count = 0;
        unsigned idx = 0;
        while (true)
        {
            ++count;
            y = Count(x);
            if (x < y) 
            {
    
                //x在1到10时,均不满足x<y,所以x>10,下面的k值肯定大于0    
                unsigned k = count_digits(x) - 1;
                x -= (y - x - 1)/k + 1; 
            }
            else if (x > y) { x = y; } 
            else
            {
               cout<< ++idx << ": " << x << endl;
               //break;
               --x;
               if (x == 0) break;
            } 
        }
    }
    
    void main()
    {
        int a;
        cout << "请输入一个正整数:";
        cin >> a;
        cout << a;
        cout << "从1到该数字出现的1的次数为:" << Count(a) << endl;
        cout << "整数与次数相同的有以下这些,最大值为第一个数:";
        get_nums();
    }

    3.实验截图:

    4.实验总结:

        (1)这个题目跟之前的同样是数学题类型的程序,需要利用大量的来分析统计,从中得出规律,否则就失去了编程的高效性;

        (2)而当完成第一步之后以为第二步很简单,其实不然。感觉当时一定是被成功的喜悦蒙蔽了双眼,只是看它一直在滚动数字,而且也得出了最后的结果,然而却没想到效率的问题,之才发现第二步的设计也包含了好多规律,所以一定要从头到尾保持清醒的头脑。

  • 相关阅读:
    mysql 全量备份和增量备份
    mysql 修改密码提示ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
    MHA 常见问题解决
    MHA 数据库高可用+ GTID 同步测试部署
    通过电脑抓手机端log
    用js递归遍历树结构
    js实现全屏
    使表格随着内容自适应宽度
    POST请求
    vue中组件通信
  • 原文地址:https://www.cnblogs.com/mengxiangjialzh/p/4548613.html
Copyright © 2020-2023  润新知