快速幂模板及讲解

——————————快速幂模板及讲解——————————–

(这篇其实是我用来练习公式编辑器滴,所以讲的内容略水,大佬们也赏脸看看吧)

定义

快速幂即快速求幂(下文为求a的x次幂模m的结果),但我们一般只在要求对一个数的幂取模时才使用,因为有可能结果很大,有可能long long都存不下,但是因为我们有:
(ab)%m=(a%m)(b%m) ( a b ) % m = ( a % m ) ( b % m )
通过转换,可得:
(ax)%m ( a x ) % m
=(a×a×a××a(xa))%m = ( a × a × a × … × a ( 共 x 个 a 相 乘 ) ) % m
=(a%m)×(a×a×a××a(x1a))%m = ( a % m ) × ( a × a × a × … × a ( 共 x − 1 个 a 相 乘 ) ) % m
=((a%m)×a%m)×(a×a×a××a(x2a))%m = ( ( a % m ) × a % m ) × ( a × a × a × … × a ( 共 x − 2 个 a 相 乘 ) ) % m

… …

按照这个规律可以一直分解下去,有点类似递归的思想,这样对没一次乘都取模,计算的过程中就不会内存爆炸了

但是只按照上面的方法分解时间复杂度是 O(x) O ( x ) ,明显不够优秀(摊手手),快速幂一般那个 x x 都是极其变态滴,比如x都是108级别的,或者有时候还要求很多次的快速幂,这个复杂度明显凉了,但是我们正统的快速幂算法只要 O(log(x)) O ( l o g ( x ) ) 的时间复杂度,这样就算x真的取 108 10 8 也才 O(27) O ( 27 ) 是不是贼快,接下来我就来介绍一下怎么实现

思路

先来说说递归的思路:

int modpow(int a,int x,int mod){……}
表示求a的x次幂,结果对mod取这个算法的核心就是用平方的方法来省事
如果x为偶数,设 x=2×k x = 2 × k
ax=a2k=(ak)2 a x = a 2 k = ( a k ) 2
然后递归调用modpow(a,x/2,mod)就可以得到 ak a k
定义一个新变量(比如tmp)把这个结果存储起来
然后tmp=(tmp\times tmp)%mod;
此时的tmp就是 a2k a 2 k ax a x
如果x为奇数,设 x=2×k+1 x = 2 × k + 1
ax=a2k+1=(ak)2×a a x = a 2 k + 1 = ( a k ) 2 × a
a2k a 2 k 还是要求的,所以上面的步骤还是要做(因为是整除,所以x/2在这里于是等于k)
所以可以在上面的步骤做完后判断一下
if(x%2==1)//即x为奇数
tmp=(tmp*a)%mod;
特别的,如果x为0,立刻返回1(这个肯定没毛病吧)
于是我们的快速幂递归版就闪亮登场了:

int modpow(int a,int x,int mod)//求a的x次幂的递归法,结果对mod取模
{
  if(x==0)
    return 1;
  int tmp=modpow(a,x/2,mod); 
  tmp=(tmp*tmp)%mod;//这里tmp就已经是a的2k次幂
  if(x%2==1)//如果x是奇数
    tmp=(tmp*a)%mod;
  return tmp;
}
然后是非递归思路:

这个方法比较玄学,可以参考一下(我还是比较喜欢递归法)
核心思路是这样的,我们把x转换为二进制,答案初值赋为1,然后如果从右往左数第k位是1,就把答案乘上 ak2 a k 2 ,同时在枚举二进制下的x的位数时可以顺便把 ak2 a k 2 搞了,可以用一个while实现以上步骤,复杂度同样是 O(log(x)) O ( l o g ( x ) ) (是不是听不懂,没关系看看代码吧):

int poww(int a,int x,int mod){//求a的x次幂的非递归法,结果对mod取模
  int ans=1,base=a;
  while(x>0){
    if(x%2==1)//如果当前这位为1
      ans=(ans*base)%mod;
    base=(base*base)%mod;//这里处理的是a的2k次幂
    x/=2;//这样下一次处理的就是x在二进制下的下一位
  }
  return ans;
}

OK这篇模板讲解就到这咯,一般不会有题目会要求直接求快速幂,可能会换个马甲,也有时候会是一道综合的题目里一个小部分,反正就是不用当心求a的x次方时会超时或超内存,快速幂很优秀

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MFC(Microsoft Foundation Classes)是Microsoft公司开发的一套用于Windows应用程序开发的C++类库,其中包括了许多模板类,如CArray、CList、CMap等。这些模板类可以大大简化开发过程,提高程序效率。 下面以CArray类为例,介绍MFC模板类的代码及讲解: ```c++ template<class TYPE, class ARG_TYPE = const TYPE&> class CArray { public: // 构造函数 CArray() noexcept; CArray(int nSize, int nGrowBy = -1); CArray(const CArray& other); CArray(CArray&& other) noexcept; // 析构函数 ~CArray(); // 重载运算符 CArray& operator=(const CArray& other); CArray& operator=(CArray&& other) noexcept; TYPE& operator[](int nIndex); const TYPE& operator[](int nIndex) const; // 其他成员函数 int GetSize() const noexcept; int GetUpperBound() const noexcept; void SetSize(int nNewSize, int nGrowBy = -1); void SetAt(int nIndex, ARG_TYPE newElement); void Add(ARG_TYPE newElement); void RemoveAll() noexcept; void RemoveAt(int nIndex); void InsertAt(int nIndex, ARG_TYPE newElement, int nCount = 1); void Append(const CArray& src); void Copy(const CArray& src); void FreeExtra(); void AssertValid() const; // 迭代器相关 typedef TYPE* iterator; typedef const TYPE* const_iterator; iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; // 受保护的成员变量 protected: TYPE* m_pData; // 数据指针 int m_nSize; // 数组大小 int m_nMaxSize; // 数组最大大小 int m_nGrowBy; // 每次增长的大小 // 私有成员函数 private: void SetGrow(int nNewGrowBy); void SetSizeInternal(int nNewSize); void SetSizeInternalGrow(int nNewSize); }; ``` 上述代码展示了CArray模板类的声明,在实际使用中,我们可以将模板类实例化为不同类型的数组,如: ```c++ CArray<int> arr; // 创建一个int类型的数组 CArray<CString> strArr; // 创建一个CString类型的数组 ``` 其中,CArray的模板参数TYPE表示数组元素的类型,ARG_TYPE表示传入数组元素的类型。CArray提供了一系列成员函数,包括构造函数、析构函数、重载运算符、其他成员函数以及迭代器相关的函数。 通过使用MFC提供的模板类,我们可以快速、方便地实现各种数据结构的操作,提高开发效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值