任务:给定一个十进制的正整数,写下从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 = ;//1的个数 int CurrentPosition = ;//当前位 int LowerNum = ;//低位数字 int CurrNum = ;//当前位数字 int HigherNum = ;//高位数字 while(n / CurrentPosition != )
{
LowerNum = n - (n / CurrentPosition) * CurrentPosition;//低位数字 CurrNum = (n / CurrentPosition) % ;//当前位数字 HigherNum = n / (CurrentPosition * );//高位数字 if(CurrNum == )//如果为0,出现1的次数由高位决定
{
count += HigherNum * CurrentPosition;//等于高位数字 * 当前位数
} else if(CurrNum == )//如果为1,出现1的次数由高位和低位决定
{
count += HigherNum * CurrentPosition + LowerNum + ;//高位数字 * 当前位数 + 低位数字 + 1
} else//如果大于1,出现1的次数由高位决定
{
count += (HigherNum + ) * CurrentPosition;//(高位数字+1)* 当前位数
} CurrentPosition *= ;//前移一位
}
return count;
} void main()
{
int a;
cout << "请输入一个正整数:";
cin >> a;
cout << a;
cout << "从1到该数字出现的1的次数为:" << Count(a) << endl;
for (int i = ; i < ; i++)
{
if( Count(i) == i)
{
cout << i << " ";
} }
}

改进之后:

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

3.实验截图:

4.实验总结:

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

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

Task 10 统计从1到某个整数之间出现的1的次数的更多相关文章

  1. C# IP地址与整数之间的转换

    IP地址与整数之间的转换 1.IP地址转换为整数 原理:IP地址每段可以看成是8位无符号整数即0-255,把每段拆分成一个二进制形式组合起来,然后把这个二进制数转变成一个无符号的32位整数. 举例:一 ...

  2. [c++]大数运算1---利用C++ string实现任意长度正小数、整数之间的加减法

    一.概述 本文属于大大维原创,未经笔者本人允许,严禁转载!!! C/C++中的int类型能表示的范围是-2E31-2E31–1.unsigned类型能表示的范围是0-2E32–1,即 0-429496 ...

  3. [c++]大数运算---利用C++ string实现任意长度正小数、整数之间的加减法

    本文为大大维原创,最早于博客园发表,转载请注明出处!!! 一.概述 C/C++中的int类型能表示的范围是-2E31-2E31–1.unsigned类型能表示的范围是0-2E32–1,即 0-4294 ...

  4. 1到n的整数中,1出现的次数

    参考链接:https://discuss.leetcode.com/topic/18054/4-lines-o-log-n-c-java-python 1到n的整数中,1出现的次数,如11中,1出现了 ...

  5. C语言中的字符和整数之间的转换

    首先对照ascal表,查找字符和整数之间的规律: ascall 控制字符  48  0  49  1  50  2  51  3  52  4  53  5  54  6  55  7  56  8 ...

  6. (算法)从0到n整数中数字2出现的次数

    题目: 数出0到n(含)中数字2出现了几次. 思路: 1.暴力方法,数出每个数字包含几个2,然后累加起来. 2.分析:分别考虑数字n每一位出现2的次数,如123123: 从左往右考虑4123123: ...

  7. 输入n行整数,每行的个数不确定,整数之间用逗号分隔

    /*===================================== 输入n行整数,每行的个数不确定. 每行内部两个数之间用逗号隔开. 例如输入数据如下: 6 1,3,5,23,6,8,14 ...

  8. 从1到整数n中1出现的次数

    题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数.例如输入12,从1到12这些整数中包含1的数字有1,10,11和12共出现了5次.   不考虑时间效率的解法: int Numbe ...

  9. python实例:利用jieba库,分析统计金庸名著《倚天屠龙记》中人物名出现次数并排序

    本实例主要用到python的jieba库 首先当然是安装pip install jieba 这里比较关键的是如下几个步骤: 加载文本,分析文本 txt=open("C:\\Users\\Be ...

随机推荐

  1. #leetcode刷题之路36-有效的数独

    判断一个 9x9 的数独是否有效.只需要根据以下规则,验证已经填入的数字是否有效即可.数字 1-9 在每一行只能出现一次.数字 1-9 在每一列只能出现一次.数字 1-9 在每一个以粗实线分隔的 3x ...

  2. ruby的循环控制命令loop等

    ruby的循环有以下几种: times方法 for while until(与while相反) each(与for极度相似,在ruby内部,for语句是用each实现的) loop(无限循环,与bre ...

  3. 1.Variables-变量(Dart中文文档)

    初次翻译,部分内容并非按字面翻译,是按本人理解进行了内容重组.如有错误望指正. 如下是变量定义和赋值的示例 var name = 'Bob'; 变量存储的是一个引用地址.如上的变量name指向了一个值 ...

  4. react canvas

    http://engineering.flipboard.com/2015/02/mobile-web/

  5. .Net Core 管道机制

    开篇先上一张中间件原理图,帮助大家对管道机制形成一个直观的认识 下面我们实现一个简单的管道机制,以此为例深入理解管道机制的原理 1. 首先定义一个委托,该委托接收一个上下文对象,返回值为Task,代码 ...

  6. P2839 [国家集训队]middle

    P2839 [国家集训队]middle 好妙的题啊,,,, 首先二分一个答案k,把数列里>=k的数置为1,=0就是k>=中位数,<0就是k<中位数 数列的最大和很好求哇 左边的 ...

  7. Python爬虫入门 之 如何在豆瓣中获取自己喜欢的TOP N电影信息

    什么是爬虫 按照一定规则自动的获取互联网上的信息(如何快速有效的利用互联网上的大量信息) 爬虫的应用 搜索引擎(Google.百度.Bing等搜索引擎,辅助人们检索信息) 股票软件(爬取股票数据,帮助 ...

  8. 【PaPaPa】集成B/S主流技术的MVC5项目 - 实干派:说做就做,我们已经起航,你还在观望吗

    我们是谁 我们是C#爱好者,互相分享技术,一起学习一起成长一起做一个项目. 我们是开源爱好者,从我们手上出来的代码都会托管在源代码管理平台(oschina),到目前为止不收费,将来也不会出现任何收费情 ...

  9. centos下安装docker,kubelet kubeadm kubectl

    目录 安装docker 安装命令 安装 kubelet kubeadm kubectl 安装命令 安装docker 安装命令 yum install docker -y 启动 systemctl en ...

  10. Linux命令的那些事(三)

    回顾linux命令那些事,前面大致总结了常用的Linux命令 回顾Linux命令那些事(一) clear/mkdir/rmdir/ls/rm/pwd/cd/touch/tree/man/--help ...