任务:给定一个十进制的正整数,写下从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. CO借贷标识及转换

    前台提前数据源0CO_PC_01的数据时发现借贷方有O.C.S.D.不是传统的FI借贷标识(S = 借记,H = 贷记). 由"D"指出贷方记帐.从以下项中发生这些记帐 财务会计中 ...

  2. 关闭生产订单时报错“订单&的未处理将来更改记录组织删除标记/完成”,消息号CO688

    消息号 CO688 诊断 仍存在未来的更改记录,或从订单的确认过程的确认中要处理的错误记录.可能的确认过程是: 自动收货 反冲 实际成本的计算 数据传输至 HR 系统响应 未打算对订单设置删除标记/‘ ...

  3. dataTable配置项说明

    Datatables是一款jquery表格插件.它是一个高度灵活的工具,可以将任何HTML表格添加高级的交互功能. 官网地址:https://datatables.net/ 中文说明地址:http:/ ...

  4. Oracletop10物理段

    Oracletop10物理段 select owner, name, type, mega, tbs from (select owner, case when segment_type = 'LOB ...

  5. Linux tree 命令乱码

    今天在执行Linux下的tree命令的时候,出现了乱码.上网查了一下说需要使用tree --charset ASCII,强制使用ASCII字符.这样确实可以输出正常了.但是我的环境里的LANG=US. ...

  6. MFC 消息映射、分派和传递

    几个重要的结构体: struct AFX_MSGMAP { AFX_MSGMAP* pBaseMessageMap; AFX_MSGMAP_ENTRY* lpEntries; } struct AFX ...

  7. JAVAWEB servlet验证登录时进行完全的非空判断防止空值登录

    如果不进行完全的非空判断,那么对 "" 这种类型的空值就会导致直接登录 所以需要用下面的字符串处理方法对其进行判断 这样就可以防止空值登录了 容易出现的混淆错误: 这里的空值登录容 ...

  8. 使用ASP.NET Identity以手机短信实现双重验证

    这篇文章将展示怎么使用SMS短信启动双重验证 创建一个ASP.NET 5项目 一开始,使用Visual studio 2015创建一个新的ASP.NET Web应用程序: 在下一步中选择ASP.NET ...

  9. Tomcat部署Web应用

    在Tomcat中部署Web有三种方法: 1,可以将Web应用文件直接复制到webapps目录下,也可以将Web应用打成war包放到webapps目录下,tomcat会自动解开war包,并在webapp ...

  10. Android Studio: Could not download junit.jar (junit:junit:4.12)

    下载了Android Studio 3.1.4,新建一个项目,选择Activity,结果gradle提示 Unable to resolve dependency for ':app@debugAnd ...