`
hao3100590
  • 浏览: 131828 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

素数的求解逐步改进

阅读更多

我的注释都写在代码里面了,就不在赘述了!如果有任何疑问欢迎留言

参考博客:

1.位操作总结:http://blog.csdn.net/morewindows/article/details/7354571

2.找素数算法总结:http://blog.csdn.net/hexiios/article/details/4400068

非常感谢上面两篇博客的仁兄,致谢!


尤其是读了位操作的那篇文章,写的非常好,希望各位都可以一次尝试哈,没准给你的程序增加一个亮点

2的总结也很好,我只是在它的基础上在详细的给了些注释,如果有不懂的地方就好理解了!

 

1.基础补习

什么是合数?什么是素数?百度一下你就知道

 

有一个重要的性质:任何一个合数一定可以表示成若干个素数之积。

如:4 = 2 * 2,6 = 2 * 3,12 = 2 * 2 * 3。也就是说,合数N一定是小于N的某个(或若干)素数的整数倍,反之,如果N不是任何比它小的素数的倍数,则N必然是素数。

这个性质就决定了下面的算法基本思想

 

 

2.检查是否是素数(这个是从基本概念出发的检测算法)

主要的改进就是检测的范围缩小从0---sqrt(n)

 

bool isPrime(int n){
	if(n ==0 || n==1) return 0;
	for(int i=2; i<=sqrt(n); i++){
		if(n%i == 0){
			 cout<<i<<endl;
			 return 0;
		}
	}
	return 1;
}
 

 

3.经典素数求解算法

它对每个数的检测就是利用2的方法

 

/**
	*经典素数求解算法
	*prime[0]中保存素数总个数
	*/
int* getPrime(){
	int prime[LEN] = {0};//判断是否为素数,初始都为素数
	prime[0] = 0; //保存素数的总个数
	for(int n=2; n<LEN; n++){
		bool flag = 1;
		//对每个数A都按照:2到sqrt(A)依次除,看是否整除,如果能则不是素数
		//这样是很低效的m*m<=n也可写成m<=sqrt(n)
		//注意:必须有m*m<=n 中的“=”
		for(int m=2; m*m<=n; m++){
			//如果能整除,不是素数
			if(n%m == 0){
				flag = 0;
				break;
			}
		}
		if(flag){
			prime[0]++;
			prime[prime[0]] = n;
		}
	}
	cout<<"\ncount:"<<prime[0]<<endl;
	return prime;
}

 

 

4.经典算法改进版

其主要的改进的地方就是,检测的思想,前面的检测方法就是2到sqrt(A)依次除,看是否整除

改进的就不同了,它利用了合数的性质,利用已经找到的素数来除,这样大大的减少了需要整除的次数,这个改进不错!

 

/**
	*它是上面经典算法改进版
	*这里prime数组中存的是素数,prime[0]中保存素数总个数
	*获取0-n的所有素数
	*/
int* getPrime1(){
	int prime[LEN] = {0};//判断是否为素数,初始都为素数
	int i, n, flag;
  prime[0] = 0; //保存素数的总个数
  for (n =2 ; n < LEN; n++)
  {
        flag = 1;
        //判断当前n是否被"已经找出的素数"整除(如果能,说明不是素数,根据合数性质)
        //使用条件prime[i] * prime[i] <= n,就是判断一个数A是否是素数的时候,不需要
        //从2-A的所有数都去看是否能整除,只需要看2-sqrt(A)即可,即最终整除位置为sqrt(A),见上面isPrime(int n)
        //这里是用prime[i]去整除,故而只需要prime[i]*prime[i]<=n也可写成prime[i]<=sqrt(n)
        for (i = 1; i <= prime[0] && prime[i] * prime[i] <= n; i++)
        	//如果不是素数,break,flag=0
            if (n % prime[i] == 0)
            {
                flag = 0;
                break;
            }
        //flag=1,则说明是素数,存储
        if (flag)
        {       
            prime[0]++;//素数个数
            prime[prime[0]] = n;//将确定了的素数放入数组
        }
  }
  cout<<"\ncount:"<<prime[0]<<endl;
	return prime;
}

 

 

 

5.厄拉多塞筛算法

所谓厄拉多塞筛算法就是:利用一个空间换时间的思想,利用一个辅助数组来存储素数的位置,然后利用的是筛选法,筛选出素数的倍数

那你就会发出一个疑问?只是筛选出素数的倍数,那是不是还有遗漏啊?这个就看看1的性质吧,即所有合数,都可以分解成若干个素数。

 

*首先,将所有的数组元素设为0,表示没有已知的非素数。

*然后,将已知为非素数(即为已知素数的倍数)的索引对应的数组元素设置为1。

*如果将所有较小素数的倍数都设置为1之后,a[i]仍然保持为0,则可判断它是所找的素数。

 

 

/**
	*厄拉多塞筛算法:prime[0]中保存素数总个数
	*/
int* getPrime2(){
	int count = 0;
	int primes[LEN / 3] = {0}, pi=1;
	int flag[LEN] = {0};//判断是否为素数,初始都为素数
	//0,1不是素数
	flag[0]=1;
	flag[1]=1;
	for(int i=2; i<LEN; i++){
		//倍数必然不是素数,把不是素数的都筛选出来
		if(!flag[i]){
			primes[pi++] = i;
			// 注意:这里只用素数来筛,因为合数筛时肯定被素数筛过了,所以没必要.
			//为什么?因为合数的性质:任何一个合数一定可以表示成若干个素数之积。如:4 = 2 * 2,6 = 2 * 3,12 = 2 * 2 * 3
			//即所有合数,都可以分解成若干个素数。
			for(int j=i; j<LEN; j+=i){
				//为什么9624会出现在素数中?
				//if(j == 9624) cout<<"-----"<<i<<" "<<j<<endl;
				flag[j]=1;
				count++;
			}
		}
	}
	//从count可以看出,对于一个位置重复访问了许多次,可以优化
	cout<<"访问数组总数:"<<count<<endl;
	primes[0] = pi - 1;
	cout<<"count:"<<primes[0]<<endl;
	return primes;
}

 

 

 

6,厄拉多塞筛算法改进版

由于对于5来说,最大的缺憾就是利用了辅助数组,那么我们可以减少空间,反正都只是存0,1,为什么不用位来存储呢?这样就大大的减少了空间的浪费了!

首先看看一个为操作的事例:

 

/**
	*用于位测试
	*
	*/
#include <iostream>
#include <cstdio>
using namespace std;

int main(){
	//在数组中在指定的位置上写1  
    int b[5] = {0};  //占用连续的20个字节空间(每个int占用4字节),每个字节8位(1Byte=8bit)
    cout<<sizeof(b)<<" "<<sizeof(int)<<endl;
    int i;  
    //在第i个位置上写1(一共有20*8=160位,现在之使用了40位) 
    for (i = 0; i < 40; i += 3)  
    		//每int占用4字节即32位,当b[0]的32位用完,就b[1]
        b[i / 32] |= (1 << (i % 32));  
    //输出整个bitset  
    for (i = 0; i < 40; i++)  
    {  
    	//将数组依次右移一位,然后和1求与,判断该位是0还是1
        if ((b[i / 32] >> (i % 32)) & 1)  
            putchar('1');  
        else   
            putchar('0');  
    }  
    putchar('\n');  
    return 0;  
	return 0;
}

 在下面的算法中就是利用了上面的操作,只要去看看我第一个链接,里面更详细!

 

 

/**
	*厄拉多塞筛算法改进版:prime[0]中保存素数总个数
	*/
int* getPrime3(){
	int primes[LEN / 3] = {0}, pi=1; //实际素数的个数最多就是LEN / 3,故而没用LEN
	//用位替代int prime[LEN] = {0};//判断是否为素数,初始都为素数
	int flag[LEN/32] = {0};//每个int是4字节=32bit(32位)可以存储32个0,1,故而只需要LEN/32个长度的数组即可
	for(int i=2; i<LEN; i++){
		//倍数必然不是素数,把不是素数的都筛选出来
		if(!((flag[i/32]>>(i%32))&1)){
			primes[pi++] = i;
			for(int j=i; j<LEN; j+=i){
				//将第j位设置为1,表示不是素数
				flag[j/32] |= (1<<(j%32));
			}
		}
	}
	primes[0] = pi - 1;
	cout<<"count:"<<primes[0]<<endl;
	return primes;
}

 

 

7.时间复杂度分析

10万个数:这个数来测试有些小,可以大些,这样效果比较明显

时间花费依次:62ms, 15ms, 0ms, 0ms 

当然最好两个是0ms这个大些才能看出来,非常明显的效果提升

 

8.所有代码

 

/**
	*说明:
	*有一个重要的性质:任何一个合数一定可以表示成若干个素数之积。
	*如:4 = 2 * 2,6 = 2 * 3,12 = 2 * 2 * 3。也就是说,
	*合数N一定是小于N的某个(或若干)素数的整数倍,反之,如果N不是任何比它小的素数的倍数,
	*则N必然是素数。
/**
	* Author: Blakequ@gmail.com
	* Data  : 2012-06-02 22:20
	* 
	*
	*/
#include <iostream>
#include <cmath>
#include <ctime>
const int LEN = 100000;
using namespace std;

//检查n是不是素数
bool isPrime(int n){
	if(n ==0 || n==1) return 0;
	for(int i=2; i<=sqrt(n); i++){
		if(n%i == 0){
			 cout<<i<<endl;
			 return 0;
		}
	}
	return 1;
}

/**
	*经典素数求解算法
	*prime[0]中保存素数总个数
	*/
int* getPrime(){
	int prime[LEN] = {0};//判断是否为素数,初始都为素数
	prime[0] = 0; //保存素数的总个数
	for(int n=2; n<LEN; n++){
		bool flag = 1;
		//对每个数A都按照:2到sqrt(A)依次除,看是否整除,如果能则不是素数
		//这样是很低效的m*m<=n也可写成m<=sqrt(n)
		//注意:必须有m*m<=n 中的“=”
		for(int m=2; m*m<=n; m++){
			//如果能整除,不是素数
			if(n%m == 0){
				flag = 0;
				break;
			}
		}
		if(flag){
			prime[0]++;
			prime[prime[0]] = n;
		}
	}
	cout<<"\ncount:"<<prime[0]<<endl;
	return prime;
}


/**
	*它是上面经典算法改进版
	*这里prime数组中存的是素数,prime[0]中保存素数总个数
	*获取0-n的所有素数
	*/
int* getPrime1(){
	int prime[LEN] = {0};//判断是否为素数,初始都为素数
	int i, n, flag;
  prime[0] = 0; //保存素数的总个数
  for (n =2 ; n < LEN; n++)
  {
        flag = 1;
        //判断当前n是否被"已经找出的素数"整除(如果能,说明不是素数,根据合数性质)
        //使用条件prime[i] * prime[i] <= n,就是判断一个数A是否是素数的时候,不需要
        //从2-A的所有数都去看是否能整除,只需要看2-sqrt(A)即可,即最终整除位置为sqrt(A),见上面isPrime(int n)
        //这里是用prime[i]去整除,故而只需要prime[i]*prime[i]<=n也可写成prime[i]<=sqrt(n)
        for (i = 1; i <= prime[0] && prime[i] * prime[i] <= n; i++)
        	//如果不是素数,break,flag=0
            if (n % prime[i] == 0)
            {
                flag = 0;
                break;
            }
        //flag=1,则说明是素数,存储
        if (flag)
        {       
            prime[0]++;//素数个数
            prime[prime[0]] = n;//将确定了的素数放入数组
        }
  }
  cout<<"\ncount:"<<prime[0]<<endl;
	return prime;
}


//------------------------------下面两个算法是典型的以空间换时间,不过空间改进即可---------------------------------------------------
/**
	*厄拉多塞筛算法:prime[0]中保存素数总个数
	*首先,将所有的数组元素设为0,表示没有已知的非素数。
	*然后将已知为非素数(即为已知素数的倍数)的索引对应的数组元素设置为1。
	*如果将所有较小素数的倍数都设置为1之后,a[i]仍然保持为0,
	*则可判断它是所找的素数。
	*/
int* getPrime2(){
	int count = 0;
	int primes[LEN / 3] = {0}, pi=1;
	int flag[LEN] = {0};//判断是否为素数,初始都为素数
	//0,1不是素数
	flag[0]=1;
	flag[1]=1;
	for(int i=2; i<LEN; i++){
		//倍数必然不是素数,把不是素数的都筛选出来
		if(!flag[i]){
			primes[pi++] = i;
			// 注意:这里只用素数来筛,因为合数筛时肯定被素数筛过了,所以没必要.
			//为什么?因为合数的性质:任何一个合数一定可以表示成若干个素数之积。如:4 = 2 * 2,6 = 2 * 3,12 = 2 * 2 * 3
			//即所有合数,都可以分解成若干个素数。
			for(int j=i; j<LEN; j+=i){
				//为什么9624会出现在素数中?
				//if(j == 9624) cout<<"-----"<<i<<" "<<j<<endl;
				flag[j]=1;
				count++;
			}
		}
	}
	//从count可以看出,对于一个位置重复访问了许多次,可以优化
	cout<<"访问数组总数:"<<count<<endl;
	primes[0] = pi - 1;
	cout<<"count:"<<primes[0]<<endl;
	return primes;
}


/**
	*厄拉多塞筛算法改进版:prime[0]中保存素数总个数
	*使用一个数组来包含最简单的元素类型,0和1两个值,
	*如果我们使用位的数组,而不是使用整数的数组,则可获得更高的空间有效性
	*/
int* getPrime3(){
	int primes[LEN / 3] = {0}, pi=1; //实际素数的个数最多就是LEN / 3,故而没用LEN
	//用位替代int prime[LEN] = {0};//判断是否为素数,初始都为素数
	int flag[LEN/32] = {0};//每个int是4字节=32bit(32位)可以存储32个0,1,故而只需要LEN/32个长度的数组即可
	for(int i=2; i<LEN; i++){
		//倍数必然不是素数,把不是素数的都筛选出来
		if(!((flag[i/32]>>(i%32))&1)){
			primes[pi++] = i;
			for(int j=i; j<LEN; j+=i){
				//将第j位设置为1,表示不是素数
				flag[j/32] |= (1<<(j%32));
			}
		}
	}
	primes[0] = pi - 1;
	cout<<"count:"<<primes[0]<<endl;
	return primes;
}


int main(){
	clock_t start, finish;
  start = clock();
	int n = 0;
	/*
	cout<<"输入判断的数:"<<endl;
	cin>>n;
	if(isPrime(n)){
		cout<<"是素数"<<endl;
	}else{
		cout<<"不是素数"<<endl;
	}
	*/
	
	//-------------------------方法1(经典算法62ms)----------------------------
	//getPrime();
	/*
	int *a = getPrime();
	for(int i=1; i<LEN; i++){
		if(a[i]==0) break;
		cout<<a[i]<<" ";
	}
	*/
	
	//-------------------------方法1(经典算法改进15ms)----------------------------
	//getPrime1();
	/*
	int *a = getPrime1();
	for(int i=1; i<LEN; i++){
		if(a[i]==0) break;
		cout<<a[i]<<" ";
	}
	*/
	
	//----------------------------方法3(厄拉多塞筛算法<1ms)----------------------------
	//getPrime2();
	/*
	int *a = getPrime2();
	for(int i=1; i<LEN; i++){
		if(a[i]==0) break;
		cout<<a[i]<<" ";
	}
	*/
	
	//----------------------------方法3(厄拉多塞筛算法改进<1ms)----------------------------
	//getPrime3();
	
	int *a = getPrime3();
	for(int i=1; i<LEN; i++){
		if(a[i]==0) break;
		cout<<a[i]<<" ";
	}
	
	
  finish = clock();
  cout<< "\nCost Time: " << finish - start << " ms";
	return 0;
}
 

 

0
0
分享到:
评论

相关推荐

    Termux (Android 5.0+).apk.cab

    Termux (Android 5.0+).apk.cab

    基于go、vue开发的堡垒机系统(运维安全审计系统)全部资料+详细文档.zip

    【资源说明】 基于go、vue开发的堡垒机系统(运维安全审计系统)全部资料+详细文档.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!

    葡萄城手册,快速上手,灵活报表

    制作报表

    基于C++与Qt的金山培训大作业源码汇总

    本项目为金山培训大作业源码汇总,采用C++与Qt技术构建,包含401个文件,涵盖106个C++源文件、72个头文件、41个PNG图片、27个项目文件以及HTML、JavaScript、CSS等多种文件类型。项目包含四个主要模块:KVector向量库、命令行会议系统、KSvg绘图板和KHttp音乐播放器。尽管最终未能入选,但展现了作者在C++编程和Qt框架应用方面的扎实功底和努力。

    (26408240)STM32F103+四个VL53L0代码(2020新).zip

    内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。

    基于课程设计:C语言爬虫、详细文档+全部资料+高分项目.zip

    【资源说明】 基于课程设计:C语言爬虫、详细文档+全部资料+高分项目.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!

    (176629254)杭州电子科技大学自动控制原理期末试卷答案 复习ppt

    《自动控制原理》是自动化及相关专业的重要课程,涵盖了控制系统的基础理论和分析方法。这份资料集是针对杭州电子科技大学自动控制原理课程的期末复习资源,包含了试卷答案和复习PPT,对于学习者来说是一份非常宝贵的参考资料。 我们来看文件"9自控原理第五章习题参考答案.doc",它提供了第五章的习题解答。第五章通常涉及根轨迹法,这是分析线性系统稳定性的一种图形方法。通过绘制根轨迹,我们可以直观地理解系统动态性能的变化,例如系统的稳定性、超调量和调节时间等。 接着,"1第一章作业(答案).docx"涵盖了课程的初步概念,如控制系统的基本组成部分、控制系统的定义以及开环与闭环控制的区别。第一章的内容通常包括控制系统的基本模型,如传递函数和信号流图。 "10测试4.docx"和"13第7章测试.docx"可能是关于控制系统设计和分析的测试题目,可能涉及到频率响应分析或状态空间模型等内容。第七章常常讨论系统稳定性分析,比如奈奎斯特稳定判据或劳斯判据。 "7测试1.docx"可能包含有关拉普拉斯变换和控制系统动态特性的问题,这是控制系统分析的基础工具。 "4自动控制第二章习题答案.pdf"提供了第二章习题

    066 - 直播逗大哥话术.docx

    066 - 直播逗大哥话术

    AOP项目demo 案例

    AOP项目demo 案例

    皮带输送线3D+2DCAD+加工件标准件清单BOMsw2016可编辑全套技术资料100%好用.zip

    皮带输送线3D+2DCAD+加工件标准件清单BOMsw2016可编辑全套技术资料100%好用.zip

    154-基于stm32单片机花样流水灯设计Proteus仿真+源程序.zip

    154-基于stm32单片机花样流水灯设计Proteus仿真+源程序.zip

    鲸鱼WOA-XGboost拟合预测建模模型,数据格式多维自变量输入,单维因变量输出,直接替数据就可以使用,程序内注释详细

    鲸鱼WOA-XGboost拟合预测建模模型,数据格式多维自变量输入,单维因变量输出,直接替数据就可以使用,程序内注释详细

    基于springboot的学生综合成绩测评系统源码(java毕业设计完整源码).zip

    项目均经过测试,可正常运行! 环境说明: 开发语言:java JDK版本:jdk1.8 框架:springboot 数据库:mysql 5.7/8 数据库工具:navicat 开发软件:eclipse/idea

    RocketDock-v1.3.5

    RocketDock-v1.3.5是一款专为Windows 7设计的桌面工具,它模仿Mac OS X的Dock功能,通过直观的图标排列提高应用启动效率。该工具提供快捷启动、动画效果、自定义设置以及扩展性,使用户能够根据个人喜好调整Dock栏并减少桌面杂乱。安装和设置过程简单,配有详细的说明文件,帮助用户更好地了解和使用工具。

    基于springboot的会员制医疗预约服务管理信息系统源码(java毕业设计完整源码+LW).zip

    会员制医疗预约服务管理信息系统的作用,可以提高会员制医疗预约服务管理的工作人员的效率,协助他们对会员制医疗预约服务信息进行统一管理,为管理者提供信息储存和查询搜索系统。一个良好的会员制医疗预约服务管理信息系统可以实现对会员制医疗预约服务的精细化管理:对在线会员制医疗预约服务管理流程的全过程进行电子化操作,其主要作用是管理和控制会员制医疗预约服务所有的信息,分析库存数据,使工作人员对会员制医疗预约服务管理信息系统进行监管,根据系统所提供的相应信息,采取适当的措施,及时补救管理中的漏洞,提高在线会员制医疗预约服务管理的工作效率,使得在线会员制医疗预约服务管理变的更加系统和规范。 环境说明: 开发语言:java JDK版本:jdk1.8 框架:springboot 数据库:mysql 5.7/8 数据库工具:navicat 开发软件:eclipse/idea

    (2368806)CCNA中文版PPT

    **CCNA(思科认证网络助理工程师)是网络技术领域中的一个基础认证,它涵盖了网络基础知识、IP编址、路由与交换技术等多个方面。以下是对CCNA中文版PPT中可能涉及的知识点的详细说明:** ### 第1章 高级IP编址 #### 1.1 IPv4地址结构 - IPv4地址由32位二进制组成,通常分为四段,每段8位,用点分十进制表示。 - 子网掩码用于定义网络部分和主机部分,如255.255.255.0。 - IP地址的分类:A类、B类、C类、D类(多播)和E类(保留)。 #### 1.2 子网划分 - 子网划分用于优化IP地址的分配,通过借用主机位创建更多的子网。 - 子网计算涉及掩码位数选择,以及如何确定可用的主机数和子网数。 - CIDR(无类别域间路由)表示法用于更有效地管理IP地址空间。 #### 1.3 私有IP地址 - 为了节省公网IP地址,私有IP地址被用于内部网络,如10.0.0.0/8,172.16.0.0/12,192.168.0.0/16。 #### 1.4 广播地址 - 每个网络都有一个特定的广播地址,所有数据包都会发送到这个地址以达到同一网络内的所有设备。

    基于springboot的藏区特产销售平台源码(java毕业设计完整源码+LW).zip

    项目实现了对特产信息管理、特产分类管理、特产分类管理、特产评分管理、系统管理、订单管理等业务进行管理。 环境说明: 开发语言:java JDK版本:jdk1.8 框架:springboot 数据库:mysql 5.7/8 数据库工具:navicat 开发软件:eclipse/idea

    “个人事务云服务”:日常事务管理系统的云计算应用

    随着21世纪网络和计算机技术的飞速发展,它们已经与我们的日常生活紧密融合。当前网络的运行速度已经达到千兆级别,覆盖范围广泛,深入到生活的每一个角落。这一进步推动了管理系统的发展,使得远程处理事务、远程工作信息管理和实时追踪工作状态成为可能。网上管理系统以其前所未有的体验满足了新时代的工作需求,因此得到了大力发展。 本系统是一个个人日常事务管理系统,利用计算机和网络技术开发的在线平台,能够实现日常安排、消费记录和重要提醒设置等功能。系统采用SSM框架和Vue技术,数据库使用MySQL,开发环境为Eclipse。系统用户角色包括普通用户和管理员,功能涵盖个人中心管理、用户管理、日常安排管理、消费记录管理和重要提醒管理。用户可以记录消费、安排日常事务和设置重要提醒,而管理员则负责管理用户信息和基础数据信息。该系统不仅方便了用户和管理员,还提高了个人事务管理的效率,更适应现代人的生活方式。

    基于java+springboot+mysql+微信小程序的考研资料分享系统 源码+数据库+论文(高分毕业设计).zip

    项目已获导师指导并通过的高分毕业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行!可以放心下载 技术组成 语言:java 开发环境:idea、微信开发者工具 数据库:MySql5.7以上 部署环境:maven 数据库工具:navicat

    如何使用Python和PIL库生成带竖排文字的封面图像

    如何使用Python和PIL库生成带竖排文字的封面图像

Global site tag (gtag.js) - Google Analytics