游戏AI的创造思路-技术基础-深度学习(5)

继续深度学习技术的探讨,填坑不断,头秃不断~~~~~

 

3.5. 自编码器(AE)

3.5.1. 定义

自编码器(Autoencoder, AE)是一种数据的压缩算法,其中压缩和解压缩函数是数据相关的、有损的、从样本中自动学习的。

它通常用于学习数据的高效编码,在神经网络的形式下,自编码器可以用于降维和特征学习。

在游戏AI中,自编码器可以被用于数据的压缩、特征提取或异常检测等任务。

3.5.2. 形成过程

自编码器起源于神经网络和深度学习的发展。

它是一种特殊的神经网络结构,由两部分组成:

  • 编码器:编码器负责将输入数据压缩成一个较低维度的表示
  • 解码器:解码器则负责从这个低维表示中恢复原始数据。

在训练过程中,自编码器的目标是让输出尽可能接近输入,从而学习到输入数据的有效表示。

3.5.3. 运行原理

3.5.3.1.运行原理及基本框架
  • 编码器:接收原始输入数据,并通过一系列神经网络层将其压缩成一个较低维度的隐藏表示(也称为编码)。这个过程通常涉及线性变换和非线性激活函数的应用。
  • 解码器:接收编码器的输出(即隐藏表示),并通过另一系列神经网络层尝试重构原始输入数据。解码器的目标是尽可能准确地还原原始输入。
  • 训练:通过反向传播算法和优化方法(如梯度下降)来训练自编码器。训练过程中,模型会尝试最小化输入数据和重构数据之间的差异,通常使用均方误差(MSE)等损失函数来衡量这种差异。

在游戏AI中,自编码器(AE)算法并没有特定的数学公式,因为它是一个神经网络结构,由编码器和解码器两部分组成,通过训练来优化模型的参数。然而,我们可以描述其关键组成部分和前向传播的过程。

编码器

编码器部分通常是一个或多个神经网络层,它将输入数据 (x) 映射到一个隐藏表示 (h)。这个过程可以用以下方式表示(尽管这不是一个具体的数学公式):

[ h = f(x; \theta_e) ]

其中,(f) 是编码器的神经网络函数,(\theta_e) 是编码器的参数(如权重和偏置),(x)是输入数据,(h)是得到的隐藏表示。

解码器

解码器部分也是一个或多个神经网络层,它将隐藏表示 (h) 映射回与原始输入数据维度相同的重构数据 (r)。这个过程可以表示为:

[ r = g(h; \theta_d) ]

其中,(g)是解码器的神经网络函数,(\theta_d) 是解码器的参数,(h)是编码器的输出(即隐藏表示),(r) 是重构的数据。

自编码器的训练

自编码器的训练目标是最小化输入数据(x) 和重构数据 (r) 之间的差异。这通常通过优化一个损失函数(如均方误差MSE)来实现:

[ \text{MSE}(x, r) = \frac{1}{n} \sum_{i=1}{n} (x_i - r_i)2 ]

其中,(n)是输入数据的维度,(x_i)(r_i) 分别是输入数据和重构数据在第(i) 维上的值。

需要注意的是,自编码器的具体实现和公式可能会因应用场景、网络结构和优化目标的不同而有所变化。上述描述和公式提供了一个基本的框架和概念。

3.5.3.2. 示例代码

在游戏AI或任何其他领域中,自编码器(Autoencoder, AE)的实现主要涉及深度学习框架,如TensorFlow或PyTorch。

由于C++不是深度学习领域的主流语言,直接使用C++来实现自编码器可能比较复杂,并且通常需要依赖额外的库,如Dlib或Tiny-DNN。(C++也有不擅长的部分哦)

下面,我将给出使用Python和PyTorch实现自编码器的示例代码,并提供C++使用Tiny-DNN库实现自编码器的基本框架。

Python代码

import torch  
import torch.nn as nn  
import torch.optim as optim  
  
# 定义自编码器模型  
class Autoencoder(nn.Module):  
    def __init__(self, input_dim):  
        super(Autoencoder, self).__init__()  
        self.encoder = nn.Sequential(  
            nn.Linear(input_dim, 64),  
            nn.ReLU(),  
            nn.Linear(64, 32)  
        )  
        self.decoder = nn.Sequential(  
            nn.Linear(32, 64),  
            nn.ReLU(),  
            nn.Linear(64, input_dim)  
        )  
  
    def forward(self, x):  
        x = self.encoder(x)  
        x = self.decoder(x)  
        return x  
  
# 设定输入维度  
input_dim = 784  # 例如,使用28x28的MNIST图像  
model = Autoencoder(input_dim)  
  
# 定义损失函数和优化器  
criterion = nn.MSELoss()  
optimizer = optim.Adam(model.parameters())  
  
# 假设我们有一些训练数据X_train  
# X_train = ... (需要加载或生成数据)  
  
# 训练自编码器  
for epoch in range(num_epochs):  
    for data in dataloader:  # 假设dataloader是加载训练数据的DataLoader  
        inputs, _ = data  
        optimizer.zero_grad()  
          
        outputs = model(inputs)  
        loss = criterion(outputs, inputs)  
          
        loss.backward()  
        optimizer.step()  
    print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item()}')

C++代码

在C++中,你可以使用Tiny-DNN这样的库来构建和训练神经网络。以下是一个简化的自编码器实现框架:

#include <tiny_dnn/tiny_dnn.h>  
  
using namespace tiny_dnn;  
using namespace tiny_dnn::layers;  
  
int main() {  
    // 创建网络模型  
    network<sequential> net;  
    net << fc(784, 64)  // 编码器第一层  
        << relu()      // 激活函数  
        << fc(64, 32)  // 编码器第二层  
        << relu()      // 激活函数  
        << fc(32, 64)  // 解码器第一层  
        << relu()      // 激活函数  
        << fc(64, 784); // 解码器第二层  
  
    // 设置优化器和损失函数  
    adam optimizer;  
    mse_loss loss;  
  
    // 假设你已经有了一些训练数据  
    // std::vector<vec_t> train_data; // 输入数据  
    // std::vector<vec_t> train_labels; // 这里的“标签”实际上就是输入数据,因为我们要重构它  
  
    // 训练网络  
    for (int epoch = 0; epoch < num_epochs; epoch++) {  
        for (size_t i = 0; i < train_data.size(); i++) {  
            const auto& in = train_data[i];  
            const auto& target = train_labels[i]; // 在自编码器中,标签就是输入  
            net.train_once(in, target, optimizer, loss);  
        }  
        // 可以添加一些代码来打印损失或验证误差等  
    }  
  
    return 0;  
}

C++代码是一个简化的框架,你需要自己添加数据加载、预处理和后处理逻辑。此外,Tiny-DNN库需要单独安装,并且可能需要根据你的系统环境进行配置。

在实际应用中,你可能还需要调整网络结构、学习率、批次大小等参数以获得最佳性能。此外,对于大规模数据集或复杂模型,通常建议使用Python和成熟的深度学习框架(如PyTorch或TensorFlow),因为这些框架提供了更丰富的功能和更高效的计算性能。

3.5.4. 优缺点

  • 优点

    • 无监督学习:自编码器是一种无监督学习方法,不需要标注数据,因此适用于大量未标注数据的场景。
    • 数据降维和特征提取:自编码器能够学习到输入数据的高效表示,从而实现数据的降维和特征提取。
    • 泛化能力强:由于自编码器是从数据中学习压缩和解压缩函数的,因此它具有一定的泛化能力,可以处理与训练数据类似的新数据。
  • 缺点

    • 对异常数据敏感:在异常检测等任务中,自编码器可能难以处理与训练数据分布差异较大的异常数据。
    • 训练数据需求:为了训练出有效的自编码器模型,通常需要大量的训练数据。此外,在异常识别等特定任务中,训练数据需要为正常数据,以确保模型能够学习到正常的数据分布。

3.5.5. 存在的问题和解决方法

以下讨论2种常见的问题,欢迎评论区探讨更多的问题和解决方法

问题1:自编码器可能无法很好地处理复杂的非线性关系或高度变化的数据分布。

解决方法:可以采用更复杂的神经网络结构(如深度自编码器、卷积自编码器等)或引入其他技术(如正则化、dropout等)来提高模型的复杂度和泛化能力。

问题2:在异常检测任务中,自编码器可能难以准确识别出与训练数据分布差异较大的异常数据。

解决方法:可以结合其他异常检测技术(如基于密度的方法、基于距离的方法等)来提高异常检测的准确性。此外,还可以尝试使用变分自编码器(VAE)等生成模型来生成可能的异常样本,从而增强模型对异常的识别能力。

3.5.6. 应用于游戏AI

自编码器(Autoencoder, AE)在游戏AI中有多种应用场景,以下是一些实际应用场景及其相应的Python代码示例。

3.5.6.1. 实际应用场景
  1. 特征降维与数据压缩
    游戏中的数据往往维度很高,例如玩家的行为数据、游戏场景的状态等。自编码器可以用于将这些高维数据压缩到较低的维度,同时保留重要特征,便于后续的分析和处理。

  2. 异常检测
    通过训练自编码器来重构正常行为的数据,当输入异常数据时,自编码器的重构误差会显著增大,从而可以用于检测游戏中的异常行为,如作弊、外挂等。

  3. 生成模型
    自编码器可以用作生成模型的一部分,生成新的游戏内容,如地图、角色或物品等。这在游戏设计和测试中非常有用。

  4. 预测与决策支持
    利用自编码器提取的特征可以输入到其他机器学习模型中,用于玩家行为预测、游戏结果预测等,从而辅助游戏AI做出更智能的决策。

3.5.6.2. Python代码示例(特征降维)

以下是一个简单的自编码器实现,用于特征降维和数据压缩。

import torch  
import torch.nn as nn  
import torch.optim as optim  
from torch.utils.data import DataLoader, TensorDataset  
  
# 假设我们有一些高维游戏数据  
# X_train 为训练数据,形状为 (n_samples, input_dim)  
# 这里只是一个示例,你需要用实际的游戏数据替换它  
input_dim = 100  # 假设输入维度为100  
n_samples = 1000  # 假设有1000个样本  
X_train = torch.randn(n_samples, input_dim)  # 生成随机数据作为示例  
  
# 定义自编码器模型  
class Autoencoder(nn.Module):  
    def __init__(self, input_dim, encoding_dim):  
        super(Autoencoder, self).__init__()  
        self.encoder = nn.Sequential(  
            nn.Linear(input_dim, encoding_dim),  
            nn.ReLU(True)  
        )  
        self.decoder = nn.Sequential(  
            nn.Linear(encoding_dim, input_dim),  
            nn.Sigmoid()  # 如果输入数据被归一化到[0,1],则使用Sigmoid作为激活函数  
        )  
  
    def forward(self, x):  
        x = self.encoder(x)  
        x = self.decoder(x)  
        return x  
  
# 设置编码维度  
encoding_dim = 32  # 压缩后的维度  
model = Autoencoder(input_dim, encoding_dim)  
criterion = nn.MSELoss()  
optimizer = optim.Adam(model.parameters(), lr=0.001)  
  
# 创建数据加载器  
dataset = TensorDataset(X_train)  
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)  
  
# 训练自编码器  
num_epochs = 100  
for epoch in range(num_epochs):  
    for data in dataloader:  
        inputs, = data  
        optimizer.zero_grad()  
        outputs = model(inputs)  
        loss = criterion(outputs, inputs)  
        loss.backward()  
        optimizer.step()  
    print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item()}')  
  
# 使用训练好的自编码器对游戏数据进行压缩和解压  
compressed = model.encoder(X_train)  
reconstructed = model.decoder(compressed)

上面示例中,我们创建了一个简单的自编码器,它由一个编码器和一个解码器组成。编码器将输入数据从100维压缩到32维,然后解码器尝试从这32维重构原始100维数据。

通过训练自编码器来最小化重构误差,我们可以得到一个能够有效压缩和解压游戏数据的模型。(C++的暂时没搞出来,先不放了)

3.5.6.3. 用于反作弊检测
3.5.6.3.1. 反作弊检测基本方法

在游戏AI中使用自编码器(AE)进行反作弊检测时,基本思路是训练自编码器来重构正常玩家的行为数据。

作弊行为的数据通常与正常行为模式差异较大,因此当输入作弊数据时,自编码器的重构误差会相对较高。通过设定一个阈值,可以检测出可能的作弊行为。

以下是一个简化的Python代码示例,展示了如何使用自编码器进行反作弊检测:

import torch  
import torch.nn as nn  
import torch.optim as optim  
from sklearn.model_selection import train_test_split  
from sklearn.metrics import mean_squared_error  
  
# 假设我们已经有了一组玩家的正常行为数据  
# X 为正常行为数据,形状为 (n_samples, input_dim)  
# 这里使用随机数据作为示例  
input_dim = 50  # 假设输入数据的维度是50  
n_samples = 1000  # 假设有1000个正常行为样本  
X = torch.randn(n_samples, input_dim)  # 生成随机数据作为正常行为数据示例  
  
# 定义自编码器模型  
class Autoencoder(nn.Module):  
    def __init__(self, input_dim):  
        super(Autoencoder, self).__init__()  
        self.encoder = nn.Sequential(  
            nn.Linear(input_dim, 256),  
            nn.ReLU(),  
            nn.Linear(256, 128),  
            nn.ReLU(),  
            nn.Linear(128, 64)  
        )  
        self.decoder = nn.Sequential(  
            nn.Linear(64, 128),  
            nn.ReLU(),  
            nn.Linear(128, 256),  
            nn.ReLU(),  
            nn.Linear(256, input_dim),  
            nn.Sigmoid()  # 假设输入数据已被归一化到[0, 1]  
        )  
  
    def forward(self, x):  
        x = self.encoder(x)  
        x = self.decoder(x)  
        return x  
  
# 初始化模型、损失函数和优化器  
model = Autoencoder(input_dim)  
criterion = nn.MSELoss()  
optimizer = optim.Adam(model.parameters(), lr=0.001)  
  
# 分割数据集为训练集和测试集  
X_train, X_test = train_test_split(X, test_size=0.2, random_state=42)  
  
# 训练自编码器  
num_epochs = 100  
for epoch in range(num_epochs):  
    optimizer.zero_grad()  
    outputs = model(X_train)  
    loss = criterion(outputs, X_train)  
    loss.backward()  
    optimizer.step()  
    if (epoch + 1) % 10 == 0:  
        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item()}')  
  
# 在测试集上评估自编码器  
X_test_reconstructed = model(X_test)  
test_mse = mean_squared_error(X_test.detach().numpy(), X_test_reconstructed.detach().numpy())  
print(f'Test MSE: {test_mse}')  
  
# 设定作弊检测的阈值  
threshold = test_mse * 2  # 这里的阈值是示例值,实际应用中需要根据情况调整  
  
# 假设我们有一些待检测的数据(可能是作弊数据)  
# X_check 形状为 (n_check_samples, input_dim)  
X_check = torch.randn(10, input_dim)  # 使用随机数据作为待检测数据的示例  
X_check_reconstructed = model(X_check)  
check_mse = nn.MSELoss()(X_check, X_check_reconstructed).item()  
  
# 判断是否存在作弊行为  
if check_mse > threshold: 
    print("可能的作弊行为被检测到!")  
else:  
    print("未检测到作弊行为。")

在这个示例中,我们首先定义了一个自编码器模型,并使用正常行为数据进行训练。

训练完成后,我们在测试集上评估模型的性能,并根据测试集上的重构误差设定一个阈值。

最后,我们使用这个阈值来检测待检查数据中是否存在作弊行为。

如果重构误差超过了设定的阈值,则可能表示存在作弊行为。

3.5.6.3.2. 阈值的设定

自编码器(AE)算法在游戏AI中用于反作弊检测时,测试集上的重构误差设定阈值的方法至关重要。

  • 阈值设定方法
  • 基于统计分布:分析测试集上的重构误差分布,可以选择一个合适的阈值,如平均值加上若干倍的标准差,来区分正常行为与异常行为。
  • 基于业务需求:根据实际业务需求和安全容忍度,设定一个可接受的最高重构误差作为阈值。
  • 交叉验证:通过交叉验证的方式,在不同的测试集上评估自编码器的性能,并根据多次实验的结果来确定一个稳定的阈值。
  • 专家判断:结合领域专家的知识和经验,对重构误差的阈值进行设定和调整。(通常,游戏公司或游戏开发者会招募专业玩家或者头部玩家进行内测和公测,同时也会长期招募反作弊专业玩家对训练数据进行标注,标注后的数据用于训练和调整阈值。当然,游戏公司或游戏开发者也会故意创造作弊器和作弊方法,用来帮助算法进行反作弊训练。~~~~~~~~小声说,有些不太OK的具有编程能力的玩家也会监守自盗偷偷的做一些不法的东东,鄙视
  • 阈值调整与优化
  • 在实际应用中,根据反馈和效果不断调整阈值,以达到最佳的反作弊检测效果。
  • 可以考虑使用动态阈值,即根据实时数据或周期性评估来调整阈值。
  • 作弊行为检测
  • 对于新的游戏行为数据,使用训练好的自编码器进行重构,并计算重构误差。
  • 如果重构误差超过设定的阈值,则将该行为标记为可能的作弊行为,进行进一步的审查和处理。

本篇先聊到这里,后面慢慢填坑接着聊 

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

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

相关文章

打开自动联网,设置静态IP

TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic # 设置为static DEFROUTEyes IPV4_FAILURE_FATALno IPV6INITyes IPV6_AUTOCONFyes IPV6_DEFROUTEyes IPV6_FAILURE_FATALno IPV6_ADDR_GEN_MODEstable-privacy NAMEens18 UUIDce34dd13-05cb-4d6d-95e4-252355b1…

“Cannot resolve ch.qos.logback:logback-classic:1.2.3”问题解决办法

当我们添加依赖配置时&#xff0c;通常会遇见如下错误&#xff1a; 这个问题是由于项目中使用了 logback-classic 版本1.2.3&#xff0c;但是无法从当前所配置的仓库中解析到这个特定的版本。可以尝试检查依赖配置&#xff0c;确保指定的仓库中包含了 logback-classic 版本1.2.…

IIC学习笔记

目录 #I2C涉及相关知识 #I2C相关介绍 欢迎指正&#xff0c;希望对你&#xff0c;有所帮助&#xff01;&#xff01;&#xff01; 个人学习笔记&#xff0c;参考文献&#xff0c;链接最后&#xff01;&#xff01;&#xff01; #I2C涉及相关知识 SDA串行数据线&#xff1a; Ser…

以创新赋能引领鸿蒙应用开发,凡泰极客亮相华为HDC2024

6月21日至23日&#xff0c;华为开发者大会2024在松山湖举行。大会现场&#xff0c;华为发布了HarmonyOS、盘古大模型等方面最新进展。国内外众多企业齐聚一堂&#xff0c;共迎新商机、共创新技术、共享新体验。 凡泰极客作为鸿蒙生态的重要战略合作伙伴&#xff0c;同时也是鸿…

Gobject tutorial 十

参考&#xff1a;GLib – 2.0: The Main Event Loop The Main Event Loop 主事件循环管理所有可用的事件源&#xff0c;事件可以是各种类型、各种数量的。比如说文件描述符(普通文件、管道以及套接字)和超时。 新类型的事件源可以通过函数g_source_attach来添加。为了使多个…

硬件开发笔记(十九):Altium Designer 21软件介绍和安装过程

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/139706278 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

java运维交接项目逆向工程

​ 背景 有承接过Java项目运维的团队估计都处理过的一件事情&#xff0c;就是同步生产代码跟本地代码&#xff0c;条件再差些甚至要直接基于生产部署包逆向本地源码工程。而哪怕是原运维团队交接了源码&#xff0c;往往也会历史久远的原因&#xff0c;给了一份不太可靠的源码…

数字心动+华为运动健康服务 使用体验指导

一、应用介绍 “数字心动”是一个体育生态平台APP&#xff0c;践行“体育大健康娱乐数字营销”模式&#xff0c;打造深度融合体育平台。APP集跑步运动记录、赛事活动报名、成绩/大众等级证书查询等多功能于一体&#xff0c;采取“线上线下”模式&#xff0c;结合协会、行业、品…

飞睿uwb芯片厂商,室内轨迹跟踪定位模块,人员、车辆和物资定位测距数据传输

在科技日新月异的今天&#xff0c;定位技术已经渗透到了我们生活的方方面面。从智能手机到自动驾驶汽车&#xff0c;再到智能仓储管理&#xff0c;定位技术都发挥着不可或缺的作用。然而&#xff0c;传统的GPS定位技术在室内环境中往往面临信号受阻、精度不足等问题。此时&…

echarts旭日图添加图例

研究了好久终于弄出来了&#xff0c;上代码&#xff0c;代码可以直接copy到官网去试&#xff0c;看看效果。 var data [{name: Grandpa,children: [{name: Uncle Leo,value: 15,children: [{name: Cousin Jack,value: 2},{name: Cousin Mary,value: 5,children: [{name: Jacks…

uniapp(全端兼容) - 最新详细实现刻度尺组件效果,uni-app实现尺子打分及手指拖动刻度尺打分评分功能,可左右滑动刻度尺改变数值、带刻度尺滑块功能、

效果图 在uniapp微信小程序/手机h5网页网站/安卓app/苹果app/支付宝小程序/nvue等(全平台完美兼容)开发中,实现uniApp各端都兼容的 “刻度尺(横格尺 | 尺子)” 手势左右两侧拖动、手指滑动刻度尺功能,水平刻度尺,支持自定义尺子颜色、大小、刻度、滑动时的步进值、最大…

大模型微调方法总结

一 LoRA&#xff1a; 1 低(秩)rank 自适应微调方法 2 背景及本质   大模型的参数更新耗费大量现存为此&#xff0c; 微软的研究者们于2021年通过论文《LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS》提出了低秩适应LoRA 它冻结了预训练的模型权重&#xff0c;并将可…

c语言常用易错记录

c语言常用易错记录 文章目录 c语言常用易错记录1.for循环 1.for循环 示例&#xff1a; #include <stdio.h>int main() {int i;for (i 0; i < 10; i) {printf("%d\n", i);}return 0; }执行顺序 备注&#xff1a;此图来源于b站鹏哥C语言视频课截图&#xf…

暴雨虐长沙,生灵受煎熬

今天&#xff0c;“湖南长沙市遭遇强降雨,一小时的降雨量足够注满54个西湖”这消息&#xff0c;终于登上互联网社交平台热搜榜。 截图&#xff1a;来源社交网站 综合多家媒体消息概述如下。 昨&#xff08;24日&#xff09;天&#xff0c;湖南长沙市遭遇强降雨&#xff0c;一…

odoo17 tree视图添加按钮

需求描述 点击下图中tree视图上的同步退货单按钮&#xff0c;弹出相应的form视图进行退货单同步&#xff0c;然后点击同步按钮调用后端python代码处理。 实现步骤 主要文件目录结构 js文件的创建 /** odoo-module **/ import { registry } from "web/core/registry&quo…

SM7055-18明微电子12/18V低成本 BUCK,BUCK-BOOST电源芯片方案

深圳市三佛科技有限公司分享SM7055-18 是采用电流模式 PWM 控制方式的功率开关芯片&#xff0c;集成高 压启动电路和高压功率管&#xff0c;可实现低成本、高性价比开关电源系统解决 方案。芯片应用于 BUCK、BUCK-BOOST 系统方案&#xff0c;支持 18V 输出电压&#xff0c;很 方…

vxe-list做列表虚拟滚动时,底部间距的优化

已知vxe-list在数据超出一定行时会自动启用纵向虚拟滚动配置&#xff0c;默认效果如图&#xff1a; 但是在滚动中我们发现有时列表底部间距不一致&#xff0c;而且会出现在感官上底部空白过多的情况&#xff1a; 这时候我们想让列表恰好显示完全应该怎么做呢&#xff0c;查看官…

2024年6月24日 (周一) 叶子游戏新闻

图吧工具箱: 全名图拉丁吧硬件检测工具箱,是开源、免费、绿色、纯净的硬件检测工具合集,专为图钉及所有DIY爱好者制作,包含常用硬件测试和检测工具,月工JS必备! 土豆录屏: 免费、无录制时长限制、无水印的录屏软件 高手在民间 粉丝玩家打造精美《黄金树幽影》巨大插画虽然不是专…

Qt项目天气预报(7) - 更新一周天气(数据和图像)

更新七天JSON信息 调用API : 专业七日天气v9:/ /同前文需要先 易客云天气API免费天气API接口|天气预报接口|全球天气API接口|气象预警|空气质量 (tianqiapi.com)注册生成自己的api http://v1.yiketianqi.com/api?unescape1&versionv9&appid65521391&appsecretDv2e…

如何做好药店布局,实现客流回归?

随着我国医药市场的不断发展&#xff0c;药店数量逐年增多&#xff0c;竞争愈发激烈。在这种背景下&#xff0c;如何做好药店布局&#xff0c;吸引客流回归&#xff0c;成为药店经营者关注的核心问题。 要想实现店铺形象美观大方、有效引导客流动向&#xff0c;增加顾客在店内…