别再说你懂快速幂算法了,看完这篇文章你才会懂

2024-08-31 09:12
文章标签 算法 快速 篇文章

本文主要是介绍别再说你懂快速幂算法了,看完这篇文章你才会懂,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

快速幂算法:你以为很简单,但其实暗藏玄机!

你以为快速幂算法只是一个普通的数学公式?天真!你以为这个算法只能用在简单的幂运算上?大错特错!很多人都觉得快速幂算法只是计算大整数幂的一个小工具而已,但真的是这样吗?今天,我——干货哥,就要用一篇爆款博文来颠覆你对快速幂算法的认知。准备好了吗?接下来我将揭开快速幂的神秘面纱,让你看到它背后的深层奥义!

快速幂算法到底是什么?

先来看看这段话,你以为在计算 (x^n) 的时候,笨办法就是把 (x) 乘以自己 (n) 次?那么时间复杂度就是 (O(n)),这效率得多低啊!但是,聪明的程序员早就不这样干了。快速幂的出现,把这个复杂度直接干到 (O(\log n)),就是这么猛!怎么做到的呢?通过一个小小的数学变换:把幂次的计算拆分成二分法的思路。

核心奥义

快速幂的精髓就在于指数拆半!举个例子,计算 (x^{13}),聪明人不会老老实实地算 (x \times x \times \ldots ) ,而是会想到:
[
x^{13} = x \times (x2)6
]
看出来了吗?指数直接被减半!再来一个,(x^{10} = (x5)2),一步到位,轻松搞定。是不是感觉打开了新世界的大门?

快速幂的两种终极实现方式

很多人觉得:“唉呀,这个快速幂是不是实现起来特别复杂?” 不!事实完全相反!干货哥今天就教你两种写法,简简单单几行代码,直接搞定!

1. 递归实现:看似简单,实则霸气!

递归实现快速幂,那真是优雅得一塌糊涂。每一次递归调用都在减半指数,代码简洁到让你一秒就能看懂,但又内含无穷玄妙。

#include <stdio.h>// 递归实现快速幂
long long power(long long x, unsigned int n) {if (n == 0) return 1; // 任何数的 0 次幂为 1long long half = power(x, n / 2); // 计算 x^(n/2)if (n % 2 == 0)return half * half; // 如果 n 为偶数elsereturn x * half * half; // 如果 n 为奇数
}int main() {long long x = 2;unsigned int n = 10;printf("%lld^%u = %lld\n", x, n, power(x, n));return 0;
}

是不是超级简洁?但你可别小看它!这就是算法的美感所在,一行代码,就是一层哲学!

2. 迭代实现:更高效的选择!直接拿捏CPU!

但是!你以为递归就完美无缺了吗?当然不是!有时候递归会带来函数调用的开销,怎么办?用迭代!它避免了递归的栈开销,性能上压递归一头,绝对是不二选择!

#include <stdio.h>// 迭代实现快速幂
long long power(long long x, unsigned int n) {long long result = 1;while (n > 0) {if (n % 2 == 1) { // 如果 n 是奇数result *= x;}x *= x; // 平方底数n /= 2; // 指数减半}return result;
}int main() {long long x = 2;unsigned int n = 10;printf("%lld^%u = %lld\n", x, n, power(x, n));return 0;
}

你以为快速幂就是这样了?别急!还有大招!

3. 快速幂的模运算:大数计算的杀手锏!

很多人都遇到过这样的问题:幂的计算结果太大,分分钟爆掉变量!怎么办?直接对结果取模!快速幂的模运算实现,才是干掉大数溢出的最佳方式!不要怀疑,这个方法简直就是程序员的福音。

#include <stdio.h>// 快速幂的模运算实现
long long modPower(long long x, unsigned int n, int mod) {long long result = 1;x = x % mod; // 先对 x 取模,避免后续溢出while (n > 0) {if (n % 2 == 1) { // 如果 n 是奇数result = (result * x) % mod;}x = (x * x) % mod; // 平方底数并取模n /= 2; // 指数减半}return result;
}int main() {long long x = 2;unsigned int n = 10;int mod = 1000000007;printf("%lld^%u mod %d = %lld\n", x, n, mod, modPower(x, n, mod));return 0;
}

每一步都稳如泰山,计算结果再大也不会翻车。这样的算法,还不赶紧学会?

干货哥总结:快速幂并非你想象的那么简单!

好了,现在你已经知道快速幂算法的多种实现方式,递归、迭代、模运算,一个都不能少!你以为算法就是简单的实现?错!真正的算法是如何在实际场景中做到最优。快速幂看似基础,实则可以优化的地方多如牛毛。比如:

  • 位运算优化:谁说只有加法和乘法能加速?位运算轻松搞定!
  • 分支预测优化:减少CPU的分支错猜,简直快到起飞!
  • 汇编级优化:在极致性能的场景下,直接上内联汇编,飙到天上去!

汇编级优化通常是针对特定的硬件平台和编译器来进行的。它能够最大化地利用底层指令集的特点,挖掘程序性能的极限。对于快速幂算法,利用汇编优化可以让计算效率再上一层楼。

以下是一个针对x86架构的示例,使用GNU汇编(AT&T语法)来实现快速幂的代码。此代码在Linux平台上使用GCC编译器。

汇编级优化:快速幂实现

#include <stdio.h>// 汇编实现快速幂
long long power_asm(long long x, unsigned int n) {long long result = 1;__asm__ ("1: \n"                   // 标签1,循环开始"testl  %1, %1\n"         // 测试n的最低位是否为0"jz     2f\n"             // 如果n为0,跳到标签2"imull  %2, %0\n"         // result *= x"2: \n"                   // 标签2,处理平方底数"mull   %2\n"             // x *= x"shrl   $1, %1\n"         // n >>= 1 (右移一位,相当于n除以2)"jnz    1b\n"             // 如果n不为0,跳回标签1继续循环: "+r" (result), "+r" (n) // 输出部分:result和n,+表示读写: "r" (x)                 // 输入部分:x: "cc"                    // 告诉编译器标志寄存器被更改);return result;
}int main() {long long x = 2;unsigned int n = 10;printf("%lld^%u = %lld\n", x, n, power_asm(x, n));return 0;
}

代码解释

  1. 寄存器使用

    • resultxn 都使用通用寄存器进行存储和操作。
    • imull 指令用于有符号整数乘法,将两个数相乘并将结果存入第一个操作数。
  2. 关键指令

    • testl %1, %1: 测试寄存器 n 的最低位是否为 0。
    • jz 2f: 如果 n 为 0,跳到标签 2(快速退出循环)。
    • imull %2, %0: 进行乘法运算,将 x 乘到 result 上。
    • shrl $1, %1: 将 n 右移一位(相当于将 n 除以 2)。
  3. 循环控制

    • 循环通过条件跳转 jnz 1b 来控制,直到 n 被移位到 0。

优化效果

  • 寄存器操作:所有计算都在寄存器内完成,避免了不必要的内存读写,极大提升了效率。
  • 指令优化:采用乘法、移位指令而非标准的函数调用,减少了CPU指令流水线的停顿。

这些才是算法的真正境界:不仅仅要能用,还要能快、能稳、能省!

你现在还敢小看快速幂吗?赶紧学起来,下次你就是别人眼中的技术大牛!

这篇关于别再说你懂快速幂算法了,看完这篇文章你才会懂的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1123476

相关文章

Python实现快速扫描目标主机的开放端口和服务

《Python实现快速扫描目标主机的开放端口和服务》这篇文章主要为大家详细介绍了如何使用Python编写一个功能强大的端口扫描器脚本,实现快速扫描目标主机的开放端口和服务,感兴趣的小伙伴可以了解下... 目录功能介绍场景应用1. 网络安全审计2. 系统管理维护3. 网络故障排查4. 合规性检查报错处理1.

MySQL快速复制一张表的四种核心方法(包括表结构和数据)

《MySQL快速复制一张表的四种核心方法(包括表结构和数据)》本文详细介绍了四种复制MySQL表(结构+数据)的方法,并对每种方法进行了对比分析,适用于不同场景和数据量的复制需求,特别是针对超大表(1... 目录一、mysql 复制表(结构+数据)的 4 种核心方法(面试结构化回答)方法 1:CREATE

SpringCloud Stream 快速入门实例教程

《SpringCloudStream快速入门实例教程》本文介绍了SpringCloudStream(SCS)组件在分布式系统中的作用,以及如何集成到SpringBoot项目中,通过SCS,可... 目录1.SCS 组件的出现的背景和作用2.SCS 集成srping Boot项目3.Yml 配置4.Sprin

SpringBoot集成iText快速生成PDF教程

《SpringBoot集成iText快速生成PDF教程》本文介绍了如何在SpringBoot项目中集成iText9.4.0生成PDF文档,包括新特性的介绍、环境准备、Service层实现、Contro... 目录SpringBoot集成iText 9.4.0生成PDF一、iText 9新特性与架构变革二、环

MySQL 批量插入的原理和实战方法(快速提升大数据导入效率)

《MySQL批量插入的原理和实战方法(快速提升大数据导入效率)》在日常开发中,我们经常需要将大量数据批量插入到MySQL数据库中,本文将介绍批量插入的原理、实现方法,并结合Python和PyMySQ... 目录一、批量插入的优势二、mysql 表的创建示例三、python 实现批量插入1. 安装 PyMyS

深入理解Mysql OnlineDDL的算法

《深入理解MysqlOnlineDDL的算法》本文主要介绍了讲解MysqlOnlineDDL的算法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小... 目录一、Online DDL 是什么?二、Online DDL 的三种主要算法2.1COPY(复制法)

使用EasyPoi快速导出Word文档功能的实现步骤

《使用EasyPoi快速导出Word文档功能的实现步骤》EasyPoi是一个基于ApachePOI的开源Java工具库,旨在简化Excel和Word文档的操作,本文将详细介绍如何使用EasyPoi快速... 目录一、准备工作1、引入依赖二、准备好一个word模版文件三、编写导出方法的工具类四、在Export

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

C#使用Spire.XLS快速生成多表格Excel文件

《C#使用Spire.XLS快速生成多表格Excel文件》在日常开发中,我们经常需要将业务数据导出为结构清晰的Excel文件,本文将手把手教你使用Spire.XLS这个强大的.NET组件,只需几行C#... 目录一、Spire.XLS核心优势清单1.1 性能碾压:从3秒到0.5秒的质变1.2 批量操作的优雅

Mybatis-Plus 3.5.12 分页拦截器消失的问题及快速解决方法

《Mybatis-Plus3.5.12分页拦截器消失的问题及快速解决方法》作为Java开发者,我们都爱用Mybatis-Plus简化CRUD操作,尤其是它的分页功能,几行代码就能搞定复杂的分页查询... 目录一、问题场景:分页拦截器突然 “失踪”二、问题根源:依赖拆分惹的祸三、解决办法:添加扩展依赖四、分页