【静态分析】软件分析课程实验A2-常量传播和Worklist求解器

Tai-e官网:

概述 | Tai-e

参考:

https://www.cnblogs.com/gonghr/p/17979609

--------------------------------------------------------

1 作业导览

  • 为 Java 实现常量传播算法。
  • 实现一个通用的 worklist 求解器,并用它来解决一些数据流分析问题,例如本次的常量传播。

在本次实验作业中,你要在 Tai-e 框架下实现常量传播算法和 worklist 求解器的关键部分。

2 实现常量传播

2.1 分析范围

在此次作业中,你需要实现针对 int 类型的常量传播。注意,在 Java 中,booleanbytecharshort 类型在运行时实际上都以 int 值的形式进行表示和计算,因此你的分析算法也应当能处理这些类型。其他基本数据类型(例如 longfloatdouble)以及引用类型(例如 class types、array types)不在此次作业的考虑范围内,所以你可以在分析时忽略它们的值。

至于语句的处理,你只需要关注等号左侧为变量且右侧只能是如下几类表达式的赋值语句:

  • 常量,如 x = 1
  • 变量,如 x = y
  • 二元运算表达式,如 x = a + bx = a >> b

下表列举出了除逻辑运算符以外所有作用在 int 类型上的二元运算符,请确保你的算法能正确处理它们:

运算类型运算符
Arithmetic+ - * / %
Condition== != < > <= >=
Shift<< >> >>>
Bitwise| & ^

逻辑运算符怎么办?

在上表中,我们没有列出逻辑运算符。这是因为,在 Java 中,逻辑运算符与(&&)和或(||)没有对应的字节码表示,而是以分支跳转的形式实现的。例如以下语句:

a = b || c;

将被转换为如下的语义等价的语句:

if (b) {t = true; // t is temp variable
} else if (c) {t = true;
} else {t = false;
}
a = t;

由于作业中的常量传播算法能够处理常量赋值(如 t = true)、变量赋值(如 a = t)和分支语句(如 if (b) {…}),所以它自然能够处理 &&||

至于等号左侧为变量、等号右侧为其它表达式的赋值语句,例如方法调用(x = m(...))和字段 load(x = o.f),你需要对它们进行保守的近似处理(也许会不够精确),即把它们当作 x = NAC。后续的作业将逐步解锁方法调用和字段访问的精确分析技巧。

对于上面没有提到的其它语句(例如字段存储 o.f = x),我们只需要使用恒等函数作为它们的 transfer 函数。在未来我们引入别名分析后,你就可以对字段存储进行精确处理了。

2.2 Tai-e 中你需要了解的类

这一节将会介绍与 IR 相关的类(pascal.taie.ir.*)以及和本次的分析算法相关的类(pascal.taie.analysis.*)。细致地了解它们将有助于完成常量分析算法。

  • pascal.taie.ir.IR

    这是 Tai-e 的 IR 的核心数据结构。它的每个实例储存了一个 Java 方法的各种信息,例如变量(variables)、参数(parameters)、语句(Stmts)等等。这里需要注意一点,一个方法体的中间表示由多个中间表示语句(Stmts)组成。如果想要加深理解,你可以自行阅读 API、源码实现以及注释。

  • pascal.taie.ir.exp.Exp

    我们已经在作业 1 中介绍过了这个接口。这是 Tai-e 的 IR 中的一个关键接口,用于表示程序中的所有表达式。它含有很多子类,对应各类具体的表达式。具体细节查看:【静态分析】软件分析课程实验A1-活跃变量分析和迭代求解器-CSDN博客

  • 在本次作业中,你需要处理更多 Exp 接口的子类。下图是一个继承关系的简单示意图(已略去与本次作业无关的类)。

  • 下面我们将逐一介绍这些子类。

  • pascal.taie.ir.exp.Var

    这个类代表 IR 中的变量。

  • pascal.taie.ir.exp.IntLiteral

    根据 Java 的语言规范,我们在 Tai-e 中把常量称作字面量(Literals)。每个 IntLiteral 类的实例都表示一个程序中的整数字面量。你可以通过调用 getValue() 方法来获取它的值。

  • pascal.taie.ir.exp.BinaryExp

    这个类代表程序中的二元表达式。这个类的各个子类对应了不同种类的二元表达式,并且每个子类中都有一个内部枚举类型用于表示该类支持的运算符。例如枚举类型 ArithmeticExp.Op 就代表了ArithmeticExp(算术表达式类)所支持的运算符,也就是 + - * /%

需要指出的是,在 Tai-e 中,BinaryExp 的两个操作数都是 Var 类型的。例如下面的语句

x = y + 6;

在 Tai-e 中会被转化成如下的 IR:

%intconst0 = 6;     // %intconst* are temp variables introduced
x = y + %intconst0; // by Tai-e to hold constant int values
  • 这样的设计简化了分析的实现:在获取 BinaryExp 的操作数时,你只需要考虑它是变量的这一种可能,而不用担心它是常量或其它可能了。

  • pascal.taie.ir.stmt.DefinitionStmt

    这是 Stmt 的一个子类。它表示了程序中所有的赋值语句,(即形如 x = yx = m(…) 的语句)。这个类很简单。你可以通过阅读源码来决定如何使用它。

  • pascal.taie.analysis.dataflow.analysis.DataflowAnalysis

    这是具体数据流分析算法需要实现的接口。和作业 1 一样,它会被求解器调用。在本次作业中你只需要关注前 5 个 API。这些 API 会被你在后面完成的 worklist 求解器调用。

  • pascal.taie.analysis.dataflow.analysis.constprop.Value

    这个类表示了常量分析中格上的抽象值。格的定义见【静态分析】静态分析笔记04 - 数据流分析(理论)-CSDN博客

  • 它的代码和注释解释了它的用法。你应该用下列的静态方法获取格上抽象值(即该类的实例):

    • Value getNAC(): 返回 NAC
    • Value getUndef(): 返回 UNDEF
    • Value makeConstant(int): 返回给定整数在格上对应的抽象值
  • pascal.taie.analysis.dataflow.analysis.constprop.CPFact

    这个类表示常量传播中的 data facts,即一个从变量(Var)到格上抽象值(Value)的映射。该类提供了各种 map 相关的操作,例如键值对的查询、更新等等。这些操作大多继承自 pascal.taie.analysis.dataflow.fact.MapFact。这些类的注释都很充分,所以你应该通过阅读源码来决定如何使用其中的 API。

  • pascal.taie.analysis.dataflow.analysis.constprop.ConstantPropagation

    这个类实现了 DataflowAnalysis。你需要在其中编写完整的常量传播算法。具体要求见第 2.3 节。

2.3 你的任务 [重点!]

首先,你需要完成 ConstantPropagation 的下述 API:

  • CPFact newBoundaryFact(CFG)

  • CPFact newInitialFact()

  • void meetInto(CPFact,CPFact)

  • boolean transferNode(Stmt,CPFact,CPFact)

你已经在作业 1 中见到过这几个 API,他们是从 DataflowAnalysis 中继承下来的,需要注意的是:在实现 newBoundaryFact() 的时候,你要小心地处理每个会被分析的方法的参数。具体来说,你要将它们的值初始化为 NAC (请思考:为什么要这么做?)。

原因是为了 safe-approximation ,我们不知道通过形参传递过来的参数是否是常量,所以为了安全,假设所有参数都是 NAC ,当然这样会导致精度损失问题,后面通过过程间分析可以有效解决这个问题。

提示

正如第 2.1 节中提到的,本次作业只关注 int 类型的常量传播。为了实现这一点,框架代码在 ConstantPropagation 类中提供了 canHoldInt(Var) 方法来判断一个变量能否储存 int 类型的值。你需要利用这个方法来判断一个变量是否在本次作业的分析范围内,并忽略那些不在范围内的变量(例如 float 类型的变量)。

此外,你还需要实现下面两个辅助方法:

  • Value meetValue(Value,Value)

    该方法对应着格上的 meet 操作(⊓),详情见【静态分析】静态分析笔记04 - 数据流分析(理论)-CSDN博客

  • 你应当在 meetInto() 方法中调用它。

  • Value evaluate(Exp,CPFact)

    这个方法会计算表达式(Exp)的值(Value)。当然,此处的值是格上的抽象值。你需要参考【静态分析】静态分析笔记04 - 数据流分析(理论)-CSDN博客

  • 来实现它的三种情况。对于其它情况,该方法会像我们在第 2.1 节提到的那样返回 NAC你应该在 transferNode() 方法中调用它来进行表达式的求值。

提示

  1. 和作业 1 一样,我们对 meetInto() 的设计比较特殊。如果你不记得了,可以再回顾一下作业 1 文档的相关部分。
  2. 条件表达式(如 a == b)的值由 0(若为 False)和 1(若为 True)来表示。
  3. 对于除以 0 的情况(出现在 /% 中),我们规定结果为 UNDEF。例如,对于 x = a / 0x 的值将会是 UNDEF

算法伪代码描述

  • newBoundaryFact :负责创建和初始化虚拟结点的 Data Flow Fact。但是注意要把方法参数初始化为 NAC 。 根据题目要求,不是所有类型的参数都考虑,只有能转换成 int 类型的参数才考虑,所以别忘了用 canHoldInt 过滤一下。
  • newInitialFact :负责创建和初始化控制流图中除了 EntryExit 之外的结点的 Data Flow Fact。控制流图中一个结点的 INOUT 分别对应一个 Data Flow Fact ,记录当前程序点时变量的状态。直接创建一个空的 CPFact 即可,方法体内还没有开始扫描。
  • meetInto :负责处理 transfer function 之前可能遇到多个 OUT 时的合并处理。具体的合并操作通过调用 meetValue 来处理。
  • meetValue :负责对格上的值进行合并。

meet 操作

  • NAC ⊓ v = NAC(非常量)
  • UNDEF ⊓ v = v(未初始化的变量不是我们分析的目标)
  • c ⊓ v = ?
    • c ⊓ c = c
    • c1 ⊓ c2 = NAC
  • transferNode :负责实现控制流图中结点的 transfer function 。如果 OUT 改变,返回 true ;否则返回 false

stmt 表示结点中的一条中间表示,一个结点只有一个中间表示。

题目要求只需要对赋值语句处理,所以用 DefinitionStmt 类型过滤。

对于所有赋值语句,只考虑具有左值,并且左值是变量且类型可以转换成 int 的语句。这些语句的右值是一个表达式,可能是常量,也能是变量、二元表达式。这个右值表达式的值将通过 evaluate 函数计算。

对于其他类型的语句,不做处理,out 直接复制 in 即可,相当于经过一个恒等函数。

  • evaluate :负责表达式值的计算。

表达式分三种情况讨论

  1. 常量:直接赋值。
  2. 变量:获取变量的值再赋值。
  3. 二元运算:针对共 12 中运算分别处理。

/** Tai-e: A Static Analysis Framework for Java** Copyright (C) 2022 Tian Tan <tiantan@nju.edu.cn>* Copyright (C) 2022 Yue Li <yueli@nju.edu.cn>** This file is part of Tai-e.** Tai-e is free software: you can redistribute it and/or modify* it under the terms of the GNU Lesser General Public License* as published by the Free Software Foundation, either version 3* of the License, or (at your option) any later version.** Tai-e is distributed in the hope that it will be useful,but WITHOUT* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General* Public License for more details.** You should have received a copy of the GNU Lesser General Public* License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.*/package pascal.taie.analysis.dataflow.analysis.constprop;import pascal.taie.analysis.dataflow.analysis.AbstractDataflowAnalysis;
import pascal.taie.analysis.graph.cfg.CFG;
import pascal.taie.config.AnalysisConfig;
import pascal.taie.ir.IR;
import pascal.taie.ir.exp.ArithmeticExp;
import pascal.taie.ir.exp.BinaryExp;
import pascal.taie.ir.exp.BitwiseExp;
import pascal.taie.ir.exp.ConditionExp;
import pascal.taie.ir.exp.Exp;
import pascal.taie.ir.exp.IntLiteral;
import pascal.taie.ir.exp.ShiftExp;
import pascal.taie.ir.exp.Var;
import pascal.taie.ir.stmt.DefinitionStmt;
import pascal.taie.ir.stmt.Stmt;
import pascal.taie.language.type.PrimitiveType;
import pascal.taie.language.type.Type;
import pascal.taie.util.AnalysisException;import pascal.taie.ir.exp.*;public class ConstantPropagation extendsAbstractDataflowAnalysis<Stmt, CPFact> {public static final String ID = "constprop";public ConstantPropagation(AnalysisConfig config) {super(config);}@Overridepublic boolean isForward() {return true;}@Overridepublic CPFact newBoundaryFact(CFG<Stmt> cfg) {// TODO - finish meCPFact cpFact = new CPFact();for (Var param : cfg.getIR().getParams()) {if (canHoldInt(param)) {                   // 只考虑可转换int类型的参数cpFact.update(param, Value.getNAC());  // 建立参数到格上值(NAC)的映射}}return cpFact;}@Overridepublic CPFact newInitialFact() {// TODO - finish mereturn new CPFact();}@Overridepublic void meetInto(CPFact fact, CPFact target) {// TODO - finish mefor (Var var : fact.keySet()) {Value v1 = fact.get(var);Value v2 = target.get(var);target.update(var, meetValue(v1, v2));}}/*** Meets two Values.*/public Value meetValue(Value v1, Value v2) {// TODO - finish meif (v1.isNAC() || v2.isNAC()) {return Value.getNAC();} else if (v1.isUndef()) {return v2;} else if (v2.isUndef()) {return v1;} else if (v1.isConstant() && v2.isConstant()) {if (v1.getConstant() == v2.getConstant()) {return v1;} else {return Value.getNAC();}} else {return Value.getNAC();}}@Overridepublic boolean transferNode(Stmt stmt, CPFact in, CPFact out) {// TODO - finish meCPFact copy = in.copy();   // 复制in给copy,避免影响in。if (stmt instanceof DefinitionStmt) { // 只处理赋值语句if (stmt.getDef().isPresent()) {  // 如果左值存在LValue lValue = stmt.getDef().get();  // 获取左值if ((lValue instanceof Var) && canHoldInt((Var) lValue)) {  // 对于符合条件的左值copy.update((Var) lValue, evaluate(((DefinitionStmt<?, ?>)  stmt).getRValue(), copy));  // 计算右值表达式的值用来更新左值变量在格上的值}}}return out.copyFrom(copy);  // copy复制给out。copy和in相比,有更新,返回true;反之返回false}/*** @return true if the given variable can hold integer value, otherwise false.*/public static boolean canHoldInt(Var var) {Type type = var.getType();if (type instanceof PrimitiveType) {switch ((PrimitiveType) type) {case BYTE:case SHORT:case INT:case CHAR:case BOOLEAN:return true;}}return false;}/*** Evaluates the {@link Value} of given expression.** @param exp the expression to be evaluated* @param in  IN fact of the statement* @return the resulting {@link Value}*/public static Value evaluate(Exp exp, CPFact in) {// TODO - finish meif (exp instanceof Var) {   // 变量return in.get((Var) exp);} else if (exp instanceof IntLiteral) {  // 常量return Value.makeConstant(((IntLiteral) exp).getValue());} else if (exp instanceof BinaryExp) {   // 二元运算Value v1 = in.get(((BinaryExp) exp).getOperand1()); // 获取运算分量在格上的值Value v2 = in.get(((BinaryExp) exp).getOperand2());if (v1.isNAC() || v2.isNAC()) {if (v1.isNAC() && v2.isConstant() && exp instanceof ArithmeticExp) {  // x = a / 0,x = a % 0,x 的值将会是 UNDEFArithmeticExp.Op operator = ((ArithmeticExp) exp).getOperator();if (operator == ArithmeticExp.Op.DIV || operator == ArithmeticExp.Op.REM) {if (v2.getConstant() == 0) return Value.getUndef();}}return Value.getNAC();}if (v1.isUndef() || v2.isUndef()) {return Value.getUndef();}if (exp instanceof ArithmeticExp) {ArithmeticExp.Op operator = ((ArithmeticExp) exp).getOperator();switch (operator) {case ADD -> {return Value.makeConstant(v1.getConstant() + v2.getConstant());}case DIV -> {if (v2.getConstant() == 0) return Value.getUndef();return Value.makeConstant(v1.getConstant() / v2.getConstant());}case MUL -> {return Value.makeConstant(v1.getConstant() * v2.getConstant());}case SUB -> {return Value.makeConstant(v1.getConstant() - v2.getConstant());}case REM -> {if (v2.getConstant() == 0) return Value.getUndef();return Value.makeConstant(v1.getConstant() % v2.getConstant());}}} else if (exp instanceof ConditionExp) {ConditionExp.Op operator = ((ConditionExp) exp).getOperator();switch (operator) {case EQ -> {if (v1.getConstant() == v2.getConstant()) return Value.makeConstant(1);else return Value.makeConstant(0);}case GE -> {if (v1.getConstant() >= v2.getConstant()) return Value.makeConstant(1);else return Value.makeConstant(0);}case GT -> {if (v1.getConstant() > v2.getConstant()) return Value.makeConstant(1);else return Value.makeConstant(0);}case LE -> {if (v1.getConstant() <= v2.getConstant()) return Value.makeConstant(1);else return Value.makeConstant(0);}case LT -> {if (v1.getConstant() < v2.getConstant()) return Value.makeConstant(1);else return Value.makeConstant(0);}case NE -> {if (v1.getConstant() != v2.getConstant()) return Value.makeConstant(1);else return Value.makeConstant(0);}}} else if (exp instanceof BitwiseExp) {BitwiseExp.Op operator = ((BitwiseExp) exp).getOperator();switch (operator) {case OR -> {return Value.makeConstant(v1.getConstant() | v2.getConstant());}case AND -> {return Value.makeConstant(v1.getConstant() & v2.getConstant());}case XOR -> {return Value.makeConstant(v1.getConstant() ^ v2.getConstant());}}} else if (exp instanceof ShiftExp) {ShiftExp.Op operator = ((ShiftExp) exp).getOperator();switch (operator) {case SHL -> {return Value.makeConstant(v1.getConstant() << v2.getConstant());}case SHR -> {return Value.makeConstant(v1.getConstant() >> v2.getConstant());}case USHR -> {return Value.makeConstant(v1.getConstant() >>> v2.getConstant());}}}else {  // 二元表达式中的其他类型表达式return Value.getNAC();}}return Value.getNAC();}
}

3 实现 Worklist 求解器

3.1 Tai-e 中你需要了解的类

与迭代求解器类似,你需要清楚 DataflowResultCFGSolver 的相关用法(我们已经在作业 1 中介绍过了)。除此之外,你还需要知道:

  • pascal.taie.analysis.dataflow.solver.WorkListSolver

    该类继承了 Solver 类,实现了 worklist 算法。它的实现是不完整的,在本此作业中你需要完成它。

3.2 你的任务 [重点!]

你的第二个任务是完成下述两个 API 的实现:

考虑到常量传播是一个前向分析,你只需要关注前向分析相关的方法。initializeForward() 方法的具体实现参考如图前三行。

doSolveForward() 则包含了你要实现的算法的主体部分。

  • Solver.initializeForward(CFG,DataflowResult)
  • WorkListSolver.doSolveForward(CFG,DataflowResult)

提示

  1. 讲义中的 worklist 算法通过比较 old_OUTOUT[B] 来决定后继节点是否应当加入 worklist 中,这个做法比较低效。Tai-e 中 DataflowAnalysis.transferNode() 会返回此次 transfer 是否改变了 OUT fact。利用好这一点可以避免多余的判断;
  2. 与作业 1 类似,不要忘了在 Solver.initializeForward() 中初始化每个语句的 INOUT

initializeForward :初始化所有的 Data Flow Fact

doSolveForward :负责实现 Worklist 求解器具体步骤。

protected void initializeForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {// TODO - finish meresult.setOutFact(cfg.getEntry(), analysis.newBoundaryFact(cfg));for (Node node : cfg) {if (cfg.isEntry(node)) continue;result.setInFact(node, analysis.newInitialFact());result.setOutFact(node, analysis.newInitialFact());}}
/** Tai-e: A Static Analysis Framework for Java** Copyright (C) 2022 Tian Tan <tiantan@nju.edu.cn>* Copyright (C) 2022 Yue Li <yueli@nju.edu.cn>** This file is part of Tai-e.** Tai-e is free software: you can redistribute it and/or modify* it under the terms of the GNU Lesser General Public License* as published by the Free Software Foundation, either version 3* of the License, or (at your option) any later version.** Tai-e is distributed in the hope that it will be useful,but WITHOUT* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General* Public License for more details.** You should have received a copy of the GNU Lesser General Public* License along with Tai-e. If not, see <https://www.gnu.org/licenses/>.*/package pascal.taie.analysis.dataflow.solver;import pascal.taie.analysis.dataflow.analysis.DataflowAnalysis;
import pascal.taie.analysis.dataflow.fact.DataflowResult;
import pascal.taie.analysis.graph.cfg.CFG;import java.util.*;class WorkListSolver<Node, Fact> extends Solver<Node, Fact> {WorkListSolver(DataflowAnalysis<Node, Fact> analysis) {super(analysis);}@Overrideprotected void doSolveForward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {// TODO - finish meArrayDeque<Node> worklist = new ArrayDeque<>();   // 双端堆栈当队列用for (Node node : cfg) {   // 添加所有结点到队列中if (cfg.isEntry(node)) {continue;}worklist.addLast(node);}while (!worklist.isEmpty()) {Node node = worklist.pollFirst();  // 弹出队头结点for (Node pred : cfg.getPredsOf(node)) {  // 对该结点以及所有前驱结点的OUT做meetanalysis.meetInto(result.getOutFact(pred), result.getInFact(node));}boolean f = analysis.transferNode(node, result.getInFact(node), result.getOutFact(node));if (f) {  // 如果该节点OUT发生了变化,将其所有后继节点添加到队列for (Node succ : cfg.getSuccsOf(node)) {worklist.addLast(succ);}}}}@Overrideprotected void doSolveBackward(CFG<Node> cfg, DataflowResult<Node, Fact> result) {throw new UnsupportedOperationException();}
}

4 运行与测试

你可以按我们在 Tai-e 框架(教学版)配置指南 中提到的方式来运行分析。在本作业中,Tai-e 对输入类的每个方法进行常量传播分析,并输出分析的结果,也就是每个语句的 OUT fact 所包含的数据流信息(变量的格上对应值)。

--------------------<Assign: void assign()> (constprop)--------------------

[0@L4] x = 1; null

[1@L5] x = 2; null

[2@L6] x = 3; null

[3@L7] x = 4; null

[4@L8] y = x; null

[5@L8] return; null

当你未完成作业的时候,OUT fact 的结果为 null,当你完成了所有空缺代码后,分析的输出应当形如:

--------------------<Assign: void assign()> (constprop)--------------------

[0@L4] x = 1; {x=1}

[1@L5] x = 2; {x=2}

[2@L6] x = 3; {x=3}

[3@L7] x = 4; {x=4}

[4@L8] y = x; {x=4, y=4}

[5@L8] return; {x=4, y=4}

此外,Tai-e 将被分析方法的控制流图输出到 output/ 文件夹,它们被存储为 .dot 文件,你可以用可视化工具Graphviz来查看这些控制流图。

我们为本次作业提供了测试驱动类 pascal.taie.analysis.dataflow.analysis.constprop.CPTest,你可以按照 Tai-e 框架(教学版)配置指南 中描述的方式来测试你的实现是否正确。

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

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

相关文章

超便捷备忘录共享方法 文字文件都可共享

在这个信息爆炸的时代&#xff0c;备忘录已成为我们生活中不可或缺的小助手。它记录着我们的工作计划、待办事项、灵感闪现&#xff0c;甚至是那些温馨的家庭琐事。然而&#xff0c;当我们在不同的设备间穿梭&#xff0c;如何在手机、电脑&#xff0c;甚至是不同品牌的手机之间…

理解机器学习中的类别不平衡问题

大家好&#xff0c;实际世界的数据集通常是杂乱的,当不同类别之间的样本分布不均匀时&#xff0c;就会出现类别不平衡。或者说&#xff0c;某些类别的样本比其他类别多得多。例如&#xff0c;考虑一个信用卡违约数据集&#xff0c;信用卡违约是一个相对较少发生的事件&#xff…

速锐得深入解析吉利几何CAN总线数据通信网络的拓扑层级框架技术

在现代汽车工业中&#xff0c;车辆的电子控制单元&#xff08;ECU&#xff09;之间的通信至关重要。这种通信大多通过控制器局域网络&#xff08;CAN&#xff09;总线实现&#xff0c;它是德国BOSCH公司于20世纪80年代初开发的一种串行数据通信协议。随着技术的不断进步&#x…

读人工智能时代与人类未来笔记01_重塑人类社会秩序

1. AlphaZero 1.1. 2017年年底&#xff0c;由谷歌旗下DeepMind公司开发的人工智能程序AlphaZero击败了当时世界上最强大的国际象棋程序Stockfish 1.1.1. AlphaZero对Stockfish的百场战绩是28胜72平0负&#xff0c;可以说获得了压倒性的胜利 1.1.2. …

外贸网站优化为什么要布置内部链接?如何优化内链?

对于外贸人来说&#xff0c;外贸网站优化已然成为推广引流、获取询盘的重要的一环。而除了外部优化&#xff0c;内部链接&#xff0c;实际上也是提升SEO效果和用户体验的重要手段。为什么这么说呢&#xff1f;内部链接又该如何进行优化呢&#xff1f;接下来一起来看一看吧~ 什…

安卓手机数据恢复全攻略:从备份到专业软件一网打尽!

随着科技的飞速发展&#xff0c;我们的生活中越来越离不开手机。然而&#xff0c;在使用手机的过程中&#xff0c;我们可能会遇到数据丢失的问题。对于安卓手机用户来说&#xff0c;如何有效地恢复丢失的数据是一个值得探讨的问题。本文将为您介绍安卓手机数据恢复的全攻略&…

市政道路工程乙级资质的资质维护策略

人员配置与持续培训&#xff1a; 定期审查技术人员队伍&#xff0c;确保注册工程师及其他专业技术人员的数量、资质和专业分布满足乙级资质要求。实施持续的职业发展计划&#xff0c;包括内部培训、外部进修和专业认证&#xff0c;提升团队的技术水平和行业知识。 工程业绩记录…

java中简单工厂模式,工厂方法模式和抽象工厂模式的区别和联系?

在Java中&#xff0c;简单工厂模式、工厂方法模式和抽象工厂模式都是创建型设计模式&#xff0c;用于解耦对象的创建过程&#xff0c;提高系统的灵活性和可扩展性。它们之间既有相似之处也有明显的区别&#xff1a; 简单工厂模式&#xff08;Simple Factory Pattern&#xff0…

【Git LFS】Git管理大文件

要在Git中有效地管理大文件&#xff0c;你可以使用Git Large File Storage (Git LFS)。Git LFS 是一个Git扩展&#xff0c;它能够更好地处理大文件的版本控制&#xff0c;而不会使仓库变得庞大和笨重。下面是如何设置和使用Git LFS的步骤&#xff1a; 1. 安装 Git LFS 首先&a…

西部首个全域直播基地,打造西部直播基地领军形象

天府锋巢直播产业基地作为西部直播产业的领军者&#xff0c;以其前瞻性的战略布局和卓越的服务体系&#xff0c;正加速推动全域直播的快速发展&#xff0c;助力直播产业实现新升级。该基地作为成都规模最大的直播基地&#xff0c;以加快全域直播为核心目标&#xff0c;通过促进…

利用香港多IP服务器优化网站访问速度的关键策略?

利用香港多IP服务器优化网站访问速度的关键策略? 随着数字化时代的不断发展&#xff0c;网站的全球访问速度成为企业吸引用户、提升竞争力的重要因素。特别对于跨国企业而言&#xff0c;如何确保全球用户都能享受到稳定快速的访问体验显得尤为重要。在这一背景下&#xff0c;…

生成ssl证书并配置到nginx

生成ssl证书并配置到nginx 安装证书生成工具 apt-get update apt install software-properties-common add-apt-repository ppa:certbot/certbot apt-get update apt-get install certbot python3-certbot-nginx生成证书 首先在新网上创建一个A链接&#xff0c;域名与服务器做…

考研OSchap4文件管理chap5磁盘管理(部分)

目录 一、整体认知 1.文件的定义 250 2.文件的属性 251 3.文件内部应该如何被组织(逻辑结构) 256 4.文件之间应该如何被组织起来(目录结构) 252 5.OS应该向上提供哪些功能 253 6.文件应该如何存放在外存中(物理结构) 258 7.OS如何管理外存中的空闲块(存储空间的管理) 25…

牛客Java面试题【面试】

牛客Java面试题【面试】 前言推荐牛客Java面试题【面试】第2章 Java笔面试高频考点&解题技巧1. Java基础[2.1 一、java-基础-1](https://www.nowcoder.com/study/live/689/2/1)1.1 为什么Java代码可以实现一次编写、到处运行&#xff1f;1.2 一个Java文件里可以有多个类吗&…

BMS-HiL主要功能

BMS HIL 系统中 PC 机中安装实验管理软件用于测试过程管理和测试序列编辑&#xff0c;通过以太网与 PXI 机箱中的处理器进行连接&#xff0c;处理器中运行实时系统&#xff08;Real Time&#xff09;并安装 Veristand 终端引擎&#xff0c;通过与 PC 中的数据传输&#xff0c;对…

catti三笔真题解读-英译汉(2012上)①

hello,大家好,欢迎来到英译汉早操练的环节。今天继续学习考试真题,在真题中发现问题,进步更大。内容较多,全文如下。分两篇输出,请参考十九、二十系列文章。 Back home in Gambia, Amadou Jallow was, at 22, a lover of reggae who had just finished colleg…

项目总结(后面应该怎做)

项目构思&#xff1a; 对于发布随机选人&#xff1a; 老师点击随机点人的时候&#xff0c;就要将这个时间记录并存进数据库&#xff0c;然后进入随机点人的界面&#xff0c;每按一次随机点人的时候就从数据库里面返回一个学生对象&#xff0c;并再随机点人的界面创建一个集合&…

有必要买超声波洗眼镜机吗?力荐四款实力超群超声波清洗机

在日常生活中&#xff0c;眼镜不仅仅是我们视野的延展&#xff0c;像太阳眼镜&#xff0c;也是有着独特的作用。但是&#xff0c;在每天的使用过程中&#xff0c;眼镜片表面难免会有灰尘&#xff0c;污迹&#xff0c;甚至油渍&#xff0c;这些都会对镜片的材质产生一定的损伤&a…

JavaSE多态

多态&#xff1a;一个对象在不同条件下表示的不同形态就叫多态。在程序中&#xff0c;多态是父类引用指定子类对象就叫多态。 多态是面向对象程序设计中的第三个特征 // 多态 class Father {String name;public void desc() {System.out.println("----------");Sys…

Lianwei 安全周报|2024.05.06

新的一周又开始了&#xff0c;以下是本周「Lianwei周报」&#xff0c;我们总结推荐了本周的政策/标准/指南最新动态、热点资讯和安全事件&#xff0c;保证大家不错过本周的每一个重点&#xff01; 政策/标准/指南最新动态 01 欧盟委员会发布《数据法》指南 欧盟委员会发布了《…