大数的除法和1到n的求1的个数

来源:互联网 发布:暗黑战神破解java 编辑:程序博客网 时间:2024/05/07 07:27

有很多人就直接在0到N的循环中计算数字1的总数,这显

然是低效的方法. 明显地应该有方法可以直接算出每一

位上出现1的次数.
拿327来说: 个位数出现了多少次1呢? 因为百和十位是

从00~32,因此首先有001,011...,311这32个1,再加上321

的1,一共13个; 看十位,百位从0变到3,每一个百位数字X

都对应着10个十位上的1,因为有X10~X19 ... 看,错了吧

? 这里虽可以这样算是因为十位数已经是2,可如果十位

数是1,那么就只有010~019,110~119,210~219,以3开头的

只有310~317,这就是为什么零头要单算,并且要按当前位

是大于1,小于1,还是等于1分别讨论;百位上数字3>1,每个1XY都有100个数,因此百位就有100个1.
综上,容易写出:

#include <stdio.h>
#include <stdlib.h>

void main() {
    int n, q, r0, r, p=1, sum1=0;
 printf("N=:");scanf("%d", &n);
    q=n;
    do {
        r=q%10; q/=10; r0=n%p;
  sum1+= q*p;
  if (r>1) sum1+=p;
  else if (r==1) sum1+=(r0+1);
  p*=10;
 } while (q>0);
 printf("The Number of 1s of %d is:%d/n", n, sum1);
}

一个有意思的数字是1111111110,输入试试看  

 

void vlong_value::divide( vlong_value& x, vlong_value& y, vlong_value& rem )
    // x:被除数,y:除数,rem:余数
{
init(0);                        //结合下面的add(s),这里应该是指设商(用q表示)q=0;
rem.copy(x);                    // rem = x;
vlong_value m,s;
m.copy(y);                      // m = y
s.init(1);                      // s = 1

// 目前的状态: assert(q==0 && rem == x && m==y && s==1);
 
while ( rem.cf(m) > 0 )         // rem > m,即现在x > m 时
{                             
 m.shl();                       // m *= 2
 s.shl();                       // s *= 2
}

// 目前的状态:assert(q==0 && rem==x && m== y*s && (s是2的整数幂) && x <=m
//     &&(m是所有满足前面条件的所有数中最小的一个))
// 即上述while让m为y乘以一个2整数幂,以找到第一个不小于x的数,并用s保存这个乘数
// “m是所有满足前面条件的所有数中最小的一个”的数学表达是: y*s/2  <= x <= y*s

while ( rem.cf(y) >= 0 )        // rem >= y
{
    while ( rem.cf(m) < 0 )     // rem < m
    {
        m.shr();                // m /= 2
        s.shr();                // s /= 2
     }
    
     // 目前的状态 assert(rem>=y && m== y*s && (s是2的整数幂) && rem>=m
     //     && (m是满足上述条件中的所有数中最大的一个))
     // 即while让m为y乘以一个2整数幂,以找到第一个不大于rem的数,并用s保存这个乘数
     // “m是满足上述条件中的所有数中最大的一个”的数学表达是: y*s <= rem <= y*s*2
 rem.subtract( m ); // rem -= m;即 rem -= y*s
 add( s ); // 商q += s
}

// 目前的状态:assert( q==x/y && rem == x%y )
 
}

这个算法的本质是将商q 用二进制表示成 Sn Sn-1 .... S0
然后从Sn开始自高向低,每次(即 while ( rem.cf(y) >= 0 ) 段)找到第一个Si==1,令s=2^i,并从rem(开始为x)中减去y*s,直到rem<y

此时 rem == x - (Sn*2^n + Sn-1*2^(n-1) + ... + S0*2^0) * y == x - q*y == x % y

原创粉丝点击