分析与解法
【问题1的解法一】
这个问题看上去并不是一个困难的问题,因为不需要太多的思考,我想大家都能找到一个最简单的方法来计算f(N),那就是从1开始遍历到N,将其中每一个数中含有“1”的个数加起来,自然就得到了从1到N所有“1”的个数的和。写成程序如下:
代码清单2-9
ULONGLONG Count1InAInteger(ULONGLONG n)
{
ULONGLONG iNum = 0;
while(n != 0)
{
iNum += (n % 10 == 1) ? 1 : 0;
n /= 10;
}
return iNum;
}
ULONGLONG f(ULONGLONG n)
{
ULONGLONG iCount = 0;
for (ULONGLONG i = 1; i <= n; i++)
{
iCount += Count1InAInteger(i);
}
return iCount;
}
这个方法很简单,只要学过一点编程知识的人都能想到,实现也很简单,容易理解。但是这个算法的致命问题是效率,它的时间复杂度是
O(N)×计算一个整数数字里面“1”的个数的复杂度 =O(N* log2N)
如果给定的N比较大,则需要很长的运算时间才能得到计算结果。比如在笔者的机器上,如果给定N=100 000 000,则算出f(N)大概需要40秒的时间,计算时间会随着N的增大而线性增长。
看起来要计算从1到N的数字中所有1的和,至少也得遍历1到N之间所有的数字才能得到。那么能不能找到快一点的方法来解决这个问题呢?要提高效率,必须摈弃这种遍历1到N所有数字来计算f(N)的方法,而应采用另外的思路来解决这个问题。
【问题1的解法二】
仔细分析这个问题,给定了N,似乎就可以通过分析“小于N的数在每一位上可能出现1的次数”之和来得到这个结果。让我们来分析一下对于一个特定的N,如何得到一个规律来分析在每一位上所有出现1的可能性,并求和得到最后的f(N)。
先从一些简单的情况开始观察,看看能不能总结出什么规律。
先看1位数的情况。
如果N= 3,那么从1到3的所有数字:1、2、3,只有个位数字上可能出现1,而且只出现1次,进一步可以发现如果N是个位数,如果N>=1,那么f(N)都等于1,如果N=0,则f(N)为0。
再看2位数的情况。
如果N=13,那么从1到13的所有数字:1、2、3、4、5、6、7、8、9、10、11、12、13,个位和十位的数字上都可能有1,我们可以将它们分开来考虑,个位出现1的次数有两次:1和11,十位出现1的次数有4次:10、11、12和13,所以f(N)=2+4=6。要注意的是11这个数字在十位和个位都出现了1,但是11恰好在个位为1和十位为1中被计算了两次,所以不用特殊处理,是对的。再考虑N=23的情况,它和N=13有点不同,十位出现1的次数为10次,从10到19,个位出现1的次数为1、11和21,所以f(N)=3+10=13。通过对两位数进行分析,我们发现,个位数出现1的次数不仅和个位数字有关,还和十位数有关:如果N的个位数大于等于1,则个位出现1的次数为十位数的数字加1;如果N的个位数为0,则个位出现1的次数等于十位数的数字。而十位数上出现1的次数不仅和十位数有关,还和个位数有关:如果十位数字等于1,则十位数上出现1的次数为个位数的数字加1;如果十位数大于1,则十位数上出现1的次数为10。
f(13) = 个位出现1的个数 + 十位出现1的个数 = 2 + 4 = 6;
f(23) = 个位出现1的个数 + 十位出现1的个数 = 3 + 10 = 13;
f(33) = 个位出现1的个数 + 十位出现1的个数 = 4 + 10 = 14;
…
f(93) = 个位出现1的个数 + 十位出现1的个数 = 10 + 10 = 20;
接着分析3位数。
如果N= 123:
个位出现1的个数为13:1, 11, 21, …, 91, 101, 111, 121
十位出现1的个数为20:10~19, 110~119
百位出现1的个数为24:100~123
f(23)= 个位出现1的个数 + 十位出现1的个数 + 百位出现1的次数 = 13 + 20 + 24 = 57;
同理我们可以再分析4位数、5位数。读者朋友们可以写一写,总结一下各种情况有什么不同。
根据上面的一些尝试,下面我们推导出一般情况下,从N得到f(N)的计算方法:
假设N=abcde,这里a、b、c、d、e分别是十进制数N的各个数位上的数字。如果要计算百位上出现1的次数,它将会受到三个因素的影响:百位上的数字,百位以下(低位)的数字,百位(更高位)以上的数字。
如果百位上的数字为0,则可以知道,百位上可能出现1的次数由更高位决定,比如12 013,则可以知道百位出现1的情况可能是100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,一共有1 200个。也就是由更高位数字(12)决定,并且等于更高位数字(12)×当前位数(100)。
如果百位上的数字为1,则可以知道,百位上可能出现1的次数不仅受更高位影响,还受低位影响,也就是由更高位和低位共同决定。例如对于12 113,受更高位影响,百位出现1的情况是100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,一共1 200个,和上面第一种情况一样,等于更高位数字(12)×当前位数(100)。但是它还受低位影响,百位出现1的情况是12 100~12 113,一共114个,等于低位数字(123)+1。
如果百位上数字大于1(即为2~9),则百位上可能出现1的次数也仅由更高位决定,比如12 213,则百位出现1的可能性为:100~199,1 100~1 199,2 100~2 199,…,11 100~11 199,12 100~12 199,一共有1 300个,并且等于更高位数字+1(12+1)×当前位数(100)。
通过上面的归纳和总结,我们可以写出如下的更高效算法来计算f(N):
代码清单2-10
LONGLONG Sum1s(ULONGLONG n)
{
ULONGLONG iCount = 0;
ULONGLONG iFactor = 1;
ULONGLONG iLowerNum = 0;
ULONGLONG iCurrNum = 0;
ULONGLONG iHigherNum = 0;
while(n / iFactor != 0)
{
iLowerNum = n - (n / iFactor) * iFactor;
iCurrNum = (n / iFactor) % 10;
iHigherNum = n / (iFactor * 10);
switch(iCurrNum)
{
case 0:
iCount += iHigherNum * iFactor;
break;
case 1:
iCount += iHigherNum * iFactor + iLowerNum + 1;
break;
default:
iCount += (iHigherNum + 1) * iFactor;
break;
}
iFactor *= 10;
}
return iCount;
这个方法只要分析N就可以得到f(N),避开了从1到N的遍历,输入长度为Len的数字N的时间复杂度为O(Len),即为O(ln(n)/ln(10)+1)。在笔者的计算机上,计算N=100 000 000,相对于第一种方法的40秒时间,这种算法不到1毫秒就可以返回结果,速度至少提高了40 000倍。
【问题2的解法】
要确定最大的数N,满足f(N)=N。我们通过简单的分析可以知道(仿照上面给出的方法来分析):
9以下为: 1个;
99以下为: 1×10+10×1=20个;
999以下为: 1×100+10×20=300个;
9999以下为: 1×1000+10×300=4000个;
…
999999999以下为: 900000000个;
9999999999以下为: 10000000000个。
容易从上面的式子归纳出:f(10n-1)=n* 10n-1。通过这个递推式,很容易看到,当n= 9时候,f(n)的开始值大于n,所以我们可以猜想,当n大于某一个数N时,f(n)会始终比n大,也就是说,最大满足条件在0~N之间,亦即N是最大满足条件f(n)=n的一个上界。如果能估计出这个N,那么只要让n从N往0递减,每个分别检查是否有f(n)=n,第一个满足条件的数就是我们要求的整数。
因此,问题转化为如何证明上界N确实存在,并估计出这个上界N。
证明满足条件f(n)=n的数存在一个上界
首先,用类似数学归纳法的思路来推理这个问题。很容易得到下面这些结论(读者朋友可以自己试着列举验证一下):
当n增加10时,f(n)至少增加1;
当n增加100时,f(n)至少增加20;
当n增加1 000时,f(n)至少增加300;
当n增加10 000时,f(n)至少增加4 000;
……
当n增加10k时,f(n)至少增加k*10k-1。
首先,当k>=10时,k*10k-1> 10k,所以f(n)的增加量大于n的增加量。
其次,f(1010-1)=1010>1010-1。如果存在N,当n=N时,f(N)-N>1010-1成立时,此时不管n增加多少,f(n)的值将始终大于n。
具体来说,设n的增加量为m:当m小于1010-1时,由于f(N)-N>1010-1,因此有f(N+m)>f(N)>N+ 1010-1>N+m,即f(n)的值仍然比n的值大;当m大于等于1010-1时,f(n)的增量始终比n的增量大,即f(N+m)-f(N)>(N+m)-N,也就是f(N+m)>f(N)+m>N+ 1010-1+m>N+m,即f(n)的值仍然比n的值大。
因此,对于满足f(N)-N> 1010-1成立的N一定是所求该数的一个上界。
求出上界N
又由于f(1010-1)=n*1010-1,不妨设N= 10K-1,有f(10K-1)-(10K-1)> 1010-1,即K*10K-1-(10K-1)> 1010-1,易得K> =11时候均满足。所以,当K= 11时,N=1011-1即为最小一个上界。
计算这个最大数n
令N= 1011-1=99 999 999 999,让n从N往0递减,每个分别检查是否有f(n)=n,第一满足条件的就是我们要求的整数。很容易解出n= 1 111 111 110是满足f(n)=n的最大整数。
扩展问题
对于其他进制表达方式,也可以试一试,看看有什么规律。例如二进制:
f(1)= 1
f(10)= 10(因为01, 10有两个1)
f(11)= 100(因为01, 10, 11有四个1)
读者朋友可以模仿我们的分析方法,给出相应
|
相关推荐
根据提供的标题、描述以及部分文件内容,我们可以提炼出与“汉字笔画数目的分类”相关的知识点。虽然实际的文本内容似乎并非清晰的汉字及其笔画数的列表,但基于题目要求,我们将尝试从已有的信息中提取并解释相关...
VMware vSphere 6.5群集报该主机的vSphere HA检测信号数据存储数目为0,不合规的情况
1. 减数分裂:减数分裂是一种特殊的细胞分裂方式,主要发生在生物的生殖细胞形成过程中。在减数分裂过程中,细胞经历两次连续的分裂,但染色体只复制一次,使得最终产生的配子(精子或卵细胞)的染色体数目减半。...
1. `num_est_main.asv` 和 `num_est_main.m` 可能是主程序,用于运行和展示源数目估计的整体流程。 2. `compound_matrix.m` 可能涉及到复合矩阵的生成,这在某些源数目估计方法中用于模拟信号混合过程。 3. `bic.m` ...
标题 "从0到N的数中总共包含1的数目" 指的是计算从0到一个整数N之间所有数中数字1出现的总次数。这个主题涉及到位运算、数学和编程,特别是对于十进制和二进制的转换及计数。在计算机科学中,这类问题通常出现在算法...
编程之美1的数目.pdf
"ConsoleApplication1_质数数目计算_" 这个项目显然是关于计算一定范围内的质数数量的程序实现。下面我们将深入探讨如何使用筛法来计算质数,并讨论这种算法的效率和局限性。 筛法,也称为埃拉托斯特尼筛法,是一种...
编写递归算法,计算二叉树中叶子结点的数目
1. **消息监听**:系统需要具备监听不同应用消息通知的能力。这通常通过API接口实现,允许程序在接收到新消息时发送通知给消息提醒系统。 2. **数据聚合**:系统需要收集来自各个应用的消息通知,并进行统计,以...
在给定的文件信息中,我们可以看到这样的一个问题描述——“从0到N中总共1的数目”,它可能来源于一个具体的编程练习或项目。这样的项目通常被设计为教学目的,用于引导学习者通过实际编码练习来理解复杂概念。 当...
我做这个作业的时候发现没有可以参考的代码,于是把自己写的发出来咯。这是一个完整的程序,包括结构定义、初始化存储空间、构造邻接表和输入控制
中国柴胡属3种1变种染色体数目报道,梁乾隆,王长宝,本文对中国伞形科柴胡属3种1变种植物进行了染色体数目观察。 结果如下:秦岭柴胡(B. longicaule var. giraldii)的染色体数目为2n=2x=12;有柄�
在这个任务中,我们需要编写一个算法,根据输入的顶点数目、弧的数目以及各自的顶点和弧信息来构建有向图的邻接表。 首先,我们需要理解输入格式。通常,这种问题会通过标准输入或者读取文件来获取数据。用户会依次...
### 实现同名后数目相加的统计功能 在IT领域中,经常需要对大量数据进行统计和处理,其中一种常见的需求就是对具有相同名称的数据进行汇总计算。本篇文章将根据给定的文件标题、描述及部分内容,详细介绍如何实现一...
1356. 根据数字二进制下 1 的数目排序可以利用递推进行线性处理bits// [&bits] 表示闭包中按引用捕获 bits// 实际上是在重载 号。
1. 预测特征值阈值(PET)方法:这是一种基于协方差矩阵预计特征值极限的方法。通过计算特征值的平均上限γ,可以确定哪些特征值属于噪声子空间,进而推断出发射天线的数量。这种方法简单且计算量小,适用于多种传播...
Consider a function which, for a given whole number n, returns the number of ones required when writing out all numbers between 0 and n... Notice that f(1)=1. What is the next largest n such that f(n)=n?
求解出从给定顶点到所有顶点的最短路径 判断一个有向图g是否是一棵有向树。(任意一个顶点可能是根实验测试数据基本要求: 第一组数据: dirtree2.grp 第二组数据: grp12.grp 第三组数据: dirtree.grp 第四组数据...
1.在二叉搜索树中插入节点 2.前序、中序、后序遍历该二叉搜索树,写出遍历序列 3.输出二叉搜索树的深度、节点数目、叶子节点数目 4.退出
如果根节点既是叶节点(即没有左子节点和右子节点),函数返回1,表示找到一个叶子节点。否则,它递归地计算左子树和右子树的叶子节点数,然后将两者相加,得到整个树的叶子节点总数。 在`main()`函数中,用户被...