《学姐教我写代码(三)》一行代码一个算法(下)

本文通过有趣的故事形式介绍了四个实用的算法实例,包括变量交换、叉乘判断、杨辉三角组合数计算及快速幂运算。
该文章已生成可运行项目,

📢博客主页:https://blog.youkuaiyun.com/WhereIsHeroFrom
📢欢迎各位 👍点赞 ⭐收藏 📝评论,如有错误请留言指正,非常感谢!
📢本文由 英雄哪里出来 原创,转载请注明出处,首发于 🙉 优快云 🙉
作者的专栏:
  👉C语言基础专栏《光天化日学C语言》
  👉算法进阶专栏《夜深人静写算法》
  👉C/C++大厂面试专栏《C/C++ 面试 100 例》
  👉奇奇怪怪的专栏《学姐教我写代码》

一、前言

  本文适合对算法处于朦胧期的初学者,文字浅显易懂,并且配有生动有趣的动图,也是作者呕心沥血之作,希望对刚入大学,或者职场上想要涉足算法的青年同僚有所启示。
  采用风趣幽默的故事的形式,对基础C语言进行了一个复习,并且引入了一些简单的一行算法,从最简单的语法开始讲起,套入故事情节,相信你一定可以看懂。
  学习算法,任何时候都不嫌晚,大不了就是大器晚成而已,所以无论你是30岁,40岁,50岁,甚至60岁,只要下了决心,就已经成功了一半!本文的故事发生在 ❤️《学姐教我写代码(二)》一行代码一个算法(上)❤️ 的几天后,剧情扑朔迷离,作者至今回忆起来还历历在目。

  • 本章能够学到的东西大概如下:

二、梦醒时分

  • 自从上次梦到学姐教了我四个算法以后。过了两天,三天,四天 一直没有等到学姐再次进入我的梦中……
  • 当我快要绝望之时,天上突然下起了倾盆大雨。
  • 然后,学姐竟然从天上下了下来!
  • 我嘞个去啊! 我扶起学姐,发现她是紫色的!说明我还在梦里咧!因为根据 《学姐教我写代码(一)》十道题搞定C语言 所说,现实中的学姐是橙色的呀!
  • 原来我根本就没有醒过,我问学姐剩下那四个算法到底是什么?
  • 学姐愣了一下!

  • 我嘞个去啊!学姐,该不会是脑子瓦特了吧!
  • 看来得帮助学姐恢复记忆,不然那剩下的四个算法我就永远学不到了。
  • 这时候身边突然出现了一颗胶囊,上面写着六个大字:
  • 我嘞个去啊!果然是做梦啊,要什么来什么!
  • 但是这颗胶囊怎么充斥着诡异的味道!
  • 于是我把药丸给学姐服下。
  • 只见一道绿光闪过!立刻风云大作!
阴雨绵绵
浊浪排空
日星隐耀
山岳潜形

  • 再回头看时,学姐竟然:
  • 成了绿色的!!!



  • 然后学姐迅速写下了四行代码,就匆匆离我而去了!
  • 这四行代码分别是:
void A(int *a, int *b) { *a = *a ^ *b, *b = *a ^ *b, *a = *a ^ *b; }
int B(struct Point a, struct Point b) { return a.x * b.y - a.y * b.x; }
int C(int n, int m) { return n == 0 ? (m == 0 ? 1 : 0) : (C(n-1, m) + C(n-1, m-1) ); }
int D(int a, int b) { return b == 0 ? 1 : ( ((b&1) ? a : 1)  * D(a*a, b/2)  ); }
  • 这时,我突然感觉头部被硬物敲击,反应过来时,发现眼前赫然站立着一个橙色的学姐。
  • 太好了,我终于回到现实世界了。

、

  • 我把梦里发生的事情一五一十的告诉了学姐……


  • 磨蹭了半个小时以后……
  • 等她回来时,我发现我的手臂上竟然有那 四!行!代!码!
  • 学姐你看!!!
  • 学姐会心一笑,嗯!然后,她告诉我这四行代码的含义。

三、算法解析

算法一

交换两个变量的值

void A(int *a, int *b) {
    *a = *a ^ *b, *b = *a ^ *b, *a = *a ^ *b;
}

在这里插入图片描述

1)逗号表达式

  • 对于C语言中的逗号表达式,你可以理解成就是为了分隔两个语句,从左往右计算即可,如下代码:
#include <stdio.h>
int main() {
	int a, b, c, d;
	d = ( a = 3, b = a + 3, c = b + 5, 56 );
	printf("%d %d %d %d\n", a, b, c, d);
	return 0;
}
  • 对于这段代码输出的答案为:
3 6 11 56
  • 可以看出,这个逗号表达式等价于:
    a = 3;
    b = a + 3;
    c = b + 5;
    56;
  • 并且整个逗号表达式的值,就是最右边的那个表达式的值,即56

2)变量取地址和指针

  • 可以用&符号对一个变量取地址,取了地址后他就变成了一个指针变量,如下:
#include <stdio.h>
int main() {
	int a = 5;
	int *pa = &a; 
	return 0;
}
  • 这里a是一个普通变量,pa是一个指针变量。

3)指针取值

  • 指针说白了,就是一个变量的地址;在一个指针前面加上*符号的含义,就是取这个地址里面的值。看个例子加深理解:
#include <stdio.h>
int main() {
	int a = 5;
	int *pa = &a; 
	printf("%d\n", *pa); 
	return 0;
}
  • 这段代码输出的值为 5。也就是a*pa是等价的;&apa是等价的;
  • 我们可以认为*&是两个互逆的操作。

4)异或的性质

  • 二进制的异或,就是两个数转换成二进制表示后,按照位进行以下运算:
  • 1)0和0异或为0;
  • 2)1和1异或为0;
  • 3)0和1异或为1;
  • 4)1和0异或为1;
  • 也就是对于 0 和 1,相同的数异或为 0,不同的数异或为 1。
  • 这样就有了两个比较清晰的性质:
  • 1)两个相同的十进制数异或的结果一定位零。
  • 2)任何一个数和 0 的异或结果一定是它本身。
  • 3)异或运算满足结合律和交换律。

5)算法解析

  • 首先,我们可以把这段代码拆分开来,因为逗号表达式是可以拆分的,于是得到如下代码:
void A(int *a, int *b) {
    *a = *a ^ *b;
    *b = *a ^ *b;
    *a = *a ^ *b;
}
  • 然后,由于这里指针取值了,所以我们可以认为就是普通变量,也就是把*a当成是A即可,把*b当成是B即可。函数内部,可以理解成是:
    A = A ^ B;
    B = A ^ B;
    A = A ^ B;
  • 我们直接来看前两句话,相当于B等于A^B^B,根据异或的几个性质,我们知道,这时候的B的值已经变成原先A的值了。
  • 而再来看最后一句话,相当于A等于A^B^A,还是根据异或的几个性质,这时候,A的值已经变成了原先B的值。

算法二

叉乘

  • 叉乘是用来判定一个顶点是在一条射线的左侧还是右侧。

在这里插入图片描述

1)点

  • 对于一个二维空间中的点,只需要两个数字就能表示,即(x, y)分别代表的是横坐标和纵坐标,如图所示:

、

  • 可以用如下数据结构来代表一个点:
struct Point {
    int x, y;
};

2)向量

  • 向量则代表了两个点之间的差值,即横坐标相减,纵坐标相减。并且向量是有方向的。如图所示红色的箭头代表的就是一个向量。
  • 我们可以表示成点 (4,7)和原点(0,0)的差。

3)叉乘的运算

  • 叉乘(又叫叉积、向量积,外积),表示的是两个向量经过运算后,和这两个向量垂直的向量(和点乘不同,它的结果还是一个向量),如下:
    a ⃗ = ( x a , y a , z a ) \vec a = (x_a,y_a,z_a) a =(xa,ya,za) b ⃗ = ( x b , y b , z b ) \vec b = (x_b,y_b,z_b) b =(xb,yb,zb) a ⃗ × b ⃗ = c ⃗ = ( y a z b − z a y b ) i ⃗ + ( z a x b − x a z b ) j ⃗ + ( x a y b − y a x b ) k ⃗ \vec a \times \vec b = \vec c = (y_az_b-z_ay_b) \vec i + (z_ax_b-x_az_b)\vec j + (x_ay_b-y_ax_b)\vec k a ×b =c =(yazbzayb)i +(zaxbxazb)j +(xaybyaxb)k
  • 其中 i ⃗ = ( 1 , 0 , 0 ) , j ⃗ = ( 0 , 1 , 0 ) , k ⃗ = ( 0 , 0 , 1 ) \vec i = (1, 0, 0), \vec j = (0, 1, 0), \vec k = (0, 0, 1) i =(1,0,0),j =(0,1,0),k =(0,0,1)
  • 当两个向量都在 z = 0 z = 0 z=0 的平面上时, z a = z b = 0 z_a=z_b=0 za=zb=0,退化为二维的情况,则有 a ⃗ × b ⃗ = ( x a y b − y a x b ) k ⃗ \vec a \times \vec b = (x_ay_b-y_ax_b)\vec k a ×b =(xaybyaxb)k
  • 代码实现如下(因为 k ⃗ \vec k k 方向已经确定,为垂直原向量,所以返回值可以定义为一个数值类型):
Type Point2D::X(const Point2D& other) const {
    return x*other.y - y*other.x;
}
  • 在二维计算几何中,叉乘可以用来判断两个点是否在一个向量的同一侧,如图二-5-1所示:
图二-5-1
  • 对于向量 s ⃗ = O S \vec s = OS s =OS,假设有任意一个点 T ( x , y ) T(x,y) T(x,y),对原点到这个点做一个向量 t ⃗ \vec t t 。那么有叉乘:
    s ⃗ × t ⃗ = ( 10 y − x 10 ) k ⃗ \vec s \times \vec t = (10y-x10) \vec k s ×t =(10yx10)k
  • 1) s ⃗ × t ⃗ > 0 \vec s \times \vec t > 0 s ×t >0,则 ( x , y ) (x,y) (x,y) s ⃗ \vec s s 左侧(参考点A);
  • 2) s ⃗ × t ⃗ = 0 \vec s \times \vec t = 0 s ×t =0,则 ( x , y ) (x,y) (x,y) s ⃗ \vec s s 共线(参考点B);
  • 3) s ⃗ × t ⃗ < 0 \vec s \times \vec t < 0 s ×t <0,则 ( x , y ) (x,y) (x,y) s ⃗ \vec s s 右侧(参考点C);
  • 所以,只要两个点分别和对应向量做叉乘,再将结果相乘,如果值大于0,则代表同侧;小于零代表异侧;
  • 叉乘还代表了两个向量组成的平行四边形的面积,有公式:
    a ⃗ × b ⃗ = ∣ a ⃗ ∣ ∣ b ⃗ ∣ s i n θ \vec a \times \vec b = |\vec a||\vec b|sin\theta a ×b =a b sinθ
  • 同样也代表了两个向量组成的三角形的面积的两倍。

4)算法解析

int B(struct Point a, struct Point b) {
     return a.x * b.y - a.y * b.x; 
}
  • 所以这个代码就是叉乘的运算。

算法三

杨辉三角

int C(int n, int m) {
    return n == 0 ? (m == 0 ? 1 : 0) : (C(n-1, m) + C(n-1, m-1) );
}
  • 杨辉三角就是组合数啦。

在这里插入图片描述

1)组合含义

  • 组合数表示为 C n m C_n^m Cnm,它的含义是:从 n n n 个不一样的物品中,选择 m m m 个物品的方案数。

2)组合递推式

  • 递推公式 C n m = C n − 1 m + C n − 1 m − 1 C_n^m=C_{n−1}^m+C_{n−1}^{m-1} Cnm=Cn1m+Cn1m1,可理解为:含特定元素的组合有 C n − 1 m − 1 C_{n−1}^{m-1} Cn1m1,不含特定元素的排列为 C n − 1 m C_{n−1}^m Cn1m
  • 举例:
  • 从 1,2,3,4,5 ( n = 5 ) (n = 5) (n=5) 中取出 2 ( m = 2 ) (m = 2) (m=2) 个元素的组合 C n m C_n^m Cnm
  • 12    13    14    15    23    24    25    34    35    45 12 \ \ 13 \ \ 14 \ \ 15 \ \ 23 \ \ 24 \ \ 25 \ \ 34 \ \ 35 \ \ 45 12  13  14  15  23  24  25  34  35  45
  • 这些组合中要么含有元素 “1”,要么不含。
  • 其中含有“1”的是:
  • 12    13    14    15 12 \ \ 13 \ \ 14 \ \ 15 12  13  14  15
  • 把里面的 “1” 都去掉,得到:
  • 2    3    4    5 2 \ \ 3 \ \ 4 \ \ 5 2  3  4  5
  • 等价于从 2,3,4,5 ( n − 1 ) (n−1) (n1) 中取出1 ( m − 1 ) (m−1) (m1) 个元素的组合。
  • 其中不含“1”的是:
  • 23    24    25    34    35    45 23 \ \ 24 \ \ 25 \ \ 34 \ \ 35 \ \ 45 23  24  25  34  35  45
  • 等价于从 2,3,4,5 ( n − 1 ) (n−1) (n1) 中取出 2 ( m ) (m) (m) 个元素的组合。

而总方案数等于上面两种情况方案数之和,即 C n m = C n − 1 m + C n − 1 m − 1 C_n^m=C_{n−1}^m+C_{n−1}^{m-1} Cnm=Cn1m+Cn1m1

3)算法解析

  • 然后我们把这个三目运算符,改成 if 语句,得到:
int C(int n, int m) {
    if(n == 0) {
        if(m == 0) {
            return 1;
        }else {
            return 0;
        }
    }else {
        return C(n-1, m) + C(n-1, m-1);
    }
}
  • 也就是当 n==0的情况进行特殊处理,否则就是上面证明的组合数的递推公式。

算法四

二分快速幂

int D(int a, int b) { 
    return b == 0 ? 1 : ( ((b&1) ? a : 1)  * D(a*a, b/2)  ); 
}

在这里插入图片描述

1)幂指数的递推

  • 对于幂指数 a b a^b ab,最简单的递推方式就是 a b = a × a b − 1 a^b = a \times a^{b-1} ab=a×ab1
  • f ( a , b ) = a b f(a,b) = a^b f(a,b)=ab, 容易得到:
  • f ( a , b ) = a f ( a , b − 1 ) f(a, b) = a f(a, b-1) f(a,b)=af(a,b1)

2)幂指数的二分方程

  • f ( a , b ) = { 1 b 为 0 a f ( a , b − 1 2 ) 2 b 为 奇 数 f ( a , b 2 ) 2 b 为 正 偶 数 f(a,b) = \begin {cases} 1 & b 为 0 \\ af(a, \frac {b-1}{2})^2 & b为奇数\\ f(a, \frac {b}{2})^2 & b为正偶数\\ \end{cases} f(a,b)=1af(a,2b1)2f(a,2b)2b0bb

3)算法解析

  • 这样,我们继续把刚才的代码的三目运算符拆开,利用上面的幂指数的二分方程,得到:
int D(int a, int b) { 
    if(b == 0) {
        return 1;
    }
    if(b & 1) {
        return a * D(a*a, b/2);
    }else {
        return D(a*a, b/2);
    }
}

四、没想到可以学到这么多知识点

  • 那天以后,我就对学姐刮目相看,但是一直没有理解,学姐这个神奇的存在,为什么会时常进入我的梦中,难道这就是传说中的 “日有所思,夜有所梦” ?
  • 现如今,据说学姐已经嫁人,不知道她过得如何………………

📢博客主页:https://blog.youkuaiyun.com/WhereIsHeroFrom
📢欢迎各位 👍点赞 ⭐收藏 📝评论,如有错误请留言指正,非常感谢!
📢本文由 英雄哪里出来 原创,转载请注明出处,首发于 🙉 优快云 🙉
作者的专栏:
  👉C语言基础专栏《光天化日学C语言》
  👉算法进阶专栏《夜深人静写算法》

本文章已经生成可运行项目
内容概要:本文详细介绍了一种基于Simulink的表贴式永磁同步电机(SPMSM)有限控制集模型预测电流控制(FCS-MPCC)仿真系统。通过构建PMSM数学模型、坐标变换、MPC控制器、SVPWM调制等模块,实现了对电机定子电流的高精度跟踪控制,具备快速动态响应低稳态误差的特点。文中提供了完整的仿真建模步骤、关键参数设置、核心MATLAB函数代码及仿真结果分析,涵盖转速、电流、转矩相电流波形,验证了MPC控制策略在动态性能、稳态精度抗负载扰动方面的优越性,并提出了参数自整定、加权代价函数、模型预测转矩控制弱磁扩速等优化方向。; 适合人群:自动化、电气工程及其相关专业本科生、研究生,以及从事电机控制算法研究与仿真的工程技术人员;具备一定的电机原理、自动控制理论Simulink仿真基础者更佳; 使用场景及目标:①用于永磁同步电机模型预测控制的学演示、课程设计或毕业设计项目;②作为电机先进控制算法(如MPC、MPTC)的仿真验证平台;③支撑科研中对控制性能优化(如动态响应、抗干扰能力)的研究需求; 阅读建议:建议读者结合Simulink环境动手搭建模型,深入理解各模块间的信号流向与控制逻辑,重点掌握预测模型构建、代价函数设计与开关状态选择机制,并可通过修改电机参数或控制策略进行拓展实验,以增强实践与创新能力。
评论 20
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值