基于pytorch的steam游戏评分的线性回归问题分析

news2024/9/29 15:29:51

请添加图片描述

前言

  • 相信已经暑假一个月的大家肯定并不陌生上面这个学习软件(),面对琳琅满目的游戏总是让人不知道挑选什么,这时候一个游戏的评分往往便成为了一个玩家选择下载的原因,那么今天我们就来研究研究,steam上一个游戏的种种数据,是如何影响到其最终评分。
  • 本文将使用pytorch对steam_datasets数据集进行线性回归分析,并在文章结尾尝试加入非线性优化模型。
  • 本文仅提供处理此问题的一种思路,并不是最优,如有错误,请多多指正。
  • 数据集链接

问题阐述

  • 以下是数据集steam_datasets.csv的一小部分截图,它包含以下几个信息
    请添加图片描述

    • Column1:行数
    • game:游戏名
    • release:发行日期(1)
    • peak_players:巅峰玩家数 (2)
    • positive_reviews:好评(3)
    • negative_reviews:差评(4)
    • total_reviews: 总评价数
    • primary_genre: 游戏分类 (5)
    • detected_technologies 游戏开发平台(6)
    • review_percentage:玩家对游戏的总体满意程度(7)
    • players_right_now:当前玩家数(8)
    • 24_hour_peak:24小时玩家巅峰数(9)
    • all_time_peak:总玩家巅峰数(10)
    • rating:游戏打分(输出)
  • 要求根据上述提供的数据集完成下述要求:

    1. 标签选取:以上述10个标签作为线性回归的输入,尝试对rating进行线性回归预测
    2. 读取数据集:自行从csv数据集挑选合适数量的数据集(请不要全拿进去)作为训练集(注意数据预处理和错误数据的剔除)
    3. 选择合适的模型及其优化算法对模型进行训练
    4. 要求使用pytorch实现上述代码

数据预处理

数据解码
  • 在拿到数据集steam_datasets.csv并对其进行回归处理之前,我们首先需要对数据进行读取,在对数据进行读取之前,我们可以使用chardet库(这是一个用于检测文本数据的字符编码的库),对数据集进行编码检测,从而在使用pandas对数据集读取时指定正确的编码格式。
import chardet  
with open('./steam_datasets.csv', 'rb') as f:  
    result = chardet.detect(f.read())  # 编码检测  
print(result['encoding'])  # 输出编码格式
  • 此外,GB2312GBKGB18030,着三个编码格式是兼容的,包含的字符个数:GB2312 <GBK< GB18030,这里chardet库给出的结果是GB2312,但是使用这个编码格式进行读取会遇到报错,故我们往上选择GBK进行数据解码。

数据读取
  • Pandas 是一个开放源码、BSD 许可的库,提供高性能、易于使用的数据结构和数据分析工具。这里我们使用Pandascsv数据集进行读取,并指定编码格式。
import pandas as pd
raw_data=pd.read_csv('./steam_datasets.csv',encoding="GBK")
print(raw_data)

请添加图片描述


异常数据清理
  • 在完成数据转换之后,我们需要进行数据清洗,这包括处理缺失值、异常值和不必要的列。
  1. 处理缺失值: 对于缺失值,可以选择填充、删除含有缺失值的行或者使用模型预测缺失值。这里我们直接丢失。
raw_data = raw_data.dropna()
  1. 删除不必要的列:对于不是10标签输入的列,我们选择删除。
raw_data = raw_data.drop(['game','Column1','total_reviews'], axis=1)
raw_data = raw_data.dropna()
  1. 剔除非数值数据:对于标签peak_players, positive_reviews, negative_reviews, review_percentage, players_right_now, 24_hour_peak, all_time_peak, rating这8列都应该是数据类型,我们观察csv文件可以发现在这些标签中,混杂着非数值类型的日期和字符串,为此我们需要进行删除。
    • 如下出现了ratingall_time_peak出现非法值的情况请添加图片描述
def to_numeric_if_possible(x):  
    try:  
        return pd.to_numeric(x)  
    except (ValueError, TypeError):  
        return pd.NA  
check_labels=['peak_players', 'positive_reviews', 'negative_reviews', 'review_percentage', 'players_right_now', '24_hour_peak', 'all_time_peak', 'rating']  
# 指定列名并应用自定义函数  
raw_data[check_labels] = raw_data[check_labels].applymap(to_numeric_if_possible)  
  
raw_data = raw_data.dropna()

数据分析–非数据类型标签转换
  • 通过观察上述10标签中,我们注意到release,primary_genre,detected_technologies着三个标签属于非数值,对于非数值类型的标签,我们需要转换为数值类型的标签来进行线性回归。
日期类数据处理
  • 观察release2023/1/26,属于日期类数据类型,我们可以将日期转换为距离某个特定日期的天数,这样可以作为一个连续变量用于回归。
  • 这里需要注意数据集中包含无法解析的日期类型,需要当作异常数据剔除。
from datetime import datetime  
# 尝试将 'release' 列转换为日期,无法解析的设置为 NaT
raw_data['release'] = pd.to_datetime(raw_data['release'], errors='coerce', format='%Y/%m/%d')  
  
# 删除包含 NaT 的行  
raw_data = raw_data.dropna(subset=['release'])  
# 选择2005-01-01作为参考点
specific_date = datetime.strptime('2005-01-01', '%Y-%m-%d')  
raw_data['release'] = pd.to_datetime(raw_data['release']).map(lambda date: (date - specific_date).days)  
print(raw_data['release'])  
# 计算依次最大最小值是为了检查是否出现非法值,此外根据最大最小值调整参考日期
print(max(raw_data['release']))  
print(min(raw_data['release']))

字符型数据处理
  • 这里先介绍几种常见的非数值类型编码方式:
    • 哑编码(独热编码,One-Hot Encoding): 独热编码是一种将分类变量转换为一系列二进制列的过程,其中每列对应一个可能的类别值。这些列中的值通常为0或1,表示某个特定类别是否存在。
      • 当分类变量的类别数量较少时,独热编码是一种有效的处理方法。
    • 分解(Factorization):分解是将分类数据转换为整数的过程。每个类别被分配一个唯一的整数。这种方法不会产生独热编码那样的稀疏矩阵,但它可能会丢失一些类别间的信息,因为它只保留了一个整数而不是整个类别结构。
      • 当类别数量较多,且不需要保留原始类别结构时,分解是一种节省空间的处理方法。
    • 标签编码(Label Encoding):类别编码是将类别转换为数值的方法
  • 那么我们现在来观察这两个字符型数据,首先来观察primary_genre
    • 请添加图片描述

    • 观察可以发现,每个游戏分类都已经分配好对应的序号,且继续观察多标签的行数基本上都属于错误数据,都已经被剔除,故这里只需要提取出单分类的游戏分类对应的数据即可

  • 我们导入正则表达式re库对括号的数字进行提取,并对其中是否为单数字进行检测
import re  
# 使用正则表达式提取括号中的数字,并将它们转换为逗号分隔的字符串  
raw_data['primary_genre'] = raw_data['primary_genre'].apply(  
    lambda x: ','.join(re.findall(r'\((\d+)\)', x)))  
  
is_single_digit = raw_data['primary_genre'].apply(lambda x: all(len(str(item)) == 1 for item in x))  
# 检查是否全部为单数字  
all_single_digit = is_single_digit.all()  
print(all_single_digit)  
print(raw_data['primary_genre'])  
# 将字符串转换为数字,使用之前的函数
raw_data['primary_genre'] = raw_data['primary_genre'].apply(to_numeric_if_possible)  
raw_data = raw_data.dropna()  
  
print(raw_data['primary_genre'])  
# 输出最大最小值进行确认
print(max(raw_data['primary_genre']))  
print(min(raw_data['primary_genre']))

  • 然后接着我们来看detected_technologies
  • 观察发现,detected_technologies由好几种开发工具组成,为此我们使用代码进行统计请添加图片描述
primary_genre_str = raw_data['detected_technologies'].str.cat(sep=';')  
  
# split 分割文本  
entries = [entry for line in primary_genre_str.split("\n") for entry in line.split(";")]  
all_categories=set()  
for entry in entries:  
    entry = entry.strip()  # 移除前后的空白字符  
    if '.' in entry:  
        all_categories.add(entry.split('.')[0])  
print(all_categories)
  • 通过统计字符.前面的字符串,我们得到了所有字符的分类请添加图片描述

  • 这里我们对所有标签进行统计,计算总类别数

all_categories=set()  
# 遍历每个条目,检查是否以 all_categories_label 中的某个标签开头  
for entry in entries:  
    entry = entry.strip()  
    for label in all_categories_label:  
        if entry.startswith(label):  
            all_categories.add(entry)  
            break  
print("all_categories ", all_categories)  
print("Number of all_categories:", len(all_categories))
  • 一共151类,够多的…这么看来使用独热编码是不太现实的,这里我采用标签编码 请添加图片描述
# 进行标签编码  
all_categories_dict = {label: idx for idx, label in enumerate(sorted(all_categories))}  
print(all_categories_dict)
  • 如下顾名思义请添加图片描述

  • 紧接着我们对原数据中的标签进行替换

# 使用正则表达式检查是否为数字  
data_as_lists = []  
for entry in raw_data['detected_technologies']:  
    # 分割字符串  
    split_entries = entry.split('; ')  
    # 只保留数字部分  
    numeric_entries = [int(num) for num in split_entries if re.match(r'^\d+$', num)]  
    data_as_lists.append(numeric_entries)  
raw_data['detected_technologies']=data_as_lists  
  
print(raw_data['detected_technologies'])
  • 原始数据detected_technologies就完成了非字符型的处理的第一步请添加图片描述

  • 仔细观察,上述detected_technologies的数据的每一行是一个长度不定的输入,对于线性模型,在不想增加其输入维度的情况下,这里我采用PCA降维

  • 主成分分析(PCA,Principal Component Analysis)是一种统计方法,它通过正交变换将一组可能相关的变量转换为一组线性不相关的变量,这组变量称为主成分。PCA的主要目的是降维,即在尽可能保留原始数据信息的前提下,减少数据的特征维度。

from sklearn.decomposition import PCA  
import numpy as np  
  
# 找到最长的列表长度  
max_length = max(len(lst) for lst in raw_data['detected_technologies'])  
  
# 使用列表推导式和列表的extend方法来填充列表,确保所有列表长度一致  
padded_technologies = [x + [0]*(max_length - len(x)) for x in raw_data['detected_technologies']]  
  
technologies_array = np.array(padded_technologies)  
# 应用PCA降维到1维  
pca = PCA(n_components=1)  
technologies_pca = pca.fit_transform(technologies_array)  
print(technologies_pca)  
raw_data['detected_technologies']=technologies_pca
  • 降维后我们得到:请添加图片描述

最终检查
  • 最终再确认一下
all_label=['rating','release','peak_players','positive_reviews','negative_reviews','primary_genre','detected_technologies','review_percentage','players_right_now','24_hour_peak','all_time_peak']  
# 再次检查每一列的数据类型  
for col in all_label:  
    print(f"{col}: {raw_data[col].dtype}")  
# 确保所有列都是数值类型  
for col in all_label:  
    if col=='detected_technologies':  
        continue  
    if raw_data[col].dtype == object:  
        # 尝试将非数值类型转换为数值类型  
        raw_data[col] = pd.to_numeric(raw_data[col], errors='coerce')  
  
# 删除任何仍然包含 NaN 值的行  
raw_data = raw_data.dropna(subset=all_label)  
# 再次检查每一列的数据类型  
for col in all_label:  
    print(f"{col}: {raw_data[col].dtype}")  
  
print(raw_data)


模型训练

  • 那么我们正式开始训练模型…
生成训练集/测试集
  • 数据标准化(Standardization):数据标准化旨在调整数据的尺度,使每个特征具有相同的数值范围,从而消除特征之间的量纲影响,确保数据在训练过程中被平等对待
  • 训练集(Training Set)-用于训练模型,即通过迭代优化模型的参数(如权重和偏置)来最小化损失函数。(有时候还会进行训练集验证集的划分)
  • 测试集(Test Set): 用于评估模型在未见过的数据上的表现,即模型的泛化能力。
import torch  
from sklearn.model_selection import train_test_split  
import torch.nn as nn  
from sklearn.preprocessing import StandardScaler  
  
# 选择除了 'rating' 之外的所有列作为特征  
X = raw_data.drop(columns=['rating']).values  
# 标准化
scaler = StandardScaler()  
X = scaler.fit_transform(X)  
y = raw_data['rating'].values  
  
# 划分训练集和测试集  
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)   
# 将数据转换为PyTorch张量  
X_train_tensor = torch.tensor(X_train, dtype=torch.float32)  
y_train_tensor = torch.tensor(y_train, dtype=torch.float32).view(-1, 1)  
X_test_tensor = torch.tensor(X_test, dtype=torch.float32)  
y_test_tensor = torch.tensor(y_test, dtype=torch.float32).view(-1, 1)

定义模型
  • 这边定义了一个简单的线性模型,继承自nn模组,需要重写forward用于前向传播
class LinearRegressionModel(nn.Module):  
    def __init__(self, input_dim):  
        super(LinearRegressionModel, self).__init__()  
        self.linear = nn.Linear(input_dim, 1)  
  
    def forward(self, x):  
        return self.linear(x)  
  
# 实例化模型  
input_dim = X_train.shape[1]  
model = LinearRegressionModel(input_dim)

定义损失函数和优化器
  • 均方误差损失(MSELoss)
    • MSELoss计算预测值和真实值之间差的平方的平均值。
    • 它适用于回归问题,特别是当输出是连续值时。
    • 数学表达式为:MSELoss=1𝑛∑𝑖=1𝑛(𝑦𝑖−𝑦𝑖)2MSELoss=n1​∑i=1n​(yi​−y​i​)2 其中,𝑦𝑖yi​ 是真实值,𝑦𝑖y​i​ 是预测值,𝑛n 是样本数量。
  • 随机梯度下降(SGD)
    • SGD是一种常见的优化算法,用于寻找使损失函数最小化的参数。
    • model.parameters() 是模型中所有可学习参数的迭代器。
    • lr 是学习率(learning rate),它控制了参数更新的步长大小。学习率的选择对模型训练至关重要,太低的学习率可能导致训练缓慢,而太高的学习率可能导致训练过程不稳定或越过最小值。
criterion = nn.MSELoss()  
optimizer = torch.optim.SGD(model.parameters(), lr=0.004)

模型训练
  • 模型的训练很简单,如下,需要记得手动进行梯度清零和设置反向传播优化即可
# 训练模型  
num_epochs = 1000  
for epoch in range(num_epochs):  
    model.train()  
    optimizer.zero_grad()  
    # 前向传播  
    outputs = model(X_train_tensor)  
    # 计算损失  
    loss = criterion(outputs, y_train_tensor)  
    # 反向传播和优化  
    loss.backward()  
    optimizer.step()  
  
    if (epoch+1) % 100 == 0:  
        print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

模型测试
  • 在测试集上进行测试
# 在测试集上验证模型  
model.eval()  
with torch.no_grad():  
    predictions = model(X_test_tensor)  
    test_loss = criterion(predictions, y_test_tensor)  
    print(f'Test Loss: {test_loss.item():}')

获取结果
  • 通过输出权重和偏差,我们可以得到我们的方程
weights = model.linear.weight.data  
bias = model.linear.bias.data  
print(f"Weights: {weights}")  
print(f"Bias: {bias}")
  • 如下我们得到10输入的权重和偏差值请添加图片描述

  • 通过上述步骤,我们观察到损失在逐渐减小最后稳定请添加图片描述


总结和优化-加入非线性

加入ReLu激活函数和BN
  • 可以看得出来,上述线性模型的效果并没用达到很好的效果,这时我们考虑引入非线性
class ComplexModel(nn.Module):  
    def __init__(self, input_size):  
        super(ComplexModel, self).__init__()  
        self.fc = nn.Linear(input_size, 64)  
        self.bn = nn.BatchNorm1d(64)  
        self.relu = nn.ReLU()  
        self.fc2 = nn.Linear(64, 1)  
  
    def forward(self, x):  
        out = self.fc(x)  
        out = self.bn(out)  
        out = self.relu(out)  
        out = self.fc2(out)  
        return out
  • 批量归一化(Batch Normalization,简称BN)是一种用于加速深度网络训练的技术,同时也可以作为一种正则化手段,提高模型的泛化能力。
  • ReLU(Rectified Linear Unit)激活函数是目前深度学习中使用最广泛的激活函数之一
    • 𝑓(𝑥)=max⁡(0,𝑥)f(x)=max(0,x)
    • 非线性:ReLU函数为网络引入了非线性特性,这对于神经网络能够捕捉复杂的数据模式至关重要。
更换优化器Adam
  • 同时我们更换优化器为Adam:Adam算法通过计算梯度的一阶矩估计(即均值)和二阶矩估计(即未中心化的方差)来适应性地调整每个参数的学习率。
optimizer = torch.optim.Adam(model.parameters(), lr=0.4)
增加迭代轮数
  • 我们稍微增加以下迭代轮数
num_epochs = 50000
结果
  • 通过训练结果可以看到,相比于线性模型,非线性的引入使模型的loss下降了,虽然还有一些参数和模型可以调整进一步优化(懒了不想做啦)
    请添加图片描述

总结

  • 从上述我们可以看出,数据预处理实际上远远比模型训练更重要也更关键,往往成败就在数据处理种
  • 非线性的引入会使模型损失大大减小
  • 本文仅提供处理此问题的一种思路,并不是最优,还有参数可以调整,如有错误,请多多指正。

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

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

相关文章

Celery注册装饰器@app.task和@shared_task

注册装饰器 app.task和shared_task是Celery中用于定义任务的两种不同装饰器, 它们之间存在明显的区别.from celery import Celery app Celery(my_app, brokeramqp://guestlocalhost//) app.task def my_task(): # 任务逻辑 passfrom celery import shared_task shared…

数据结构——双链表详解(超详细)

前言&#xff1a; 小编在之前已经写过单链表的创建了&#xff0c;接下来要开始双链表的讲解了&#xff0c;双链表比单链表要复杂一些&#xff0c;不过确实要比单链表更好进行实现&#xff01;下面紧跟小编的步伐&#xff0c;开启今天的双链表之旅&#xff01; 目录 1.概念和结构…

Pixart LED调变开发笔记

Pixart提供基础的鼠标和键盘代码, 开发者可以基于此快速的建置自己的firmware, application, 以下介绍代码中用来控制LED的API函式. 常亮模式 (Always on) 常亮模式, 调用API "pwm_led_set_always_on" 及 channel 来设置对应LED常亮. 闪烁模式 (Flash mode) 闪烁模…

华为OD机试 - 字符串编码校验(Java 2024 D卷 100分)

华为OD机试 2024D卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;D卷C卷A卷B卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华…

Unity3D 物体圆周运动

Unity3D 实现一个 2D 物体沿着圆周进行运动。 物体圆周运动 前段时间在开发一个小游戏时&#xff0c;需要实现火箭沿着一个圆形轨道进行圆周运动。 以前面试的时候也被问到过这类问题&#xff08;如何让一个 2D 物体做圆周运动&#xff09;&#xff0c;所以还是记录一下实现…

ICML 2024 | 矛与盾的较量!北大提出提示无关数据防御保护算法PID

文章链接&#xff1a;https://arxiv.org/pdf/2406.15305 代码地址&#xff1a;https://github.com/PKU-ML/Diffusion-PID-Protection 亮点直击 本文在实证观察中发现&#xff0c;保护阶段和利用阶段之间的提示不匹配可能会削弱当前数据保护算法的有效性。本文深入探讨了利用LDM…

【机器学习第7章——贝叶斯分类器】

机器学习第7章——贝叶斯分类器 7.贝叶斯分类器7.1贝叶斯决策论7.2 朴素贝叶斯分类器条件概率的m估计 7.3 极大似然估计优点基本原理 7.4 贝叶斯网络7.5 半朴素贝叶斯分类器7.6 EM算法7.7 EM算法实现 7.贝叶斯分类器 7.1贝叶斯决策论 一个医疗判断问题 有两个可选的假设&#…

从日常到专业,2024年必备在线翻译神器

现在全球交流越发的简单、频繁&#xff0c;很多时候外语成为了我们汲取新鲜知识的绊脚石。这时候我们就可以借助一些翻译在线的工具来解决这个问题。这次我们一起探索几款我搜集到的翻译工具。 1.福晰在线翻译 链接直通&#xff1a;https://fanyi.pdf365.cn/doc 这个工具支…

本地部署启动PmHub

文章目录 相关配置版本关系拉取代码使用Git clone下载源码 MYSQL配置Nacos配置Windows本地下载也可以Docker部署Nacos持久化配置启动Nacos访问Nacos Redis配置RocketMQ配置新建相应目录rocketmq, 然后在里面新建broker文件夹, 放broker.conf在 rocketmq 新建 data 文件夹&#…

MRAM FRAM在医疗设备场景的应用

便携式超声波扫描仪是一种检测从物体反射的声波并将其转换为实时图像的设备。通常使用配置存储器和图像/报告存储器两种类型的存储器。配置存储器存储来自外部硬件的标识和配置信息&#xff0c;图像/报告存储器存储图像和相应的报告数据。这些存储器即使在突然断电的情况下&…

MySQL介绍和安装与配置

文章目录 MySQL介绍什么是数据库什么是关系型数据库什么是非关系型数据库MySQL概述和历史 MySQL安装和配置在线安装方式MySQL5.7的安裝1.下载yum Repository2.安装yum Repository3.安装mysql5.7的服务3.后续命令 离线安装方式1、卸载已有的MySQL文件2、安装mysql3、后续命令 修…

详细分析Python生成项目依赖包的工具

目录 前言1. pipreqs2. pip freeze3. poetry4. conda5. 总结 前言 在Python项目开发中&#xff0c;管理依赖包是确保项目正常运行的关键步骤 本博客将详细分析几种流行的依赖管理工具&#xff0c;包括 pipreqs、pip freeze、poetry 和 conda&#xff0c;以及它们的使用场景和…

智能硬件创新实训平台-嵌入式、物联网、移动互联网、人工智能实验实训教学平台

智能硬件是继智能手机之后的一个科技概念&#xff0c;通过软硬件结合的方式&#xff0c;对传统设备进行改造&#xff0c;进而让其拥有智能化的功能。智能化之后&#xff0c;硬件具备连接的能力&#xff0c;实现互联网服务的加载&#xff0c;形成“云端”的典型架构&#xff0c;…

黑马Java零基础视频教程精华部分_11_面向对象进阶(3)_抽象类、接口、适配器

《黑马Java零基础视频教程精华部分》系列文章目录 黑马Java零基础视频教程精华部分_1_JDK、JRE、字面量、JAVA运算符 黑马Java零基础视频教程精华部分_2_顺序结构、分支结构、循环结构 黑马Java零基础视频教程精华部分_3_无限循环、跳转控制语句、数组、方法 黑马Java零基础视…

【Linux】文件变身大作战:Linux下的文件重命名艺术

欢迎来到 CILMY23 的博客 &#x1f3c6;本篇主题为&#xff1a;文件变身大作战&#xff1a;Linux下的文件重命名艺术 &#x1f3c6;个人主页&#xff1a;CILMY23-CSDN博客 &#x1f3c6;系列专栏&#xff1a;Python | C | C语言 | 数据结构与算法 | 贪心算法 | Linux | 算法…

OpenGL3.3_C++_Windows(33)

PBR渲染管线 Physically Based Rendering渲染管线 :使用一种更符合物理学规律的算法模型来模拟光线&#xff0c;由于它与物理性质非常接近&#xff0c;可以直接以物理参数为依据来编写表面材质判断是否基于物理的渲染,有三个数学模型&#xff1a;微平面&#xff0c;能量守恒&a…

Go语言实现支持泛型的二分查找算法

二分查找基本原理图&#xff1a; 参考代码&#xff1a; 泛型的定义&#xff1a; package v2024type Integer interface {int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 }type Float interface {float32 | float64 }type Number interface …

无需变更环境变量,一键管理和运行不同JDK版本的jar包

前言 之前公司的项目一直用的都是JDK8&#xff0c;然后前段时间&#xff0c;公司要求以后的新项目必须要用JDK17及以上的版本。但是以前的旧项目还有很多都在维护。 这样子的话&#xff0c;电脑上就至少有两个版本的JDK了。我想在不变动环境变量的情况下&#xff08;环境变量…

可穿戴设备与健康科技:迈向个性化医疗的未来

在数字化和智能化浪潮的推动下&#xff0c;可穿戴设备与健康科技正迅速改变我们的健康管理方式。这些设备不仅使我们能够实时监测健康指标&#xff0c;还为个性化医疗提供了宝贵的数据支持。本文将详细探讨可穿戴设备的作用、健康监测技术的发展、个性化医疗的趋势&#xff0c;…

代码随想录——买卖股票的最佳时机含冷冻期(Leetcode 309)

题目链接 动态规划 class Solution {public int maxProfit(int[] prices) {if(prices.length 0 || prices.length 1){return 0;}// 初始化int[][] dp new int[prices.length 1][2];dp[1][0] -prices[0];for(int i 2; i < prices.length; i){dp[i][0] Math.max(dp[…