小数第n位(详解)

题目描述

我们知道,整数做除法时,有时得到有限小数,有时得到无限循环小数。

如果我们把有限小数的末尾加上无限多个 0,它们就有了统一的形式。

本题的任务是:在上面的约定下,求整数除法小数点后的第 nn 位开始的 3 位数。

输入描述

输入一行三个整数:a\ b\ na b n,用空格分开。aa 是被除数,bb 是除数,nn 是所求的小数后位置(0<a,b,n<10^90<a,b,n<109

输出描述

输出一行 3 位数字,表示:aa 除以 bb,小数后第 nn 位开始的 3 位数字。

输入输出样例

示例
输入
1 8 1
输出
125

运行限制

  • 最大运行时间:1s

  • 最大运行内存: 256M

// int main(int argc, char *argv[])
// {
//   double m, a=1,b=3;
//   long long n;
  
//   // scanf("%d%d%lld",&a,&b,&n);
// m=a/b;
// printf("%lf",m);
//   return 0;
// }


//开始时企图直接取10^9,10^8这样直接取整求余计算,结果溢出了

后来使用pow,for循环,同样也是溢出导致数据错误

#include <stdio.h>
#include<math.h>
long long a,b,n,c=1000;

int main(){
  scanf("%lld%lld%lld",&a,&b,&n);

 long long ans=(a*pow(10,n+2)/b);
  printf("%lld\n",ans%1000);
//注意%1000不能连续写在一起,不然数据类型会冲突
  return 0;
}
#include <stdio.h>
long long a=1,b,n;

int main(){
  scanf("%lld%lld%lld",&a,&b,&n);
  for(long long i=0;i<n+2;i++)
  {
     a*=10;
  }
  long long ans=(a/b)%1000;
  printf("%lld\n",ans);
  return 0;
}

后来使用了快速幂,但同样因为n的增大同样面临溢出,即使算法更高效也是难以避免数据过大的溢出

先讲解一下快速幂

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

long long qpow(int x,long long m)
{
  long long ans=1;
  while(m)
  {
    if(m&1)
    {
       ans*=x;
    }
    // else err
    //不能是else,不然你这个进位就没办法执行了
    //位置一直都在前进,每次数位挪动也要跟着更新
    //x的值才行,所以每次循环都应该自增x
    x*=x;
    m>>=1;
  }

  return ans;
}
int main(int argc, char *argv[])
{
 
  long long a,b,n;
  
  scanf("%lld%lld%lld",&a,&b,&n);

printf("%lld",(a*qpow(10,n+2))/b%1000);
  return 0;
}

正解

// 本质上是因为幂随n的增大导致长整型的溢出,因此我们必须缩小数据大小
// 同时也是为了提高算法效率同样也需要用快速幂解题
// 我们的qpow函数并没有达到做到直接计算出a^n这个答案,我们仅仅只是为题设服务
//就是输出指定位置的3个数据,将庞大的数据压缩,一边检索一边舍去不要的答案来实现成功输出

#include <stdio.h>
long long a,b,n;

long long qpow(long long x,long long y){
  long long res=1;

  while(y)
{
    if(y&1)
    {
      res=res*x%(b*1000);
    }
    y>>=1;
    x=x*x%(b*1000);
  }
  return res;
}   
 //在快速幂的基础上,取模b*1000;
  //在主函数中说过都是从1开始计算的
  //这里的1000也是为了直接输出整数答案
  //我们先不用1000
// if(y&1){
//       res=res*x%(b);
//     }
//     y>>=1;
//     x=x*x%(b);
//   {

  //假设现在是10/7
  //计算得知为1.42857...
  //然后10%7=3
  //3/7=0.42857....
  //可以发现前面的数字一被这个操作去除了
  //假设n=3,b=7,a=1
  //那么程序开始时,因为是10^(n+2)
  //所以n=1时,ans=1000<b*1000
  //假设直接带回输出,然后/7
  //答案为142,从第一位开始,很合理
  //而从第二位,ans=10000>b*1000
  //取模可得ans=3000
  //a/b=0.142857...
  //3000/7=428,第二位,合理
  //然后n=3时,快速幂算法得知
  //10<b*1000
  //10*10^4=100000>b*1000
  //取模得到2000
  //2000/7=285得到第三位
  
  //实际上是因为取整就是拿了小数点之前的数
  //取模就是之后的数,将之后的数*1000就是答案
  //而随着你的幂不断增大
  //如1变为100
  //1/7=0.142857...
  //100/7=14.2857...
  //数字变大自然而然地会增加整数,减少小数
  //因此取模之后小数部分也就自然向后走了
  //包括10^4*10^8=10^12也是一样
  //保留了res继续取模也是接着取模去除的
int main(){

  scanf("%lld%lld%lld",&a,&b,&n);
  long long ans=(a*qpow(10,n+2)%(b*1000))/b;

  //实际数字a只是倍数,都是在a=1的运算基础上乘上一个倍数,
  // 所以再乘a就可以得出想要的结果
  //这里还取一次模是因为a会改变qpow中的ans的大小
  //同理要重新确定范围,然后取出小数范围,
  //最后/b输出小数部分!


  printf("%lld\n",ans);
  return 0;
}
//   总结:取模相当于分割整数和小数,
//   可以连续取模,不影响最终结果

// 10^(n+2)还有1000都是为了小数点后三位直接输出服务的
//如1000,用不到取模就要10^3/8=125输出结果
//如果用上了取模就要保留千位来输出三位整数
//千位或者以上,全看b的大小,但因为b为除数,
//怎么输出都是3位数字的结果

//题目本质是余数/除数得出小数后位的结果!
//幂的增和a会影响整数范围,决定划分点,b起到分划的作用
//(//1/7=0.142857...
  //100/7=14.2857...)
//a%b相当于划定整数和小数范围
//a/b是取到整数的范围
//而a%b是取到了小数的范围,
//但本身是余数,需要再次/b才能转化成小数部分


  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值