0%

Here's something encrypted, password is required to continue reading.
阅读全文 »

[toc]

概念和场景#

做法#

  1. 1666111705052

模板#

1666111685258

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private int f(int i, boolean isLimit, boolean isNum) {
if (i == s.length) return isNum ? 1 : 0; // 如果填了数字,则为 1 种合法方案
if (!isLimit && isNum && dp[i] >= 0) return dp[i]; // 在不受到任何约束的情况下,返回记录的结果,避免重复运算
var res = 0;
if (!isNum) // 前面不填数字,那么可以跳过当前数位,也不填数字
// isLimit 改为 false,因为没有填数字,位数都比 n 要短,自然不会受到 n 的约束
// isNum 仍然为 false,因为没有填任何数字
res = f(i + 1, false, false);
var up = isLimit ? s[i] : '9'; // 根据是否受到约束,决定可以填的数字的上限
// 注意:对于一般的题目而言,如果此时 isNum 为 false,则必须从 1 开始枚举,由于本题 digits 没有 0,所以无需处理这种情况
for (var d : digits) { // 枚举要填入的数字 d
if (d.charAt(0) > up) break; // d 超过上限,由于 digits 是有序的,后面的 d 都会超过上限,故退出循环
// isLimit:如果当前受到 n 的约束,且填的数字等于上限,那么后面仍然会受到 n 的约束
// isNum 为 true,因为填了数字
res += f(i + 1, isLimit && d.charAt(0) == up, true);
}
if (!isLimit && isNum) dp[i] = res; // 在不受到任何约束的情况下,记录结果
return res;
}



相关题目#

902. 最大为 N 的数字组合 - 力扣(LeetCode)

https://leetcode.cn/problems/number-of-digit-one/

https://leetcode.cn/problems/number-of-2s-in-range-lcci/

https://leetcode.cn/problems/digit-count-in-range/

[toc]

2022-10-20#

2269. 找到一个数字的 K 美丽值 - 力扣(LeetCode)

字符串截取一段的函数是substring(起始位置闭区间, 终止位置开区间)

注意0不能用来取余

2022-10-13#

取前后石子的博弈论#

877. 石子游戏 - 力扣(LeetCode)

先是用dp动态规划+博弈论求解

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
class Solution {
public boolean stoneGame(int[] piles) {
int n = piles.length;
int[][][] dp = new int[n][n][2];
for (int len=1;len<=n;len++) {
for (int i = 0;i < n;i++) {
int j = i + len-1;
if (j >= n) {
continue;
}
int p1 = piles[i];
int p2 = piles[j];
if (len == 1) {
dp[i][j][0] = p1;
dp[i][j][1] = -p1;
continue;
}

dp[i][j][0] = Math.max(p1 + dp[i+1][j][1], p2
+ dp[i][j-1][1]);

dp[i][j][1] = Math.min(-p1 + dp[i+1][j][0], -p2 + dp[i][j-1][0]);
}
}

return dp[0][n-1][0] > 0;
}
}

然而实际上先手方可以做到一直只取奇数位, 或者只取偶数位,那么只要求出奇数位总和和偶数位总和拿个大即可,所以先手必胜。

分块排序最终有序问题#

769. 最多能完成排序的块 - 力扣(LeetCode)

我先是用了一个O(n^2logn)的麻烦方法, 欺负他量级小。

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
class Solution {
public int maxChunksToSorted(int[] arr) {
int left = 0;
int needMin = 0;
int count = 0;
for (int r = left;r<arr.length;r++) {
if (isValid(arr, left, r, needMin)) {
count++;
left = r+1;
needMin = r + 1;
}
}
return count;
}

public boolean isValid(int[] arr, int left ,int right , int needMin) {
int[] newa = new int[right -left + 1];
boolean found = false;
for (int i = 0; i < newa.length;i++) {
newa[i] = arr[left + i];
if (needMin == newa[i]) {
found = true;
}
}
Arrays.sort(newa);
for (int i = 1; i < newa.length;i++) {
if (newa[i] != newa[i-1] +1) {
return false;
}
}
return found;
}
}

实际上应该是 如果 当前最大值且正在在对应位置, 则左边的都可以排序了,因为自己一定是稳定排的(左边没有比自己小的,而自己又正好在自己这个值的索引处,那么左边肯定能排)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public int maxChunksToSorted(int[] arr) {
int count = 0;
int max = -1;
for (int i = 0; i < arr.length;i++) {
max = Math.max(arr[i], max);
if (max == i) {
count++;
}
}
return count;
}


}

2022-10-12#

负二进制问题#

1073. 负二进制数相加 - 力扣(LeetCode)

模拟了半天才知道规律好头疼。下面这个感觉很难记住,其实就是

-1的时候,此位变1,下一位+1

1的时候,此位变1,下一位-1

0的时候不变

1665585947374

1665586073799

提交二里看到一个,没看懂怎么利用

1665586055842

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
class Solution {
public int[] addNegabinary(int[] arr1, int[] arr2) {
int add =0;
List<Integer> res = new ArrayList<>();
for (int i = 0; i < Math.max(arr1.length, arr2.length);i++) {
int index1 = arr1.length - 1 - i;
int index2 = arr2.length - 1 - i;
int num1 = index1 < 0 ? 0 : arr1[index1];
int num2 = index2 < 0 ? 0 : arr2[index2];
int num3 = add + num1 + num2;
if (num3 == -1) {
num3 = 1;
add = 1;
} else {
add = -(num3 / 2);
num3 %= 2;
}
res.add(num3);
}

if (add==-1) {
res.add(1);
res.add(1);
} else if (add==1){
res.add(add);
}

// 去除前导0
while(res.size() > 1 && res.get(res.size()-1) == 0) {
res.remove(res.size()-1);
}

int[] resArr = new int[res.size()];
for (int i = 0; i < res.size();i++) {
resArr[i] = res.get(res.size()-1-i);
}
return resArr;
}
}

[toc]

性能优秀的几个要点#

参考:知乎:Presto运行时浅析

1. 完全基于内存的并行计算#

不考虑存盘,因此很多操作都尽可能设计成在内存完成,几乎不考虑落盘的可能性。

2. 流水线计算#

只要Task中的算子从上游Task中可以获取到数据,便开始执行计算,计算产出的数据直接放入该Task对应的OutputBuffer中等待下游Task来访问,也就是说在执行非聚合算子的情况下,Client是可以源源不断的流式获取到计算结果,而不用等到所有数据都计算完成

3. 本地化计算#

Presto会根据元信息读取分区数据,提供的conectorApi可以适配不同的数据源做本地数据读取
合理的分区能减少Presto数据读取量,提升查询性能。
且可以提前本地计算,包括支持broadcast-join

4. 动态编译执行计划#

原始的逻辑执行计划,直接表示用户所期望的操作,未必是性能最优的,
逻辑执行计划fragment发送到机器上后,由结点树形式转写成operator list,根据逻辑代码动态编译生成字节码。动态生成字节码,主要是利用编译原理:

5. 小心使用内存和数据结构#

对于数据的计算是以Page为单位,
Page是由多个Block组成,每个Block为单一类型
例如Int Block, Long Block, Varchar Block等,其代表了Table中的某一列中的部分数据
因此数据可以被成为列数据
0fcd7788d61bdcbf62d73d3765b4ab778e7f7d95

6. 类BlinkDB的近似查询#

支持提供一个近似查询函数
例如approx_distinct() 函数比Count(distinct x)有大概2.3%的误差

近似查询概念:
如果数据库所存放的数据的数量十分庞大,要完成完整查询,则会需要花费较长的时间。如果我们使用近似查询技术,就可以以采样的方式,对均值的结果进行估计,以一定的精度损失,快速的获取对精确值的估计。这在某些场景下,会有一定的应用。不止是COUNT,SUM,AVG等常用的聚集估计方法都可以得到支持。除了简单的查询之外,近似查询还可以处理关联查询(join),嵌套查询,范围查询等复杂查询场景。

7. GC控制#


https://www.zhihu.com/question/461560750
https://www.sohu.com/a/310831715_115128
https://zhuanlan.zhihu.com/p/93711386

presto性能调优的几个方式#

Presto性能调优的五大技巧
Presto介绍与常用查询优化方法

提升并行度#

通过task.concurrency参数来设置join、aggregator等操作的并发度

但这个并行度是分多线程,还是用于控制多诘?

提升worker处理spilt的线程数#

如果工作器CPU利用率低并且所有线程都在使用中,则增加task.max-worker-threads

可以提高吞吐量,但是会导致堆空间使用率增加。将该值设置得太高可能会由于上下文切换而导致性能下降。(看起来并行度指的是节点并行度?)

worker中由于采用线程模型,因此Query对应算子的执行会从线程池中分配一个线程负责,而该线程对于Operator的执行时长为duration表示,超过duration所表示的时间,该线程就有机会切换执行其他query的算子,从而从粗粒度实现用户态CPU资源的分时调度,进而实现cpu层面的多租户。

增大每个worker上处理的split数#

node-scheduler.max-splits-per-node参数。

类似于一次性取多少spilt数据过来。

元数据有配置支持缓存,避免频繁获取元数据,加快加载速度。#

使用Join语句时将大表放在左边:#

Presto中join的默认算法是broadcast join,即将join左边的表分割到多个worker,然后将join右边的表数据整个复制一份发送到每个worker进行计算。

UNION ALL 代替 UNION :不用去重#