编程之美--高效率算出1的数目之扩展问题

问题:给定一个十进制数N,写下从1开始,到N的所有二进制数,然后数一下其中出现的所有“1”的个数。

问题分析:

十进制二进制最右边1的个数总和倒数第二1的个数总和 倒数第三1的个数总和倒数第四1的个数总和倒数第五1的个数总和十进制二进制最右边1的个数总和倒数第二1的个数总和倒数第三1的个数总和倒数第四1的个数总和倒数第五1的个数总和
00000000000130110176660
10000110000140111077770
20001011000150111188880
30001122000161000088881
40010022100171000198882
50010132200181001099883
6001103330019100111010884
7001114440020101001010985
80100044410211010111101086
90100154420221011011111187
100101055430231011112121288
110101166440241100012121299
12011006655025110011312121010

 

首先我们看2,4816四个数,2的二进制数总共有21,4的有5个,8的有13个,1633个。而且他们都有一定的规律。比如1624次方,二进制数是10000,在最右边1的个数和倒数第二、第三、第四都是8(即48),最高位是1823次方,二进制数是01000,在最右边1的个数和倒数第二、第三都是434),倒数第四个是1;以此类推42,那么我们可以看出符合这样的公式:

0~2^n的所有二进制数1的总和sum=2^(n-1)*n+1    其中2^n表示2n次方

 

现在我们观察0~24的数的二进制的最右边位的1的个数总和,0,1,1,2,2,3,3,4,4,5,5,6,6...,可以得出这样的公式:

0~n的所有二进制数最右边位的1的个数总和sum:当n%2=0时,sum=n/2; 否则sum=n/2+1

 

观察倒数第二位1的总和。0~10,其他的都是一个常数带上三个相同的常数;比如1,2,2,2,3,4,4,45,6,6,6....  那么设他们的规律是1+3为一组。

我们设sequenceLength=1,repeatLength=3;

这样子我们可以计算出18的二进制数的倒数第二位的1的总和为:看上面表格知道从0开始,那么18就是第19位,看倒数第二1的个数这一列前面是两个0,推出(18+1-2)/(1+3)取整为4,就是说有4组,每组的值其实是两个,那么sum=4*2=8,而(18+1-2)%(1+3)=1;所以sum=4*2+1=9

 

观察倒数第三位1的总和。0~30,其他的都是三个常数带上五个相同的常数;比如1,2,3,4,4,4,4,45,6,7,8,8,8,8,89,10,11,12,12,12,12,12....  那么设他们的规律是3+5为一组。

这样子我们可以计算出18的二进制数的倒数第三位的1的总和为:看上面表格知道从0开始,那么18就是第19位,看倒数第三1的个数这一列前面是四个0,推出(18+1-4)/(3+5)取整为1,就是说有1组,每组的值其实是4个,那么sum=1*4=4,而(18+1-4)%(3+5)=7;就是说18这个数为一组还多七个,由3+5为一组可知,多出来的七个的前三个的值是连续的,后面四个是一样的。那么这多出来的七个数其实就是3+1个值,推出sum=1*4+(3+1)=8

 

观察倒数第四位1的总和。0~70,其他的都是三个常数带上五个相同的常数;比如1,2,3,4,5,6,7,8,8,8,8,8,8,8,8,8....  那么设他们的规律是7+9为一组

这样子我们可以计算出18的二进制数的倒数第四位的1的总和为:看上面表格知道从0开始,那么18就是第19位,看倒数第四1的个数这一列前面是八个0,推出(18+1-8)/(7+9)取整为0,就是说有0组,每组的值其实是8个,那么sum=0*8=0,而(18+1-8)%(7+9)=11;就是说18这个数为零组还多十一个,由7+9为一组可知,多出来的十一个的前七个的值是连续的,后面九个是一样的。那么这多出来的十一个数其实就是7+1个值,推出sum=0*8+(7+1)=8

倒数第二位、倒数第三位。。。倒数第N位的组的规律。

1+33+57+9  可知(1+2^1)+(3+2^1)=3+5;  (3+2^2)+(5+2^2)=7+9......

和面组是前面组的2的幂次方。倒数第二位是2的一次方,倒数第三位是2的两次方...以此类推。

代码分析:

View Code
 1     public class NumberOfOne
 2     {
 3         public long Sum { get; set; }
 4         public void BinaryCount(int n)
 5         {
 6             CountLowest(n);
 7             CountCommont(n);
 8         }
 9 
10         public void CountLowest(int n)
11         {
12             //计算最低位(最右边位)1的总数
13             Sum = (n / 2) * 2 < n ? (n / 2) + 1 : Sum = n / 2;
14         }
15 
16         public void CountCommont(int n)
17         {
18             #region 开始计算除开最低位的1的总数
19             //比如说数列:1,2,3,4,4,4,1,2,3,4,4,4,1,2,3,4,4,4...,那么sequenceLength就是1,2,3的长度3,
20             //repeatLength就是4,4,4的长度(重复次数)3
21             long sequenceLength = 1;
22             long repeatLength = 3;
23             long exponent = 1;
24 
25             for (int i = 0; i < 32; i++)
26             {
27                 // exponent*2表示上面表格的每一列开头有多少个0,成2为幂底的指数增长
28                 long temp = (n + 1) - exponent * 2;
29                 if (temp <= 0)
30                 {
31                     break;
32                 }
33                 if (temp <= sequenceLength)
34                 {
35                     Sum += temp;
36                 }
37                 else
38                 {
39                     //组数
40                     long valueOfDivide = temp / (sequenceLength + repeatLength);
41                     long valueOfMockup = temp % (sequenceLength + repeatLength);
42 
43                     if (valueOfMockup <= sequenceLength)
44                     {
45                         //每组长度是sequenceLength+repeatLength,不过他们只有sequenceLength+1个不同的联系的值,
46                         //比如1,2,3,4,4这组有五个数,不过只有四个不同的连续的值:1,2,3,4。
47                         Sum += valueOfDivide * (sequenceLength + 1) + valueOfMockup;
48                     }
49                     else
50                     {
51                         Sum += valueOfDivide * (sequenceLength + 1) + sequenceLength + 1;
52                     }
53                 }
54                 exponent *= 2;
55                 sequenceLength += exponent;
56                 repeatLength += exponent;
57             }
58             #endregion
59         }
60 }

调用:

View Code
 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             NumberOfOne numberOfOne = new NumberOfOne();
 6             while (true)
 7             {
 8                 int number = int.Parse(Console.ReadLine());
 9                 numberOfOne.BinaryCount(number);
10                 Console.WriteLine("0~{0}的所有二进制数的1的总和为:{1}", number, numberOfOne.Sum);
11 
12                 if (number == 444)
13                 {
14                     break;
15                 }
16             }
17             Console.ReadLine();
18         }
19     }

 

 

转载于:https://www.cnblogs.com/silongxu/archive/2012/12/26/2833539.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值