【Time Series】LSTM代码实战

news2024/11/23 18:45:13

一、简介

        还是那句话,"时间序列+金融"是一个很有"钱"景的话题,还是想尝试采用Stock+时间序列预测任务+DeepLearning。本文提供了LSTM预测股票的源代码。

二、算法原理

        长短期记忆网络(LSTM)是一种特殊的循环神经网络(RNN),用于处理和预测序列数据的时间依赖性。LSTM 能够学习长期依赖信息,解决了传统 RNN 在长序列训练过程中遇到的梯度消失或梯度爆炸问题。

        LSTM 通过引入三个门(遗忘门、输入门和输出门)和一个单元状态来解决长期依赖问题。这些门控制着信息的保留、遗忘与更新,使得 LSTM 能够有效地保留长期记忆并过滤掉不相关信息。单元状态在时间序列中穿行,允许信息的长期流动。门控制机制让 LSTM 有能力学习决定何时更新记忆、何时重置记忆以及何时让记忆通过无损坏地。

        1. 输入门(Input Gate)

        LSTM的每个单元接收三个输入:当前时刻的输入数据(x_t),上一个时刻的隐藏状态(h_{t-1}),以及上一个时刻的单元状态(C_{t-1})。输入门用来决定哪些新的信息将被添加到单元状态。

  • 忘记门层(Forget Gate Layer):首先,用一个sigmoid函数来决定从单元状态中丢弃什么信息。这个步骤通过 (f_t = \sigma(W_f \cdot [h_{t-1}, x_t] + b_f))计算得出,其中(W_f)是权重矩阵,(b_f)是偏置项。

  • 输入门层(Input Gate Layer):接着,使用另一个sigmoid层来决定将哪些新信息更新到单元状态中,(i_t = \sigma(W_i \cdot [h_{t-1}, x_t] + b_i))。同时,一个tanh层会创建一个新的候选值向量(\tilde{C}t = \tanh(W_C \cdot [h{t-1}, x_t] + b_C)),与(i_t)相乘,以决定更新什么值。

    2. 更新单元状态(Update Cell State)

    单元状态(C_{t-1})通过忘记旧信息(乘以(f_t))和增加新信息((i_t * \tilde{C}_t))来更新。

  • 首先,之前的单元状态通过与忘记门的输出相乘,丢弃我们决定忘记的信息。
  • 接着,将输入门的输出与新的候选值相乘,来增加新的信息。
  • 单元状态的更新公式为(C_t = f_t * C_{t-1} + i_t * \tilde{C}_t),这使得网络能够在每个时刻自行调节信息的存储。

    3. 输出门(Output Gate)和隐藏状态

        最后,决定输出的部分信息基于单元状态,但首先会过一个激活函数(通常是tanh)来确保数据值位于-1到1之间,然后乘以输出门(通过sigmoid层决定哪一部分的单元状态将输出)的输出。

  • 输出门层(Output Gate Layer)(o_t = \sigma(W_o \cdot [h_{t-1}, x_t] + b_o)),决定了单元状态中哪些信息将用于输出。

  • 计算当前时刻隐藏状态((h_t))(h_t = o_t * \tanh(C_t)),这里,我们将单元状态通过tanh激活函数处理(为了规范化),并且将其与输出门的输出相乘,来决定最终的输出是什么。

三、代码

        运行代码时的注意事项:按照配置项创建好对应的文件夹,准备好数据,数据来源我的上一篇blog《【Time Series】获取股票数据代码实战》可以找到。

import os
import random
from tqdm import tqdm
import joblib
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error,mean_absolute_error

#配置项
class configs():
    def __init__(self):
        # Data
        self.data_input_path = r'../data/input'
        self.data_output_path = r'../data/output'
        self.save_model_dir = '../data/output'

        self.data_inputfile_name = r'五粮液.xlsx'
        self.data_BaseTrue_infer_output_name = r'基于真实数据推理结果.xlsx'
        self.data_BaseSelf_infer_output_name = r'基于自回归推理结果.xlsx'

        self.data_split_ratio = "0.8#0.1#0.1"


        self.model_name = 'LSTM'
        self.seed = 2024

        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.epoch = 50
        self.train_batch_size = 16
        self.in_seq_embeddings = 1 #输入的特征维度
        self.out_seq_embeddings = 1 #输出的特征维度
        self.in_seq_length = 5 #输入的时间窗口
        self.out_seq_length = 1 #输出的时间窗口

        self.hidden_features = 16  # 隐层数量

        self.learning_rate = 0.001
        self.dropout = 0.5

        self.istrain = True
        self.istest = True
        self.BaseTrue_infer = True
        self.BaseSelf_infer = True
        self.num_predictions = 800

cfg = configs()

def seed_everything(seed=2024):
    random.seed(seed)
    os.environ['PYTHONHASHSEED']=str(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)

seed_everything(seed = cfg.seed)

#数据
class Define_Data():
    def __init__(self,task_type='train'):
        self.scaler = MinMaxScaler()
        self.df = pd.DataFrame()
        self.task_type = task_type

    #用于更新输入数据,设定选用从m行到n行的数据进行训/测,use_lines = "[m,n]"/"-1"
    def refresh_df_data(self,tmp_df_path,tmp_df_sheet_name,use_lines):
        self.df = pd.read_excel(tmp_df_path, sheet_name=tmp_df_sheet_name)
        if use_lines != "-1":
            use_lines = eval(use_lines)
            assert use_lines[0] <= use_lines[1]
            self.df = self.df.iloc[use_lines[0]:use_lines[1],:]

    #创建时间窗口数据,in_seq_length 为输入时间窗口,out_seq_length 为输出时间窗口
    def create_inout_sequences(self,input_data, in_seq_length, out_seq_length):
        inout_seq = []
        L = len(input_data)
        for i in range(L - in_seq_length):
            # 这里确保每个序列将是 tw x cfg.out_seq_length 的大小,这对应于 (seq_len, input_size)
            train_seq = input_data[i:i + in_seq_length][..., np.newaxis]  # np.newaxis 增加一个维度
            train_label = input_data[i + in_seq_length:i + in_seq_length + out_seq_length, np.newaxis]
            inout_seq.append((train_seq, train_label))
        return inout_seq

    #将时序数据转换为模型的输入形式
    def _collate_fn(self,batch):
        # Each element in 'batch' is a tuple (sequence, label)
        # We stack the sequences and labels separately to produce two tensors
        seqs, labels = zip(*batch)

        # Now we reshape these tensors to have size (seq_len, batch_size, input_size)
        seq_tensor = torch.stack(seqs).transpose(0, 1)

        # For labels, it might be just a single dimension outputs,
        # so we only need to stack and then add an extra dimension if necessary
        label_tensor = torch.stack(labels).transpose(0, 1)
        if len(label_tensor.shape) == 2:
            label_tensor = label_tensor.unsqueeze(-1)  # Add input_size dimension
        return seq_tensor, label_tensor

    #将表格数据构建成tensor格式
    def get_tensor_data(self):
        #缩放
        self.df['new_close'] = self.scaler.fit_transform(self.df[['close']])

        inout_seq = self.create_inout_sequences(self.df['new_close'].values,
                                                in_seq_length=cfg.in_seq_length,
                                                out_seq_length=cfg.out_seq_length)

        if self.task_type == 'train':
            # 准备训练数据
            X = torch.FloatTensor(np.array([s[0] for s in inout_seq]))
            y = torch.FloatTensor(np.array([s[1] for s in inout_seq]))

            # 划分训练集和测试集
            data_split_ratio = cfg.data_split_ratio
            data_split_ratio = [float(d) for d in data_split_ratio.split('#')]
            train_size = int(len(inout_seq) * data_split_ratio[0])
            val_size = int(len(inout_seq) * (data_split_ratio[0]+data_split_ratio[1])) - train_size
            test_size = int(len(inout_seq)) - train_size - val_size
            train_X, train_y = X[:train_size], y[:train_size]
            val_X, val_y = X[train_size:val_size], y[train_size:val_size]
            test_X, test_y = X[val_size:], y[val_size:]

            # 注意下面的 batch_first=False
            batch_size = cfg.train_batch_size
            train_data = TensorDataset(train_X, train_y)
            train_loader = DataLoader(train_data, shuffle=True, batch_size=batch_size, drop_last=True,
                                      collate_fn=self._collate_fn)
            val_data = TensorDataset(val_X, val_y)
            val_loader = DataLoader(val_data, shuffle=False, batch_size=1, collate_fn=self._collate_fn)
            test_data = TensorDataset(test_X, test_y)
            test_loader = DataLoader(test_data, shuffle=False, batch_size=1, collate_fn=self._collate_fn)

            return train_loader,val_loader, test_loader, self.scaler
        elif self.task_type == 'test' or 'infer':
            # 准备测试数据
            X = torch.FloatTensor(np.array([s[0] for s in inout_seq]))
            y = torch.FloatTensor(np.array([s[1] for s in inout_seq]))

            test_data = TensorDataset(X, y)
            test_loader = DataLoader(test_data, shuffle=False, batch_size=1, collate_fn=self._collate_fn)

            return test_loader, self.scaler




# 模型定义
#################网络结构#################
class LSTM(nn.Module):
    def __init__(self, input_size=10, hidden_layer_size=20, output_size=1):
        super(LSTM,self).__init__()

        self.hidden_layer_size = hidden_layer_size
        self.lstm = nn.LSTM(input_size, hidden_layer_size)
        self.linear = nn.Linear(hidden_layer_size, output_size)
        self.batch_size = cfg.train_batch_size
        self.hidden_cell = (torch.zeros(1, self.batch_size, self.hidden_layer_size),
                            torch.zeros(1, self.batch_size, self.hidden_layer_size))

    def forward(self, input_seq):
        lstm_out, self.hidden_cell = self.lstm(input_seq, self.hidden_cell)
        predictions = self.linear(lstm_out.view(len(input_seq) * self.batch_size, -1))
        # Only return the predictions from the last timestep
        return predictions.view(len(input_seq), self.batch_size, -1)[-1]

    def reset_hidden_state(self,tmp_batch_size):
        ###该函数
        self.batch_size = tmp_batch_size
        self.hidden_cell = (torch.zeros(1, tmp_batch_size, self.hidden_layer_size),
                            torch.zeros(1, tmp_batch_size, self.hidden_layer_size))

class my_run():
    def train(self):
        Dataset = Define_Data(task_type='train')
        Dataset.refresh_df_data(tmp_df_path=os.path.join(cfg.data_input_path,cfg.data_inputfile_name),
                                tmp_df_sheet_name='数据处理',
                                use_lines='[0,3000]')
        train_loader,val_loader,test_loader,scaler = Dataset.get_tensor_data()
        model = LSTM(cfg.in_seq_embeddings, cfg.hidden_features,cfg.out_seq_length).to(cfg.device)
        # 定义损失函数和优化器
        loss_function = nn.MSELoss()
        optimizer = torch.optim.Adam(model.parameters(), lr=cfg.learning_rate, weight_decay=5e-4)

        model.train()
        loss_train_all = []
        for epoch in tqdm(range(cfg.epoch)):
            #训练集
            predictions = []
            test_labels = []
            for seq, labels in train_loader:
                optimizer.zero_grad()
                model.reset_hidden_state(tmp_batch_size=cfg.train_batch_size)  # 重置LSTM隐藏状态
                y_pred = model(seq)
                loss_train = loss_function(torch.squeeze(y_pred), torch.squeeze(labels))
                loss_train_all.append(loss_train.item())
                loss_train.backward()
                optimizer.step()
                predictions.append(y_pred.squeeze().detach().numpy())  # Squeeze to remove extra dimensions
                test_labels.append(labels.squeeze().detach().numpy())
            train_mse,train_mae = self.timeseries_metrics(predictions=predictions,
                                                  test_labels=test_labels,
                                                  scaler=Dataset.scaler)
            #测试val集
            predictions = []
            test_labels = []
            with torch.no_grad():
                for seq, labels in test_loader:
                    model.reset_hidden_state(tmp_batch_size=1)
                    y_test_pred = model(seq)
                    # 保存预测和真实标签
                    predictions.append(y_test_pred.squeeze().detach().numpy())  # Squeeze to remove extra dimensions
                    test_labels.append(labels.squeeze().detach().numpy())
            val_mse,val_mae = self.timeseries_metrics(predictions=predictions,
                                                test_labels=test_labels,
                                                scaler=Dataset.scaler)
            print('Epoch: {:04d}'.format(epoch + 1),
                  'loss_train: {:.4f}'.format(np.mean(loss_train_all)),
                  'mae_train: {:.8f}'.format(train_mae),
                  'mae_val: {:.8f}'.format(val_mae)
                  )

        torch.save(model, os.path.join(cfg.save_model_dir, 'latest.pth'))  # 模型保存
        joblib.dump(Dataset.scaler,os.path.join(cfg.save_model_dir, 'latest_scaler.save')) # 数据缩放比例保存

    def test(self):
        #Create Test Processing
        Dataset = Define_Data(task_type='test')
        Dataset.refresh_df_data(tmp_df_path=os.path.join(cfg.data_input_path,cfg.data_inputfile_name),
                                tmp_df_sheet_name='数据处理',
                                use_lines='[2995,4000]')
        Dataset.scaler = joblib.load(os.path.join(cfg.save_model_dir, 'latest_scaler.save'))
        test_loader,_ = Dataset.get_tensor_data()

        model_path = os.path.join(cfg.save_model_dir, 'latest.pth')
        model = torch.load(model_path, map_location=torch.device(cfg.device))
        model.eval()
        params = sum(p.numel() for p in model.parameters())
        predictions = []
        test_labels = []
        with torch.no_grad():
            for seq, labels in test_loader:
                model.reset_hidden_state(tmp_batch_size=1)
                y_test_pred = model(seq)
                # 保存预测和真实标签
                predictions.append(y_test_pred.squeeze().detach().numpy())  # Squeeze to remove extra dimensions
                test_labels.append(labels.squeeze().detach().numpy())
        _, val_mae = self.timeseries_metrics(predictions=predictions,
                                             test_labels=test_labels,
                                             scaler=Dataset.scaler)
        print('Test set results:',
              'mae_val: {:.8f}'.format(val_mae),
              'params={:.4f}k'.format(params / 1024)
              )

    def BaseTrue_infer(self):
        # Create BaseTrue Infer Processing
        Dataset = Define_Data(task_type='infer')
        Dataset.refresh_df_data(tmp_df_path=os.path.join(cfg.data_input_path, cfg.data_inputfile_name),
                                tmp_df_sheet_name='数据处理',
                                use_lines='[4000,4870]')
        Dataset.scaler = joblib.load(os.path.join(cfg.save_model_dir, 'latest_scaler.save'))
        test_loader, _ = Dataset.get_tensor_data()

        model_path = os.path.join(cfg.save_model_dir, 'latest.pth')
        model = torch.load(model_path, map_location=torch.device(cfg.device))
        model.eval()
        params = sum(p.numel() for p in model.parameters())
        predictions = [] #模型推理值
        test_labels = [] #标签值,可以没有
        with torch.no_grad():
            for seq, labels in test_loader:
                model.reset_hidden_state(tmp_batch_size=1)
                y_test_pred = model(seq)
                # 保存预测和真实标签
                predictions.append(y_test_pred.squeeze().detach().numpy())  # Squeeze to remove extra dimensions
                test_labels.append(labels.squeeze().detach().numpy())

        predictions = np.array(predictions)
        test_labels = np.array(test_labels)
        predictions_rescaled = Dataset.scaler.inverse_transform(predictions.reshape(-1, 1)).flatten()
        test_labels_rescaled = Dataset.scaler.inverse_transform(test_labels.reshape(-1, 1)).flatten()

        pd.DataFrame({'test_labels':test_labels_rescaled,'模型推理值':predictions_rescaled}).to_excel(os.path.join(cfg.save_model_dir,cfg.data_BaseTrue_infer_output_name),index=False)
        print('Infer Ok')

    def BaseSelf_infer(self):
        # Create BaseSelf Infer Processing
        Dataset = Define_Data(task_type='infer')
        Dataset.refresh_df_data(tmp_df_path=os.path.join(cfg.data_input_path, cfg.data_inputfile_name),
                                tmp_df_sheet_name='数据处理',
                                use_lines='[4000,4870]')
        Dataset.scaler = joblib.load(os.path.join(cfg.save_model_dir, 'latest_scaler.save'))
        test_loader, _ = Dataset.get_tensor_data()
        initial_input, labels = next(iter(test_loader))

        model_path = os.path.join(cfg.save_model_dir, 'latest.pth')
        model = torch.load(model_path, map_location=torch.device(cfg.device))
        model.eval()
        params = sum(p.numel() for p in model.parameters())
        predictions = [] #模型推理值
        with torch.no_grad():
            for _ in range(cfg.num_predictions):

                model.reset_hidden_state(tmp_batch_size=1)
                y_test_pred = model(initial_input)
                # 将预测结果转换为适合再次输入模型的形式
                next_input = torch.cat((initial_input[1:, ...], y_test_pred.unsqueeze(-1)), dim=0)
                initial_input = next_input
                # 保存预测和真实标签
                predictions.append(y_test_pred.squeeze().item())  # Squeeze to remove extra dimensions

        predictions_rescaled = Dataset.scaler.inverse_transform(np.array(predictions).reshape(-1, 1)).flatten()

        pd.DataFrame({'模型推理值': predictions_rescaled}).to_excel(os.path.join(cfg.save_model_dir,cfg.data_BaseSelf_infer_output_name), index=False)
        print('Infer Ok')

    def timeseries_metrics(self,predictions,test_labels,scaler):
        # 反向缩放预测和标签值
        predictions = np.array(predictions)
        test_labels = np.array(test_labels)

        # 此处假设predictions和test_labels是一维数组,如果不是,你可能需要调整reshape的参数
        predictions_rescaled = scaler.inverse_transform(predictions.reshape(-1, 1)).flatten()
        test_labels_rescaled = scaler.inverse_transform(test_labels.reshape(-1, 1)).flatten()

        # 计算MSE和MAE
        mse = mean_squared_error(test_labels_rescaled, predictions_rescaled)
        mae = mean_absolute_error(test_labels_rescaled, predictions_rescaled)
        # print(f"Test MSE on original scale: {mse}")
        # print(f"Test MAE on original scale: {mae}")
        return mse,mae

if __name__ == '__main__':
    myrun = my_run()
    if cfg.istrain == True:
        myrun.train()
    if cfg.istest == True:
        myrun.test()
    if cfg.BaseTrue_infer == True:
        myrun.BaseTrue_infer()
    if cfg.BaseSelf_infer == True:
        myrun.BaseSelf_infer()

 四、结果展示

   本文代码,配置了两种预测模式,第一种,BaseTrue_infer:根据真实数据预测下一个点,然后循环用的真实数据;第二种,BaseSelf_infer:根据预测数据自回归预测下一个点,然后循环用的预测数据。实际用的一般都是第二种才有实用价值,当然本文时序预测的训练模式没有采用长距离自动纠偏的trick,所以第二种预测就直接坍塌了。后续可以研究探讨长时间预测如何进行。下面贴上在"五粮液"股价收盘价上的实验结果。

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

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

相关文章

瑞士0.5米高程地形瓦片数据介绍

一、背景 瑞士是位于中欧的一个国家&#xff0c;以其美丽的自然风光、高质量的生活和强大的金融体系而闻名&#xff0c;其位于欧洲中部&#xff0c;四面环山&#xff0c;与德国、法国、意大利、奥地利和列支敦士登等国家接壤。瑞士境内有许多湖泊和阿尔卑斯山脉的一部分。瑞士…

Redis常见数据类型[上]

目录 前言&#xff1a; 基本全局命令 KEYS EXISTS DEL EXPIRE TTL TYPE 数据结构和内部编码 内部编码&#xff1a; 单线程架构 引出单线程模型&#xff1a; 为什么单线程还这么快&#xff1f; String字符串 字符串数据类型&#xff1a; 常见命令&#xff1a; S…

uniapp微信小程序-分包

一、为什么要分包 微信小程序每个分包的大小是2M&#xff0c;总体积一共不能超过20M,当然你也可以提升启动速度&#xff0c;降低首次加载时间&#xff0c;模块化开发&#xff0c;按需加载&#xff0c;提高性能。 二、分包步骤 1.首先在 mainfest.json mp-weixin添加以下代码&a…

用Python库pillow处理图像

入门知识 颜色。如果你有使用颜料画画的经历&#xff0c;那么一定知道混合红、黄、蓝三种颜料可以得到其他的颜色&#xff0c;事实上这三种颜色就是美术中的三原色&#xff0c;它们是不能再分解的基本颜色。在计算机中&#xff0c;我们可以将红、绿、蓝三种色光以不同的比例叠加…

Git快速入门+常用指令

Git创建本地仓库 1、创建一个文件夹&#xff0c;右键选择Git Bash Here 2、选择下列其中一个方法 方法一&#xff1a;创建初始化仓库 git init 方法二&#xff1a;克隆远程仓库 git clone [url] IDEA集成Git 按步骤操作会自动创建本地仓库 上传步骤 1、提交 2、输入上…

客户端熔断器基于golang Grpc具体实现

目录 前言 一、什么是Google SRE 二、Google SRE 熔断器的工作流程&#xff1a; 三、Google SRE GRPC 代码实现 四、测试用例 大家可以关注个人博客&#xff1a;xingxing – Web Developer from Somewhere 有关后端问题探讨 前言 当某个用户超过资源配额时&#xff0c…

EPSON RC 机器人-第一个程序

创建项目 有机械人且用USB线连接好。可以USB。没有真机的选择 C4 Sample 可以运行程序。 否刚会提示【不能连接到控制器&#xff0c;未安装USB驱动器】 代码 按F5打开运行窗口 再点【开始】 点 【是】&#xff0c;查看运行结果

分布式ID介绍实现方案总结

分布式 ID 介绍 什么是 ID&#xff1f; 日常开发中&#xff0c;我们需要对系统中的各种数据使用 ID 唯一表示&#xff0c;比如用户 ID 对应且仅对应一个人&#xff0c;商品 ID 对应且仅对应一件商品&#xff0c;订单 ID 对应且仅对应一个订单。 我们现实生活中也有各种 ID&…

[BUUCTF]-Web:[GXYCTF2019]Ping Ping Ping解析

先看网页 按照正常流程&#xff0c;先ipls&#xff0c;可以看到flag 但是不能直接得flag&#xff0c;他应该是过滤掉了一点东西。 这里考虑过滤掉了空格 空格过滤绕过&#xff1a; %20 %09 ${IFS} $IFS$1 {IFS} < <> 空格绕过之后发现还是打不开flag.php,但报错语句…

【Vue】二、Vue 组件展示控制的优雅解决方案

vue项目中展示的组件&#xff0c;我平常都是通过v-show进行展示控制&#xff0c;类似这样 通常情况下&#xff0c;一个正常展示组件的流程&#xff0c;是通过前端用户点击触发函数&#xff0c;在函数中对data数据进行操作&#xff0c;从而展示不同的页面 showWork: false, sho…

ansible 常用命令 基本说明 个人备忘

linux下设置一台机器的名称为ansible hostnamectl set-hostname ansible //设置一台机器的名称为master-01 hostnamectl set-hostname master-01 hostnamectl set-hostname master-02 hostnamectl set-hostname node01 hostnamectl set-hostname node02 hostnamectl set-…

仅2个月就实现37家门店自动化覆盖100%的集团公司,到底做对了啥?

在各种AI技术不断被广泛运用于各大企业数字化转型的2024年&#xff0c;有这样一家拥有全国数百家汽车销售门店的500强集团公司&#xff0c;在迎接不断激增门店业务量的同时&#xff0c;持续探索能够确保数百家门店准确、高效运转&#xff0c;并努力将客户满意度维持在一个较高水…

虹科方案|释放总线潜力:汽车总线离线模拟解决方案

导读&#xff1a;传统的ECU模拟工具通常需要依赖上位机软件来发起通信&#xff0c;这在离线场景和自动化产线中带来不便。为了应对这一挑战&#xff0c;虹科推出了创新的汽车总线离线模拟解决方案&#xff0c;基于PCAN-Router系列网关&#xff0c;通过内部可编程固件&#xff0…

配电箱设计软件SuperBox V3.0新功能分享

功能亮点 1 多软件平台互通 SuperBox与图晓晓、ExWinner、D-Hub、SuperWORKS、SuperHarness全打通&#xff0c;报价工程师、技术工程师协同高效工作&#xff0c;配电箱报价、设计、出图火速提升。 2 软件平台更加开放 SuperBox V3.0我们开放了图章库&#xff0c;壳体库&…

python coding with ChatGPT 打卡第15天| 二叉树:翻转二叉树、对称二叉树

相关推荐 python coding with ChatGPT 打卡第12天| 二叉树&#xff1a;理论基础 python coding with ChatGPT 打卡第13天| 二叉树的深度优先遍历 python coding with ChatGPT 打卡第14天| 二叉树的广度优先遍历 文章目录 翻转二叉树Key Points相关题目视频讲解重点分析递归遍历…

Leetcode—2396. 严格回文的数字【中等】

2024每日刷题&#xff08;一零六&#xff09; Leetcode—2396. 严格回文的数字 算法思想 实现代码 class Solution { public:bool isStrictlyPalindromic(int n) {return false;} };运行结果 之后我会持续更新&#xff0c;如果喜欢我的文章&#xff0c;请记得一键三连哦&…

ISCTF wp

web 圣杯战争 题目源码 <?php highlight_file(__FILE__); error_reporting(0);class artifact{public $excalibuer;public $arrow;public function __toString(){echo "为Saber选择了对的武器!<br>";return $this->excalibuer->arrow;} }class pre…

C#,德兰诺依数(Dealnnoy Number)的算法与源代码

1 Dealnnoy Number 德兰诺依数&#xff0c;德兰诺伊数 德兰诺依数是以法国军官、业余数学家亨利德兰诺依&#xff08;Henry Dealnnoy&#xff09;的名字命名。 Henry Dealnnoy 在组合数学中&#xff0c;德兰诺依数描述了从(0,0)到(m,n)的格路问题中&#xff0c; 只允许按照(0…

《【Python】如何设置现代 Python 日志记录 | Python 基础教程 | Python 冷知识 | 十分钟高手系列》学习笔记

《【Python】如何设置现代 Python 日志记录 | Python 基础》 2 PUT ALL HANDLERS/FILTERS ON THE ROOT&#xff1a;扁平化的设计有助于简化维护成本 5 STORE CONFIG IN JSON OR YAML FILE&#xff1a;使用配置文件可以将配置和代码解耦&#xff0c;减少代码量 日志设置示例 7 …