您的位置:首页 > 其它

统计数字问题

2012-03-23 18:31 211 查看
问题描述:
一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排,
每个页码都不含多余的前导数字0。例如,第6 页用数字6 表示,而不是06 或006 等。数
字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,
2,…,9。
编程任务:
给定表示书的总页码的10 进制整数n (1≤n≤109) 。编程计算书的全部页码中分别用
到多少次数字0,1,2,…,9。

分析与解答:

由0,1,2……9组成的所有的n位数,从n个0到n个9共有10^n个n位数,其中全排列的情况下,每个数字使用的次数一样,设为f(n).





我们其实可以知道:f(n)=n10n-1

据此,可以从高向低位进行统计,再减去多余的0的个数即可。

本例程序,参考/article/4620134.html 但有多处开进。

#include <iostream>
#include <string>
#include <sstream>
#include <cmath>
#include <vector>
#include <fstream>
using namespace std;

double string_to_num(string str)
{//字符串转换为double
double back;
istringstream instr(str);
instr>>back;
return back;
}
int main()
{
int len;
double mm;  //数字的个数
int countNum[10] = {0};//保存统计结果
vector<int> posNum;
string pageNum;
cout<<"input:";
cin>>pageNum;
double num = string_to_num(pageNum);
////写入文件
//ofstream outfil("C:\\Users\\xxx\\Desktop\\input.txt");
//outfil<<num;

len = pageNum.size();
if( len <= 1 )
{    //如果只有个位数,直接放到数组输出
for(int i = 1; i <= int(num); i++)
countNum[i] += 1;
}
else
{
const char* p = pageNum.c_str();
for(int i = 0; i < len; i++)
{
int curPosNum = int( *(p+i) ) - 48;//当前处理位置上的数字,从高位到低位
//书上的公式为全数组的形式,即000-999
//所以此公式适用于当前位为[0,curPosNum)时,其前面所有位的全形式。
//应该当前位数减一,由书上公式得f(n-1)=(n-1)pow(10,n-2)
mm = pow(10.0,len-i-2) * (len-i-1); //当个位的时候,位数n=len-i=1,mm为0
if( mm != 0)
{//判断是不是个位
//对低于当前位的位置先统计每个数的个数,当前位留到下一次
for(int j = 0; j < curPosNum; j++)  //小于这个数的每个数的低位置的数的个数
{
for(int k = 0; k < 10; k++)
countNum[k] += mm;
countNum[j] += pow(10.0,len-i-1);  //加入当前位的个数
}
if( !posNum.empty() )
{ //判断是否为空
for(int m = 0; m < posNum.size(); m++)
{ //当不为空的时候,计算每个容器vector中前个元素(即高位)应该增加的
//个数(在当前数字为curPosNum时),不包括全为0的情况(因为只有一个
//全为0,不能加多次,留在后面处理)
//核心:(出来一个低位,就把高位+相应个数)
int tt = posNum.at(m); //安全防止溢出
countNum[tt] += ( pow(10.0,len-i-1)*curPosNum );
}
}
posNum.push_back(curPosNum);
if( i == 0) //第0位
{
//去掉多余的0
for(int j = 0; j < len; j++)
countNum[0] -= pow(10.0,len-j-1) ; //我得方法,从高位开始
//如:0xx,0个数+10^2;再看0x,0个数+10^1;x,0个数+10^0
}
}
else
{//指针位置来到个位上了,我的方法
for(int n=0;n<=curPosNum;n++)
{//首先个位数上的数[0,curPosNum]每个数+1
countNum
+=1;
}
if( !posNum.empty() )
{
for(int m = 0; m < posNum.size(); m++)
{
//其次,再考虑个位时,每个前面的高位对应的数都要+相应的个位数值的个数
int tt = posNum.at(m);
countNum[tt] += ( pow(10.0,len-i-1)*curPosNum )+1 ; //+1就是为了处理全0的情况
}
}
}
}
}
//cout<<"output:"<<endl;
//ofstream outfile("C:\\Users\\xxx\\Desktop\\output.txt");
//for (int i=0; i<10; i++)
//{
//	outfile<<countNum[i]<<endl;
//}
for(int i = 0; i < 10; i++)
{//输出个数
cout<<countNum[i]<<endl;
}
return 0;
}


上面程序中,

at函数

语法:

TYPE at( size_type loc );


at() 函数 返回当前Vector指定位置loc的元素的引用. at() 函数 比 [] 运算符更加安全, 因为它不会让你去访问到Vector内越界的元素. 例如, 考虑下面的代码:

vector<int> v( 5, 1 );

for( int i = 0; i < 10; i++ ) {
cout << "Element " << i << " is " << v[i] << endl;
}

这段代码访问了vector末尾以后的元素,这将可能导致很危险的结果.以下的代码将更加安全:

vector<int> v( 5, 1 );

for( int i = 0; i < 10; i++ ) {
cout << "Element " << i << " is " << v.at(i) << endl;
}


取代试图访问内存里非法值的作法,at() 函数能够辨别出访问是否越界并在越界的时候抛出一个out_of_range异常.

程序网上还找到一种方法,虽然好理解,但是,不是很优化,算法复杂度不高,因为按照数逐个运算,随着数的增大。。。作为对比:

//这个题目有个最容易想到的n*log10(n)的算法。这是自己写的复杂度为O(n*log10(n))的代码:

void statNumber(int n) {

int i, t;

int count[10] = {0};

for(i = 1; i <= n; i++) {

t = i;

while(t) {

count[t%10]++;

t/=10;

}

}

for(i = 0; i < 10; i++) {

printf("%d\n", count[i]);

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: