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)而当完成第一步之后以为第二步很简单,其实不然。感觉当时一定是被成功的喜悦蒙蔽了双眼,只是看它一直在滚动数字,而且也得出了最后的结果,然而却没想到效率的问题,之才发现第二步的设计也包含了好多规律,所以一定要从头到尾保持清醒的头脑。
转载于:https://www.cnblogs.com/mengxiangjialzh/p/4548613.html
Task 10 统计从1到某个整数之间出现的1的次数相关推荐
- 本题要求实现一个函数,可统计任一整数中某个位数出现的次数。例如-21252中,2出现了3次,则该函数应该返回3。
本题要求实现一个函数,可统计任一整数中某个位数出现的次数.例如-21252中,2出现了3次,则该函数应该返回3. 函数接口的定义 int Count_Digit ( const int N, cons ...
- 统计个位数字 (本题要求实现一个函数,可统计任一整数中某个位数出现的次数。例如-21252中,2出现了3次,则该函数应该返回3)
5.本题要求实现一个函数,可统计任一整数中某个位数出现的次数.例如-21252中,2出现了3次,则该函数应该返回3. 实现代码: int Count_Digit ( const int N, cons ...
- C# IP地址和整数之间的转换,IP地址和数字ip地址的转换
IP地址与整数之间的转换 1.IP地址转换为整数 原理:IP地址每段可以看成是8位无符号整数即0-255,把每段拆分成一个二进制形式组合起来,然后把这个二进制数转变成一个无符号的32位整数. 举例:一 ...
- 第五节:Task构造函数之TaskCreationOptions枚举处理父子线程之间的关系。
一. 整体说明 揭秘: 通过F12查看Task类的源码(详见下面的截图),发现Task类的构造函数有有一个参数为:TaskCreationOptions类型,本章节可以算作是一个扩展章节,主要就来研究 ...
- 1到n的整数中,1出现的次数
参考链接:https://discuss.leetcode.com/topic/18054/4-lines-o-log-n-c-java-python 1到n的整数中,1出现的次数,如11中,1出现了 ...
- 软件的基本是要处理好”算法“及其基础(一)流-字-字符(包括某个数字、字母、符号和某个汉字等)-字符串-字节动态数组-字节-整数之间的转化关系和算法
目录 软件的基本是要处理好"算法"及其基础(一): 流->计算机字->字符(包括某个数字.字母.符号和某个汉字等)->字符串->字节动态数组->字节- ...
- C语言: 定义一个函数int isprime(int n),用来判别一个正整数n是否为素数。在主函数中输入两个正整数m和n(m>=1,n>m),统计并输出m和n之间的素数的个数以及这些素数的和。
原题: 定义一个函数int isprime(int n),用来判别一个正整数n是否为素数.在主函数中输入两个正整数m和n(m>=1,n>m),统计并输出m和n之间的素数的个数以及这些素数的 ...
- 日期类对象与整数之间的加法运算
日期类对象与整数之间的加法运算 采用友元函数形式, 定义两个友元函数
- Java实现 String类型的ip与整数之间的相互转换(2021.8.1百度提前批面试题)
题目: 实现 String 类型的 ip 与整数之间的相互转化.例如:将 "192.168.0.1" 转换为整数,然后给定这个整数仍然可以得到这个 String 类型的 ip. / ...
最新文章
- 2022-2028年中国树脂行业市场研究及前瞻分析报告
- 程序员:提高编程效率的技巧
- 安装和使用Smokeping(二)
- 50个国内外最棒的C/C++源码站点分享
- oc09--NSString
- 人脸识别 轻量级高精度网络推荐
- 20181127-1附加作业 软件工程原则的应用实例
- 算法提高课-搜索-Flood fill算法-AcWing 1106. 山峰和山谷:flood fill、bfs
- SpringBoot实现Java高并发秒杀系统之DAO层开发(一)
- 爱我少一点,我请求你
- graphpad怎么修改图片大小_Graphpad Prism 8.0进阶篇-绘制小提琴图
- .NET平台下几种SOCKET模型的简要性能供参考
- MIS 740: Software Concepts Use different GUI components i
- CPLEX-求解VRPTW模型
- 【参考】MTK线刷工具错误代码大全及解决方法
- 调整视频播放速度,如何让视频加速或放慢播放
- 历届博客之星获奖博客分享
- javascript与css压缩工具
- python将excel转换成图片_python-尝试将Excel文件保存为图片并加上水印-阿里云开发者社区...
- 抖音数据分析(基于播放、点赞、投稿、背景音乐)--pyecharts可视化