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

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

相关文章

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

MybatisX快速生成增删改查的方法示例

《MybatisX快速生成增删改查的方法示例》MybatisX是基于IDEA的MyBatis/MyBatis-Plus开发插件,本文主要介绍了MybatisX快速生成增删改查的方法示例,文中通过示例代... 目录1 安装2 基本功能2.1 XML跳转2.2 代码生成2.2.1 生成.xml中的sql语句头2

8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)

《8种快速易用的PythonMatplotlib数据可视化方法汇总(附源码)》你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python的Matplotlib库是你数据可视化的... 目录引言1. 折线图(Line Plot)——趋势分析2. 柱状图(Bar Chart)——对比分析3

一文教你Java如何快速构建项目骨架

《一文教你Java如何快速构建项目骨架》在Java项目开发过程中,构建项目骨架是一项繁琐但又基础重要的工作,Java领域有许多代码生成工具可以帮助我们快速完成这一任务,下面就跟随小编一起来了解下... 目录一、代码生成工具概述常用 Java 代码生成工具简介代码生成工具的优势二、使用 MyBATis Gen

使用animation.css库快速实现CSS3旋转动画效果

《使用animation.css库快速实现CSS3旋转动画效果》随着Web技术的不断发展,动画效果已经成为了网页设计中不可或缺的一部分,本文将深入探讨animation.css的工作原理,如何使用以及... 目录1. css3动画技术简介2. animation.css库介绍2.1 animation.cs

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

SpringBoot快速搭建TCP服务端和客户端全过程

《SpringBoot快速搭建TCP服务端和客户端全过程》:本文主要介绍SpringBoot快速搭建TCP服务端和客户端全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录TCPServerTCPClient总结由于工作需要,研究了SpringBoot搭建TCP通信的过程

Springboot实现推荐系统的协同过滤算法

《Springboot实现推荐系统的协同过滤算法》协同过滤算法是一种在推荐系统中广泛使用的算法,用于预测用户对物品(如商品、电影、音乐等)的偏好,从而实现个性化推荐,下面给大家介绍Springboot... 目录前言基本原理 算法分类 计算方法应用场景 代码实现 前言协同过滤算法(Collaborativ

一文教你Python如何快速精准抓取网页数据

《一文教你Python如何快速精准抓取网页数据》这篇文章主要为大家详细介绍了如何利用Python实现快速精准抓取网页数据,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录1. 准备工作2. 基础爬虫实现3. 高级功能扩展3.1 抓取文章详情3.2 保存数据到文件4. 完整示例

快速修复一个Panic的Linux内核的技巧

《快速修复一个Panic的Linux内核的技巧》Linux系统中运行了不当的mkinitcpio操作导致内核文件不能正常工作,重启的时候,内核启动中止于Panic状态,该怎么解决这个问题呢?下面我们就... 感谢China编程(www.chinasem.cn)网友 鸢一雨音 的投稿写这篇文章是有原因的。为了配置完