Android 圆环统计图(带外延折线可点击)

news2024/11/25 18:51:19

需求先看UI效果图吧

看到这肯定去找轮子,找了半天,没找到相似的,大部分搜到的都是点击外凸,而这个UI是内凸,其实外凸内凸区别还不小,没找到一样的,于是乎,和iOS说好了要不就放弃吧,然而第二天,人家夸夸夸撸完了,还是贝塞尔写的,当场晕死,我是真看不懂那公式,然后不甘心,拼命的改别人的轮子,就算不用贝塞尔,也得搞出来啊,于是乎,套两个圆环不就好了,于是,开始下手干活,下面贴出来iOS和Android的效果,左iOS右Android

虽然没有人家漂亮,但是基本也实现了,代码贴在下面了

辅助工具类GeomTool.class

import android.graphics.Point;
import android.graphics.RectF;

/**
 * 与2D屏幕有关的计算,屏幕约定为X轴向右,Y轴向下,顺时针角度增加。
 * Created by hxw on 2016/8/25.
 */
public  class GeomTool {

    /**
     * 这个方法放在这里展示了原始的计算过程
     *
     * @see #calcCirclePoint
     */
    @Deprecated
    private static Point calcCirclePoint2(int angle, float radius, float cx, float cy,Point resultOut) {
        if (resultOut == null) {
            resultOut = new Point();
        }

        // 将angle控制在0-360,注意这里的angle是从X正轴顺时针增加。而sin,cos等的计算是X正轴开始逆时针增加
        angle = clampAngle(angle);
        double radians = angle / 180f * Math.PI;
        double sin = Math.sin(radians);
        double cos = Math.cos(radians);

        double x = 0, y = 0;

        if (angle == 0 || angle == 360) {
            // sin:0 cos: 1
            x = cx + radius;
            y = cy;
        } else if (angle > 0 && angle < 90) {
            // sin:0~1 cos: 1~0
            double dy = radius * sin;
            double dx = radius * cos;
            x = cx + dx;
            y = cy + dy;
        } else if (angle == 90) {
            // sin:1 cos: 0
            x = cx;
            y = cy + radius;
        } else if (angle > 90 && angle < 180) {
            // sin:1~0 cos: 0~-1
            double dy = radius * sin;
            double dx = radius * cos;
            x = cx + dx;
            y = cy + dy;
        } else if (angle == 180) {
            // sin:0 cos: -1
            x = cx - radius;
            y = cy;
        } else if (angle > 180 && angle < 270) {
            // sin:0~-1 cos: -1~0
            double dy = radius * sin;
            double dx = radius * cos;
            x = cx + dx;
            y = cy + dy;
        }  else if (angle == 270) {
            // sin:-1 cos: 0
            x = cx;
            y = cy - radius;
        } else if (angle > 270 && angle < 360) {
            // sin:-1~0 cos: 0~1
            double dy = radius * sin;
            double dx = radius * cos;
            x = cx + dx;
            y = cy + dy;
        }

        resultOut.set((int) x, (int) y);
        return resultOut;
    }

    /**
     * 计算指定角度、圆心、半径时,对应圆周上的点。
     * @param angle 角度,0-360度,X正轴开始,顺时针增加。
     * @param radius 圆的半径
     * @param cx 圆心X
     * @param cy 圆心Y
     * @param resultOut 计算的结果(x, y) ,方便对象的重用。
     * @return resultOut, or new Point if resultOut is null.
     */
    public static Point calcCirclePoint(int angle, float radius, float cx, float cy, Point resultOut) {
        if (resultOut == null) resultOut = new Point();

        // 将angle控制在0-360,注意这里的angle是从X正轴顺时针增加。而sin,cos等的计算是X正轴开始逆时针增加
        angle = clampAngle(angle);
        double radians = angle / 180f * Math.PI;
        double sin = Math.sin(radians);
        double cos = Math.cos(radians);

        double dy = radius * sin;
        double dx = radius * cos;
        double x = cx + dx;
        double y = cy + dy;

        resultOut.set((int) x, (int) y);
        return resultOut;
    }

    /**
     * 计算坐标(x, y)到圆心(cx, cy)形成的角度,角度从0-360,360度就是0度,顺时针增加
     * (x轴向右,y轴向下)若2点重合返回-1;
     */
    public static int calcAngle(float x, float y, float cx, float cy) {
        double resultDegree = 0;

        double vectorX = x - cx; // 点到圆心的X轴向量,X轴向右,向量为(0, vectorX)
        double vectorY = cy - y; // 点到圆心的Y轴向量,Y轴向上,向量为(0, vectorY)
        if (vectorX == 0 && vectorY == 0) {
            // 重合?
            return -1;
        }
        // 点落在X,Y轴的情况这里就排除
        if (vectorX == 0) {
            // 点击的点在Y轴上,Y不会为0的
            if (vectorY > 0) {
                resultDegree = 90;
            } else {
                resultDegree = 270;
            }
        } else if (vectorY == 0) {
            // 点击的点在X轴上,X不会为0的
            if (vectorX > 0) {
                resultDegree = 0;
            } else {
                resultDegree = 180;
            }
        } else {
            // 根据形成的正切值算角度
            double tanXY = vectorY / vectorX;
            double arc = Math.atan(tanXY);
            // degree是正数,相当于正切在四个象限的角度的绝对值
            double degree = Math.abs(arc / Math.PI * 180);
            // 将degree换算为对应x正轴开始的0-360的角度
            if (vectorY < 0 && vectorX > 0) {
                // 右下 0-90
                resultDegree = degree;
            } else if (vectorY < 0 && vectorX < 0) {
                // 左下 90-180
                resultDegree = 180 - degree;
            } else if (vectorY > 0 && vectorX < 0) {
                // 左上 180-270
                resultDegree = 180 + degree;
            } else {
                // 右上 270-360
                resultDegree = 360 - degree;
            }
        }

        return (int) resultDegree;
    }

    /**
     * 计算指定区域中可放置的最大正方形区域。
     * @param region 指定的区域
     * @param squareRect 正方形区域,将在原区域中居中
     * @return squareRect, or new RectF if squareRect is null.
     */
    public static RectF calcMaxSquareRect(RectF region, RectF squareRect) {
        if (squareRect == null) squareRect = new RectF();
        if (region == null) return squareRect;

        float w = region.width();
        float h = region.height();
        if (w == h) {
            squareRect.set(region);
        } else if (w > h) {
            float padding = (w - h) / 2;
            squareRect.set(region);
            squareRect.inset(padding, 0);
        } else { // (w < h)
            float padding = (h - w) / 2;
            squareRect.set(region);
            squareRect.inset(0, padding);
        }
        return squareRect;
    }

    /**
     * 将角度变换为0-360度。
     * @param angle 原角度
     * @return 0-360之间的等效角度
     */
    public static int clampAngle(int angle) {
        return ((angle % 360) + 360) % 360;
    }

    /**
     * 返回给定值在区间[min, max]上的最近值。
     */
    public static float clamp(float value, float min, float max) {
        if (min > max) {
            min = min + max;
            max = min - max;
            min = min - max;
        }
        if (value < min) {
            return min;
        } else if (value > max) {
            return max;
        }
        return value;
    }

    /**
     * 计算点(x1, y1)和(x2, y2)之间的距离。
     */
    public static float calcDistance(float x1, float y1, float x2, float y2) {
        return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }

}

自定义View RingView.class


import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Point;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;

import com.dq.demo.R;

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

public class RingView extends View {

    private Context mContext;
    private Paint mPaint;
    private int mPaintWidth = 0;        // 画笔的宽
    private int topMargin = 30;         // 上边距
    private int leftMargin = 80;        // 左边距
    private Resources mRes;
    private DisplayMetrics dm;
    private int showRateSize = 12; // 展示文字的大小

    private int circleCenterX = 96;     // 圆心点X  要与外圆半径相等
    private int circleCenterY = 96;     // 圆心点Y  要与外圆半径相等

    private int ringOuterRidus = 96;     // 外圆的半径
    private int ringPointRidus = 80;    // 点所在圆的半径

    private float rate = 0.4f;     //点的外延距离  与  点所在圆半径的长度比率
    private float extendLineWidth = 80;     //点外延后  折的横线的长度

    private RectF rectF;                // 外圆所在的矩形
    private RectF rectFPoint;           // 点所在的矩形

    private List<Integer> colorList;
    private List<Float> rateList;
    private boolean isShowRate;

    private Float maxTotal = 0F;

    private String lengthenLineColor = "#3A66AF";
    private String lengthenTextColor = "#455B72";

    public RingView(Context context) {
        super(context, null);
    }

    public RingView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        initView();
    }

    public void setShow(List<Integer> colorList, List<Float> rateList) {
        setShow(colorList, rateList, false);
    }

    public void setShow(List<Integer> colorList, List<Float> rateList, boolean isShowRate) {
        this.colorList = colorList;
        this.rateList = rateList;
        this.isShowRate = isShowRate;

        angles = new float[rateList.size()];
        for (int i = 0; i < rateList.size(); i++) {
            maxTotal += rateList.get(i);
        }

        for (int j = 0; j < rateList.size(); j++) {
            angles[j] = (float) ((rateList.get(j) / maxTotal) * 360f);
        }

    }

    private void initView() {
        this.mRes = mContext.getResources();
        this.mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        dm = new DisplayMetrics();
        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getMetrics(dm);
        int screenWidth = wm.getDefaultDisplay().getWidth();
        leftMargin = (px2dip(screenWidth) - (2 * circleCenterX)) / 2;
        mPaint.setColor(getResources().getColor(R.color.red));
        mPaint.setStrokeWidth(dip2px(mPaintWidth));
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setAntiAlias(true);
        rectF = new RectF(dip2px(mPaintWidth + leftMargin),
                dip2px(mPaintWidth + topMargin),
                dip2px(circleCenterX + ringOuterRidus + mPaintWidth * 2 + leftMargin),
                dip2px(circleCenterY + ringOuterRidus + mPaintWidth * 2 + topMargin));
        rectFPoint = new RectF(dip2px(mPaintWidth + leftMargin + (ringOuterRidus - ringPointRidus)),
                dip2px(mPaintWidth + topMargin + (ringOuterRidus - ringPointRidus)),
                dip2px(circleCenterX + ringPointRidus + mPaintWidth * 2 + leftMargin),
                dip2px(circleCenterY + ringPointRidus + mPaintWidth * 2 + topMargin));
        Log.e("矩形点:", dip2px(circleCenterX + ringOuterRidus + mPaintWidth * 2) + " --- " + dip2px(circleCenterY + ringOuterRidus + mPaintWidth * 2));

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        pointList.clear();
        if (colorList != null) {
            for (int i = 0; i < colorList.size(); i++) {
                mPaint.setColor(mRes.getColor(colorList.get(i)));
                mPaint.setStyle(Paint.Style.FILL);
                drawOuter(canvas, i);
            }
        }

        if (colorList != null) {
            for (int i = 0; i < colorList.size(); i++) {
                drawInouter(canvas, i);
            }
        }

        mPaint.setStyle(Paint.Style.FILL);

        Paint paint1 = new Paint();
        paint1.setColor(Color.parseColor("#A7A6FF"));
        canvas.drawCircle(rectF.centerX(), rectF.centerY(), 150, paint1);

    }

    private float preRate;

    private void drawArcCenterPoint(Canvas canvas, int position) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mRes.getColor(R.color.transparent));
        mPaint.setStrokeWidth(dip2px(1));
        canvas.drawArc(rectFPoint, preAngle, (endAngle) / 2, true, mPaint);
        dealPoint(rectFPoint, preAngle, (endAngle) / 2, pointArcCenterList);
        Point point = pointArcCenterList.get(position);
        mPaint.setColor(Color.parseColor(lengthenLineColor));

        if (position == mCurrentItem) {
            /**
             * 折线初始圆点
             */
            canvas.drawCircle(point.x, point.y, dip2px(2), mPaint);
        }

        if (preRate / 2 + rateList.get(position) / 2 < 5) {
            extendLineWidth += 40;
            rate -= 0.05f;
        } else {
            extendLineWidth = 40;
            rate = 0.4f;
        }

        // 外延画折线
        float lineXPoint1 = (point.x - dip2px(leftMargin + ringOuterRidus)) * (1 + rate);
        float lineYPoint1 = (point.y - dip2px(topMargin + ringOuterRidus)) * (1 + rate);

        float[] floats = new float[8];
        floats[0] = point.x;
        floats[1] = point.y;
        floats[2] = dip2px(leftMargin + ringOuterRidus) + lineXPoint1;
        floats[3] = dip2px(topMargin + ringOuterRidus) + lineYPoint1;
        floats[4] = dip2px(leftMargin + ringOuterRidus) + lineXPoint1;
        floats[5] = dip2px(topMargin + ringOuterRidus) + lineYPoint1;
        if (point.x >= dip2px(leftMargin + ringOuterRidus)) {
            mPaint.setTextAlign(Paint.Align.LEFT);
            floats[6] = dip2px(leftMargin + ringOuterRidus) + lineXPoint1 + dip2px(extendLineWidth);
        } else {
            mPaint.setTextAlign(Paint.Align.RIGHT);
            floats[6] = dip2px(leftMargin + ringOuterRidus) + lineXPoint1 - dip2px(extendLineWidth);
        }
        floats[7] = dip2px(topMargin + ringOuterRidus) + lineYPoint1;


        mPaint.setColor(Color.parseColor(lengthenLineColor));
        if (position == mCurrentItem) {
            /**
             * 折线线段
             */
            canvas.drawLines(floats, mPaint);
        }
        mPaint.setTextSize(dip2px(showRateSize));

        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.parseColor(lengthenTextColor));

        if (position == mCurrentItem) {
            /**
             * 文字渲染
             */
            if (point.x >= dip2px(leftMargin + ringOuterRidus)) {
                canvas.drawText(rateList.get(position) + "%", floats[6] - dip2px(extendLineWidth), floats[7] - dip2px(showRateSize) / 3, mPaint);
            } else {
                canvas.drawText(rateList.get(position) + "%", floats[6] + dip2px(extendLineWidth), floats[7] - dip2px(showRateSize) / 3, mPaint);
            }
        }

        preRate = rateList.get(position);
    }

    List<Point> pointList = new ArrayList<>();
    List<Point> pointArcCenterList = new ArrayList<>();

    private void dealPoint(RectF rectF, float startAngle, float endAngle, List<Point> pointList) {
        Path orbit = new Path();
        //通过Path类画一个90度(180—270)的内切圆弧路径
        orbit.addArc(rectF, startAngle, endAngle);
        PathMeasure measure = new PathMeasure(orbit, false);
        Log.e("路径的测量长度:", "" + measure.getLength());
        float[] coords = new float[]{0f, 0f};
        int divisor = 1;
        measure.getPosTan(measure.getLength() / divisor, coords, null);
        Log.e("coords:", "x轴:" + coords[0] + " -- y轴:" + coords[1]);
        float x = coords[0];
        float y = coords[1];
        Point point = new Point(Math.round(x), Math.round(y));
        pointList.add(point);
    }

    private void drawOuter(Canvas canvas, int position) {
        if (rateList != null) {
            endAngle = getAngle(rateList.get(position));
        }
        canvas.drawArc(rectF, preAngle, endAngle, true, mPaint);

        if (isShowRate) {
            /**
             * 绘制折线外延
             */
            drawArcCenterPoint(canvas, position);
        }

        preAngle = preAngle + endAngle;
    }

    /**
     * 绘制内圆环
     */
    private void drawInouter(Canvas canvas, int position) {
        if (rateList != null) {
            endAngle = getAngle(rateList.get(position));
        }

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(getResources().getColor(R.color.white));
        paint.setStyle(Paint.Style.FILL);

        RectF rectF1 = new RectF(dip2px(mPaintWidth + 130),                                       //左
                dip2px(mPaintWidth + 60),                                       //上
                dip2px(circleCenterX + ringOuterRidus + mPaintWidth * 2 + 70),                    //右
                dip2px(circleCenterY + ringOuterRidus + mPaintWidth * 2 + 0)); //下

        if (mCurrentItem != position) {
            canvas.drawArc(rectF1, preAngle, endAngle, true, paint);
        }

        preAngle = preAngle + endAngle;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            int item = calcClickItem(event.getX(), event.getY());
            if (item >= 0 && item < rateList.size()) {
                setCurrentItem(item);
            } else {
                mCurrentItem = -1;
                invalidate();
            }
        }
        return super.onTouchEvent(event);
    }

    private int mCurrentItem = -1;
    public float[] angles;

    private void setCurrentItem(int item) {
        Log.e(RingView.class.getSimpleName(), "Click To Position " + item);
        if (mCurrentItem != item) {
            mCurrentItem = item;
        }
        invalidate();
    }

    private int calcClickItem(float x, float y) {
        if (rateList == null) return -1;
        final float centerX = rectF.centerX();
        final float centerY = rectF.centerY();
        float outerRadius = rectF.width() / 2;
        float innerRadius = 80;

        // 计算点击的坐标(x, y)和圆中心点形成的角度,角度从0-360,顺时针增加
        int clickedDegree = GeomTool.calcAngle(x, y, centerX, centerY);
        double clickRadius = GeomTool.calcDistance(x, y, centerX, centerY);

        if (clickRadius < innerRadius) {
            // 点击发生在小圆内部,也就是点击到标题区域
//            return -1;
        } else if (clickRadius > outerRadius) {
            // 点击发生在大圆环外
            return -2;
        }

        // 计算出来的clickedDegree是整个View原始的,被点击item需要考虑startAngle。
        int startAngle = -90;
        int angleStart = startAngle;
        for (int i = 0; i < angles.length; i++) {
            int itemStart = (angleStart + 360) % 360;
            float end = itemStart + angles[i];
            if (end >= 360f) {
                if (clickedDegree >= itemStart && clickedDegree < 360) return i;
                if (clickedDegree >= 0 && clickedDegree < (end - 360)) return i;
            } else {
                if (clickedDegree >= itemStart && clickedDegree < end) {
                    return i;
                }
            }

            angleStart += angles[i];
        }

        return -3;
    }

    private float preAngle = -90;
    private float endAngle = -90;

    /**
     * @param percent 百分比
     * @return
     */
    private float getAngle(float percent) {
        float a = 360f / maxTotal * percent;
        return a;
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public int dip2px(float dpValue) {
        return (int) (dpValue * dm.density + 0.5f);
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public int px2dip(float pxValue) {
        return (int) (pxValue / dm.density + 0.5f);
    }
}

xml中引入布局

<com.dq.demo.ui.view.RingView
    android:id="@+id/ringView"
    android:layout_marginTop="300dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

activity中使用

RingView ringView = (RingView) mView.findViewById(R.id.ringView);
// 添加的是颜色
List<Integer> colorList = new ArrayList<>();
colorList.add(R.color.color_ff3e60);
colorList.add(R.color.color_ffa200);
colorList.add(R.color.color_31cc64);
colorList.add(R.color.yellow_2);
colorList.add(R.color.grey_600);
colorList.add(R.color.text_top_1);
//  添加的是百分比
List<Float> rateList = new ArrayList<>();
rateList.add(10f);
rateList.add(15f);
rateList.add(25f);
rateList.add(40f);
rateList.add(30f);
rateList.add(28f);
ringView.setShow(colorList, rateList, true);

至此结束!!!
如有更好的方法,欢迎在评论区讨论。

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

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

相关文章

途乐证券-充电性能最高提升3.8倍,固态电池有新突破!

固态电池当时处于起步阶段&#xff0c;工业化仍需时刻。组织预计2030年商场空间有望达200亿元。 固态电池技能继续取得突破发展 过去10年&#xff0c;液态锂离子电池的能量密度现已提高了2倍~3倍&#xff0c;现在现已挨近理论上限。而全固态电池运用固体电解质替代了传统锂离子…

科技资讯|苹果Vision Pro预计2024年末全球发售

据彭博社记者古尔曼消息&#xff0c;苹果首款头显Vision Pro计划于2024年初在美国市场指定店铺进行开售&#xff0c;这些商店将会有专属区域用于产品演示&#xff0c;配备座位、配件和测量尺寸的工具等。知情人士透露&#xff0c;将有270家美国的苹果商店会销售Vision Pro&…

简述MySQL体系结构以及安装部署MySQL

目录 一、简述MySQL体系结构 1、连接层&#xff1a; 2、SQL层&#xff1a; 3、存储引擎层&#xff1a; 4、物理文件层&#xff1a; 5、一条SQL语句执行过程&#xff1a; 二、安装部署MySQL&#xff08;使用yum以及通用二进制方式&#xff09; 1、前期准备 1.1、关闭防火…

金九银十Java八股文面试题整理(含阿里、腾迅大厂java面试真题)

前言 看到了许多朋友在焦急的准备“金九银十”跳槽面试&#xff0c;甚至很多即将毕业的大学生都在备战秋招&#xff0c;对于学历还算优秀的大学生来说&#xff0c;这是一次离大厂最近的机会&#xff0c;毕竟是应届毕业生&#xff0c;不会对技术有非常严格的要求。 而对于工作…

注解开发配置实例

对比一下xm的配置&#xff1a; 新&#xff1a;注解开发&#xff0c;现在任何框架都有两套配置&#xff0c;一套xml配置&#xff0c;一套注解配置 现在我想把user Dao 和user serve 放到spring 中进行配置 service业务层 看到reipaositerei,就是我们Dao 层 用他来标注初始化方…

WORD模板替换,将文件给前端下载

1.word模板设计填充字段加{{填充字段名}} 2.后端依赖 <poi-tl.version>1.7.3</poi-tl.version> <poi.version>4.1.2</poi.version> <dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId>&l…

基于Mediapipe深度学习算法的手势识别系统【含python源码+PyqtUI界面+原理详解】-python手势识别 深度学习实战项目

功能演示 摘要&#xff1a;手势识别是一种通过技术手段识别视频图像中人物手势的技术。本文详细介绍了手势识别实现的技术原理&#xff0c;同时基于python与pyqt开发了一款带UI界面的手势识别系统软件&#xff0c;以便于进行结果显示。手势识别采用了mediapipe的深度学习算法进…

[MMDetection]绘制PR图

评价指标 平均精度与平均召回率 AP 是所有类别的平均值。传统上&#xff0c;这称为“平均精度”(mAP)。我们不区分 AP 和 mAP&#xff08;同样&#xff0c;AR 和 mAR&#xff09; APAP at IoU.50:.05:.95 (primary challenge metric)AP50AP at IoU.50 (PASCAL VOC metric)AP…

数据结构初阶--绪论

目录 一.什么是数据结构 二.什么是算法 三.算法的时间复杂度 四.算法的空间复杂度 五.复杂度练习 题一&#xff1a;消失的数字 题二&#xff1a;旋转数组 一.什么是数据结构 数据结构&#xff1a;是相互之间存在一种或多种特定关系的数据元素的集合。 数据结构的三要素…

【LittleXi】 N-gram模型(C++实现)

LittleXi N-gram模型&#xff08;C实现&#xff09;马尔科夫性 (独立性假设)代码实现英文训练版本中文训练版本 训练效果 N-gram模型&#xff08;C实现&#xff09; 定义&#xff1a;通俗地讲&#xff0c;就是利用前文的单词&#xff0c;来推算下一个最大概率出现的单词 马尔…

Web入门-Web服务器

Web服务器是一个程序软件&#xff0c;对HTTP协议的操作进行封装&#xff0c;使得程序员不必直接对程序进行操作&#xff0c;让Web开发更加便捷&#xff0c;简化web程序开发。主要功能是“通过网上信息浏览服务”。 Tomcat 概念&#xff1a;Tomcat是阿Apache软件基金会一个核心…

Coggle 30 Days of ML(23年7月)任务八:训练BILSTM模型

Coggle 30 Days of ML&#xff08;23年7月&#xff09;任务八&#xff1a;训练BILSTM模型 任务八&#xff1a;使用Word2Vec词向量&#xff0c;搭建BILSTM模型进行训练和预测 说明&#xff1a;在这个任务中&#xff0c;你将使用Word2Vec词向量&#xff0c;搭建BILSTM模型进行文…

【雕爷学编程】Arduino动手做(158)---VL53L0X激光测距模块3

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

从零开始制作一个Web蜜罐扫描器(2)

从零开始制作一个Web蜜罐扫描器(0)_luozhonghua2000的博客-CSDN博客 从零开始制作一个Web蜜罐扫描器(1)_luozhonghua2000的博客-CSDN博客 文件读取和写入实现 上面的工作已经完成了逻辑判断的部分,下面还需要进一步完善一些旁支末节的部分因为爬虫生成的文件是一个ison文件…

熵权法 —— matlab

目录 一、熵权法介绍 二、熵权法赋权步骤 1.指标正向化 mapminmax介绍 2.数据标准化 3.计算信息熵 4.计算权重以及得分 三、实例分析 1.读取数据 2.指标正向化 2.1 越小越优型处理 2.2 某点最优型指标处理 3.数据标准化 4.计算信息熵 5.计算权重 6.计算得分 总…

第三章 技术选型

1、需要考虑以下几点 可控性 必须有人可以兜底&#xff0c;可以解决 稳定性 修改的版本最好可以上下兼容 核心项目不要用最新的技术 commit的时长 是否有团队维护 适用性 更多人都会的技术 易用性 学习曲线 2、有哪些好用的移动端React组件库 3、为什么使用NestJS 1、最像…

【LeetCode: 16. 最接近的三数之和 | 双指针专题 】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

港联证券|涨停不封板意味着什么?

涨停不封板或许是因为股票价格到达了涨停价&#xff0c;可是买卖量还未到达涨停板规则的要求&#xff0c;因而买卖并没有被暂停。涨停不封板也有或许是个股当天涨停之后&#xff0c;又呈现开板的状况。 总归&#xff0c;涨停不封板意味着上方的抛压较多&#xff0c;空方力气大于…

Node.js开发

Node.js是一个基于V8 JavaScript引擎的JavaScript运行时环境。 也就是说Node.js基于V8引擎来执行JavaScript的代码&#xff0c;但是不仅仅只有V8引擎&#xff1a;  前面我们知道V8可以嵌入到任何C 应用程序中&#xff0c;无论是Chrome还是Node.js&#xff0c;事实上都是嵌入…

C++智能指针使用及原理

在讲解之前&#xff0c;先讲述一种RAII思想. 目录 RAII 智能指针原理 auto_ptr auto_ptr的介绍 auto_ptr的实现 unique_ptr unique_ptr的介绍 unique_ptr实现 shared_ptr shared_ptr的介绍 shared_ptr实现 weak_ptr weak_ptr的介绍 weak_ptr的实现 RAII RAII&a…