开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

最小生成树Prim算法Java版 最小生成树Prim算法Ja

发表于 2021-11-29

「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战」

最小生成树Prim算法Java版

image.png

算法描述:

  1. 在一个加权连通图中,顶点集合V,边集合为E
  2. 任意选出一个点作为初始顶点,标记为visit,计算所有与之相连接的点的距离,选择距离最短的,标记visit.
  3. 重复以下操作,直到所有点都被标记为visit:
    在剩下的点钟,计算与已标记visit点距离最小的点,标记visit,证明加入了最小生成树。

对比克鲁斯卡尔算法

假设网中有n个节点和e条边,普利姆算法的时间复杂度是O(n^2),克鲁斯卡尔算法的时间复杂度是O(eloge),可以看出前者与网中的边数无关,而后者相反。因此,普利姆算法适用于边稠密的网络而克鲁斯卡尔算法适用于求解边稀疏的网。

克鲁斯卡尔算法的时间复杂度主要由排序方法决定,而克鲁斯卡尔算法的排序方法只与网中边的条数有关,而与网中顶点的个数无关,当使用时间复杂度为O(elog2e)的排序方法时,克鲁斯卡尔算法的时间复杂度即为O(log2e),因此当网的顶点个数较多、而边的条数较少时,使用克鲁斯卡尔算法构造最小生成树效果较好

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
ini复制代码package com.company;
import java.util.*;

public class MinTree {
public static void main(String[] args) {
int[][] arr = new int[][]{
// 0 1 2 3 4 5 6 7 8
{-0, 4, 0, 0, 0, 0, 0, 7, 0},
{4, -0, 8, 0, 0, 0, 0, 11, 0},
{0, 8, -0, 7, 0, 4, 0, 0, 2},
{0, 0, 7, -0, 9, 14, 0, 0, 0},
{0, 0, 0, 9, -0, 10, 0, 0, 0},
{0, 0, 4, 14, 10, -0, 2, 0, 0},
{0, 0, 0, 0, 0, 2, -0, 1, 6},
{7, 11, 0, 0, 0, 0, 1, -0, 7},
{0, 0, 2, 0, 0, 0, 6, 7,-0}
};

boolean[] visited=new boolean[arr.length];
visited[0]=true;
PriorityQueue<Integer[]> queue=new PriorityQueue<>(new Comparator<Integer[]>() {
@Override
public int compare(Integer[] o1, Integer[] o2) {
return o1[0]-o2[0];
}
});

int count=1;
for (int i = 0; i < arr.length; i++) {
if(arr[0][i]!=0&&!visited[i]){
queue.add(new Integer[]{arr[0][i],0,i});
}
}
while (count<arr.length){
Integer[] temp=null;
while (!queue.isEmpty()){
temp=queue.poll();
if(visited[temp[2]])continue;
break;
}
visited[temp[2]]=true;
System.out.println("起点:"+temp[1]+" 终点:"+temp[2]+" 长度为:"+temp[0]);
for (int i = 0; i < arr.length; i++) {
if(arr[temp[2]][i]!=0&&!visited[i]){
queue.add(new Integer[]{arr[temp[2]][i],temp[2],i});
}
}
count++;
}
}
}

输出

1
2
3
4
5
6
7
8
9
vbnet复制代码起点:0 终点:1  长度为:4
起点:0 终点:7 长度为:7
起点:7 终点:6 长度为:1
起点:6 终点:5 长度为:2
起点:5 终点:2 长度为:4
起点:2 终点:8 长度为:2
起点:2 终点:3 长度为:7
起点:3 终点:4 长度为:9
Process finished with exit code 0

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

786 第 K 个最小的素数分数 「优先队列(堆)」&「

发表于 2021-11-29

「这是我参与11月更文挑战的第 29 天,活动详情查看:2021最后一次更文挑战」。

题目描述

这是 LeetCode 上的 786. 第 K 个最小的素数分数 ,难度为 困难。

Tag : 「优先队列」、「多路归并」、「二分」、「双指针」

给你一个按递增顺序排序的数组 arr 和一个整数 k 。

数组 arr 由 111 和若干 素数 组成,且其中所有整数互不相同。

对于每对满足 0<i<j<arr.length0 < i < j < arr.length0<i<j<arr.length 的 iii 和 jjj ,可以得到分数 arr[i]/arr[j]arr[i] / arr[j]arr[i]/arr[j] 。

那么第 kkk 个最小的分数是多少呢? 以长度为 222 的整数数组返回你的答案, 这里 answer[0]==arr[i]answer[0] == arr[i]answer[0]==arr[i] 且 answer[1]==arr[j]answer[1] == arr[j]answer[1]==arr[j] 。

示例 1:

1
2
3
4
5
6
7
ini复制代码输入:arr = [1,2,3,5], k = 3

输出:[2,5]

解释:已构造好的分数,排序后如下所示:
1/5, 1/3, 2/5, 1/2, 3/5, 2/3
很明显第三个最小的分数是 2/5

示例 2:

1
2
3
ini复制代码输入:arr = [1,7], k = 1

输出:[1,7]

提示:

  • 2<=arr.length<=10002 <= arr.length <= 10002<=arr.length<=1000
  • 1<=arr[i]<=3∗1041 <= arr[i] <= 3 * 10^41<=arr[i]<=3∗104
  • arr[0]==1arr[0] == 1arr[0]==1
  • arr[i]arr[i]arr[i] 是一个 素数 ,i>0i > 0i>0
  • arrarrarr 中的所有数字 互不相同 ,且按严格递增排序
  • 1<=k<=arr.length∗(arr.length−1)/21 <= k <= arr.length * (arr.length - 1) / 21<=k<=arr.length∗(arr.length−1)/2

优先队列(堆)

数据范围只有 10310^3103,直接扫描所有点对的计算量不超过 10610^6106。

因此我们可以使用「扫描点对」+「优先队列(堆)」的做法,使用二元组 (arr[i],arr[j])(arr[i], arr[j])(arr[i],arr[j]) 进行存储,构建大小为 kkk 的大根堆。

根据「堆内元素多少」和「当前计算值与堆顶元素的大小关系」决定入堆行为:

  • 若堆内元素不足 kkk 个,直接将当前二元组进行入堆;
  • 若堆内元素已达 kkk 个,根据「当前计算值 arr[i]arr[j]\frac{arr[i]}{arr[j]}arr[j]arr[i]​ 与堆顶元素 peek[0]peek[1]\frac{peek[0]}{peek[1]}peek[1]peek[0]​ 的大小关系」进行分情况讨论:
    • 如果当前计算值比堆顶元素大,那么当前元素不可能是第 kkk 小的值,直接丢弃;
    • 如果当前计算值比堆顶元素小,那么堆顶元素不可能是第 kkk 小的值,使用当前计算值置换掉堆顶元素。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Java复制代码class Solution {
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
int n = arr.length;
PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->Double.compare(b[0]*1.0/b[1],a[0]*1.0/a[1]));
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
double t = arr[i] * 1.0 / arr[j];
if (q.size() < k || q.peek()[0] * 1.0 / q.peek()[1] > t) {
if (q.size() == k) q.poll();
q.add(new int[]{arr[i], arr[j]});
}
}
}
return q.poll();
}
}
  • 时间复杂度:扫描所有的点对复杂度为 O(n2)O(n^2)O(n2);将二元组入堆和出堆的复杂度为 O(log⁡k)O(\log{k})O(logk)。整体复杂度为 O(n2∗log⁡k)O(n^2 * \log{k})O(n2∗logk)
  • 空间复杂度:O(k)O(k)O(k)

多路归并

在解法一中,我们没有利用「数组内元素严格单调递增」的特性。

由于题目规定所有的点对 (i,j)(i, j)(i,j) 必须满足 i<ji < ji<j,即给定 arr[j]arr[j]arr[j] 后,其所能构建的分数个数为 jjj 个,而这 jjj 个分数值满足严格单调递增:arr[0]arr[j]<arr[1]arr[j]<arr[2]arr[j]<…<arr[j−1]arr[j]\frac{arr[0]}{arr[j]} < \frac{arr[1]}{arr[j]} < \frac{arr[2]}{arr[j]} < … < \frac{arr[j - 1]}{arr[j]}arr[j]arr[0]​<arr[j]arr[1]​<arr[j]arr[2]​<…<arr[j]arr[j−1]​。

问题等价于我们从 n−1n - 1n−1 个(下标 000 作为分母的话,不存在任何分数)有序序列中找到第 kkk 小的数值。这 n−1n - 1n−1 个序列分别为:

  • [arr[0]arr[1]][\frac{arr[0]}{arr[1]}][arr[1]arr[0]​]
  • [arr[0]arr[2],arr[1]arr[2]][\frac{arr[0]}{arr[2]}, \frac{arr[1]}{arr[2]}][arr[2]arr[0]​,arr[2]arr[1]​]
  • [arr[0]arr[3],arr[1]arr[3],arr[2]arr[3]][\frac{arr[0]}{arr[3]}, \frac{arr[1]}{arr[3]}, \frac{arr[2]}{arr[3]}][arr[3]arr[0]​,arr[3]arr[1]​,arr[3]arr[2]​]
    …
  • [arr[0]arr[j],arr[1]arr[j],arr[2]arr[j],…,arr[j−1]arr[j]][\frac{arr[0]}{arr[j]}, \frac{arr[1]}{arr[j]}, \frac{arr[2]}{arr[j]}, … , \frac{arr[j - 1]}{arr[j]}][arr[j]arr[0]​,arr[j]arr[1]​,arr[j]arr[2]​,…,arr[j]arr[j−1]​]

问题彻底切换为「多路归并」问题,我们使用「优先队列(堆)」来维护多个有序序列的当前头部的最小值即可。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Java复制代码class Solution {
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
int n = arr.length;
PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->{
double i1 = arr[a[0]] * 1.0 / arr[a[1]], i2 = arr[b[0]] * 1.0 / arr[b[1]];
return Double.compare(i1, i2);
});
for (int i = 1; i < n; i++) q.add(new int[]{0, i});
while (k-- > 1) {
int[] poll = q.poll();
int i = poll[0], j = poll[1];
if (i + 1 < j) q.add(new int[]{i + 1, j});
}
int[] poll = q.poll();
return new int[]{arr[poll[0]], arr[poll[1]]};
}
}
  • 时间复杂度:起始将 n−1n - 1n−1 个序列的头部元素放入堆中,复杂度为 O(nlog⁡n)O(n\log{n})O(nlogn);然后重复 kkk 次操作得到第 kkk 小的值,复杂度为 O(klog⁡n)O(k\log{n})O(klogn)。整体复杂度为 O(max⁡(n,k)∗log⁡n)O(\max(n, k) * \log{n})O(max(n,k)∗logn)
  • 空间复杂度:O(n)O(n)O(n)

二分 + 双指针

进一步,利用 arrarrarr 递增,且每个点对 (i,j)(i, j)(i,j) 满足 i<ji < ji<j,我们可以确定 (i,j)(i, j)(i,j) 对应的分数 arr[i]arr[j]\frac{arr[i]}{arr[j]}arr[j]arr[i]​ 必然落在 [0,1][0, 1][0,1] 范围内。

假设最终答案 arr[i]arr[j]\frac{arr[i]}{arr[j]}arr[j]arr[i]​ 为 xxx,那么以 xxx 为分割点的数轴(该数轴上的点为 arrarrarr 所能构造的分数值)上具有「二段性」:

  • 小于等于 xxx 的值满足:其左边分数值个数小于 kkk 个;
  • 大于 xxx 的值不满足:其左边分数值个数小于 kkk 个(即至少有 kkk 个)。

而当确定 arr[j]arr[j]arr[j] 时,利用 arrarrarr 有序,我们可以通过「双指针」快速得知,满足 arr[i]arr[j]<=x\frac{arr[i]}{arr[j]} <= xarr[j]arr[i]​<=x 的分子位置在哪(找到最近一个满足 arr[i]arr[j]>x\frac{arr[i]}{arr[j]} > xarr[j]arr[i]​>x 的位置)。

另外,我们可以在每次 check 的同时,记录下相应的 arr[i]arr[i]arr[i] 和 arr[j]arr[j]arr[j]。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Java复制代码class Solution {
double eps = 1e-8;
int[] arr;
int n, a, b;
public int[] kthSmallestPrimeFraction(int[] _arr, int k) {
arr = _arr;
n = arr.length;
double l = 0, r = 1;
while (r - l > eps) {
double mid = (l + r) / 2;
if (check(mid) >= k) r = mid;
else l = mid;
}
return new int[]{a, b};
}
int check(double x){
int ans = 0;
for (int i = 0, j = 1; j < n; j++) {
while (arr[i + 1] * 1.0 / arr[j] <= x) i++;
if (arr[i] * 1.0 / arr[j] <= x) ans += i + 1;
if (Math.abs(arr[i] * 1.0 / arr[j] - x) < eps) {
a = arr[i]; b = arr[j];
}
}
return ans;
}
}
  • 时间复杂度:二分次数取决于精度,精度为 C=108C = 10^8C=108,二分复杂度为 O(log⁡C);O(\log{C});O(logC);check 的复杂度为 O(n)O(n)O(n)。整体复杂度为 O(n∗log⁡C)O(n * \log{C})O(n∗logC)
  • 空间复杂度:O(1)O(1)O(1)

最后

这是我们「刷穿 LeetCode」系列文章的第 No.786 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

数据分析从零开始实战,Pandas读取HTML页面+数据处理

发表于 2021-11-29

这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战

零 写在前面

本系列学习笔记参考书籍: 《数据分析实战》托马兹·卓巴斯,会将自己学习本书的笔记分享给大家,同样开成一个系列『数据分析从零开始实战』。

点击查看第一篇文章:# 数据分析从零开始实战,Pandas读写CSV数据

点击查看第二篇文章:# 数据分析从零开始实战,Pandas读写TSV/Json数据

点击查看第三篇文章:# 数据分析从零开始实战,Pandas读写Excel/XML数据

前面三篇文章讲了数据分析虚拟环境创建和pandas读写CSV、TSV、JSON、Excel、XML格式的数据,今天我们继续探索pandas。

一 基本知识概要

1.利用Pandas检索HTML页面(read_html函数)

2.实战训练使用read_html函数直接获取页面数据

3.基本数据处理:表头处理、dropna和fillna详解

4.基本数据可视化分析案例

二 开始动手动脑

1.Pandas的read_html函数

这里我们要介绍的是Pandas里解析HTML页面的函数:read_html。

查看源码后我们可以看出,该函数的参数比较多,下面我挑重点给大家解释几个。

(1)io(最关键参数)

源码注释

1
2
3
typescript复制代码		A URL, a file-like object, or a raw string containing HTML. Note that
lxml only accepts the http, ftp and file url protocols. If you have a
URL that starts with ``'https'`` you might try removing the ``'s'``.

我的理解

1
2
3
css复制代码	数据地址(网页地址、包含HTML的文件地址或者字符串)。
注意lxml只接受HTTP、FTP和文件URL协议。
如果你有以“https”开头的URL,你可以尝试删除“s”再传入参数。

(2)match

源码注释

1
2
3
4
5
6
7
vbnet复制代码		str or compiled regular expression, optional
The set of tables containing text matching this regex or string will be
returned. Unless the HTML is extremely simple you will probably need to
pass a non-empty string here. Defaults to '.+' (match any non-empty
string). The default value will return all tables contained on a page.
This value is converted to a regular expression so that there is
consistent behavior between Beautiful Soup and lxml.

我的理解

1
2
3
4
5
css复制代码	字符串或编译的正则表达式,可选
包含与此正则表达式或字符串匹配的文本的一组表将返回。
除非HTML非常简单,否则您可能需要在此处传递一个非空字符串。
默认为“.+”(匹配任何非空字符串)。默认值将返回页面上包含的所有<table>标签包含的表格。
该值将转换为正则表达式,以便Beautiful Soup和LXML之间一致。

(3)flavor

源码注释

1
2
3
4
5
javascript复制代码		flavor : str or None, container of strings
The parsing engine to use. 'bs4' and 'html5lib' are synonymous with
each other, they are both there for backwards compatibility. The
default of ``None`` tries to use ``lxml`` to parse and if that fails it
falls back on ``bs4`` + ``html5lib``.

我的理解

1
2
3
arduino复制代码	要使用的解析引擎。'bs4'和'html5lib'是彼此的同义词,
它们都是为了向后兼容。默认为空,尝试用于lxml解析的默认值,
如果失败,则使用bs4和 html5lib。

2.数据基本处理

(1)处理列名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
python复制代码# 处理列名
import re
# 匹配字符串中任意空白字符的正则表达式
space = re.compile(r"\s+")

def fix_string_spaces(columnsToFix):
'''
将列名中的空白字符转变成下划线
'''
tempColumnNames = [] # 保存处理后的列名
# 循环处理所有列
for item in columnsToFix:
# 匹配到
if space.search(item):
# 处理并加入列表
tempColumnNames.append('_'.join((space.split(item))))
'''
这句有点长涉及到列表的一些操作,我解释一下
str1.split(str2) str1 表示被分隔的字符串;str2表示分隔字符串
str3.join(list1) str2 表示按什么字符串进行连接;list1表示待连接的列表
list2.append(str4) 表示在列表list2的末尾添加str4这个元素
'''
else :
# 否则直接加入列表
tempColumnNames.append(item)
return tempColumnNames

上面这段代码来自书本,其目的是处理列名,将列名里为空的字符转变成-符号,仔细一想,其实这个是可以通用的,比如处理某行数据里为空的,处理某个列表里为空的数据等,复用性很强。

(2)对缺失数据处理之dropna函数

dropna()函数:对缺失的数据进行过滤。

常用参数解析:
axis:

源码注释

1
2
3
4
5
sql复制代码		 axis : {0 or 'index', 1 or 'columns'}, default 0
Determine if rows or columns which contain missing values are removed.
* 0, or 'index' : Drop rows which contain missing values.
* 1, or 'columns' : Drop columns which contain missing value.
.. deprecated:: 0.23.0: Pass tuple or list to drop on multiple axes.

我的理解

1
markdown复制代码	少用,默认值为0,表示删除包含缺少值的行;值为1,表示删除包含缺少值的列。

how:

源码注释

1
2
3
4
sql复制代码		how : {'any', 'all'}, default 'any'
Determine if row or column is removed from DataFrame, when we have at least one NA or all NA.
* 'any' : If any NA values are present, drop that row or column.
* 'all' : If all values are NA, drop that row or column.

我的理解

1
2
r复制代码	默认值为any,表示如果存在任何NA(空)值,则删除该行或列;
值为all,表示如果全都是NA值,则删除该行或列。

thresh:

源码注释

1
2
arduino复制代码		thresh : int, optional
Require that many non-NA values.

我的理解

1
markdown复制代码	不为NA的个数,满足要求的行保留,不满足的行被删除。

inplace:

源码注释

1
2
python复制代码		inplace : bool, default False
If True, do operation inplace and return None.

我的理解

1
2
3
python复制代码	默认为False,表示不在原对象上操作,
而是复制一个新的对象进行操作并返回;
值为True时,表示直接在原对象上进行操作。
(3)对缺失数据处理之fillna函数

fillna()函数:用指定值或插值的方法填充缺失数据。

常用参数解析:
value:

源码注释

1
2
3
4
5
6
perl复制代码	value : scalar, dict, Series, or DataFrame
Value to use to fill holes (e.g. 0), alternately a
dict/Series/DataFrame of values specifying which value to use for
each index (for a Series) or column (for a DataFrame). (values not
in the dict/Series/DataFrame will not be filled). This value cannot
be a list.

我的理解

1
2
css复制代码简单点说,就是替换NA(空值)的值。如果是直接给值,表示全部替换;
如果是字典: {列名:替换值} 表示替换掉该列包含的所有空值。

method:

源码注释

1
2
3
4
sql复制代码	method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
Method to use for filling holes in reindexed Series
pad / ffill: propagate last valid observation forward to next valid
backfill / bfill: use NEXT valid observation to fill gap

我的理解

1
2
3
4
复制代码在重新索引系列中填充空白值的方法。
pad / ffill:按列检索,将最后一次不为空的值赋给下一个空值。
backfill / bfill:按列检索,将下一个不为空的值赋给该空值。
注意:该参数不可与value 同时存在

limit:

源码注释

1
2
3
4
5
6
csharp复制代码limit : int, default None
If method is specified, this is the maximum number of consecutive.
NaN values to forward/backward fill. In other words, if there is a gap
with more than this number of consecutive NaNs, it will only be partially
filled. If method is not specified, this is the maximum number of entries
along the entire axis where NaNs will be filled. Must be greater than 0 if not None.

我的理解

1
2
bash复制代码其实很简单,就是按列搜索空值,然后limit的值表示最大的连续填充空值个数。
比如:limit=2,表示一列中从上到下搜索,只替换前两个空值,后面都不替换。

吐个槽:别看源码里的英文注释单词都很简单,但,太简单了,根本连不成句子,我都是一个个实践+表面翻译,然后才能弄明白参数的意思。

3.数据爬取实战训练

五行代码爬取2019富豪榜(60亿美元以上的)

1
2
3
4
5
6
7
8
9
10
python复制代码import pandas as pd

# 排行榜
for i in range(15):
# 页面地址
url = "https://www.phb123.com/renwu/fuhao/shishi_%d.html" % (i+1)
# 调用read_html函数,解析页面获取数据 List
url_read = pd.read_html(url, header=0)[0]
# 将数据存入csv文件
url_read.to_csv(r'rich_list.csv', mode='a', encoding='utf_8_sig', header=0, index=False)

页面数据:

爬取结果

通过上面实战,你需要知道:
1、不要觉得怎么这么简单啊(是因为我找好了网站,这个网站数据里只有一个table,数据也比较干净);
2、真正工作中网站可能是不配合的,数据可能是不配合的,这个时候最好的方法是见仁见智,多看源代码。

4.数据可视化分析实战训练

基于我们上面拿到的数据,我们做个简单的数据可视化和分析报告。
上面我们已经拿到了2019富豪榜(60亿美元以上的)的数据,包含排名、姓名、财富数额、财富来源、国家这些信息,明确数据属性后,我们就该想一下我们能从那些方面去分析那些问题?
我想到的几个方面:
(1)排行榜上各个国家的人数各多少?那些国家最多?
(2)那些公司上榜的人数最多?
(3)排行榜上的人所在的行业分布?

(0)读取数据和数据可视化

读取数据我们直接利用pandans的read_csv函数。

1
2
3
4
5
6
7
8
9
10
11
12
python复制代码import pandas as pd

# 原始数据文件路径
rpath_csv = 'rich_list.csv'
# 读取数据
csv_read = pd.read_csv(rpath_csv)
# 提取出来的数据是pandans的Series对象
# 后期处理可以直接转换成列表
name_list = csv_read["名字"]
money_list = csv_read["财富(10亿美元)"]
company_list = csv_read["财富来源"]
country_list = csv_read["国家/地区"]

数据可视化,我们从最简单的pyecharts模块。

1
ini复制代码	pip install pyecharts==0.5.11
(1)排行榜上各个国家的人数各多少?那些国家最多?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
python复制代码# 排行榜上各个国家的人数各多少?那些国家最多?
"""
1、统计数据
利用collections模块的Counter函数
"""
country_list = list(country_list)
from collections import Counter
dict_number = Counter(country_list)

"""
2、数据可视化
利用pyecharts模块的Bar类
"""
bar = Bar("富豪国家分布柱状图")
bar.add("富豪", key_list, values_list, is_more_utils=True, is_datazoom_show=True,
xaxis_interval=0, xaxis_rotate=30, yaxis_rotate=30, mark_line=["average"], mark_point=["max", "min"])
bar.render("rich_country.html")

从上面数据,我们可以很明显的发现,富豪榜上富豪的国籍,美国居多,而且可以说是遥遥领先,总共是300人,美国国籍的有106人,占了总数据的1/3还多,这个比较好理解,美国一直是一个超级大国,各个方面的发展都位列全球前列。

位列第二的是中国,占了43人,也是特别多的,而且对于中国,发展到现在是非常非常不容易的,从1949年成立,到今年2019年,建国70年,从“为中华之崛起而读书”到“为实现中国梦、建设富强民主文明和谐美丽的社会主义现代化强国而奋斗”,作为中国人,我是骄傲的。

第三名是德国和俄罗斯,各占20人,德国是个工业大国,欧洲最大经济体,所以德国的强健是显而易见的,另外俄罗斯,世界面积最大的国家,曾经苏联也是世界第二经济强国,虽然苏联解体后不如从前,但近几年普京执政,经济稳步回升。

再后面的国家中以欧洲国家居多,其中第五是印度,其科技实力十分发达。

(2)那些公司上榜的人数最多?

注意哦~能上这个榜的,财富最低都是60亿美元,从统计数据来看,玛氏公司上榜人数最多,有6个上榜的富豪来自玛氏公司,其次是沃尔玛百货有限公司,有3个人来自该公司,这两个公司都是日化类公司,接下来的:微软、Facebook、谷歌都是科技类公司

不查一下,我还真不知道原来“饿货,快来条士力架”的士力架、“德芙,纵享丝滑”的德芙是来自一家公司的,而且是玛氏公司的,此处双击666。另外沃尔玛在2018年被评选为世界五百强的第一位,莫种意义来说,这就是宇宙最强公司啊~(小时候我一直以为富迪是最厉害的超市,长大后我又以为万达是最厉害的超市,现在,我知道了,是沃尔玛!)

(3)排行榜上的人所在的行业分布?等你回答

这部分其实是不好做的,因为我们获取到的数据里没有直接和行业相连的数据,唯一能和行业有点联系的就是公司,这就需要我们通过公司名称去判断(或者在网上获取)该公司的类别属性,比如是互联网公司,还是传统行业等等方面。

三 送你的话

坚持 and 努力 : 终有所获。

思想很复杂,

实现很有趣,

只要不放弃,

终有成名日。

—《老表打油诗》

下期见,我是爱猫爱技术的老表,如果觉得本文对你学习有所帮助,欢迎点赞、评论、关注我!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

ORACLE一键安装11G/12C/18C/19C并建库脚本

发表于 2021-11-29

「这是我参与11月更文挑战的第29天,活动详情查看:2021最后一次更文挑战」

前言

ORACLE一键安装单机11G/12C/18C/19C并建库脚本(shell)

一、介绍

本脚本旨在通过无人值守方式初始化安装Oracle软件。

Github

功能:

  • 1.配置操作系统
  • 2.安装Grid软件
  • 3.安装Oracle软件
  • 4.安装PSU&&RU补丁
  • 5.创建数据库
  • 6.数据库优化

目前支持:

ORACLE版本: 11GR2、12CR2、18C、19C。
操作系统版本: Linux6(x86_64)、Linux7(x86_64)、Linux8(x86_64)。

19C 操作系统要求

  • Red Hat Enterprise Linux 8: 4.18.0-80.el8.x86_64 or later
  • Red Hat Enterprise Linux 7.5: 3.10.0-862.11.6.el7.x86_64 or later
包括Single、Oracle Restart、Oracle Real Cluster模式。

目前RAC只支持双节点安装

二、使用

2.1 安装准备

2.1.1 创建软件目录,例如:/soft

mkdir /soft

2.1.2 挂载镜像 ISO

1
2
3
4
5
shell复制代码## 通过cdrom挂载
mount /dev/cdrom /mnt
or
##通过安装镜像源挂载
mount -o loop /soft/rhel-server-7.9-x86_64-dvd.iso /mnt

2.1.3 上传安装介质和脚本到软件目录

2.1.4 设置好主机IP(Public&&Private)

1
2
3
4
5
6
7
8
9
shell复制代码#For Example:
##Linux 6
vi /etc/sysconfig/network-scripts/ifcfg-eth0
IPADDR=10.211.55.100
NETMASK=255.255.255.0
GATEWAY=10.211.55.1

##Linux 7
nmcli connection modify eth0 ipv4.addresses 10.211.55.100/24 ipv4.gateway 10.211.55.1 ipv4.method manual autoconnect yes

2.1.5 如果需要安装Rac,需提前配置ASM共享磁盘

1
2
3
4
5
6
7
8
9
10
11
12
bash复制代码##For Example
##通过iscsi配置共享盘
1.StarWind(Windows)
2.Openfiler(Linux)

##假设已配置好共享存储服务器,IP为10.211.55.18。
##配置iscsi连接共享存储
yum install -y iscsi-initiator-utils*
##输出targetname
iscsiadm -m discovery -t st -p 10.211.55.18
##连接共享存储
iscsiadm -m node -T iqn.2008-08.com.starwindsoftware:10.211.55.18-lucifer -p 10.211.55.18 -l

共享存储之–StarWind高级配置

共享存储之–Openfiler高级配置

2.2 脚本参数

2.2.1 通过运行 ./OracleShellInstall --help 可以查看参数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Para复制代码-i,			--PUBLICIP				PUBLICIP NETWORK ADDRESS
-n, --HOSTNAME HOSTNAME(orcl)
-rs, --ROOTPASSWD ROOT USER PASSWORD(oracle)
-gp, --GRIDPASSWD GRID USER PASSWORD(oracle)
-op, --ORAPASSWD ORACLE USER PASSWORD(oracle)
-b, --ENV_BASE_DIR ORACLE BASE DIR(/u01/app)
-o, --ORACLE_SID ORACLE_SID(orcl)
-s, --CHARACTERSET ORACLE CHARACTERSET(AL32UTF8)
-c, --ISCDB IS CDB OR NOT(FALSE)
-pb, --PDBNAME PDBNAME(pdb01)
-pb1, --RAC1PUBLICIP RAC NODE ONE PUBLIC IP
-pb2, --RAC2PUBLICIP RAC NODE SECONED PUBLIC IP
-vi1, --RAC1VIP RAC NODE ONE VIRTUAL IP
-vi2, --RAC2VIP RAC NODE SECOND VIRTUAL IP
-pi1, --RAC1PRIVIP RAC NODE ONE PRIVATE IP
-pi2, --RAC2PRIVIP RAC NODE SECOND PRIVATE IP
-pi3, --RAC1PRIVIP1 RAC NODE ONE PRIVATE IP
-pi4, --RAC2PRIVIP1 RAC NODE SECOND PRIVATE IP
-puf, --RACPUBLICFCNAME RAC PUBLIC FC NAME
-prf, --RACPRIVFCNAME RAC PRIVATE FC NAME
-prf1, --RACPRIVFCNAME1 RAC PRIVATE FC NAME
-si, --RACSCANIP RAC SCAN IP
-dn, --ASMDATANAME RAC ASM DATADISKGROUP NAME(DATA)
-on, --ASMOCRNAME RAC ASM OCRDISKGROUP NAME(OCR)
-dd, --DATA_BASEDISK RAC DATADISK DISKNAME
-od, --OCRP_BASEDISK RAC OCRDISK DISKNAME
-or, --OCRREDUN RAC OCR REDUNDANCY(EXTERNAL|NORMAL|HIGH)
-dr, --DATAREDUN RAC DATA REDUNDANCY(EXTERNAL|NORMAL|HIGH)
-ts, --TIMESERVER RAC TIME SERVER IP
-txh --TuXingHua Tu Xing Hua Install
-udev --UDEV Whether Auto Set UDEV
-dns --DNS RAC CONFIGURE DNS(Y|N)
-dnss --DNSSERVER RAC CONFIGURE DNSSERVER LOCAL(Y|N)
-dnsn --DNSNAME RAC DNSNAME(orcl.com)
-dnsi --DNSIP RAC DNS IP
-m, --ONLYCONFIGOS ONLY CONFIG SYSTEM PARAMETER(Y|N)
-g, --ONLYINSTALLGRID ONLY INSTALL GRID SOFTWARE(Y|N)
-w, --ONLYINSTALLORACLE ONLY INSTALL ORACLE SOFTWARE(Y|N)
-ocd, --ONLYCREATEDB ONLY CREATE DATABASE(Y|N)
-gpa, --GRID RELEASE UPDATE GRID RELEASE UPDATE(32072711)
-opa, --ORACLE RELEASE UPDATE ORACLE RELEASE UPDATE(32072711)

2.3 脚本运行

Notes:必须提前上传所需安装介质,否则安装失败

cdb 12C后开始支持容器,只需要加上如下参数即可:

1
2
shellscript复制代码-c TRUE `# cdb` \
-pb singlepdb `# pdbname` \

脚本须Root用户下执行

chmod +x OracleShellInstall.sh

2.3.1 Single模式安装

1
2
3
4
5
6
7
8
shellscript复制代码cd /soft
./OracleShellInstall.sh -i 10.211.55.100 `#Public ip`\
-n single `# hostname`\
-o nocdb `# oraclesid`\
-op oracle `# oracle user password`\
-b /oracle/app `# install basedir`\
-s AL32UTF8 `# characterset`\
-opa 31537677 `# oracle psu number`

2.3.2 Oracle Restart模式安装

1
2
3
4
5
6
7
8
9
10
11
12
shellscript复制代码cd /soft
./OracleShellInstall.sh -i 10.211.55.100 `#Public ip`\
-n restart `# hostname`\
-o nocdb `# oraclesid`\
-gp oracle `# grid user password`\
-op oracle `# oracle user password`\
-b /u01/app `# install basedir`\
-s AL32UTF8 `# characterset`\
-dd /dev/sde,/dev/sdf `# asm data disk`\
-dn DATA `# asm data diskgroupname`\
-dr EXTERNAL `# asm data redundancy`\
-gpa 31718723 `# grid psu number`

2.3.3 Oracle Rac模式安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
shellscript复制代码cd /soft
./OracleShellInstall.sh -i 10.211.55.100 `#Public ip`\
-n rac `# hostname`\
-rs oracle `# root password`\
-op oracle `# oracle password`\
-gp oracle `# grid password`\
-b /u01/app `# install basedir`\
-o nocdb `# oraclesid`\
-s AL32UTF8 `# characterset`\
-pb1 10.211.55.100 -pb2 10.211.55.101 `# node public ip`\
-vi1 10.211.55.102 -vi2 10.211.55.103 `# node virtual ip`\
-pi1 10.10.1.1 -pi2 10.10.1.2 `# node private ip`\
-puf eth0 -prf eth1 `# network fcname`\
-si 10.211.55.105 `# scan ip`\
-dd /dev/sde,/dev/sdf `# asm data disk`\
-od /dev/sdb,/dev/sdc,/dev/sdd `# asm ocr disk`\
-or EXTERNAL `# asm ocr redundancy`\
-dr EXTERNAL `# asm data redundancy`\
-on OCR `# asm ocr diskgroupname`\
-dn DATA `# asm data diskgroupname`\
-gpa 32580003 `# GRID PATCH`

三. 功能介绍

3.1 配置节点间互信

RAC模式自动配置节点间互信

3.2 配置DNS服务器

1
2
3
4
bash复制代码-dns Y `# DNS` \
-dnss Y `# LOCAL DNSSERVER` \
-dnsn lucifer.com `# DNS SERVER NAME` \
-dnsi 10.211.55.200 `# DNS SERVER IP` \

3.3 记录安装日志

日志记录在软件目录中,格式为:

oracleAllSilent_$(date +"20%y%m%d%H%M%S").log

3.4 可重复执行

执行失败支持多次执行安装。

3.5 帮助功能

./OracleShellInstall --help

3.6 自动配置Multipath+UDEV绑盘

1
2
shellscript复制代码-dd /dev/sde,/dev/sdf `# asm data disk`\
-od /dev/sdb,/dev/sdc,/dev/sdd `# asm ocr disk`\

3.7 配置时间同步crontab

1
shellscript复制代码-tsi 10.211.55.18 `# timeserver` \

3.8 自动安装补丁(PSU,RU,RUR)

1
2
shellscript复制代码-gpa 32580003 `# Grid PATCH` \
-opa 32580014 `# Oracle PATCH` \

3.9 数据库优化

  • 1.自动优化数据库参数
  • 2.创建备份crontab+scripts
  • 3.设置数据库开机自启动
  • 4.设置pdb随cdb启动

3.10 最多支持2组Private IP

1
2
shellscript复制代码-pi1 10.10.1.1 -pi2 10.10.1.2 `# node private ip`\
-prf eth1 -prf1 eth2 `# network fcname`\

3.11 最多支持3组Scan IP

必须配置DNS才可使用多个scanip

1
shellscript复制代码-si 10.211.55.104,10.211.55.105,10.211.55.106 `# scan ip`\

3.12 支持图形化安装+VNC

1
shellscript复制代码-txh Y `#tuxinghua` \

3.13 支持只配置主机环境

1
shellscript复制代码-m Y `#Only Config System` \

如果觉得文章对你有帮助,点赞、收藏、关注、评论,一键四连支持,你的支持就是我创作最大的动力,谢谢

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【力扣-贪心】5、跳跃游戏I(55)II(45) 55 跳

发表于 2021-11-29

「这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战」

55. 跳跃游戏

题目描述

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

示例 1:

1
2
3
ini复制代码输入: nums = [2,3,1,1,4]
输出: true
解释: 可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:

1
2
3
ini复制代码输入: nums = [3,2,1,0,4]
输出: false
解释: 无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

解析

在计算是否能跳跃到最后一个位置,不需要考虑每次要跳 1步还是几步,只需要判断 调到的一格 是否能跳到最后一个数组元素位置,每次取最大的跳跃步数,判断是否有元素的跳跃范围达到最大的数组数目。

  • 贪心算法:
    • 局部最优:每次取最大的跳跃距离
    • 全局最优:最后判断局部最优中是否有能满足条件的,满足则就是全局最优的结果

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
c++复制代码class Solution
{
public:
bool canJump(vector<int> &nums)
{
int cover = 0;
if (nums.size() == 1)
{
return true;
}

for (int i = 0; i <= cover; i++)
{
// 记录每一步的最大跳跃范围
cover = max(i + nums[i], cover);
// 如果跳跃的范围能够满足数组的数目,则可以到达最后一个下标
if (cover >= nums.size() - 1)
{
return true;
}
}
}
};

45. 跳跃游戏 II

题目描述

给你一个非负整数数组 nums ,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

假设你总是可以到达数组的最后一个位置。

示例 1:

1
2
3
4
makefile复制代码输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
  从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

示例 2:

1
2
ini复制代码输入: nums = [2,3,0,1,4]
输出: 2

解析

与上一题思路相似,判断每一步能够到达的最远下标。如果当前能到达最远下标,则停止;如果不能到达最远下标,就继续走一步,判断下一步能否到达最远下标。

  • 当移动下标到达了当前覆盖的最远距离下标时:
    • 1、如果当前覆盖的最远距离下标时终点,直接返回,不需再走一步
    • 2、如果当前覆盖的最远距离下标不是终点,需要再走一步

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
c++复制代码class Solution
{
public:
int jump(vector<int> &nums)
{
// 当前覆盖的最远下标
int curDistance = 0;
// 下一步覆盖的最远距离下标
int nextDistance = 0;
// 最大步数
int steps = 0;

for (int i = 0; i < nums.size(); i++)
{
// 更新下一步覆盖的最远下标
nextDistance = max(nextDistance, nums[i] + i);
// 遇到当前的最大覆盖下标
if (i == curDistance)
{
// 如果当前的最大覆盖下标还不是最后一个元素的下标
// 就需要再走一步
if (curDistance != nums.size() - 1)
{
steps++;
curDistance = nextDistance;
// 如果下一步的最大覆盖下标已经可以到达最后一个元素就停止
if (nextDistance >= nums.size() - 1)
{
break;
}
}
// 如果是终点就停止
else
{
break;
}
}
}
return steps;
}
};

改进

不考虑移动后是否到达终点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
c++复制代码class Solution
{
public:
int jump(vector<int> &nums)
{
// 当前覆盖的最远下标
int curDistance = 0;
// 最大步数
int steps = 0;
// 下一步覆盖的最远下标
int nextDistance = 0;

// 这里下标 i < nums.size()-1
for (int i = 0; i < nums.size() - 1; i++)
{
// 更新下一步的最远覆盖下标
nextDistance = max(nextDistance, nums[i] + i);
// 遇到当前覆盖的最远下标时
if (i == curDistance)
{
// 更新当前覆盖的最远下标
curDistance = nextDistance;
steps++;
}
}
return steps;
}
};

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【JS逆向系列】某海关企业进出口信用信息公示平台 前言 一、

发表于 2021-11-29

本文用于学习使用,禁止用于非法活动


前言

目标网站:aHR0cDovL2NyZWRpdC5jdXN0b21zLmdvdi5jbi9jY3Bwd2Vic2VydmVyL3BhZ2VzL2NjcHAvaHRtbC9kZWNsQ29tcGFueS5odG1s


一、页面分析

进去页面后,抓包发现返回数据是加密

<font color=#999AAA >示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

调用堆栈,下断点后,可以找到解密的入口

在这里插入图片描述

看着方法名字,好像是AES加密来着哦


二、获取解密方法

然后我们就跟进去瞅瞅看,进到里面后,发现又调用了MuData_KXC函数,那一般都是进去就能看到,AES解密的一些参数配置,现在却又套了一层加密

在这里插入图片描述

不慌,继续跟,进到里面后,迎面而来的就是ob混淆了,可以搞个ast还原下,但是里面调用的变量很多,就懒得写了,直接就可以刚他

在这里插入图片描述

这里_0x1f5250 是 key的utf8编码

在这里插入图片描述

然后下面的return里又搞了两个函数

在这里插入图片描述

先看第一个,应该是拿到AES的配置信息吧,但是这个里面的参数跟平常见到的不一样,由于没有研究过AES的源码,看不到这玩意,反正扣扣代码 拿到_0x3ca6b6就行了
(ps:由于是配置信息,只要key不改,可以直接拿过来)

在这里插入图片描述

然后下一个函数就是大大的for循环解密,传入参数是返回数据经过编码后的,也不管他啥方法,扣他就完事了

在这里插入图片描述
扣完整个方法后,就能拿到解密的数据

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


三、总结

最后尝试着用AES-EBC,key= ‘0123456789abcdef’ 去解密,但是是不行,应该是里面做了其他操作吧,
所以还是老老实实扣了代码哈哈哈

在这里插入图片描述

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

媒体声音 阿里云王伟民:阿里云数据库的策略与思考

发表于 2021-11-29

简介:DTCC 2021大会上,阿里云数据库事业部 产品与解决方案部总经理 王伟民(花名:唯敏)发表主题演讲《云原生数据库2.0,一站式全链路数据管理与服务》,并接受IT168企业级&ITPUB执行总编 老鱼 专题采访,本文内容就现场采访整理而成。

受访嘉宾:阿里云数据库事业部 产品与解决方案部总经理 王伟民(花名:唯敏)

采访人:IT168企业级&ITPUB执行总编 老鱼

(本文根据DTCC 2021大会现场采访整理)

记者:请介绍下您目前在阿里云数据库所负责的日常工作

**王伟民:**首先介绍一下自己,我先后经历过Oracle、Microsoft、华为等企业在这些企业里面都是从事数据库相关的工作,担任过不同的岗位。

今年加入了阿里云,目前我主要负责四个方面的工作。

第一部分是产品管理,和我以前的工作是完全一样的,主要是以市场、客户需求为导向,去看我们需要研发哪些产品。

第二部分是解决方案,为产品的商业成功负责,包括行业、区域和国际市场。

第三部分是产品体验,包括文档、体验设计等,主要是为了让用户有更好的体验并更高效地使用我们的产品和服务。

第四部分是品牌和生态,品牌方面包括要持续对行业内构筑产品的影响力。生态方面,数据库作为基础类软件,在PaaS层,不像计算、网络存储、安全这些通用能力,大家都需要;也不像SaaS应用那样自带流量入口。所以,单独去推广数据库不是特别高效,我们需要通过生态的方式去做市场、推广,和伙伴们一起去更快速地推广和复制到各大应用场景。

以上是目前我在阿里云的工作。

记者:相当于OLAP、OLTP还有NoSQL,研发针对各自产品做集成与研发,你这边就变成产品化的。

**王伟民:**对,客户需求都是到我们团队。我们会去规划产品,研发来承接需求、投入资源、按产品规划的路标来推动产品上市。另外产品的商业模式、定价等GTM工作,也都是在我们团队负责。

记者:您今天的演讲主题是“云原生数据库2.0,一站式全链路数据管理与服务”。为什么会选择这个主题,这个主题您觉得对于参会的人来说,它能够获得哪些收益?

**王伟民:**我们首先强调的是“一站式”,这个“站”是“One Stop”的意思,不是“技术栈”的“栈”,这个叫“Stack”。我们现在做的是云服务,各类引擎上架后实现了多样化的供给;每一款产品或引擎是为特定的用户场景做定制优化的,这也是经典的General Purpose(GP)和Special Purpose(SP)之间的trade-off。有很多公司尝试用General Purpose的方式解决所有的问题,但目前为止,它可能在某些场景里是最优解的,但在所有其他场景里都是次优解。当然General Purpose有个好处,对产品研发团队来说,它的ROI是最高的,持续性投入打造一款产品试图解决所有场景。

但其实存在一个问题,我们看到有很多为专用场景设计的产品,比如缓存、嵌入式的IoT场景、边缘场景、多模,都不是GP产品能很好地解决的。到目前为止,**千行百业都在做数字化转型和上云,想用一款产品解决所有的问题,从哲学上来讲,这是一种过于理想的状态,是不可能实现的。**我们想用的方式是用产品和产品的组合去满足各类业务场景的需求,相当于我们是GP+SP的模式去做。所以这是我们选这个主题的原因。

其次要强调“全链路”,我们做过调研,绝大部分企业里,各类产品和解决方案都是非常纷繁复杂的,但目前它们之间的拉通和协调仍存在很大问题。我们用云的方式可能能解决“烟囱”的问题,但其实只是解决了资源的“烟囱”,业务和数据上仍然没有打通。既然数据是新的生产资料,就需要有一个数据的操作系统,我们想用DMS来实现DataOS。这是我们的想法,也是我们一直努力的方向。

到目前为止,丰富的业务场景以及里面不同的业务负载特性,使得很难用一款产品最终解决所有问题,这是我们最大的洞察。所以我们希望可以带来实际的变化。

记者:您正好提到了这个观点,和我下面问到的趋势是一样的。专用数据库、多模数据库方面,显然你们的专用数据库是类似于亚马逊的,而非像微软的“一库包打天下”——一套产品应用于所有场景。现在所有的厂商其实都在聊云原生,你们的云原生架构和友商的,在演进时候有什么不一样吗?

**王伟民:**首先,像微软和Oracle其实只是品牌比较聚焦和统一,但它其实也是GP、SP相组合的,Oracle有比较多的品牌,比如面向缓存场景的叫Oracle TimesTen ,面向高可用场景的是Oracle RAC,所以Oracle有一级品牌和二级品牌之分。同样微软针对分析、报表、集成等有SSIS、SSAS、SSRS,面向多模有Azure CosmosDB;

所以我认为大家之所以不约而同地选择了“迈向云原生2.0”这条路,核心原因是大家都看到负载的多样性对底层基础软件的设计太挑战了,无法用一个架构、一套code去cover所有。

提到和友商的差异,在云原生1.0那个既往时代,大家其实已经把产品化和服务化做完了,基本都已完成基础设施池化了,在管控上做到多租户、按需使用、按量计量,而且能够做不同程度的扩缩容。

而云原生2.0时代,更看重产品与服务之间的联动。在我刚才的分享中(第十二届中国数据库技术大会)提到七个客户案例,其中没有一个场景是用单款产品去应对的。为了满足客户的转型需求,基本都需要交易型、分析型、数据实时入仓、库仓一体等很多需求,都需要实现数据的发现、洞察和价值变现。

在我们看来,云原生2.0时代可能会朝两个方向演进,第一,持续不断夯实、增强单品的差异化竞争力。第二,多种产品之间的高效协同和体验提升。

记者:单品方面,要继续强化自己的优势。整体上,你们也会强调整体解决方案的优势,不管是OLTP、OLAP还是NoSQL,所有产品寻求更好地协同。

王伟民:我们现在有一个想法,即把解决方案产品化。比如客户想要一个在线电商系统,我们可以直接提供一个有缓存和交易,中间带着链路,像一个小型数仓一样已就绪的系统。因为起步的时候可能没有那么多数据,所以这个数仓是可扩缩容的。

虽然是在云上,但也是垂直的,业务是烟囱,资源是水平拉通的,这是我们正在做的尝试。

解决方案产品化,其实业界有很多公司都做过。我们可能也只能在通用方案上去做,想要结合行业领域know how去做,可能还是比较困难的。

记者:如果要用一个比较简单的方式、一句话两句话去描述“云原生数据库2.0”,应该怎样去说呢?让别人一下就能理解“云原生数据库2.0”的概念。

王伟民:“一站式全链路”还是相对偏技术术语,如果用业务术语来讲的话,应该是多引擎的高效组合,以满足客户多样化的业务负载。另外一种提法是“全场景”、“数据全生命周期”,但这些相对偏Marketing术语。

记者:对,对于非技术人员来说,理解起来还是有点晦涩。下一个问题,其实来源于TiDB黄东旭曾提到的一个观点,他认为目前所谓的云原生数据库都不是真正的云原生,凡是能回归到线下部署的都不叫云原生。您怎么看这个观点?

**王伟民:**这个观点挺新颖的,之前确实没听说过。我们遇到过一类上云的客户会要求数据可回流、随时可以下云。客户有这个诉求的核心原因是担心云是一个超级的lock in,担心“我上了你的云,除非我挂了,否则今生今世在你这上面”。

在与客户交流的过程中,会发现有些客户比较抵制差异化特性,担心因为差异化特性而对某厂商产生依赖。当然,在这方面也有很多不同的技术流派,有些客户在写SQL时候用的并不用数据库专用语法,它中间有一层抽象层,可以适配底层的MySQL,也可以换成SQL Server或者Oracle,对他来说不可见。业界其实有这样的产品,比如Source Pro,能够对上层应用屏蔽底层数据库的差异性,所以我不太赞同这个论断。

数据库产品化和云化应该是两个阶段,它可以是一个产品,这个产品可以provision在on-premise,也可以在云上,但是在云上能够发挥资源弹性伸缩等很多云上所特有的能力,但并非只能在云上。

比如,RDS MySQL不能下云吗?它一定能下云,如果按照刚才的标准,它就不是云原生数据库。但如果给它做了解耦,变成AWS Aurora那样,计算和存储解耦了,Aurora是很难下来的。

是否属于“云原生”,应以客户业务场景和需求出发去考虑,而非从技术实现上去判别,毕竟很多客户的要求是不一样的。比如拎包入住酒店,今天入住这个酒店,明天入住那个酒店,但酒店并不能不让客户走。

记者:你觉得他的说法是否是基于国外市场得出的结论?像您说的这种既能上又能下的,是否是国内市场特有的需求?还是说,这是全球市场的需求?其实最近我看到一篇文章,其观点是“国外都不用分布式,所以国内要用分布式”。

**王伟民:**关于您提到的,这是否是国内市场的特殊情况?我觉得,未来公有云成为主流,一定会出现用户如何考虑平台中立、云中立的问题。现在已经看到很多类似需求了,比如客户的主站在A云,容灾在B云;另一种是交叉的,一部分业务生产在A,备份或Standby在B,另一部分业务主站在B,备站在A。**客户的需求在将来可能会持续不断的变化,我们的理念是“客户第一”,这是我们必须考虑的事情。**东旭的观点目前来说比较新颖,但你说TiDB可不可以线下部署?肯定是可以的。

关于最近看到这篇文章指的是InfoQ最近才实现底层数据库的分库,之前都是单库。就产品及其架构实现而言,首先云厂商本身是有目标市场选择的,比如说AWS不做线下市场,它不做on-premise部署,最多有一个Outposts边缘部署,只提供受限的能力,可能是他客户选择的问题。

记者:东旭的观点比较超前,他们好多客户在国外,国外的市场对于公有云的拥抱程度很高,但对国内来说,显然对线下部署、混合云、私有云等,要比对公有云的拥抱程度更高,至少在金融、电信等行业贡献大户来说,相对来说是这样的。

**王伟民:**在公有云的大趋势方面,我的判断跟东旭是一样的。对于国内金融、电信行业很大业务负载还没有在公有云上,我认为可能有两个原因:第一,国内在信息安全、行业监管、合规遵循等方面的政策要求;第二,客户在系统软件运维管理方面的自主可控需求。

其实你说在公有云上就不安全吗?不一定的。美国国防部100亿的JEDI项目都是在公有云上,美国第一大银行CapitalOne是100% 在公有云上的,没有用私有云。我认为,公有云的未来是比较乐观的。很多行业都需要一个过程,上云可能是长达十几年的过程,赛道很长。

记者:因为国外不用分布式,所以分布式就没有意义或应用价值吗?

**王伟民:这又是一个特别值得讨论的问题,我认为复杂度是守恒的事情。国外,比如GitHub之前没有用分布式,但不代表这部分工作就不做了,它的这部分工作可能在应用层、中间件层做了;只不过从数据库角度来说,我们希望把复杂留给数据库,把简单留给应用。**我们希望分布式数据库能像单机一样去做应用开发、去做管理运维,这样应用就不需要关注底层数据库容量、事务、伸缩扩容、一致性备份等问题。

其实国外数据库在并发用户量和用户规模方面,没有国内这么凸显,毕竟我们有人口红利。比如阿里的双十一“剁手节”,可不可以把支付系统用Oracle数据库支撑呢?肯定可以,把底下的OceanBase换成Oracle也可以,但这样TCO太高。

因此,到底是用分布式产品去解决,还是用分布式解决方案去解决,这又是另外一个思路。比如应用接分布式中间件,比如ShardingSphere和底下接单机数据库,都一样可以做这个事情。

我觉得并非说国外没有用它,它就没有意义,而是看这些投入是在哪个层面去做的。比如我认为从产品的维度来讲,随着使用量的增加,边际成本是持续下降的,所以我认为做这件事情是非常有意义的,比如可以**让应用不用关心柔性事务、业务冲正等,这个复杂度都在数据库层面处理了,所以我觉得分布式数据库还是非常有意义的。**在国外,可能不需要用分布式数据库,但可以把分布式数据库退化成单实例使用,在Oracle RAC也可以只用一个节点,这都是没问题的。

记者:过去我一直觉得国内云厂商里,在数据库方面,生态做的最好的是华为,经常会看到谁又加入了它的生态伙伴圈。阿里是云方面最早吃螃蟹的,所以阿里云保持了一定的先发优势,同时阿里也有一些类似于双十一的极端场景,使得它会有技术方面的积累。当然,这只是我个人的看法。从您的角度来说,阿里云数据库从技术、产品、商业、生态等各个角度,相对于其他竞争对手,它的优势在哪?

**王伟民:**首先,针对我们产研团队,我认为第一个优势是从组织上的高度统一和一致,我们能做到力出一孔。李飞飞既是阿里云数据库事业部的一号位,他同时又是达摩院数据库与存储实验室的一号位,所以相当于他统领了整个阿里体系所有数据库的产品、研发和预研团队,在产品的战略、沟通、市场等策略方面是高度一致的,这是第一个优势。

第二,阿里确实有先发优势,我们很多客户不是才上云,比如基于IDC,刚上云。而是day one就是长在云上,生于云长在云。

第三,在产品方面比较聚焦,我们没有非常多的产品,我们的产品分成三大类:开源托管类、商业产品托管类和自研产品类,核心是希望聚焦,而且针对这三类产品,我们有不同的策略。

比如开源托管类聚焦的是简单易用、安全可靠和高性价比;商业产品主要利用它的开放生态,在这方面,我们主要是解决业务合规生态多元的问题,我们和SQL Server、MongoDB是直接商业合作的,这些是需要阿里云持续地去合规的;自研产品,我们聚焦四个大的品类,PolarDB、AnalyticDB、Lindorm、Tair,并没有很发散。

讲到生态,其实各个厂商做生态的方式是不一样的。有些友商虽然在做数据库,但并不是要把数据库作为一个独立产业来做。通过打造一个第二平面去解决“卡脖子”的问题,这也非常了不起,我们也需要这样的技术。

阿里在做生态方面,尤其我们更多地是希望以“被集成”的方式来做。生态要开放繁荣,最主要的就是要能够和伙伴实现利益分享,如果做不到这一点,我觉得生态是做不起来的。所以我们一直在探索怎么样让生态的参与者和我们一起,去把饼做大,同时去看如何更好地兼顾各方。

生态方面我们也一直在持续投入,比如分销的伙伴、SaaS被集成、ISV、交付伙伴、培训认证等都在搞,但的确我们这一块的投入量还需要持续加强。

记者:上个月阿里媒体沟通会上,我采访李飞飞,他提到你们要去打线下市场,因为他觉得线上格局基本已经定了。针对线下市场的数据库领域高端客户,比如金融政企等,是不是有些友商也比阿里更有优势?阿里现在去打线下这块儿市场,准备怎么做?你们的差异化优势在哪儿?

**王伟民:**首先,线下市场的需求是刚性的,这是客观存在的。目前,我们积累了包括运营商、金融行业、政府等很多客户。大家都在讨论一个问题,如果重新再做一次,是原封不动地换平台还是在原有基础上做转型升级?

目前我们看到,包括运营商在内的客户有一个普遍需求,今天我也分享了一个运营商案例,它的业务系统(包括boss系统)都直接云化了。在这个过程中,他们要将包括“云”在内的很多理念、对上层应用做“泳道化”切割、微服务化,以及整个运维开发都要结合起来,我认为这个替换不是单纯地去拿一个产品替换,而是用解决方案替换。

第二,在政企市场去和Oracle直接PK,比方说性能,我们认为这个可能是被误导的。因为用户在选型的时候,性能不是唯一因素,还有非常多其他的因素。我们希望可以用DBStack敏捷交付,无缝集成,将这样一个轻量化、可以部署到客户本地的云,把用户可能会用到的数据引擎都放上去。现在,广东移动、安徽移动等客户,都是用了这些产品。

相比友商,他们可能会有他们的灵活度,我们也有自己差异化特色。比如友商明确了不做私有云,就会把很大一块市场放出来,也势必有很多友商去填补这个市场空白。

关于李飞飞提到的这一点,我理解我们要去做的核心原因有两个:第一,这是高端市场,也是标杆,是摆在明面上的市场刚需。第二,这些业务场景和业务负载对牵引产品研发、检验和持续提升产品稳定性、可靠性都非常有帮助。我觉得结合这两方面因素,我们肯定会坚定不移地去做。

记者:就像您提到“云原生数据库2.0时代的一站式全链路数据管理与服务”,也是我们看到的现实情况,针对不同场景,需要不同数据库去解决问题,没有所谓的“一库包打天下”。我们看到的这些所谓的趋势,比如云原生+分布式、软硬一体化、湖仓一体、HTAP等,它本身都是一种融合,背后是用户简单化需求的推动。对于用户来说,管理一个数据库和同时管理四个数据库的复杂度、难度肯定不一样。趋势上,大家希望简化,但我们看到大家还是专用的场景用专用的数据库,这个是不是有点矛盾?

**王伟民:**我们要解决的很多问题其实都是多变量的。首先,想要兼顾所有变量,是非常有挑战性的,以及这些变量之间要如何排优先级,比如可靠性、开发、管理、运维等一系列因素,都要综合考虑。

其次,既然通用产品无法满足需求,那就用“通用+专用”的组合去搞,又要降低复杂度,怎么办?我们用DAS、DMS等技术手段去解决。我们希望DAS可以把以前传统DBA的补丁升级、日常巡检、参数配置或调整、备份告警等日常工作用AI的技术手段来解决。现在有一个特别好的趋势,我们已经积累了海量的数据库负载、水位线、慢SQL治理等最佳实践,可供我们去做这些工作。和深度学习一样,杨立昆老师90年代刚提出的时候,它只能在90年代美国邮政里面检测邮政编码,不像今天有非常广泛的用途,主要是数据和算力上来了。

回到刚才讲的问题,这个矛盾可能放在以前的确是难以逾越的。但在今天,我们看到解决的契机了,我们可以用AI4DB的方式来解决。这样,人就可以更多的聚焦类似于Application DBA、逻辑设计、数据的data placement等高价值的工作,常规工作让程序自动化来做,将两者有机的结合起来。

记者:咱们之前新闻稿提到,阿里云要全面进军线下市场。线下市场里,大家其实都盯紧的是金融行业,尤其是银行,我观察到银行去做分布式改造其实是一个必然的趋势。李飞飞之前也有提到,这次云栖大会上,你们开源PolarDB-X这样一个分布式数据库,我可以理解为,这是你们进军线下市场的组合拳吗?

**王伟民:**对,可以理解为这是其中一个关键的action。国内做数据库的各参与方热情高涨,厂家非常多。但对很多企业而言,他们非常担心你这家公司有多大规模?我们是不是会贡献你50%-60%的营收?他们其实非常担心数据库公司的可持续经营能力,这其实还是信任的问题。开源在我看来包括两个方面:第一,用开源的方式、开放的心态去解决开放的问题和挑战,吸引更多人参与,第二,开源相当于智力的众筹;

另外,开源也表示一种自信,是一种可以让用户检视质量的方式,这样能够把信任建立过程中的挑战大幅度降低。

最后,当然我们也希望通过这种方式做生态,比如应用的生态、人才的生态。

记者:所以,开源只是一种策略,它并不是决定数据库公司、厂商能否在未来竞争中脱颖而出的必要条件?

**王伟民:**对,它并不是。有很多开源产品,可能叫好,但未必叫座(商业化成功)。也有很多产品,从不叫好也不叫座,直接就玩不下去了,它就只好Open Source了。我们希望用这种方式持续去做,不代表这样做了就一定成功,因为的确有太多开源项目,最后其实是商业化不成功的。不光在数据库领域,其他领域也一样。比如以前有OpenOffice,Open Solaris。即便我们严肃认真地去做Open Source,中间也有很多需要学习的地方和规避的坑,这期间要做的事情非常多。

记者:国产数据库落地应用越来越多,但实际上更多是存在于边缘业务上,核心业务相对来说比较少。很多企业在部署国产数据库的同时,还在续费Oracle数据库,甚至到现在为止两条线并着跑。这种现象是由于客户心里的顾虑——怕国产数据库撑不住,还是目前国产数据库在承担高端、核心业务上,确实有一定的问题?

**王伟民:**这两方面因素都有,而且还不止这两方面因素。确实很多客户在尝试用更多的供应商,包括国产数据库厂商,同时持续不断地使用商业数据库的产品,这是目前的现状。原因有几方面:

第一,商业数据库经过几十年的发展和海量用户(上百万)长期真实业务负载的考验,它的企业级特性、稳定性、商业模式、服务等各方面,都是经过考验的。不管用什么产品,用户业务系统都能高效的运转起来。

而目前国内很多厂商,无论是从研发的投入、产品的成熟度,还是产品资料、生态、人才等各方面,与商业数据库相比还是有差距的。这是客观事实,我们也应该正视这个客观事实。人家干了五十年,我们干了十年,投入比人家少很多,不可能就超越人家,这不符合客观规律。

第二,的确有很多业务场景不需要那么高端的产品。这句话有两层意思,一是目前很多产品在企业功能、特性、完备度、性能、稳定性等各方面(与商业数据库)有差距,但不代表这个差距不可逾越,只是需要时间去逾越。二是很多业务场景里,很多商业数据库的功能、特性其实都是没有被用到的也不需要用到,这个是有统计数据表明的。

记者:之前我了解到,使用Oracle的绝大部分企业可能连一半的功能、性能都未必用得上,对于很多企业来说,性能问题可以被很多架构很好地解决。

**王伟民:**在很大程度上,我们的产品对很多核心业务场景、业务典型负载来说,都是够用的,但是我们产品的稳定性或其他方面,可能还有较长的路要走,还需要接受海量用户长时间、持续的负载考验。

记者:我是不是可以这样理解?目前来说,国产数据库是可用的,但是离好用还有一定距离?之前我看到关于国产数据库的目标分为技术目标和市场目标。市场目标,第一个是实现国产化的自主可控,第二个是走向全球。从技术目标来说,分为四个目标:人有我无、人有我有、人无我有、人有我优。您觉得,目前整个国产数据库市场从技术目标、市场目标来说,分别是什么阶段?

**王伟民:**我觉得从技术目标、功能特性角度来讲,可能有一些特性是人无我有的,但对于很多普遍需求的特性、共性,我们还处在追赶和补充阶段,“人有我有一部分”或“人有我优一小部分”,不同厂商在清单的完备程度上不太一样。

市场方面差的就更远了。如果统计线上、线下市场,阿里云在中国做到了第一,超过了Oracle。但如果回到全球视角,根据2020年Gartner统计数据,微软第一,Oracle第二,阿里云排到全球第七,在To B、云和数据库市场,我们和领先企业还有不小差距。

举个例子,Oracle一个季度的净利润差不多100多亿美金,就算数据库只占40%,除去它的标准服务,可能也有几十亿美金,这是个不得了的数据。中国的软件业百强的净利润加起来可能都没有这么多。

记者:目前,你们帮客户去O、去MySQL、去Teradata等的时候,会面临哪些挑战?你们通常是以什么方式帮助客户?

**王伟民:**目前最大的挑战是生态层面的。没有哪个客户的业务系统能做到drop-in replacement,北向可能有应用侧,纵向可能有数据的集成,还有南侧可能有操作系统、硬件等一系列的适配。你要想去替换的时候,很多客户会说“不光要替换这个产品,还要保留原来的管控、开发、运维、应用等一系列流程的对接。所以,生态方面是一个很大的挑战。

第二是人才方面。很多企业在人才方面要求有一定的专业积累,换产品可能需要有一个学习的过程,对人才来说,要降低学习曲线的坡度,学习成本不要太高。所以我们在兼容性方面,要做很多工作。关于兼容性的工作又是动态移动靶,今天做到了某个点,被兼容的对象又往前走了,又要继续去做。兼容性工作,做到90分和80分的区别不大,但是从90分到95分工作量巨大,要从95分到100分几乎是不可能的任务。所以我们在想是不是要跟随,哪些方面我们要跟随,哪些方面我们要用别的方式来替代——或者干脆不考虑兼容性了等等,这些都是要思考的问题。当然,这中间的取舍是非常难的。

记者:今天的采访就到这儿,感谢王总接受采访。

原文链接

本文为阿里云原创内容,未经允许不得转载。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

没人陪你聊天?教你使用java制作专属智能陪聊机器人

发表于 2021-11-29

🚀 作者 :“大数据小禅”

🚀 文章简介 :最近有小伙伴留言,“很久没人找我聊天了,可以写一个陪聊机器人展现一下程序员的浪漫吗?”,小禅:“安排!”

🚀 关注我 :关注我一起学习,交流,资料共享哦。

🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬


1.智能陪聊机器人演示

人工智能一直是最近的热点话题,自动人工智能但是以来应用领域就不断的扩大,在未来人工智能也会在人们的生活中不断普及与应用。这篇博文中的陪聊机器人,使用java进行编写,可以根据你发的信息进行智能的回应,还算挺有意思的一个小玩意。最终效果的演示如下图~

在这里插入图片描述

2.智能问答平台API介绍

这个陪聊机器人项目使用了青云课的智能API,通过调用API得到信息反馈。

具体的调用格式如下:

1
2
perl复制代码http://api.qingyunke.com/api.php?key=free&appid=0&msg=%s
其中的%s传入我们需要发送给机器人的内容,就可以得到API调用结果的反馈。
  • key 固定参数 free
  • appid 设置成0,为智能识别
  • msg 为搜索关键词
  • result 表示返回状态,返回0表示正常
  • content api返回的信息内容

可以看到数据是以JSON的形式进行返回。

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

3.整合一下第三方JSON开源库

Gson是Google提供的类库,可以用来处理java对象与JSON数据之间的映射,将一个JSON字符串转换成一个java对象,方便我们对API返回的JSON格式的数据进行处理,下面演示如何将Gson类库导入到我们的工程中。

首先可以去官网下载对应的jar包,或者直接私信我获取。获取jar包之后找个全英文路径进行保存。这里我们使用的编辑器是IDEA,所以使用IDEA进行演示,小伙伴们使用的是其他编辑器的话导入方法都是类似的哦。在IDEA打开如下界面,找到jar包导入即可。

在这里插入图片描述

4.智能机器人项目框架搭建与模块划分

项目搭建:搭建的部分无太多要求,只需要使用IDEA创建一个新的普通java工程即可

项目模块搭建:

  • model 类 用来存放请求所返回的对象
  • util 类用来存放工程所用到的工具类,比如说HTTP请求解析类
  • app 类用来当作机器人项目的入口
  • service 类用来实现业务的接口

相关的两个实体类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
typescript复制代码public class Request {
​
   private String key = "free";
​
   private String appid = "0";
​
   private String msg = "";
​
​
   public Request(){}
​
​
   public Request(String msg){
       this.msg = msg;
  }
​
   public String getKey() {
       return key;
  }
​
   public void setKey(String key) {
       this.key = key;
  }
​
   public String getAppid() {
       return appid;
  }
​
   public void setAppid(String appid) {
       this.appid = appid;
  }
​
   public String getMsg() {
       return msg;
  }
​
   public void setMsg(String msg) {
       this.msg = msg;
  }
}
​
public class Response {
​
​
   private int code;
​
   private String content;
​
   public int getCode() {
       return code;
  }
​
   public void setCode(int code) {
       this.code = code;
  }
​
   public String getContent() {
       return content;
  }
​
   public void setContent(String content) {
       this.content = content;
  }
}
​
​

5.封装一个机器人HTTP工具类

HTTP工具类主要用来对api进行请求,获取返回的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
ini复制代码​
public class HttpUtils {
​
   public static String request(String api){
​
       HttpURLConnection connection = null;
       int responseCode = 0;
       try{
           URL url = new URL(api);
           //获取对应的连接对象
           connection = (HttpURLConnection) url.openConnection();
           responseCode = connection.getResponseCode();
      }catch (Exception e){
           e.printStackTrace();
      }
​
       if(200 <= responseCode && responseCode<=299){
           try(InputStream inputStream = connection.getInputStream();
               BufferedReader in =  new BufferedReader(new InputStreamReader(inputStream));
          ){
               StringBuilder response = new StringBuilder();
               String currentLine;
               while ((currentLine = in.readLine())!= null){
                   response.append(currentLine);
              }
               String result = response.toString();
               return result;
          }catch (Exception e){
               e.printStackTrace();
          }
      }
       return null;
​
  }
​
}

6.实现机器人service层的接口与定义

实现机器人接口层

1
2
3
4
5
6
arduino复制代码​
public interface RobotService {
​
   Response qa(String msg) ;
​
}

实现机器人接口实现类,这个类用来实现API的请求,将结果进行封装成实体类返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码public class QkyRobotServiceImpl implements RobotService {
​
   private static final String apiTpl = "http://api.qingyunke.com/api.php?key=free&appid=0&msg=%s";
   private static  final Gson gson = new Gson();
   @Override
   public Response qa(String msg) {
       
​
       String api = null;
       try {
           api = String.format(apiTpl, URLEncoder.encode(msg,"UTF-8") );
      } catch (UnsupportedEncodingException e) {
           e.printStackTrace();
      }
​
       String result =  HttpUtils.request(api);
​
       //可以做逻辑判断,比如null的时候,或者出错
​
       Response response = gson.fromJson(result,Response.class);
​
       return response;
  }
}

7.制作专属于你的机器人入口

编写入口主类,调用封装好的模块进行机器人入口主类的编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
csharp复制代码public class Main {
​
   private static final RobotService robotService = new QkyRobotServiceImpl();
​
​
   public static void main(String[] args)throws Exception {
​
       Scanner scanner = new Scanner(System.in);
       System.out.println("尊敬的C站大佬,请给我取个响亮的名字!!");
       System.out.println("-------------------------------");
       String name = scanner.nextLine();
       System.out.println("大佬好,我是大数据小禅博客里的机器人,直接给我下达指令哦~");
       System.out.println("-------------------------------");
       while (true){
​
           String input = scanner.nextLine();
           if("88".equalsIgnoreCase(input)){
               System.out.println("欢迎下次使用,拜拜");
               break;
          }else {
             Response response = robotService.qa(input);
             if(response != null && response.getCode() == 0){
                 System.out.println("-------------------------------");
                 System.out.println(name+":"+ new String(response.getContent().getBytes(),"UTF-8"));
                 System.out.println("-------------------------------");
            }else {
                 System.out.println(name+": 大佬你刚刚这句话我没听懂,可否再陈述一次~");
            }
          }
​
      }
       scanner.close();
​
​
  }
}
​

8.把你的机器人打包使用

为了方便我们对项目的使用,这里我们使用IDEA将项目打包成jar包。通过下面的步骤,就可以将我们项目里的全部模块与类库打包,需要调用的时候只需要使用 java -jar jar名字 即可。

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

在这里插入图片描述

在这里插入图片描述

9.总结

打包完成后我们的机器人项目就完成啦,希望小伙伴们通过这篇博文可以有所收获。💪

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Kerberos03:Kerberos的认证请求过程

发表于 2021-11-29

这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

Kerberos认证请求认证过程

  1. KRB_AS_REQ:Client用明文向AS发起请求,请求体为Client Info
  2. KRB_AS_REP:AS通过AD验证用户是否存在,不验证密码正确性,AS生成SKDC-Client与TGS Name用CMK加密后和KDC加密的TGT一起返回,Client密码不对则解不开CMK加密部分
  3. KRB_TGT_REQ:Client对Server进行请求,获得KDC加密的Server的TGT(内含SKDC-Server)对于Server来说,可以像Client一样通过AS Exchange获得和KDC之间的SessionKey以及Server的TGT,获得这个TGT后 Server会缓存它,以待Client对它的请求
  4. KRB_TGT_REP:如果Server的TGT存在于缓存,将该TGT返回给Client
  5. KRB_TGS_REQ:Client提供自己的TGT,Server的TGT以及SKDC-Client加密的Authenticator以及要访问的Server请求TGS
  6. KRB_TGS_REP:KDC先解密Client的TGT获得SKDC-Client,用此解密Authenticator和TGT中Client Info对比,TimeStamp与系统时间对比(PRE_AUTH_FAILED)。通过后再解密Server的TGT获得SKDC-Server来加密Session Ticket和SKDC-Client加密的SServer-Client一起返回给Client
  7. KRB_AP_REQ:Client用Session Ticket和SServer-Client加密的Authenticator请求,包含一个Flag是否启用双向认证
  8. KRB_AP_REP:Server用SKDC-Server解密Session Ticket,用SServer-Client解密Auth,对比Client Info和Time,if Flag:Server提取Auth中的TimeStamp,并用SServer-Client加密返回给Client

认证总结

  1. Session-Key是参与的双方都拥有的,KDC颁发,想让对方解密的信息都用Session-key加密
  2. TGT都是KDC加密,Session Tikcet是SKDC-Server加密。Server的TGT是为了提供SKDC-Server
  3. Authenticator (ClientInfo+TimeStamp):为有效的证明自己提供证据,用请求者和被请求者的Session加密
    Client Info包含:Client-Name(Principle),IP,TGT有效时间/生命周期
    TGT包含:Client Info+ Session key
    Session Ticket包含:Client和Server的principal、ip、st生命周期、SServer-Client
  4. Long-term Key加密的数据不应该在网络中传递,所以都替换成Short-term key(Session Key)
  5. TGS:可以在这里添加权限认证服务
  6. Client Master Key:用户输入的密码或kerberos.keytab文件中的密码hash生成:为了加密①请求
  7. 一个TGT可以让Client获取多个Server的Session Ticket,TGT和每个Ticket都具有有效期,TGT要是过期了申请的Session Ticket也过期了,在Session Ticket未过期情况下,Client直接可以与Server验证

为什么KDC不直接将Session-Key发给server

  1. Server需要维护一个Session-Key的列表
  2. Session-Key可能等Client去认证还没到Server
  3. KDC都是从TGT获取Session-key来解密

Master Key从哪里来

从Domain的Account Database中提取Client和Server的Master Key

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

PriorityQueue 源码

发表于 2021-11-29

PriorityQueue

1
2
3
4
5
6
7
8
9
arduino复制代码public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator) {
// Note: This restriction of at least one is not actually needed,
// but continues for 1.5 compatibility
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.queue = new Object[initialCapacity];
this.comparator = comparator;
}

上浮图片

siftup.png

offer 向堆中添加元素 向上比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ini复制代码public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
// 扩容
if (i >= queue.length)
grow(i + 1);
// 数量+1
size = i + 1;
if (i == 0)
queue[0] = e;
else
// 下标的位置i e
siftUp(i, e);
return true;
}

siftUp 向上比较

1
2
3
4
5
6
scss复制代码private void siftUp(int k, E x) {
if (comparator != null)
siftUpUsingComparator(k, x);
else
siftUpComparable(k, x);
}

siftUpComparable 构建最小堆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ini复制代码private void siftUpComparable(int k, E x) 
{
// 梳理逻辑 每次加入一个元素 都要向上比较 然后 重新构建一个最小堆
Comparable<? super E> key = (Comparable<? super E>) x;
// k=0 的时候到达了根节点
while (k > 0)
{
int parent = (k - 1) >>> 1;
// 父元素的值
Object e = queue[parent];
// 当添加的值比父元素大的时候 退出循环
if (key.compareTo((E) e) >= 0)
//添加的值
break;
// 当添加的值比父元素小的时候 把父元素的值对调到传入值的位置
//
queue[k] = e;
k = parent;
}
// 传入的x 最终放的位置
queue[k] = key;
}

poll 取出队列中的队首数据 删除元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
scss复制代码public E poll() {
if (size == 0)
return null;
int s = --size;
modCount++;
// 1、取到队列中的第一个元素
E result = (E) queue[0];
// 2、取到最后一个元素
E x = (E) queue[s];
//3、GC 清空数据
queue[s] = null
// 4、往下比较
if (s != 0)
siftDown(0, x);
// 返回队首元素
return result;
}

siftDown 下沉 k=0 | x 最后一个元素

1
2
3
4
5
6
scss复制代码private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}

siftDownComparable(k, x); k=0 | x 最后一个元素 就是从第一个元素开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
ini复制代码private void siftDownComparable(int k, E x) {
Comparable<? super E> key = (Comparable<? super E>)x;
int half = size >>> 1; // loop while a non-leaf
while (k < half)
{
// 1、左孩子节点index 默认左节点最小值
int child = (k << 1) + 1;
// 2、左孩子节点值
Object c = queue[child];
// 3、右孩子节点值
int right = child + 1;
// 比较左右节点的最大值 取到最小值index
if (right < size &&
// 如果左边>右边 表示右节点是最小值节点 不是默认的左节点
((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
//
c = queue[child = right];
// 再次比较根节点的值 也就是传入的x 和孩子节点中的最小值做比较 如果小于的话 退出循环
// 已经是最小堆了
if (key.compareTo((E) c) <= 0)
break;
// 也就是父节点的值比孩子节点中的最小值要大 不符合最小堆 继续调整
queue[k] = c;
k = child;
}
queue[k] = key;
}

下沉图片

siftdown.png

heapify 将一个无序数组构造成最小堆

1
2
3
4
5
6
7
8
9
arduino复制代码private void heapify() 
{
/**int i = (size >>> 1) - 1,
这行代码是为了找寻最后一个非叶子节点,然后倒序进行"下移"siftDown操作
*/
for (int i = (size >>> 1) - 1; i >= 0; i--)
// i 数组下标 queue[i] 要下沉的值
siftDown(i, (E) queue[i]);
}

heapify图形结合

图片.png

图片.png

图片.png

图片.png
我们观察下用数组a建成的二叉堆,很明显,对于叶子节点4、15、11、1、3来说,它们已经是一个合法的堆。所以只要最后一个节点的父节点,也就是最后一个非叶子节点a[4]=10开始调整,然后依次调整a[3]=12,a[2]=5,a[1]=6,a[0]=7,分别对这几个节点做一次”下移”操作就可以完成了堆的构造。ok,我们还是用图解来分析下这个过程。

假设有一个无序的数组,要求我们将这个数组建成一个二叉堆,你会怎么做呢?最简单的办法当然是将数组的数据一个个取出来,调用入队方法。但是这样做,每次入队都有可能会伴随着元素的移动,这么做是十分低效的。那么有没有更加高效的方法呢,我们来看下。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…124125126…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%