RSA加密解密算法原理以及实现

news2024/10/5 13:33:08

文章目录

  • 前言
  • 一、RSA加密算法是什么?
  • 二、RSA算法使用c++语言实现时遇到的问题以及解决方案
    • 1.问题1:模反元素的求解
    • 问题2:大数的幂运算
    • 问题3:大数的加/减/乘/除/模运算
    • 问题4:大数产生随机数
    • 问题5:大数产生质数
  • 三、RSA加密算法代码实现
  • 四、运行结果


前言

从古至今,如何用最有效的加密手段保护信息的安全性使之不被窃取、篡改或者破坏都是人们在信息传播中普遍关注的重大问题。最古老的文件加密手段莫过于对称加密,什么是对称加密,打个比方,有一个商人需要给合作伙伴送一批贵重的货物,他便将货物放在一个设置好密码的箱子中,这个密码只有商人知道,同时他又将设置好的密码提前告知合作伙伴,货物送达后,合作伙伴便可以用被告知的密码打开箱子取出货物。即用一种方法加密, 用同一种方法解密, 即为对称加密。对称加密从古至今都是比较广泛使用的一种加密方式,比如在宋朝就将代码法用于军事保密文件的传输,简而言之,比如双方约定某一本书,根据书中的字所在位子,比如“20页第2行第9个字”,破译的时候只要对照好,就能解码。如果不知道双方约定的解码书本,根本无法破解。这种方式现在还管用。比如在谍战片中,情报人员发出的电报,需要用对应的密码本来解译,如何密码本落入到地方手中,后果不堪设想。但是对称加密缺点在于,通过对称加密方法进行加密后,需要告知接收方加密方法才能解密,而将加密方法传输给接受方这一过程中,充满了各种泄密的可能。比如说在战争中,一旦密码本在传输的过程中被敌方截获,那么所有的电报内容都会被地方破解,以至于影响最终战争胜利的走向。所以,对称加密的安全性,最首先取决于加密方式的保密性,其次才是密码破译的难度。 如何能够消除了对称加密中用户交换密钥的需要的同时也能保证其保密性?因此,诞生的非对称加密非对称加密算法需要两个密钥:公开密钥 (publickey:简称公钥) 和 私有密钥 (privatekey:简称私钥). 公钥与私钥是一 一对应的, 如果用公钥对数据进行加密, 只有用对应的私钥才能解密. 因为加密和解密使用的是两个不同的密钥, 所以这种算法叫作非对称加密算法. 基本过程为:甲方生成一对密钥与公钥, 并将公钥公开, 需要向甲方发送信息的其他角色(乙方, 丙方等) 使用甲方公开的公钥对机密信息进行加密后再发送给甲方. 甲方再用自己私钥对加密后的信息进行解密.甲方想要回复乙方时正好相反, 使用乙方公开的公钥对数据进行加密,同理,乙方使用自己的私钥来进行解密.本文中介绍非对称加密算法中的一种------RSA加密解密算法


一、RSA加密算法是什么?

RSA是1977年由罗纳德·李维斯特(Ron Rivest), 阿迪·萨莫尔(Adi Shamir)和 伦纳德·阿德曼(Leonard Adleman)一起提出的. 当时他们三人都在麻省理工学院工作. RSA就是他们三人姓氏开头字母拼在一起组成的.百度百科

加密过程

1、选择一对不相等且足够大的质数

选择一对不相等且足够大的质数,我们将它描述为p和q。

2、计算p、q的乘积

n=p*q

3、计算n的欧拉函数

φ ( n ) = ( p − 1 ) ∗ ( q − 1 ) \varphi(n)=(p-1)*(q-1) φ(n)=(p1)(q1)

这个公式是如何得到的?那么就要从以下几个概念入手。

互质:如果两个整数的公约数只有1,此时它们叫做互质整数。

欧拉函数:欧拉函数是小于n的正整数中与n互质的数的数目。例如,与6互质且小于6的正整数有两个,分别是1和5,则 φ ( 6 ) = 2 \varphi(6)=2 φ(6)=2。如果n为质数,那么 φ ( n ) = n − 1 \varphi(n)=n-1 φ(n)=n1,因为质数在大于1的自然数中,除了1和它本身以外不会再有其他因数的自然数。

此时如果n可以分解成2个互质的整数之积,那么n的欧拉函数等于这两个因子的欧拉函数之积。即若n=p*q,且p、q互质,则 φ ( n ) = φ ( p ∗ q ) = φ ( p ) ∗ φ ( q ) \varphi(n)=\varphi(p*q)=\varphi(p)*\varphi(q) φ(n)=φ(pq)=φ(p)φ(q)

证明:如果a与p1互质(a<p1),b与p2互质(b<p2),c与p1p2互质(c<p1p2),则c与数对 (a,b) 是一一对应关系。由于a的值有φ(p1)种可能,b的值有φ(p2)种可能,则数对 (a,b) 有φ(p1)φ(p2)种可能,而c的值有φ(p1p2)种可能,所以φ(p1p2)就等于φ(p1)φ(p2)。

又由欧拉函数的性质可知,质数的欧拉函数为质数值减去1,则进一步可得 φ ( n ) = φ ( p ∗ q ) = φ ( p ) ∗ φ ( q ) = ( p − 1 ) ∗ ( q − 1 ) \varphi(n)=\varphi(p*q)=\varphi(p)*\varphi(q)=(p-1)*(q-1) φ(n)=φ(pq)=φ(p)φ(q)=(p1)(q1)

4、选择一个与 φ ( n ) \varphi(n) φ(n)互质的整数e

1<e< φ ( n ) \varphi(n) φ(n)

5、计算出e对于 φ ( n ) \varphi(n) φ(n)的模反元素d

de mod φ ( n ) \varphi(n) φ(n) =1

在这里解释下什么是模反元素

如果两个正整数e和 φ ( n ) \varphi(n) φ(n)互质,那么一定可以找到一个整数d,使得ed-1被 φ ( n ) \varphi(n) φ(n)整除,或者说ed除以 φ ( n ) \varphi(n) φ(n)所得的余数为1,此时,d就叫做e的模反元素。
也就是说在e和 φ ( n ) \varphi(n) φ(n)已知的情况下,d只要满足ed-1=k φ ( n ) \varphi(n) φ(n),k为任意整数,d便是e的模反元素。同时也可得到,e的模反元素d并不是唯一的。
例如,e=3, φ ( n ) = 11 \varphi(n)=11 φ(n)=11,则d=4 ± \pm ±k·11。

至此,公钥,私钥便都已经得到

6、将e、n公开作为公钥进行加密

假设明文为M,密文为C,则加密过程为 M e m o d n = C M^e mod n =C Memodn=C

7、将d,n作为私钥进行解密

C d m o d n = M C^d mod n =M Cdmodn=M

证明: C d m o d n = ( M e m o d n ) d m o d n C^d mod n =(M^e mod n )^d mod n Cdmodn=(Memodndmodn
= M e d M^ed Med

二、RSA算法使用c++语言实现时遇到的问题以及解决方案

1.问题1:模反元素的求解

在第5步中,我们需要计算出e对于 φ ( n ) \varphi(n) φ(n)的模反元素d。
如果使用暴力法求解,那么时间复杂度势必为O(n),这对于一个很大的整数来说,势必需要很长的运行时间去遍历查找,效率低下,因此需要用一种更快的算法去优化该过程。
解决办法:欧几里得算法:gcd(a,b)=gcd(b,a mod b)最大公约数(优化算法)

#include<iostream>
#include<algorithm>
#include<string.h> 
using namespace std;
int isoushu(long x)
{
	if(x%2==0)  return 1;
	else return 0;
}
 int fun(long long a,long long b)
{   if(b>a)  return fun(b,a);
	else if(a==0)return b;

	
	else 
	{  if(isoushu(a))
	      if(isoushu(b))
	   {
	   	return (fun(a>>1,b>>1)<<1);
	   }
	   else return fun(a>>1,b);
	   
	   else 
	   {    if(isoushu(b))
	   	return  fun(a,b>>1);
	   	 else return (a-b,b);
	   	 
	   }
	
	
	}
}
main()
{
	long long a,b;
	cin>>a>>b;
    cout<<fun(a,b);

}

证明:
设x=k1x1,y=k1y1,则F(x,y)=k1 F(x1,y1)。
设x=px1,(p为素数且y%p!=0)则F(x,y)=F(x1,y)。
现在取p=2。
若x,y均为偶数,则F(x,y)=2F(x/2,y/2)。
若x为偶数,y为奇数则F(x,y)=F(x/2,y)。
若x为奇数,y为偶数则F(x,y)=F(x,y/2)。
若x和y同时为奇数,F(x,y)=F(y,x-y)。(两个奇数相减后必定会出现一个偶数)
这种做法的算法复杂度为O(log2(max(x,y)))。

问题2:大数的幂运算

  1. 在计算机计算大数的指数的过程中, 计算的数字不断增大, 非常的占用我们的计算资源
  2. 我们计算的中间过程数字大的恐怖, 我们现有的计算机是没有办法记录这么长的数据的, 会导致溢出.

解决方案:在程序设计过程中,经常要去求一些大数对于某个数的余数,为了得到更快、计算范围更大的算法,产生了快速幂取模算法
从一个最简单的例子入手:求a^b % c = ?

int ans=1;
for(int i=1;i<=b;i++)
{
    ans=ans*a;
}
ans=ans%c;

该程序实现存在很大的问题,首先算法时间度为O(n),其次如果a和b过大,很容易就会溢出。
如何进行改进?这里就要用到中学时学到的同余定理:积的取余等于取余的积的取余。

a b a^b ab% c = (a * a * a a) % c
= ((a % c) * (a % c) * (a % c) (a % c)) % c
= (a % c) b ^b b% c

此时我们可以从上述公式中想到既然某个因子取余之后相乘再取余余数保持不变,那么可以通过改良程序得到以下版本。

int ans=1;
a=a%c;//加上这一句
for(int i=1;i<=b;i++)
{
    ans=(ans*a)%c;//取余
}
ans=ans%c;

但是这个算法在时间复杂度上没有改进,仍为O(b),不过已经好很多的,但是在c过大的条件下,还是很有可能超时,所以,我们需要用到快速幂算法来进行改进。

关于快速幂的算法的介绍,我么从推导其公式入手。

首先我们将十进制的指数b转换为2进制的形式

在这里插入图片描述

其中 a n a_n an代表对应对应第n位的二进制值,非0即1。那么由二进制转换为十进制的公式我们可以得到

在这里插入图片描述
在这里插入图片描述
化简上述公式可得
在这里插入图片描述
a b a^b abmod c可重写为

在这里插入图片描述

此处的 a i a_i ai要么为0,要么为1,如果 a i a_i ai为0,那么这一项值就为1。所以可以再次将上述式子化简为

在这里插入图片描述
其中 k i k_i ki表示 a i a_i ai不为0的项。

同时发现,对于每一项的计算时,计算后一项的结果时用前一项的结果的平方取余。用公式表示为

在这里插入图片描述

形如上式的公式推导,我们将其用代码实现。

int ans=1;
a=a%c;
while(b>0)
{
    if(b%2==1)
        ans=(ans*a)%c;
        b=b/2;
        a=(a*a)%c;
}

此时,当b=0时,所有的因子都已经相乘,算法结束。于是便可以在O(log b)的时间内完成了。所以在进行模幂运算时, 运算的复杂度已经和指数b的大小没有直接关系了, 有直接关系的是b的二进制bit位数。


问题3:大数的加/减/乘/除/模运算

在RSA算法中的数字极大, 是我们现有计算机无法直接计算的, 所以需要我们手动实现大数的加减乘除. 所以在实现中,直接调用boost库中的大数运算库cppp_int。

boost中的cpp_int库的介绍文档
https://www.boost.org/doc/libs/1_58_0/libs/multiprecision/doc/html/boost_multiprecision/tut/ints/cpp_int.html
项目使用的是boost_1_83_0版本的大数库,下载链接https://www.boost.org/users/download/
在VS中还需要将解压好的boost库路径添加到附加目录, 步骤如下
在这里插入图片描述
在这里插入图片描述

问题4:大数产生随机数

我们平时用的随机数函数是 srand()设置随机数种子, 一般传入time(0), 用 rand()获取随机数, 随机数 = rand % num; 随机数范围是 [0, num). 在不同平台的不同编译器下rand()函数所能获取的随机数的范围不同, 但由于目前CPU位数的限制, 顶多也就是个64位的数字, 所以我们还需要能产生大随机数的方法.

解决方案:在boost库的random库中 有着大数随机数的获取方法,在调用时包含头文件#include<boost/multiprecision/random.hpp>

问题5:大数产生质数

大数的素性检测有专门的算法, 比如fermat检测, Miller-Rabin等算法. 在boost库中的实现了Miller-Rabin素性检测算法,使用时添加头文件#include <boost/multiprecision/miller_rabin.hpp>

三、RSA加密算法代码实现

rsa.h

#pragma once
#include<iostream>
#include<boost/multiprecision/cpp_int.hpp>//大数库
#include<boost/multiprecision/random.hpp>//随机数库
#include<boost/multiprecision/miller_rabin.hpp>//素性检测
#include<boost/algorithm/string.hpp> //spilt()接口就在这个库中
 
namespace br = boost::random;
namespace bm = boost::multiprecision;
 
#define NUMBER 128 //临时缓冲区大小
#define SECRET_KEY_PATH "secret_key.txt" //存储N,D,E的文件名
#define SIZE 128 //控制随机数大小, 范围是 0 ~ 1 << SIZE
 
typedef struct {
	bm::int1024_t m_ekey;//公钥e
	bm::int1024_t m_dkey;//私钥d
	bm::int1024_t m_nkey;//公共模数n
}Key;
 
class RSA {
	Key m_key;
	bm::int1024_t GetPrime();
	bool isPrime(bm::int1024_t& num);
	bm::int1024_t getNkey(bm::int1024_t& prime1, bm::int1024_t& prime2);//获取公共模数n
	bm::int1024_t getOrla(bm::int1024_t& prime1, bm::int1024_t& prime2);//欧拉函数, 得到f(n)
	bm::int1024_t getEkey(bm::int1024_t& orla);//获取公钥
	bm::int1024_t getDkey(bm::int1024_t& ekey, bm::int1024_t& orla);//获取私钥
	void exGcd(bm::int1024_t a, bm::int1024_t b, bm::int1024_t* x, bm::int1024_t* y);//求模反元素
	bm::int1024_t getGcd(bm::int1024_t num1, bm::int1024_t num2);//最大公约数
	bm::int1024_t _encrypt(bm::int1024_t data, bm::int1024_t ekey, bm::int1024_t nkey);//加密,需要加密数据和公钥(e, n)
	bm::int1024_t _decrypt(bm::int1024_t data, bm::int1024_t dkey, bm::int1024_t nkey);//解密,需要要解密的数据和私钥(d, n)
	void getKeys();
public:
	RSA();
	bool encrypt(const std::string filename, const std::string outname);//加密
	bool decrypt(const std::string filename, const std::string outname);//解密
};

rsa.cpp

#include<fstream>
#include<vector>
#include"rsa.h"
using namespace std;
 
RSA::RSA() {
	getKeys();
}
bm::int1024_t RSA::getNkey(bm::int1024_t& prime1, bm::int1024_t& prime2) {
	return prime1 * prime2;
}
bm::int1024_t RSA::getOrla(bm::int1024_t& prime1, bm::int1024_t& prime2) {//prime1和prime2必须互质
	return (prime1 - 1) * (prime2 - 1);
}
bm::int1024_t RSA::getEkey(bm::int1024_t& orla) {
	bm::int1024_t ekey;
	br::mt11213b gen((size_t)time(0));
	br::uniform_int_distribution<bm::int1024_t> dist(bm::int1024_t(0), (bm::int1024_t(orla)));
	do {
		ekey = dist(gen);
	} while (ekey < 2 || getGcd(ekey, orla) != 1);
	return ekey;
}
bm::int1024_t RSA::getDkey(bm::int1024_t& ekey, bm::int1024_t& orla) {
	bm::int1024_t x, y;
	exGcd(ekey, orla, &x, &y);
	return (x % orla + orla) % orla;//变换, 让解密密钥是一个比较小的数
}
bm::int1024_t RSA::getGcd(bm::int1024_t num1, bm::int1024_t num2) {
	bm::int1024_t num;
	while ((num = num1 % num2)) {
		num1 = num2;
		num2 = num;
	}
	return num2;
}
void RSA::exGcd(bm::int1024_t a, bm::int1024_t b, bm::int1024_t* x, bm::int1024_t* y) {
	if (b == 0) {
		*x = 1;
		*y = 0;
		return;
	}
	exGcd(b, a % b, x, y);
	bm::int1024_t tmp = *x;
	*x = *y;
	*y = tmp - a / b * (*y);
}
bm::int1024_t RSA::_encrypt(bm::int1024_t Ai, bm::int1024_t ekey, bm::int1024_t nkey) {
	//data^ekey % nkey
	//只和ekey的位数有关
	bm::int1024_t res = 1;
	for (; ekey; ekey >>= 1) {
		if (ekey & 1) {
			res = (res*Ai) % nkey;
		}
		Ai = (Ai*Ai) % nkey;
	}
	return res;
}
bm::int1024_t RSA::_decrypt(bm::int1024_t data, bm::int1024_t dkey, bm::int1024_t nkey) {
	return _encrypt(data, dkey, nkey);
}
bool RSA::isPrime(bm::int1024_t& num) {
	br::mt11213b gen((size_t)time(0));//要和产生随机数的发生器不一样
	if (miller_rabin_test(num, 25, gen)) {
		if (miller_rabin_test((num - 1) / 2, 25, gen)) {
			return true;
		}
	}
	return false;
}
bm::int1024_t RSA::GetPrime() {
	bm::int1024_t res;
	br::mt19937 gen((size_t)time(0));
	br::uniform_int_distribution<bm::int1024_t> dist(bm::int1024_t(0), (bm::int1024_t(1) << SIZE));
	while (!isPrime(res = dist(gen)));
	return res;
}
void RSA::getKeys() {
	FILE* fp;
	if ((fp = fopen(SECRET_KEY_PATH, "r")) == NULL) {
		cout << "生成密钥, 公钥中...\n";
		bm::int1024_t prime1, prime2 = GetPrime();
		while ((prime1 = GetPrime()) == prime2);
		m_key.m_nkey = getNkey(prime1, prime2);
		bm::int1024_t orla = getOrla(prime1, prime2);
		m_key.m_ekey = getEkey(orla);
		m_key.m_dkey = getDkey(m_key.m_ekey, orla);
		stringstream tmp;
		tmp << m_key.m_nkey << '\n' << m_key.m_ekey << '\n' << m_key.m_dkey << '\n';
		ofstream fout(SECRET_KEY_PATH, ofstream::binary);
		if (fout.is_open() == false) {
			perror("file open failed!");
			return;
		}
		fout.write(tmp.str().c_str(), tmp.str().size());
		if (fout.good() == false) {
			cout << "file " << SECRET_KEY_PATH << " read data failed!\n";
		}
		fout.close();
	}
	else {
		fseek(fp, 0L, SEEK_END);
		size_t fsize = ftell(fp);
		fclose(fp);
		ifstream fin(SECRET_KEY_PATH, ifstream::binary);
		if (fin.is_open() == false) {
			perror("file open failed!");
			return;
		}
		string buf;
		buf.resize(fsize);
		fin.read(&buf[0], fsize);
		if (fin.good() == false) {
			cout << "file " << SECRET_KEY_PATH << " read data failed!\n";
		}
		vector<string> secret_key;
		//split用于分割字符串
		boost::split(secret_key, buf, boost::is_any_of("\n"), boost::token_compress_on);
		m_key.m_nkey = bm::int1024_t(secret_key[0]);
		m_key.m_ekey = bm::int1024_t(secret_key[1]);
		m_key.m_dkey = bm::int1024_t(secret_key[2]);
		fin.close();
	}
}
bool RSA::encrypt(const string filename, const string outname) {
	ifstream fin(filename, ifstream::binary);
	ofstream fout(outname, ifstream::binary);
	if (!fin.is_open()) {
		perror("input file open failed!");
		return false;
	}
	char* buffer = new char[NUMBER];
	bm::int1024_t* bufferOut = new bm::int1024_t[NUMBER];
	while (!fin.eof()) {
		fin.read(buffer, NUMBER);
		streamsize ret = fin.gcount();
		for (streamsize i = 0; i < ret; ++i) {
			bufferOut[i] = _encrypt(buffer[i], m_key.m_ekey, m_key.m_nkey);
		}
		fout.write((char*)bufferOut, ret * sizeof(bm::int1024_t));
	}
	delete[] bufferOut;
	delete[] buffer;
	fin.close();
	fout.close();
	return true;
}
bool RSA::decrypt(const string filename, const string outname) {
	ifstream fin(filename, ifstream::binary);
	ofstream fout(outname, ifstream::binary);
	if (!fin.is_open()) {
		perror("file open failed");
		return false;
	}
	bm::int1024_t* buffer = new bm::int1024_t[NUMBER];
	char* bufferOut = new char[NUMBER];
	while (!fin.eof()) {
		fin.read((char*)buffer, NUMBER * sizeof(bm::int1024_t));
		streamsize ret = fin.gcount() / sizeof(bm::int1024_t);
		for (streamsize i = 0; i < ret; ++i) {
			bufferOut[i] = (char)_decrypt(buffer[i], m_key.m_dkey, m_key.m_nkey);
		}
		fout.write(bufferOut, ret);
	}
	delete[] bufferOut;
	delete[] buffer;
	fin.close();
	fout.close();
	return true;
}

main.cpp

#include<iostream>
#include <windows.h>
#include"rsa.h"
using namespace std; 
void Test(const string filename, const string EncName, const string DecName) {
	RSA rsa;
	if (rsa.encrypt(filename, EncName)) {
		cout << "加密完成!\n";
		if (rsa.decrypt(EncName, DecName)) {
			cout << "解密完成!";
		}
	}
	cout << "3秒后程序退出!\n";
	Sleep(3000);//在Linux下为sleep(3), 需要添加头文件 unistd.h
}
int main() {
	string filename("明文.txt"), EncName("密文.txt"), DecName("解密后的文件.txt");
	Test(filename, EncName, DecName);
	return 0;
}

四、运行结果

明文
在这里插入图片描述

密文
在这里插入图片描述
解密后的文件
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/886164.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

步入React正殿 - 生命周期

目录 资料 三个阶段的生命周期函数 创建阶段 初始化阶段constructor 挂载阶段componentWillMount 挂载阶段render 挂载阶段componentDidMount 更新阶段【props或state改变】 更新阶段componentWillReceiveProps 更新阶段shouldComponentUpdate【可不使用&#xff0c;…

Mysql事务篇——Day02

Mysql事务篇——Day02 事务有哪些特性并发事务引发的问题脏读不可重复读幻读 事务隔离级别Read View 在 MVCC里如何工作 事务有哪些特性 事务是依赖MySQL的储存引擎是实现的&#xff0c;我们常见的Innodb引擎就是支持事务的。 不过并不是所有的存储引擎都可以支持事务&#xf…

SecureCRT8.5安装教程

第一步&#xff1a; 将文件下载解压 第二步&#xff1a; 双击进行安装&#xff0c;或者右键以管理员的方式运行 第三步&#xff1a; 直接点下一步 第四步&#xff1a; 选择接受协议&#xff0c;然后点下一步 第五步&#xff1a; 我这里选择所有用户&#xff0c;然后点下一…

js实现按创建时间戳1609459200000 开始往后开始显示运行时长-demo

运行时长 00日 00时 17分 59秒 代码 function calculateRuntime(timestamp) {const startTime Date.now(); // 获取当前时间戳//const runtimeElement document.getElementById(runtime); // 获取显示运行时长的元素function updateRuntime() {const currentTimestamp Date…

spring入门基本介绍及注入方式---详细介绍

一&#xff0c;spring的简介 Spring是一个开源框架&#xff0c;它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。 提供了许多功能强大且易于使用的特性&#xff0c;使得开发者能够更加轻松地构建可维护且可扩展的应用程序&#xff0c;简单来说: Spring使用基…

攻击LNMP架构Web应用

环境配置(centos7) 1.php56 php56-fpm //配置epel yum install epel-release rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm//安装php56&#xff0c;php56-fpm及其依赖 yum --enablereporemi install php56-php yum --enablereporemi install php…

深入了解唯品会API及其应用

随着电商行业的快速发展&#xff0c;API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;已经成为许多企业实现系统对接、数据交换和功能扩展的重要工具之一。唯品会作为国内领先的电商平台之一&#xff0c;也提供了丰富的API接口&#xff…

component:()=>import(“@/views/Home.vue“) 报错,ts说没有找到类型声明文件

1 没有写.vue文件的类型声明&#xff0c;要在env.d.ts文件中写.vue的类型声明文件 2 ts.config.josn的incluede字段中&#xff0c;没有把.d.ts文件的路径写对。 如果没写对&#xff0c;就会在项目启动的时候&#xff0c;找不到.d.ts文件。找不到类型声明文件

【自动电压调节器】无功功率控制的终端电压控制研究(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

ATF(TF-A)安全通告 TFV-5 (CVE-2017-15031)

安全之安全(security)博客目录导读 ATF(TF-A)安全通告汇总 目录 一、ATF(TF-A)安全通告 TFV-5 (CVE-2017-15031) 二、CVE-2017-15031 一、ATF(TF-A)安全通告 TFV-5 (CVE-2017-15031) Title 未初始化或保存/恢复PMCR_EL0可能会泄露安全世界的时间信息 CVE ID CVE-2017-1503…

cs231n assignment 3 Q2 Image Captioning with Vanilla RNNs

文章目录 嫌啰嗦直接看代码Q2 Image Captioning with Vanilla RNNs一个给的工具代码里的bug问题展示问题解决思路解决办法 rnn_step_forward题面解析代码输出 rnn_step_backward题面解析代码输出 rnn_forward题面解析代码输出 rnn_backward题面解析代码输出 word_embedding_for…

时序预测 | MATLAB实现基于CNN-BiLSTM卷积双向长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价)

时序预测 | MATLAB实现基于CNN-BiLSTM卷积双向长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价) 目录 时序预测 | MATLAB实现基于CNN-BiLSTM卷积双向长短期记忆神经网络的时间序列预测-递归预测未来(多指标评价)预测结果基本介绍程序设计参考资料 预测结果 基本介绍…

leetcode 518. 零钱兑换 II

本题是背包问题系列的完全背包问题&#xff0c;和0-1背包唯一的区别就在于&#xff1a;物品是可以重复选取的。 经过之前背包问题的拷打&#xff0c;本题也是一遍AC了。 接下来将给出二维和一维两种做法。 二维dp数组做法&#xff1a; 本题的背包大小即为题中给出的总金额&am…

与微软Office抗衡,两大巨头布局,打造新办公软件,再上新台阶

办公软件是我们日常工作中不可或缺的工具。 除了广为人知的微软Office套件外&#xff0c;近几年来&#xff0c;中国市场有许多优秀的国产办公软件抢眼登场&#xff0c;这些软件在提高工作效率、简化工作流程方面发挥着重要作用。 简单介绍几款值得知道的国产办公软件&#xff…

【学习FreeRTOS】第9章——FreeRTOS任务调度

1.开启任务调度器 vTaskStartScheduler() 作用&#xff1a;用于启动任务调度器&#xff0c;任务调度器启动后&#xff0c; FreeRTOS 便会开始进行任务调度【动态创建任务为例】 创建空闲任务如果使能软件定时器&#xff0c;则创建定时器任务关闭中断&#xff0c;防止调度器开…

99%的Python用户都不知道的f-string隐秘技巧

f-string想必很多Python用户都基础性的使用过&#xff0c;作为Python3.6版本开始引入的特性&#xff0c;通过它我们可以更加方便地向字符串中嵌入自定义内容&#xff0c;但f-string真正蕴含的功能远比大多数用户知道的要丰富&#xff0c;今天我们就来一起get它们~ 「最基础用法…

JVS开源基础框架:平台基本信息介绍

JVS是面向软件开发团队可以快速实现应用的基础开发脚手架&#xff0c;主要定位于企业信息化通用底座&#xff0c;采用微服务分布式框架&#xff0c;提供丰富的基础功能&#xff0c;集成众多业务引擎&#xff0c;它灵活性强&#xff0c;界面化配置对开发者友好&#xff0c;底层容…

MySQL性能分析之慢查询日志查看

一、背景 MySQL的慢查询日志是MySQL提供的一种日志记录,他用来记录在MySQL中响应的时间超过阈值的语句,具体指运行时间超过long_query_time(默认是10秒)值的SQL,会被记录到慢查询日志中。 慢查询日志一般用于性能分析时开启,收集慢SQL然后通过explain进行全面分析,一…

GPU Dissolve(GPU 消散)学习GPU Instancing

一&#xff1a;摘要 通过制作一个模型GPU消散效果来学习GPU Instancing 也就是实例化。 目标效果是杨超大佬文章《GPU shatter》里面的消散效果如图&#xff1a; Tags&#xff1a;模型顶点分裂(Mesh Vertex Splitting)&#xff0c; 实例化绘制(GPU Instancing Drawing)&#x…

【100天精通python】Day36:GUI界面编程_高级功能操作和示例

专栏导读 专栏订阅地址&#xff1a;https://blog.csdn.net/qq_35831906/category_12375510.html 一、GUI 高级功能 1 自定义主题和样式 自定义主题和样式可以让你的GUI应用程序在外观方面更加出色。在使用Tkinter时&#xff0c;你可以使用ttkthemes库来应用不同的主题和样式。…