一个基于C++实现的页码统计算法

引入:

如果你有一本C++ Primer Plus,一共有678页,页码从1~678。那么问题来了,打印页码时,打印0~9数字多少次呢?
正好刚学算法分析结合之前数据结构的知识,写个算法分析一下。

输入描述:

输入一个整数n [1,1000000000]

 输出表述:

输出数字0~9对应的个数,每个数字各占一行 

实例:

输入:

100

输出:

0:11
1:21
2:20
3:20
4:20
5:20
6:20
7:20
8:20
9:20
(3.2473ms)

接下来我们回来文章开头的问题,让算法来统计C++ Primer Plus页码包含的数字个数:

输入:678

输出:

        0:127
        1:238
        2:238
        3:238
        4:238
        5:238
        6:217
        7:137
        8:128
        9:127
        (2.5688ms)

算法实现(内部有具体的算法描述):

#include<iostream>
#include<valarray>
#include<chrono>
int length (int number);
const std::valarray<int> calNumberCount(int number);
void print(const std::valarray<int> arr);
int main()
{
    using namespace std;
    while(1)
    {
        int page;                                          //页数
        std::valarray<int> page_number;                    //数字0~9的个数
        std::cin>>page;
        auto beforeTime = std::chrono::steady_clock::now();//获取当前时间
        page_number=calNumberCount(page);
        for(int i=0;i<length(page);++i)
            page_number[0]-=pow(10,i);
        print(page_number);
        auto afterTime = std::chrono::steady_clock::now();
        double duration_millsecond = std::chrono::duration<double, std::milli>(afterTime - beforeTime).count();
        cout<<"("<<duration_millsecond<<"ms)"<<endl;
    }
}

/**
 * @brief 
 * 设页码数n,对应最高位h(n),对应的长度l(n),n的去头剩余部分p(n)
 * 递归公式:calNumberCount(n) =[0~h(n)-1]{10^(l(n)-1)} + [EACH]{ h(n)*[(l(n)-1)*10^(l(n)-2)] } + [0]{(l(n)-l(p(n))-1)*(p(n)+1)} + [h(n)]{p(n)+1} +calNumberCount(p(n))
 * [注]:calNumberCount的结果是一个数组,[EACH]{x}表示数组所用内容全部加x.[0]{x}表示数组下标为0的值加x,也就是数字0的个数.'|'表示'或','x~y'表示x到y所有值
 * 该递归公式多包含0,多余0的个数为:[0]{10^(0+1+...+l(n)-1)}
 * 最后将多余的0减掉即可
 * 
 * 涉及公式:
 * 对于000.. —— 999.. 设长度为n, 每个数字出现的频率相同,所以得出公式:每个数字的个数都相等 = n*10^n/10 = n*10^(n-1)
 * 大体思路:
 * 对于1024:可看成0(000~999)<完整组>|10(00~24)<非完整组>对于前面部分结果可以直接算出,
 * 而后面10(00~24),1和0也可以直接算出,最后只需单独求出00~24即可,也就是求24页码对应的数字个数,递归由此得来
 * 详细过程:
 * 对于1024代入公式有:calNumberCount(1024)=[0]{1000}+[EACH]{300}+[0]{24}+[1]{24}+calNumber(24)
 * 1024可一次分解为0(000~999)<完整组>+1(000~024)
 * 1.[0~h(n)-1]{10^(l(n)-1)}:计算完整组外部增量.对于1024而言,1024只有一个完整组0(000~999),其中外部是0,个数为1000
 * 2.[EACH]{ h(n)*[(l(n)-1)*10^(l(n)-2)] }:计算完整组内部增量,h(n)为完整组个数,[(l(n)-1)*10^(l(n)-2)]是增量.
 * 如1024最高位是1所以只有一个完整组0(000~999),内部为(000~999),包含0~9各300个
 * 3.[0]{(l(n)-l(p(n))-1)*(p(n)+1)}和 [h(n)]{p(n)+1}:计算非完整组外部增量.对于非完整组有一点特殊:它的外部可能有后缀0,如果最高位后面有0的话
 * //对于1024其非完整外部增量为10(00~24),外部有两个数字组成10,因为1024最高位1后面有0.所以1024的外部增量为[1]{24}和[0]{24}
 * 4.calNumberCount(p(n)):计算非完整内部增量.对于1024,即10(00~24)内部增量为calNumberCount(24)
 * 5.最后去除多包含的0.注意多余零只出现在1~999..中,对于1024,多余0只出现在1~999,因为对于1024而言,我们递归算法计算的是000~999,但实际上我们的页码是1~999.
 * @param number 
 * @return const std::valarray<int> 
 */

//页码递归求解算法:给出一个页码n,计算出1~n中各个数字(0~9)出现的个数
const std::valarray<int> calNumberCount(int number)
{
    std::valarray<int> page_number(10);
    int len = length(number);
    if(len == 1)                                  //递归出口
    {
        for(int i=0;i<=number;i++)
            page_number[i]++;
        return page_number;
    }
    int high = number/(int)pow(10,len-1);         //取得最高位
    
    page_number+=high*(len-1)*pow(10,len-2);      //计算整体部分
    int i;
    for(i=0;i<high;i++)
        page_number[i]+=pow(10,len-1);
    int surplus = number%(int)pow(10,len-1);
    page_number[0]+=(len-length(surplus)-1)*(surplus+1);
    page_number[i]+= surplus + 1;
    return page_number+calNumberCount(surplus);
}
//计算数字长度
int length (int number)
{
    int i = 1;
    while(number/=10)
        i++;
    return i;
}
//打印0~9数字个数
void print(const std::valarray<int> arr)
{
    for(int i=0;i<arr.size();i++)
        std::cout<<i<<":"<<arr[i]<<std::endl;
}

 算法说明:

使用的数据结构:标准模板valarray

实现方式:递归

时间复杂度:常数

空间复杂度:常数

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值