hive自定义函数及案例

一.自定义函数

1.Hive自带了一些函数,比如:max/min等,但是数量有限,自己可以通过自定义UDF来方便的扩展。

2.当Hive提供的内置函数无法满足你的业务处理需要时,此时就可以考虑使用用户自定义函数。

3.根据用户自定义函数类别分为以下三种:

(1)UDF(User-Defined-Function) 一进一出。

(2)UDAF(User-Defined Aggregation Function) 用户自定义聚合函数,多进一出 。

(3)UDTF(User-Defined Table-Generating Functions) 用户自定义表生成函数,一进多出。

4.编程步骤

(1)继承Hive提供的类

org.apache.hadoop.hive.ql.udf.generic.GenericUDF
org.apache.hadoop.hive.ql.udf.generic.GenericUDTF
org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator

(2)实现类中的抽象方法

(3)在hive的命令行窗口创建函数

(4) 创建临时函数

需要把jar包上传到服务器上面

添加jar。

add jar linux_jar_path

创建function

create temporary function dbname.function_name AS class_name;

删除函数

drop temporary function if exists dbname.function_name;

(5)创建永久函数

需要把jar包上传到hdfs上面,创建函数时jar包的位置使用hdfs的地址。

创建function

create function if exists my_udtf as "com.zxl.hive.udf.ExplodeJSONArray" using jar "hdfs://flinkv1:8020/my_function/hive_udtf_funtion.jar";

删除函数

drop function if exists my_udtf ;

注意:永久函数跟会话没有关系,创建函数的会话断了以后,其他会话也可以使用。 永久函数创建的时候,在函数名之前需要自己加上库名,如果不指定库名的话,会默认把当前库的库名给加上。 永久函数使用的时候,需要在指定的库里面操作,或者在其他库里面使用的话加上,库名.函数名。

二.UDF

官方案例:https://cwiki.apache.org/confluence/display/Hive/HivePlugins#HivePlugins-CreatingCustomUDFs

计算给定基本数据类型的长度

package com.zxl.hive.udf;


import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;

public class MyUDF extends GenericUDF {

    /*
    * 判断传进来的参数的类型和长度
    * 约定返回的数据类型
    * */
    @Override
    public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
        //判断传进来的参数的长度
        if (arguments.length !=1) {
            throw  new UDFArgumentLengthException("please give me  only one arg");
        }
        //判断传进来的参数的类型
        if (!arguments[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)){
            throw  new UDFArgumentTypeException(1, "i need primitive type arg");
        }
        // 约定返回的数据类型
        return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
    }

    /*
    * 具体解决逻辑
    * */
    @Override
    public Object evaluate(DeferredObject[] arguments) throws HiveException {
        Object o = arguments[0].get();
        if(o==null){
            return 0;
        }
        return o.toString().length();
    }

    /*
    * 用于获取解释的字符串
    * */
    @Override
    public String getDisplayString(String[] strings) {
        return "";
    }
}

(1)创建永久函数
注意:因为add jar本身也是临时生效,所以在创建永久函数的时候,需要制定路径(并且因为元数据的原因,这个路径还得是HDFS上的路径)。
在这里插入图片描述

在这里插入图片描述

三.UDTF

官网案例:https://cwiki.apache.org/confluence/display/Hive/DeveloperGuide+UDTF

执行步骤

要实现UDTF,我们需要继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF,实现initialize, process, close三个方法。
UDTF首先会调用initialize方法,此方法返回UDTF的返回行的信息,返回个数,类型;
初始化完成后,会调用process方法,真正的处理过程在process函数中,在process中,每一次forward()调用产生一行;如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数;
最后close()方法调用,对需要清理的方法进行清理。

关于HIVE的UDTF自定义函数使用的更多详细内容请看:
转载原文链接:https://blog.csdn.net/lidongmeng0213/article/details/110877351

下面是json日志解析案例:

package com.zxl.hive.udf;


import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.json.JSONArray;

import java.util.ArrayList;
import java.util.List;

public class ExplodeJSONArray extends GenericUDTF {

    /**
     * 初始化方法,里面要做三件事
     * 1.约束函数传入参数的个数
     * 2.约束函数传入参数的类型
     * 3.约束函数返回值的类型
     */

    @Override
    public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {

        // TODO: 2023/12/6  返回结构体类型。udtf函数,有可能炸开之后形成多列。所以用返回结构体来封装。属性名:属性值。属性名就是列名;属性值就是列的类型。

        //用结构体,来约束函数传入参数的个数
        //List<? extends StructField> allStructFieldRefs = argOIs.getAllStructFieldRefs(); --见名知意,获取结构体所有属性的引用  可以看见返回值是个list类型.
        if(argOIs.getAllStructFieldRefs().size()!=1){ //只要个数不等于1,就抛出异常
            throw new UDFArgumentLengthException("explode_json_array()函数的参数个数只能为1");
        }

        //2.约束函数传入参数的类型
        // StructField structField = argOIs.getAllStructFieldRefs().get(0);//只能有一个参数,所以index给0  可以看见,是获得结构体的属性
        //ObjectInspector fieldObjectInspector = argOIs.getAllStructFieldRefs().get(0).getFieldObjectInspector();//获得属性的对象检测器 。通过检查器我们才能知道是什么类型.
        String typeName = argOIs.getAllStructFieldRefs().get(0).getFieldObjectInspector().getTypeName();
        //我们要确保传入的类型是string
        if(!"string".equals(typeName)){
            throw new UDFArgumentTypeException(0,"explode_json_array函数的第1个参数的类型只能为String."); //抛出异常
        }

        //3.约束函数返回值的类型
        List<String> fieldNames = new ArrayList<>(); //② 表示我建立了一个String类型的集合。表示存储的列名

        List<ObjectInspector> fieldOIs = new ArrayList<>(); //②
        fieldNames.add("item"); //炸裂之后有个列名,如果不重新as,那这个item就是列名

        fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); //表示item这一列是什么类型.基本数据类型工厂类,获取了个string类型的检查器

        //用一个工厂类获取StructObjectInspector类型。
        return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames,fieldOIs);//①获取标准结构体检查器。fieldNames,fieldOI是两个变量名

    }


    //这里是实现主逻辑的方法。首先分析下需求:把json array字符串变成一个json字符串
    @Override
    public void process(Object[] args) throws HiveException {
        //1 获取函数传入的jsonarray字符串
        String jsonArrayStr = args[0].toString(); //我要把jsonArrayStr字符串划分为一个一个的json,通过字符串这种类型是不好划分的。不知道如何split切分
        //2 将jsonArray字符串转换成jsonArray数组。正常情况下我们要引入依赖,比如fastjson啥的。
        JSONArray jsonArray = new JSONArray(jsonArrayStr); //通过JSONArray这种类型,我们就比较容易获得一条条的json字符串
        //3 得到jsonArray里面的一个个json,并把他们写出。将actions里面的一个个action写出
        for (int i = 0; i < jsonArray.length(); i++) { //普通for循环进行遍历
            String jsonStr = jsonArray.getString(i);//前面定义了,要返回String
            //forward是最后收集数据返回的方法
            forward(jsonStr);
        }

    }

    @Override
    public void close() throws HiveException {
    }
}

在这里插入图片描述

注意:UDTF函数不能和其他字段同时出现在select语句中,负责SQL会执行失败

三.UDAF

官网案例:https://cwiki.apache.org/confluence/display/Hive/GenericUDAFCaseStudy#GenericUDAFCaseStudy-Writingtheresolver

执行步骤:

编写自定义函数需要创建三个类:
1.继承 AbstractGenericUDAFResolver重写 getEvaluator方法,对传入的值进行判断。
2.创建数据缓存区,创建一些变量来进行调用赋值,作为中间值,类似于flink的checkpoints。
3.继承GenericUDAFEvaluator类重写方法即可,实现具体逻辑的类。

参考文章:

UDAF重要的类及原理分析(UDAF继承类的各个方法的用法)
原文链接:https://blog.csdn.net/lidongmeng0213/article/details/110869457
Hive之ObjectInspector详解(UDAF中用到的类型详解)
原文链接:https://blog.csdn.net/weixin_42167895/article/details/108314139

一个类似于SUM的自定义函数:

import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.parse.SemanticException;
import org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFParameterInfo;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoUtils;


// TODO: 2023/12/9 继承 AbstractGenericUDAFResolver重写 getEvaluator方法
public class FieldSum extends AbstractGenericUDAFResolver {

    @Override
    public GenericUDAFEvaluator getEvaluator(TypeInfo[] info) throws SemanticException {
        // TODO: 2023/12/9 判断传入的参数是否为一个
        if (info.length != 1) {
            throw new UDFArgumentLengthException("只能传入一个参数, 但是现在有 " + info.length + "个参数!");
        }

        /*TypeInfoUtils是一个Java类,它提供了一些用于处理Hive数据类型的实用方法。以下是TypeInfoUtils类中的一些方法及其功能:
        getTypeInfoFromTypeString(String typeString) - 将类型字符串转换为Hive数据类型信息对象。
        getStandardJavaObjectInspectorFromTypeInfo(TypeInfo typeInfo) - 从Hive数据类型信息对象中获取标准Java对象检查器。
        isExactNumericType(PrimitiveTypeInfo typeInfo) - 检查给定的Hive原始数据类型是否为精确数值类型。
        getCategoryFromTypeString(String typeString) - 从类型字符串中获取Hive数据类型的类别。
        getPrimitiveTypeInfoFromPrimitiveWritable(Class<? extends Writable> writableClass) -
        从Hadoop Writable类中获取Hive原始数据类型信息对象。*/

        ObjectInspector objectInspector = TypeInfoUtils.getStandardJavaObjectInspectorFromTypeInfo(info[0]);

        // TODO: 2023/12/9 判断是不是标准的java Object的primitive类型
        if (objectInspector.getCategory() != ObjectInspector.Category.PRIMITIVE) {

            throw new UDFArgumentTypeException(0, "Argument type must be PRIMARY. but " +

                    objectInspector.getCategory().name() + " was passed!");

        }

        // 如果是标准的java Object的primitive类型,说明可以进行类型转换

        PrimitiveObjectInspector inputOI = (PrimitiveObjectInspector) objectInspector;

        // 如果是标准的java Object的primitive类型,判断是不是INT类型,因为参数只接受INT类型

        if (inputOI.getPrimitiveCategory() != PrimitiveObjectInspector.PrimitiveCategory.INT) {

            throw new UDFArgumentTypeException(0, "Argument type must be INT, but " +

                    inputOI.getPrimitiveCategory().name() + " was passed!");

        }
        return new FieldSumUDAFEvaluator();
    }

    @Override
    public GenericUDAFEvaluator getEvaluator(GenericUDAFParameterInfo info) throws SemanticException {
        return super.getEvaluator(info);
    }
}
import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator;

// TODO: 2023/12/9 创建数据缓存区,创建一些变量来进行调用赋值,作为中间值,类似于flink的checkpoints。
public class FieldSumBuffer extends GenericUDAFEvaluator.AbstractAggregationBuffer {
    Integer num = 0;

    // TODO: 2023/12/9 实现变量的get,set方法方便后面赋值,取值
    public Integer getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    // TODO: 2023/12/9 创建累加的方法,方便对变量进行累加
    public Integer addNum(int aum) {
        num += aum;
        return num;
    }
}
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;

// TODO: 2023/12/9 实现具体逻辑的地方 直接继承GenericUDAFEvaluator类重写方法即可
public class FieldSumUDAFEvaluator extends GenericUDAFEvaluator {

    // TODO: 2023/12/9 初始输入的变量 PrimitiveObjectInspector是Hadoop里面原始数据类别
    PrimitiveObjectInspector inputNum;
    PrimitiveObjectInspector middleNum;
    // TODO: 2023/12/9 最终输出的变量
    ObjectInspector outputNum;
    // TODO: 2023/12/9 最终统计值的变量
    int sumNum;

    // TODO: 2023/12/7 Model代表了UDAF在mapreduce的各个阶段。

    //* PARTIAL1: 这个是mapreduce的map阶段:从原始数据到部分数据聚合
    //* 将会调用iterate()和terminatePartial()

    //* PARTIAL2: 这个是mapreduce的map端的Combiner阶段,负责在map端合并map的数据::从部分数据聚合到部分数据聚合:
    //* 将会调用merge() 和 terminatePartial()

    //* FINAL: mapreduce的reduce阶段:从部分数据的聚合到完全聚合
    //* 将会调用merge()和terminate()

    //* COMPLETE: 如果出现了这个阶段,表示mapreduce只有map,没有reduce,所以map端就直接出结果了:从原始数据直接到完全聚合
    //* 将会调用 iterate()和terminate()

    // TODO: 2023/12/7  确定各个阶段输入输出参数的数据格式ObjectInspectors
    @Override
    public ObjectInspector init(Mode m, ObjectInspector[] parameters) throws HiveException {
        super.init(m, parameters);
        // TODO: 2023/12/9 COMPLETE或者PARTIAL1,输入的都是数据库的原始数据所以要确定输入的数据格式
        if (m == Mode.PARTIAL1 || m == Mode.COMPLETE) {
            inputNum = (PrimitiveObjectInspector) parameters[0];
        } else {
            middleNum = (PrimitiveObjectInspector) parameters[0];
        }

        // TODO: 2023/12/9 ObjectInspectorFactory是创建新的ObjectInspector实例的主要方法:一般用于创建集合数据类型。输出的类型是Integer类型,java类型
        outputNum = ObjectInspectorFactory.getReflectionObjectInspector(Integer.class,
                ObjectInspectorFactory.ObjectInspectorOptions.JAVA);

        return outputNum;
    }

    // TODO: 2023/12/9   保存数据聚集结果的类
    @Override
    public AggregationBuffer getNewAggregationBuffer() throws HiveException {
        return new FieldSumBuffer();
    }

    // TODO: 2023/12/9   重置聚集结果
    @Override
    public void reset(AggregationBuffer agg) throws HiveException {
        //重新赋值为零
        ((FieldSumBuffer) agg).setNum(0);
    }

    // TODO: 2023/12/9  map阶段,迭代处理输入sql传过来的列数据,不断被调用执行的方法,最终数据都保存在agg中,parameters是新传入的数据
    @Override
    public void iterate(AggregationBuffer agg, Object[] parameters) throws HiveException {
        // TODO: 2023/12/9 判断如果传入的值是空的直接返回
        if (parameters == null || parameters.length < 1) {
            return;
        }
        Object javaObj = inputNum.getPrimitiveJavaObject(parameters[0]);
        ((FieldSumBuffer) agg).addNum(Integer.parseInt(javaObj.toString()));
    }

    // TODO: 2023/12/9 map与combiner结束返回结果,得到部分数据聚集结果
    @Override
    public Object terminatePartial(AggregationBuffer agg) throws HiveException {
        return terminate(agg);
    }

    // TODO: 2023/12/9 combiner合并map返回的结果,还有reducer合并mapper或combiner返回的结果。
    @Override
    public void merge(AggregationBuffer agg, Object partial) throws HiveException {
        ((FieldSumBuffer) agg).addNum((Integer) middleNum.getPrimitiveJavaObject(partial));
    }

    // TODO: 2023/12/9 map阶段,迭代处理输入sql传过来的列数据
    @Override
    public Object terminate(AggregationBuffer agg) throws HiveException {
        Integer num = ((FieldSumBuffer) agg).getNum();
        return num;
    }
}

打包上传,注册函数进行测试:

可以看到实现了对参数的判断和参数类型的判断
在这里插入图片描述

在这里插入图片描述

执行查询测试:

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Axure元件的介绍使用以及登录界面和个人简历的绘制

目录 一、Axure元件介绍 1.1 简介 1.2 特点 1.3 元件操作 二、基本元件的使用 2.1 矩形和圆形 2.2 图片 2.2.1 图片元件特点 2.2.2 具体操作 2.3 占位符 2.3.1 使用规范方法举例 2.4 文本元件 2.4.1 图示 2.5 热区 2.5.1 图示 2.5.2 热区辅助页面排版 2.6 线段…

鸿蒙开发 - ohpm安装第三方库

前端开发难免使用第三方库&#xff0c;鸿蒙亦是如此&#xff0c;在使用 DevEco Studio 开发工具时&#xff0c;如何引入第三方库呢&#xff1f;操作步骤如下&#xff0c;假设你使用的是MacOS&#xff0c;假设你已经创建了了一个项目&#xff1a; 一、配置 HTTP Proxy 在打开了…

「PPT 下载」Google DevFest Keynote | 复杂的海外网络环境下,如何提升连接质量

&#xff08;全网都在找的《社交泛娱乐出海作战地图》&#xff0c;点击获取&#x1f446;&#xff09; 12 月 10 日&#xff0c;“Google DevFest 2023 上海站”大会如期在上海市东方万国宴会中心举办。延续过往的技术交流碰撞、前沿技术学习基调传统&#xff0c;本届大会聚焦行…

从手工测试进阶中高级测试?如何突破职业瓶颈...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、手工测试如何进…

fastadmin配置教程

第一. 打开小皮&#xff0c;创建一个网站 第二. 打开fastadmin官网&#xff0c;下载压缩包 下载好后是这个样子 打开网站的根目录&#xff0c;将这个压缩包压缩到你网站的根目录里 第三&#xff0c;小皮里面创建一个数据库 第四&#xff0c;然后打开网站&#xff0c;输入创…

RocketMQ Streams详解

一、RocketMQ Streams 概览 RocketMQ Streams是基于RocketMQ的轻量级流计算引擎。能以SDK方式被应用依赖&#xff0c;无须部署复杂的流计算服务端即可获得流计算能力。 因此具有资源消耗少、扩展性好、支持流计算算子丰富的特点。 1、整体架构​ 数据从RocketMQ中被RocketMQ-…

发明/实用新型/软件著作权申请及书写相关事项

发明/实用新型/软件著作权申请及书写相关事项 零、前言一、关于写专利前的准备二、专利申请怎么调研三、发明/实用新型包括哪些&#xff1f;四、软件著作权需要哪些文件&#xff1f;五、提交文件及费用细节 零、前言 这里写了一些专利相关申请的注意事项及流程。这里仅仅参考的…

网络安全公司梳理,看F5如何实现安全基因扩增

应用无处不在的当下&#xff0c;从传统应用到现代应用再到边缘、多云、多中心的安全防护&#xff0c;安全已成为企业数字化转型中的首要挑战。根据IDC2023年《全球网络安全支出指南》&#xff0c;2022年度中国网络安全支出规模137.6亿美元&#xff0c;增速位列全球第一。有专家…

基于OpenCV+CNN+IOT+微信小程序智能果实采摘指导系统——深度学习算法应用(含python、JS工程源码)+数据集+模型(二)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境TensorFlow 环境Jupyter Notebook环境Pycharm 环境微信开发者工具OneNET云平台 相关其它博客工程源代码下载其它资料下载 前言 本项目基于Keras框架&#xff0c;引入CNN进行模型训练&#xff0c;采用Dropout梯度…

QML WebEngineView 全屏和退出

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 在使用浏览器时,我们经常会用到全屏模式,最常见的场景有:观看视频、阅读文章、在线演示等。全屏模式的优点在于,它可以让用户充分地利用有限的屏幕空间,更好地专注于内容本身,从而提供丰富的沉浸式视觉…

CentOS7 安装包 MariaDB 10.4.x

CentOS7 安装包 MariaDB 10.4.x 统一 MariaDB安装包 https://www.alipan.com/s/fvLg3gN7LPX 提取码: nh81 打开「阿里云盘」

讲解一手CSRF,如何防御CSRF

简介&#xff1a; CSRF&#xff08;Cross-Site Request Forgery&#xff0c;跨站请求伪造&#xff09;是一种网络安全漏洞&#xff0c;它允许攻击者通过欺骗用户在当前已登录的Web应用程序上执行未经用户授权的操作。 攻击者利用用户在目标网站上已经建立的身份认证&#xff…

基于单片机音乐盒仿真仿真系统设计

**单片机设计介绍&#xff0c;基于单片机音乐盒仿真仿真系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的音乐盒仿真仿真系统是一种基于嵌入式系统技术的设计方案&#xff0c;用于模拟传统的音乐盒功能。它通…

【Netty的线程模型】

Netty的线程模型 Netty的线程模型知识拓展单Reactor单线程模型单Reactor多线程模型主从Reactor模型 Netty的线程模型 Netty通过Reactor模型基于多路复用器接收并处理用户请求的&#xff0c;多路复用IO模型参考&#xff1a; 多路复用IO模型: 操作系统的IO模型有哪些&#xff1f…

AMC8历年真题在线练习、解析全新按年份独立,更便捷练习和巩固

告诉大家一个好消息&#xff01; 根据家长朋友们的反馈&#xff0c;六分成长独家制作的AMC8美国数学竞赛的历年真题在练已全新架构和上线&#xff0c;改为了按年份独立一套试卷&#xff0c;这样在线练习加载更快&#xff0c;随需练习也更方便。 先来一睹为快&#xff0c;练习的…

排序算法4:【快速排序】、查看每趟归并后的结果,定义一个全局变量,用来计数作为总趟数

一、快速排序——时间复杂度&#xff1a;、 最坏的情况 1、原理&#xff1a; 快速排序是通过多次比较和交换来实现排序&#xff0c;首先&#xff0c;先从数列中&#xff0c;任意选择一个数作为基准&#xff08;或叫分界值&#xff09;&#xff0c;比如&#xff0c;第一个数&a…

用23种设计模式打造一个cocos creator的游戏框架----(十四)观察者模式

1、模式标准 模式名称&#xff1a;观察者模式 模式分类&#xff1a;行为型 模式意图&#xff1a;定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 结构图&#xff1a; 适用于&#xff1a;…

OFDM模糊函数仿真

文章目录 前言一、OFDM 信号及模糊函数1、OFDM 信号表达式2、模糊函数表达式 二、MATLAB 仿真1、MATLAB 核心源码2、仿真结果①、OFDM 模糊函数②、OFDM 距离模糊函数③、OFDM 速度模糊函数 前言 本文进行 OFDM 的仿真&#xff0c;首先看一下 OFDM 的模糊函数仿真效果&#xf…

SpringBoot程序的打包与运行

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开心好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…

【C语言】数据结构——小堆实例探究

&#x1f497;个人主页&#x1f497; ⭐个人专栏——数据结构学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 导读&#xff1a; 我们在前面学习了单链表和顺序表&#xff0c;以及栈和队列。 今天我们来学习小堆。 关注博主或是订阅专栏&a…