操作系统实验四 死锁问题

一、问题描述

看上图,有五位哲学家,面前都有一个盘子,盘子左边和右边都有一根筷子,他们在吃面之前需要先拿起左边的筷子再拿起右边的筷子,有了一双筷子就可以吃面了。

二、流程

  • 先拿起左手的筷子
  • 然后拿起右手的筷子
  • 如果筷子被人使用了,那就等别人用完
  • 吃完后,把筷子放回原位

三、死锁的产生

由图可知,生动形象

四、整体代码

#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <curses.h>
#include <time.h>
#include <semaphore.h>
#include <string.h>
#define MAX_PHILOSOPHERS 5 
#define ZERO 48  
#define DELAY (rand()%25)/1000
 //为什么要转换字符串
 //在使用像 printw 这样的函数打印字符串时,我们通常需要将所有内容转换为字符或字符串。如果 philosopher_number 是一个整数,直接加入字符串会造成问题,因为它会被解释为一个非字符值。
//通过将 philosopher_number 加上 ZERO,我们将数字转换为其字符表示,从而可以将其正确地嵌入到要打印的字符串中。
typedef enum {thinking,hungry,eating}status;
status state[MAX_PHILOSOPHERS];

pthread_mutex_t pre_mutex;
sem_t pre_self[MAX_PHILOSOPHERS];
pthread_mutex_t h_mutex_chopsticks[MAX_PHILOSOPHERS];

int thread_number[MAX_PHILOSOPHERS]={0,1,2,3,4};

void pre_test(int i);
void pre_pick_fork(int i);
void pre_put_fork(int i);


void* deadlock_philosopher(void* data){
	int philosopher_number=*(int *)(data);
	int i=0;

	for(;;)
	{
		srand( (unsigned)time( NULL ) * ( philosopher_number+ 1) );
                sleep(DELAY);
		if(i>=5){
			i=0;
			clear();
			refresh();
		}
		else
			i++;

		printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is waiting chopstick ",ZERO+philosopher_number);
		refresh();
		pthread_mutex_lock(&h_mutex_chopsticks[philosopher_number]);
		
	    sleep(DELAY/4);

		printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is waiting chopstick ",ZERO+(1+philosopher_number)%MAX_PHILOSOPHERS);
		refresh();
		pthread_mutex_lock(&h_mutex_chopsticks[(1+philosopher_number)%MAX_PHILOSOPHERS]);
	
	   printw("%s%c%s\n","Philosopher ",ZERO+philosopher_number," is eating.");                             
		refresh();
	
        sleep(DELAY);
		
	
		printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is releasing chopstick ",ZERO+philosopher_number);
		refresh();
		pthread_mutex_unlock(&h_mutex_chopsticks[philosopher_number]);
		printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is releasing chopstick ",ZERO+(1+philosopher_number)%MAX_PHILOSOPHERS);
		refresh();
        pthread_mutex_unlock(&h_mutex_chopsticks[(1+philosopher_number)%MAX_PHILOSOPHERS]);

		sleep(DELAY);
	} 
	return 0;
}


void deadlock(){
	int i=0;
	pthread_t h_thread[MAX_PHILOSOPHERS];

	printw("deadlock possible.\n");
	refresh();
	for(i=0;i<MAX_PHILOSOPHERS;i++){
		pthread_mutex_init(&h_mutex_chopsticks[i],NULL);
	};
	for(i=0;i<MAX_PHILOSOPHERS;i++){
		pthread_create(&h_thread[i],NULL,deadlock_philosopher,&thread_number[i]);
	};
	for(i=0;i<MAX_PHILOSOPHERS;i++){
		pthread_join(h_thread[i],NULL);
	}
	
}

void* ordered_allocation_philosopher(void* data){
	int philosopher_number=*(int *)(data);
	int i=0;

	for(;;)  //模拟哲学家不断重复的思考和就餐行为。
	{
		srand( (unsigned)time( NULL ) * ( philosopher_number+ 1) );
                sleep(DELAY);
		if(i>=5){
			i=0;
			clear();
			refresh();
		}
		else  //处理最后一个哲学家
			i++;  
	        if(philosopher_number==MAX_PHILOSOPHERS-1){
			printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is waiting chopstick ",ZERO+(1+philosopher_number)%MAX_PHILOSOPHERS);
		   refresh();
			pthread_mutex_lock(&h_mutex_chopsticks[(1+philosopher_number)%MAX_PHILOSOPHERS]);
            //尝试获取右边的筷子 使用互斥锁 确保同时只有一个哲学家可以持有特定的筷子。如果该筷子已被其他哲学家持有,这个调用将阻塞,直到筷子可用。
           	sleep(DELAY/4); //模拟哲学家等待筷子的时间。
	        	printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is waiting chopstick ",ZERO+philosopher_number);
			refresh();
			pthread_mutex_lock(&h_mutex_chopsticks[philosopher_number]);//尝试获取左边的筷子
		}
		else{
			printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is waiting chopstick ",ZERO+philosopher_number);
			refresh();
		    pthread_mutex_lock(&h_mutex_chopsticks[philosopher_number]);
			sleep(DELAY/4);
			printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is waiting chopstick ",ZERO+(1+philosopher_number)%MAX_PHILOSOPHERS);
			refresh();
		        pthread_mutex_lock(&h_mutex_chopsticks[(1+philosopher_number)%MAX_PHILOSOPHERS]);
		}
	    printw("%s%c%s\n","Philosopher ",ZERO+philosopher_number," is eating.");
		refresh();
	
        sleep(DELAY);
		printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is releasing chopstick ",ZERO+philosopher_number);
		refresh();
		pthread_mutex_unlock(&h_mutex_chopsticks[philosopher_number]);
	    printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is releasing chopstick ",ZERO+(1+philosopher_number)%MAX_PHILOSOPHERS);
		refresh();
        pthread_mutex_unlock(&h_mutex_chopsticks[(1+philosopher_number)%MAX_PHILOSOPHERS]);

		sleep(DELAY);
	} 
	return 0;
}



void* ordered_allocation(){
	int i=0;
	pthread_t h_thread[MAX_PHILOSOPHERS];

	printw("orderded allocation:deadlock impossible.\n");
	refresh();
	for(i=0;i<MAX_PHILOSOPHERS;i++){
		pthread_mutex_init(&h_mutex_chopsticks[i],NULL);
	};
	for(i=0;i<MAX_PHILOSOPHERS;i++){
		pthread_create(&h_thread[i],NULL,ordered_allocation_philosopher,&thread_number[i]);
	};

	for(i=0;i<MAX_PHILOSOPHERS;i++){
		pthread_join(h_thread[i],NULL);
	}
}

void* pre_allocation_philosopher(void* data){
	int philosopher_number=*((int*)(data));
	int i=0;
	
	for(;;)// 模拟哲学家思考时间
	{
		srand( (unsigned)time( NULL ) * ( philosopher_number+ 1) ); //确保每个哲学家线程产生的随机数序列是不同的。
        sleep(DELAY); //模拟了哲学家的思考时间。
		if(i>=10){
			i=0;
			clear();
			refresh();
		}
		else
			i++;
		printw("%s%c%s\n","Philosopher ",ZERO+philosopher_number," is thinking ");
		refresh();
		state[philosopher_number]=thinking;
		pre_pick_fork(philosopher_number);
	    printw("%s%c%s\n","Philosopher ",ZERO+philosopher_number," is eating.");
		refresh();

		state[philosopher_number]=eating;
	    sleep(DELAY);

		pre_put_fork(philosopher_number);
		sleep(DELAY);
	}

	return 0;
}

void pre_pick_fork(int i){
	pthread_mutex_lock(&pre_mutex);  //使用互斥锁 保证当一个哲学家改变状态或尝试拿起筷子时,不会有其他哲学家同时进行类似的操作
	state[i]=hungry;
	printw("%s%c%s\n","Philosopher ",ZERO+i," is hungry. ");  //ZERO+i 可能是将整数转换为对应的字符形式,用于显示。
	pre_test(i); //检查哲学家 i 是否可以开始就餐。
	pthread_mutex_unlock(&pre_mutex);//释放互斥锁 pre_mutex。以便其他哲学家可以进行自己的操作 
	sem_wait(&pre_self[i]); //控制哲学家的行为 如果哲学家 i 不能立即就餐该信号量会阻塞哲学家 i 的线程,直到哲学家 i 可以开始就餐时被唤醒
}

void pre_put_fork(int i){
	pthread_mutex_lock(&pre_mutex);  //使用互斥锁 pre_mutex 来保证当一个哲学家改变状态或尝试拿起筷子时,不会有其他哲学家同时进行类似的操作
	state[i]=thinking;
	pre_test((i-1)%MAX_PHILOSOPHERS);  //会检查哲学家 i 的左右两边是否有其他哲学家正在就餐,
	pre_test((i+1)%MAX_PHILOSOPHERS);  //个函数会检查哲学家 i 的左右两边是否有其他哲学家正在就餐,
	pthread_mutex_unlock(&pre_mutex); //释放互斥锁 pre_mutex。以便其他哲学家可以进行自己的操作
}

void pre_test(int i){
	if((state[i]==hungry)
&&(state[(i-1)%MAX_PHILOSOPHERS]!=eating)
&&(state[(i+1)%MAX_PHILOSOPHERS]!=eating)){
		state[i]=eating;
		sem_post(&pre_self[i]);
	}
}

void pre_alloction(){
	int i=0;
	pthread_t h_thread[MAX_PHILOSOPHERS];  //哲学家的数量
	pthread_mutex_init(&pre_mutex,NULL);  //初始化互斥锁 控制对共享资源的访问以避免并发问题。

	printw("pre_allocation:deadlock impossible.\n");
	refresh();
	for(i=0;i<MAX_PHILOSOPHERS;i++){  //所有哲学家一开始都处于思考状态。
		sem_init(&pre_self[i],0,0);
		state[i]=thinking;
	};
	for(i=0;i<MAX_PHILOSOPHERS;i++){  //创建哲学家线程
		pthread_create(&h_thread[i],NULL,pre_allocation_philosopher,&thread_number[i]);
	};

	for(i=0;i<MAX_PHILOSOPHERS;i++){
		pthread_join(h_thread[i],NULL);  //确保主线程会等待每个哲学家线程结束。
	}
	pthread_mutex_destroy(&pre_mutex);  //清理并销毁互斥锁
}


int main(int argc,char *argv[]){
	char select;
	bool end=false;
	initscr();
	while(!end){
		clear();
		refresh();
		printw("|-----------------------------------------|\n");
		printw("|  1:deadlock                             |\n");
		printw("|  2:non_deadlock by ordered allocation   |\n");
		printw("|  3:non_deadlock by pre_allocation       |\n");
		printw("|  4:exit                                 |\n");
		printw("|-----------------------------------------|\n");
		printw("select a function(1~4):");
		do{
			select=(char)getch();
		}while(select!='1'&&select!='2'&&select!='3'&&select!='4');
		clear();
		refresh();
		switch(select){
		case '1':
			deadlock();
			break;
		case '2':
			ordered_allocation();
			break;
		case '3':
			pre_alloction();
			break;
		case '4':
			end=true;
		}
		printw("\nPress any key to return to main menu.");
		getch();
		clear();
		refresh();
	}
	endwin();
	return 0;
}

 

五、资源有序分配法预防死锁和预先分配法代码区别

资源有序分配法

资源有序分配法的核心思想是强制所有资源(在哲学家就餐问题中是筷子)按照一定的顺序进行分配。通常,这意味着每个哲学家首先尝试获取编号较低的筷子,然后再尝试获取编号较高的筷子。

代码实现特点:

  1. 有序获取筷子:哲学家首先尝试获取其左边和右边中编号较低的筷子,然后再尝试获取另一根。
  2. 避免循环等待:由于所有哲学家都遵循相同的顺序获取筷子,循环等待的条件被破坏,从而避免了死锁。

预先分配法

预先分配法则是指在哲学家开始就餐之前,必须一次性获取到所有需要的资源(两根筷子)。如果不能获取到全部资源,则不会开始就餐,而是释放已持有的资源。

代码实现特点:

  1. 原子性资源请求:哲学家在尝试就餐前会一次性请求两根筷子。如果不能同时获取到两根筷子,他们不会持有任何一根筷子。
  2. 避免死锁:由于哲学家要么同时持有两根筷子,要么一根也不持有,因此避免了因部分资源被持有而导致的死锁。

区别

  1. 资源获取策略

    • 资源有序分配法:哲学家按顺序获取单根筷子。
    • 预先分配法:哲学家尝试一次性获取两根筷子。
  2. 实现复杂度

    • 资源有序分配法通常实现起来较为简单,因为它只需要改变获取资源的顺序。
    • 预先分配法可能需要更复杂的逻辑来处理一次性获取所有资源的情况。
  3. 对哲学家行为的影响

    • 资源有序分配法可能导致某些哲学家更频繁地就餐(特别是那些两边筷子编号差异大的哲学家)。
    • 预先分配法可能导致更多的等待,因为哲学家需要两根筷子同时可用才能开始就餐。

六、采用预先分配法预防死锁的哲学家就餐问题

在资源分配法中,系统的设计是让哲学家们不是直接尝试拿起两根筷子,而是首先请求整体资源(即两根筷子)。如果一位哲学家能同时获取到左右两根筷子,他就可以开始就餐;如果不能,则他会等待,直到这两根筷子都可用。这种方式可以防止死锁

例子: 假设有五位哲学家围坐在一张圆桌旁,编号为1到5。每位哲学家在他们右边有一根筷子,左边有另一根筷子。每位哲学家在思考结束后,会尝试拿起两根筷子。

  • 哲学家1请求使用1号和2号筷子。
  • 同时,哲学家3请求使用3号和4号筷子。
  • 因为1号和2号筷子都可用,哲学家1可以开始就餐。
  • 同时,3号和4号筷子也都可用,所以哲学家3也可以开始就餐。
  • 哲学家1和3就餐完毕后,放下筷子。
  • 然后,哲学家2可以请求2号和3号筷子,哲学家4可以请求4号和5号筷子。

代码如下:

void* pre_allocation_philosopher(void* data){
	int philosopher_number=*((int*)(data));
	int i=0;
	
	for(;;)
	{
		srand( (unsigned)time( NULL ) * ( philosopher_number+ 1) );
        sleep(DELAY);
		if(i>=10){
			i=0;
			clear();
			refresh();
		}
		else
			i++;
		printw("%s%c%s\n","Philosopher ",ZERO+philosopher_number," is thinking ");
		refresh();
		state[philosopher_number]=thinking;
		pre_pick_fork(philosopher_number);
	    printw("%s%c%s\n","Philosopher ",ZERO+philosopher_number," is eating.");
		refresh();
		state[philosopher_number]=eating;
	    sleep(DELAY);
		pre_put_fork(philosopher_number);
		sleep(DELAY);
	}

	return 0;
}

void pre_pick_fork(int i){
	pthread_mutex_lock(&pre_mutex);
	state[i]=hungry;
	printw("%s%c%s\n","Philosopher ",ZERO+i," is hungry. ");
	pre_test(i);
	pthread_mutex_unlock(&pre_mutex);
	sem_wait(&pre_self[i]);
}

void pre_put_fork(int i){
	pthread_mutex_lock(&pre_mutex);
	state[i]=thinking;
	pre_test((i-1)%MAX_PHILOSOPHERS);
	pre_test((i+1)%MAX_PHILOSOPHERS);
	pthread_mutex_unlock(&pre_mutex);
}
void pre_test(int i){
	if((state[i]==hungry)
&&(state[(i-1)%MAX_PHILOSOPHERS]!=eating)
&&(state[(i+1)%MAX_PHILOSOPHERS]!=eating)){
		state[i]=eating;
		sem_post(&pre_self[i]);
	}
}

void pre_alloction(){
	int i=0;
	pthread_t h_thread[MAX_PHILOSOPHERS];
	pthread_mutex_init(&pre_mutex,NULL);

	printw("pre_allocation:deadlock impossible.\n");
	refresh();
	for(i=0;i<MAX_PHILOSOPHERS;i++){
		sem_init(&pre_self[i],0,0);
		state[i]=thinking;
	};
	for(i=0;i<MAX_PHILOSOPHERS;i++){
		pthread_create(&h_thread[i],NULL,pre_allocation_philosopher,&thread_number[i]);
	};

	for(i=0;i<MAX_PHILOSOPHERS;i++){
		pthread_join(h_thread[i],NULL);
	}
	pthread_mutex_destroy(&pre_mutex);
}

代码解释

  1. pre_allocation_philosopher 函数:

    • 这是哲学家线程执行的函数。每个哲学家线程都会不断地循环执行思考、尝试拿起筷子、就餐、放下筷子的过程。
    • philosopher_number 是哲学家的编号。
    • 在尝试拿起筷子之前,哲学家处于思考状态(state[philosopher_number]=thinking)。
    • 调用 pre_pick_fork 函数尝试拿起筷子,然后进入就餐状态(state[philosopher_number]=eating)。
    • 就餐结束后,调用 pre_put_fork 放下筷子。
  2. pre_pick_forkpre_put_fork 函数:

    • pre_pick_fork 函数在尝试拿起筷子之前设置哲学家的状态为饥饿(state[i]=hungry),并调用 pre_test 函数检查是否可以就餐。
    • pre_put_fork 函数在放下筷子后,更新哲学家的状态为思考,并通知相邻的哲学家他们可以尝试拿起筷子。
  3. pre_test 函数:

    • 此函数检查哲学家 i 是否可以开始就餐。哲学家可以就餐的条件是他处于饥饿状态且两边的哲学家都没有在就餐。
    • 如果条件满足,哲学家 i 的状态设置为就餐,同时使用信号量 sem_post(&pre_self[i]) 唤醒哲学家。
  4. 互斥锁和信号量:

    • 使用 pthread_mutex_lockpthread_mutex_unlock 来确保对哲学家状态的修改是互斥的。
    • 使用信号量 pre_self 来控制每个哲学家的就餐行为。
  5. pre_allocation 函数:

    • 初始化互斥锁和信号量,创建哲学家线程并启动它们。
    • 在所有哲学家线程结束后,销毁互斥锁。

  1. srand( (unsigned)time( NULL ) * ( philosopher_number+ 1) );

    • 这行代码设置了随机数生成器的种子。srand 用于初始化随机数生成器,它的参数是一个种子值。这里,种子值是当前时间(time(NULL) 返回的是自1970年1月1日以来的秒数)乘以哲学家的编号(philosopher_number + 1)。这样做可以确保每个哲学家线程产生的随机数序列是不同的。
  2. sleep(DELAY);

    • 这行代码使线程暂停一段时间,其中 DELAY 是预先定义的延迟时间。这个延迟模拟了哲学家的思考时间。
  3. if(i>=10){ i=0; clear(); refresh(); } else i++;

    • 这段代码是一个计数器,用于控制屏幕上显示的信息。当计数器 i 达到10时,它会重置为0,并清除屏幕(clear()),然后刷新显示(refresh())。如果计数器 i 小于10,它就简单地增加1。这可能用于限制屏幕上显示的信息数量,防止信息溢出屏幕。
  4. printw("%s%c%s\n","Philosopher ",ZERO+philosopher_number," is thinking ");

    • printw 函数类似于 printf,但它是用于在屏幕上显示信息。这行代码显示哲学家的编号和他们正在思考的信息。ZERO+philosopher_number 可能是将整数哲学家编号转换为字符形式,例如,如果 ZERO 被定义为 '0',那么 ZERO+1 会是字符 '1'。
  5. refresh();

    • 最后,refresh() 函数刷新屏幕,以便新的输出信息能够显示出来。

七、采用有序分配法预防死锁的哲学家就餐问题

略,上面已经提及到了

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Outlook无法显示阅读窗格

Outlook无法显示阅读窗格 故障现象 Outlook主界面不显示阅读窗格 故障截图 故障原因 阅读窗格被关闭 解决方案 1、打开Outlook - 视图 – 阅读窗格 2、选择“靠右”或者“底部”&#xff0c;正常显示阅读窗格

linux之用户管理

一、是什么 Linux是一个多用户的系统&#xff0c;允许使用者在系统上通过规划不同类型、不同层级的用户&#xff0c;并公平地分配系统资源与工作环境 而与 Windows 系统最大的不同&#xff0c; Linux 允许不同的用户同时登录主机&#xff0c;同时使用主机的资源 既然是多用户…

洗眼镜手动清洗还是用超声波清洗机洗好?值得入手超声波清洗机

眼镜清洗的方法有很多&#xff0c;但是一定要选择合适的正确的清洗方式&#xff0c;使用错误清洗眼镜方法会非常容易缩短眼镜使用寿命。一副眼镜正常情况下是可以使用2~3年的&#xff0c;大家可千万要多注意清洗眼镜的手法&#xff01;像最常见眼镜清洗的手法是用衣服擦拭一下或…

Linux之输入输出重定向和管道

一、是什么 linux中有三种标准输入输出&#xff0c;分别是STDIN&#xff0c;STDOUT&#xff0c;STDERR&#xff0c;对应的数字是0、1、2&#xff1a; STDIN 是标准输入&#xff0c;默认从键盘读取信息STDOUT 是标准输出&#xff0c;默认将输出结果输出至终端STDERR 是标准错误…

《视觉SLAM十四讲》-- 后端 1(下)

8.2 BA 与图优化 Bundle Adjustment 是指从视觉图像中提炼出最优的 3D 模型和相机参数&#xff08;内参和外参&#xff09;。 8.2.1 相机模型和 BA 代价函数 我们从一个世界坐标系中的点 p \boldsymbol{p} p 出发&#xff0c;把相机的内外参数和畸变都考虑进来&#xff0c;…

2023-2024 年适用于 Windows 电脑的顶级视频录制软件

想捕捉您正在在线观看的视频吗&#xff1f;使用网络摄像头录制视频会议以供日后参考。正在寻找可以完成这些任务的视频捕捉软件&#xff1f;这篇文章说明了一切。以下是一些适用于 Windows PC 的最佳视频录制工具。 什么是视频录制软件&#xff1f; 顾名思义&#xff0c;视频捕…

部署百川大语言模型Baichuan2

Baichuan2是百川智能推出的新一代开源大语言模型&#xff0c;采用 2.6 万亿 Tokens 的高质量语料训练。在多个权威的中文、英文和多语言的通用、领域 benchmark 上取得同尺寸最佳的效果。包含有 7B、13B 的 Base 和 Chat 版本&#xff0c;并提供了 Chat 版本的 4bits 量化。 模…

SM5212 是一款完整的采用恒定电流/恒定电压的单节锂电池线性充电器

SM5212 双向防反接功能 1.2A 锂电池线性充电芯片 概述&#xff1a; SM5212 是一款完整的采用恒定电流/恒定电压的单节锂电池线性充电器&#xff0c;并带有锂电池正负极反接保护和 VIN 正负反接保护功能&#xff0c;可以保护芯片和用户安全。 由于采用了内部 PMOSFET 架构&am…

基于Qt 多线程(继承 QObject 的线程)

​ 继承 QThread 类是创建线程的一种方法,另一种就是继承QObject 类。继承 QObject 类更加灵活。它通过 QObject::moveToThread()方法,将一个 QObeject的类转移到一个线程里执行。恩,不理解的话,我们下面也画个图捋一下。 通过上面的图不难理解,首先我们写一个类继承 QObj…

Windows使用ssh远程连接(虚拟机)Linux(Ubuntu)的方法

步骤 1.Windows下载一个SSH客户端软件 要使用SSH连接&#xff0c;当然得先有一个好用的客户端软件才方便。 我这里使用的是WindTerm&#xff0c;一个开源免费的SSH连接工具&#xff0c;用什么软件不是重点。 这里默认你已经生成过SSH的密钥了&#xff0c;如果没有&#xff0c…

基于群居蜘蛛算法优化概率神经网络PNN的分类预测 - 附代码

基于群居蜘蛛算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于群居蜘蛛算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于群居蜘蛛优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

双重预防隐患排查融合巡检系统,对无意义重复工作说不

隐患排查在行动&#xff0c;智能巡检系统在进行&#xff0c;然而有些隐患排查工作与巡检工作内容重复却不能同时上传系统&#xff0c;系统之间无法实现数据互联互通信息共享&#xff1f;融合智能巡检管理系统&#xff0c;不仅可以完美实现双重预防隐患排查管理、生产工艺巡检管…

一篇综述读懂m6A甲基化+分型+免疫浸润+机器学习。快来get

今天给同学们分享一篇生信文章“Comprehensive characterization of tumor microenvironment and m6A RNA methylation regulators and its effects on PD-L1 and immune infiltrates in cervical cancer”&#xff0c;这篇文章发表在Front Immunol期刊上&#xff0c;影响因子为…

八股文-面向对象的理解

近年来&#xff0c;IT行业的环境相较以往显得有些严峻&#xff0c;因此一直以来&#xff0c;我都怀有一个愿望&#xff0c;希望能够创建一个分享面试经验的网站。由于个人有些懒惰&#xff0c;也较为喜欢玩乐&#xff0c;导致计划迟迟未能实现。然而&#xff0c;随着年底的临近…

绿茵场上洒汗水,激情飞扬现活力——碧桂园社区足球课堂开课啦

“伙伴计划”是由共青团中央发起实施&#xff0c;中央专项彩票公益金提供支持&#xff0c;面向社区青少年开展的一项示范性服务项目。实施这个项目的主要目的是针对青少年在成长过程中遇到的实际困难和普遍性需求&#xff0c;开展“团团活力圈”青少年身心健康提升服务&#xf…

JUC“阻塞队列”水很深,你把握不住!

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 提到阻塞队列&#xff…

手把手教你实现贪吃蛇

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;实现贪吃蛇 > 毒鸡汤&#xff1a;时间并不可真…

前端JS解构数组对象

// 3. 对象数组解构const arr [{username: 小明,age: 18,agw:19},{username: 小ha,age: 18,agw:19}]arr.map(item>item.age)//js结构数组对象console.log( arr.map(item>{return {aaa:item.age,bbb:item.username}}))

zabbix的agent的安装部署

zabbix的agent的部署 主机ipagent-1192.168.10.129 zabbix官网部署教程 但是不全&#xff0c;建议搭配这篇文章一起看 下面有教程 zabbix服务端配置 修改主机名 hostnamectl set-hostname agent-1 exit配置zabbix的yum源 [rootagent-1 ~]# rpm -Uvh https://repo.zabbix…

193. 二叉搜索树的最小公共祖先

题目 题解 递归 def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:if root.val < p.val and root.val < q.val:return self.lowestCommonAncestor(root.right, p, q)if root.val > p.val and root.val > q.val:return …