统计从1到n所有正整数中1出现的次数

这是来自《编程之美》2.4中的题目,我只给出我的新方法,书上的方法这里略过不提,因为网上已经有很多解释书上的内容的博文了。

以下是分析:

我们先从最简单的数字开始分析。

0~9:个位数>=1时,1的出现次数均为1.个位数=0时,出现0次1.......................................................................①

10~99:十位数字=1时,比如17.先分析10~17中所有两位数中个位数上1的个数=1,再分析10~17所有两位数中十位数上1的个数=17-10+1,然后再分析1~9中所有个位数1的出现个数=1,所以总和为第一次循环计算10~17所有两位数中个位数中1的个数为1+第二次循环计算10~17所有1出现次数和2+17-10(1~10中1的个数+11~17所有十位数上1的出现次数)=10

十位数>1时,比如58,先分析10~58所有两位数中个位数上1的个数=5,再分析10~58所有两位数中十位数上1的个数=19-10+1,然后再分析1~9中所有个位数1的出现个数=1,所以总和为第一次循环计算个位数中1的个数为1+第二次循环计算10~58中所有1出现次数和10+(2-1)*5=16。

十位数=0时,转到①执行。

100~999:百位数=1时,比如123,先分析101~123中个位数+十位数中1的个数和相当于求0~23的所有1出现次数为13,再计算101~123中百位数上的1的个数和=123-101+1.然后计算1~100中1出现次数和。所以总和为(21+123-100)(百位上1的出现次数+100以前所有1出现次数)+13(1~23所有1的个数和相当于100~123中十位数和个位数1的个数和)。

百位数>1时,比如246,先分析前200中1的出现次数=前99中1的出现次数+100~200中1的出现次数=21+101~200中个位和十位1个数和+百位数1的个数和=21+前99中的出现次数和+101~200中百位数1的出现次数和=21-1+21-1+100=100+(21-1)*2.然后再计算200~246中十位和个位数1的个数和(相当于求0~46所有1的个数和15)。最后总和=100+(21-1)*2+15=155

百位数=0时,转到上面十位数的情况。

我写的程序是,比如计算123,先计算1~3中1的出现次数和,然后再次循环计算1~23中1的出现次数和,这些都计算好了,第3次循环才计算1~123中1的出现次数和。另外在求给定数字之前,我们先把n=10,100,1000,10000........诸如这些整数求好存到数组里,然后利用这些数来求给定数。

现在给出代码:

#include <iostream>
#include <time.h>
using namespace std;
#define n 11//11位
__int64 f(int x)
{
    __int64 s=1;
	while (x--)
	{
		s*=10;
	}
	return s;
}
__int64 Total_Count(__int64 x,__int64 a[])
{
	int temp[n]={0},i=0,t=x;
	__int64 sum=0,s=1,q=0;
	while (x)
	{
		temp[i++]=x%10;
		x/=10;
	}
	for (int j=0;j<i;j++)
	{
		q+=temp[j]*s;
		if (temp[j]>1)
		{
			if(s==1) sum+=1;
			else sum+=s+(a[j]-1)*temp[j];
		}
		else if(temp[j]==1)
		{
			if(s==1) sum+=1;
			else sum+=a[j]+q-s;
		}
		s*=10;
	}
	return sum;
}
void main()
{
	long    i = 10000000L;
	   clock_t start, finish;
	   double  duration;
	   /* 测量一个事件持续的时间*/
	   printf( "Time to do %ld empty loops is ", i );
	   start = clock();
	   srand(unsigned int (time(NULL)));
	   __int64  a[n+1]={1,2};
	   for (int k=2;k<=n;k++)
	   {
		   a[k]=10*a[k-1]+f(k-1)-9;
	   }
	   int t=0;//注释部分是求1出现次数和最大正整数相等时的值,通过这个循环比较两种方法的运行时间不失为一种好的方法。
	   /*for (__int64 j=1;j<1000000000;j++)
	   {
		   if (Total_Count(j,a)==j)//这里换成下面那个书上的函数作为对比,您就知道这种方法的效率了。
		   {
			   printf("%I64d\n", j);
			   t++;
		   }
	   }*/
	   cout<<endl;
	   printf("%I64d\n", Total_Count(246,a));
	   cout<<"共"<<t<<"个"<<endl;
	   finish = clock();
	   duration = (double)(finish - start) / CLOCKS_PER_SEC;
	   printf( "%f seconds\n", duration );
}

下面给出《编程之美》上面的代码作为对比:

__int64 Count(__int64 m){
    //1的个数
    __int64 count = 0;
    //当前位
    __int64 Factor = 1;
    //低位数字
    __int64 LowerNum = 0;
    //当前位数字
    __int64 CurrNum = 0;
    //高位数字
    __int64 HigherNum = 0;
    if(m <= 0){
        return 0;
    }
    while(m / Factor != 0){
        //低位数字
        LowerNum = m - (m / Factor) * Factor;
        //当前位数字
        CurrNum = (m / Factor) % 10;
        //高位数字
        HigherNum = m / (Factor * 10);
        //如果为0,出现1的次数由高位决定
        if(CurrNum == 0){
            //等于高位数字 * 当前位数
            count += HigherNum * Factor;
        }
        //如果为1,出现1的次数由高位和低位决定
        else if(CurrNum == 1){
            //高位数字 * 当前位数 + 低位数字 + 1
            count += HigherNum * Factor + LowerNum + 1;
        }
        //如果大于1,出现1的次数由高位决定
        else{
            //(高位数字+1)* 当前位数
            count += (HigherNum + 1) * Factor;
        }
        //前移一位
        Factor *= 10;
    }
    return count;
}  



时间: 2024-08-07 12:20:40

统计从1到n所有正整数中1出现的次数的相关文章

(转) 统计在从1到n的正整数中1出现的次数

转自:http://blog.csdn.net/sjf0115/article/details/8600599 问题: 给定一个十进制正整数N,写下从1开始,到N的所有整数,然后数一下其中出现的所有“1”的个数. 例如:N= 2,写下1,2.这样只出现了1个“1”. N= 12,我们会写下1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.这样,1的个数是5. 问题一: 写一个函数f(N),返回1到N之间出现1的个数,比如f(12)= 5. 解法一: 让我们首先想到的一个

统计重1到n的正整数中1的个数

转:http://blog.csdn.net/sjf0115/article/details/8600599 问题: 给定一个十进制正整数N,写下从1开始,到N的所有整数,然后数一下其中出现的所有“1”的个数. 例如:N= 2,写下1,2.这样只出现了1个“1”. N= 12,我们会写下1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12.这样,1的个数是5. 问题一: 写一个函数f(N),返回1到N之间出现1的个数,比如f(12)= 5. 解法一: 让我们首先想到的一个方

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

本实例主要用到python的jieba库 首先当然是安装pip install jieba 这里比较关键的是如下几个步骤: 加载文本,分析文本 txt=open("C:\\Users\\Beckham\\Desktop\\python\\倚天屠龙记.txt","r", encoding='utf-8').read() #打开倚天屠龙记文本 words=jieba.lcut(txt) #jieba库分析文本 对数据进行筛选和处理 for word in words:

12周(分离正整数中个位数)

/* *copyright(c) 2014,烟台大学计算机学院 *All rights reserved. *文件名称:分离正整数中个位数 *作者:王忠 *完成日期:2014.11.10 *版本号:v1.0 * *问题描述:输入1234,输出4 3 2 1 *输入描述:输入一串数字 *程序输出:分离这组数 #include <iostream> using namespace std; int separate (int); int main() { int m,n; cin>>m

Java实现统计某字符串在另一个字符串中出现的次数

面试时会经常考这样的题目,估计也不让使用正则表达式.还好这个算法还算简单,不过在草稿纸上写难免会出现运行异常,好吧,面试官赢了,乃们屌丝就实实在在的把代码码出来吧. 下面是实现代码: /** * 统计某字符串在另一个字符串中出现的次数 * * */ public class CountHit { public static void main(String[] args) { String a = "123456abcde6ab"; String b = "6abc"

《找出1到正整数N中出现1的次数》

编程思想:依次求出正整数每个位数上出现1的次数,累加即可得到最后想要的结果:而每一位上出现1的个数与和它相邻的其它位数上的数字有关系(以此位置上的数为对称轴,其左边的所有数字作为其最高位,其右边的数字作为其最低位:当然若此位置已处于最低位或最高位,那么它对应的最低位或最高位置0),与它们有一个可求出1的固定的关系式(一个数各个位置上的数分离后,它们都对应着各个位置的基准,例如个位上的数对应的基准为1,以此类推即可),即若此位置上的数字为0,则在此位置上出现1的个数为最高位乘以基准:若为1,则等于

技巧之C#统计字符串中字符出现的次数(转)

方法1.自定义类 class CharNum { private char c; private int num; public char C { get { return c; } } public int Num { get { return num; } set { num = value; } } public CharNum(char ch) { this.c = ch; this.num = 1; } } static void Main(string[] args) { /* */

java . 请在小于99999的正整数中找符合下列条件的数,它既是完全平方数,又有两位数字相同,如:144,676。

1 import java.util.HashMap; 2 import java.util.Map; 3 import java.util.Map.Entry; 4 //请在小于99999的正整数中找符合下列条件的数,它既是完全平方数, 5 //又有两位数字相同,如:144,676. 6 public class wqs { 7 8 //完全平方数 9 public static boolean iswqs(int n){ 10 int i; 11 double dn=Math.sqrt(n)

N个任务掌握java系列之统计一篇文章中单词出现的次数

问题:统计一篇文章中单词出现的次数 思路: (1)将文章(一个字符串存储)按空格进行拆分(split)后,存储到一个字符串(单词)数组中. (2)定义一个Map,key是字符串类型,保存单词:value是数字类型,保存该单词出现的次数. (3)遍历(1)中得到的字符串数组,对于每一个单词,考察Map的key中是否出现过该单词,如果没出现过,map中增加一个元素,key为该单词,value为1(第一次出现): 如果,在map的key中发现了该单词,则通过key找到对应的value(单词出现的次数)