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
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
public class VisitorDemo {
public static void main(final String[] args) {
Car car = new Car();
car.accept(new CarElementPrintVisitor());
}
}
// supertype of all objects in the structure
interface CarElement {
void accept(CarElementVisitor visitor);
}
// supertype of all operations
interface CarElementVisitor {
void visit(Body body);
void visit(Car car);
void visit(Engine engine);
}
class Body implements CarElement {
@Override
public void accept(CarElementVisitor visitor) {
visitor.visit(this);
}
}
class Engine implements CarElement {
@Override
public void accept(CarElementVisitor visitor) {
visitor.visit(this);
}
}
class Car implements CarElement {
private final List<CarElement> elements;
public Car() {
this.elements = List.of(new Body(), new Engine());
}
@Override
public void accept(CarElementVisitor visitor) {
for (CarElement element : elements) {
element.accept(visitor);
}
visitor.visit(this);
}
}
class CarElementPrintVisitor implements CarElementVisitor {
@Override
public void visit(Body body) {
System.out.println("Visiting body");
}
@Override
public void visit(Car car) {
System.out.println("Visiting car");
}
@Override
public void visit(Engine engine) {
System.out.println("Visiting engine");
}
}

2 利用封装接口和类型switch特性(java17) 快速达到目标

代码少了一半,但是效果确完全相同

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
public class VisitorDemo {
public static void main(final String[] args) {
Car car = new Car();
print(car);
}
// 这就是visitor
private static void print(Car car) {
car.elements()
.map(element -> switch (element) {
case Body body -> "Visiting body";
case Car car_ -> "Visiting car";
case Engine engine -> "Visiting engine";
})
.forEach(System.out::println);
}
}
// supertype of all objects in the structure
sealed interface CarElement
permits Body, Engine, Car { }
class Body implements CarElement { }
class Engine implements CarElement { }
class Car implements CarElement {
private final List<CarElement> elements;
public Car() {
this.elements = List.of(new Body(), new Engine());
}
public Stream<CarElement> elements() {
return Stream.concat(elements.stream(), Stream.of(this));
}
}

使用注意:
使结构类型的接口密封 (你不应修改密封接口,若要修改,参见3)
对于操作,使用模式开关来确定每种类型的代码路径 (从switch到具体的类的类型)
避免使用默认分支,这样当你修改类型接口时,你才能获得在每个switch中出现编译错误,这可以引导你去修改

1 考虑这么一部分代码

1
2
3
4
5
6
7
flatMap(queryResponseAfterAsr ->
instanceService.getSaaSByPaasEnvAndRegionWithAlibaba(instance)
.publishOn(Schedulers.boundedElastic())
.flatMap(saas -> { // 分出来三个流,分别是saas1 saas2 saas3
queryResponseAfterAsr.saasInstance = saas; // 1
return saasLogGetter.dealInputRequest(queryResponseAfterAsr) // 2
.flatMap(saasLogGetter::buildAndExecQuerySql); // 3

注意,上述看起来保证了在2,3过程当中,1的saasInstance为2,3中的instanceId。

但实际上可能由saas1流里的3的某个部分block了(比如异步开线程查了数据库)
,然后faltmap的第saas2,saas3对应的流修改queryResponseAfterAsr中的saasInstance,
从而导致saas1流看到的queryResponseAfterAsr.saasInstance不再最初的saas1。

2 总结

一个好的思维模式:流之间去耦合是必要的,若不同的流有相同的依赖状态,确保每个流拥有它的一个复制。

1 运行pointnet2

https://github.com/yanx27/Pointnet_Pointnet2_pytorch#part-segmentation-shapenet

下载s3dis数据:
https://docs.google.com/forms/d/e/1FAIpQLScDimvNMCGhy_rmBA2gHfDu3naktRm6A8BPwAWWDv-Uhm6Shw/viewform?c=0&w=1

2 准备自己的测试数据

思路:将自己的一个obj模型,包装成为s3dis的一样的格式

方式如下:

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
# 1 准备原始数据:
目录结构如下:smallRoom.txt的每一行为 x y z r g b, floor_1.txt 是 smallRoom.txt的复制
nash5@gas:~/prjs/Pointnet_Pointnet2_pytorch/data/s3dis$ tree ./Stanford3dDataset_v1.2_Aligned_Version
./Stanford3dDataset_v1.2_Aligned_Version
└── Area_5
└── office_1
├── Annotations
│   └── floor_1.txt
└── smallRoom.txt

# 2 使用s3dis脚本生成测试数据:(你需要修改对应的meta文件,只留下一行)
nash5@gas:~/prjs/Pointnet_Pointnet2_pytorch/data/s3dis$ cat ../../data_utils/meta/anno_paths.txt
Area_5/office_1/Annotations
nash5@gas:~/prjs/Pointnet_Pointnet2_pytorch/data/s3dis$

然后:
cd data_utils
python collect_indoor3d_data.py

之后会在data目录下生产stanford_indoor3d目录,将其移动到
3dis下面:
nash5@gas:~/prjs/Pointnet_Pointnet2_pytorch/data/s3dis$ ls ..
modelnet40_normal_resampled s3dis shapenetcore_partanno_segmentation_benchmark_v0_normal stanford_indoor3d_ori
myData s3dis_ori
nash5@gas:~/prjs/Pointnet_Pointnet2_pytorch/data/s3dis$ ls
Stanford3dDataset_v1.2_Aligned_Version stanford_indoor3d

# 3 运行测试脚本:(你需要改动batch_size,1080可以设置为16),然后你会在log里的几层嵌套中找到这个
nash5@gas:~/prjs/Pointnet_Pointnet2_pytorch$ python test_semseg.py --log_dir pointnet2_sem_seg --test_area 5 --visual --batch_size 16
ay(total_correct_class_tmp) / (np.array(total_iou_deno_class_tmp, dtype=np.float) + 1e-6)
[0. 0.03883974 0. 0. 0. 0.
0. 0. 0. 0. 0. 0.
0. ]
Mean IoU of Area_5_office_1: 0.0388
----------------------------
test_semseg.py:185: DeprecationWarning: `np.float` is a deprecated alias for the builtin `float`. To silence this warning, use `float` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.float64` here.
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
IoU = np.array(total_correct_class) / (np.array(total_iou_deno_class, dtype=np.float) + 1e-6)
test_semseg.py:190: RuntimeWarning: invalid value encountered in true_divide
total_correct_class[l] / float(total_iou_deno_class[l]))
------- IoU --------
class ceiling , IoU: 0.000
class floor , IoU: 0.039
class wall , IoU: 0.000
class beam , IoU: 0.000
class column , IoU: 0.000
class window , IoU: 0.000
class door , IoU: 0.000
class table , IoU: nan
class chair , IoU: 0.000
class sofa , IoU: 0.000
class bookcase , IoU: 0.000
class board , IoU: 0.000
class clutter , IoU: 0.000

eval point avg class IoU: 0.002988
test_semseg.py:194: DeprecationWarning: `np.float` is a deprecated alias for the builtin `float`. To silence this warning, use `float` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.float64` here.
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
np.mean(np.array(total_correct_class) / (np.array(total_seen_class, dtype=np.float) + 1e-6))))
eval whole scene point avg class acc: 0.002988
eval whole scene petails and guidance: https://numpy.org/devdocs/release

# 4 查看结果: 用meshlab看那个pred.obj
nash5@gas:~/prjs/Pointnet_Pointnet2_pytorch$ ls log/sem_seg/pointnet2_sem_seg/visual/Area_5_office_1
Area_5_office_1_gt.obj Area_5_office_1_pred.obj Area_5_office_1.txt

1 滑动窗口

priority_queue经常用 或者st、ed

2 例题:

1425constrainedSubsetSum 带限制最大子序列和

1 题目

https://leetcode.cn/problems/constrained-subsequence-sum/

2 解题思路

  • 1 解题思路:
    • 1.1 dp[i]表示以第i个数据结尾的带限制最大子序列和
    • 1.2 dp[i] = nums[i] + max(0, dp[i-k], dp[i-k+1], …, dp[i-1])
    • 1.3 如何在i-1到i-k的数的集合中快速找到最大的满足限制要求的k?使用堆即可,只需要找到第一个满足要求的下标对应的值即可,不满足要求的下标可以pop掉(因为如果现在都无法满足要求,那么后面更加无法满足要求)
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
class Solution {
public:
using pii = pair<int, int>;


int constrainedSubsetSum(vector<int>& nums, int k) {
// Let dp[i] be the solution for the prefix of the array that ends at index i,
// if the element at index i is in the subsequence.
// dp[i] = nums[i] + max(0, dp[i-k], dp[i-k+1], ..., dp[i-1])

int n = nums.size();
vector<int> dp(n, 0);
dp[0] = nums[0];
int curRes = dp[0];

// <idx, value>
auto cmp = [](const pii& a, const pii& b) {
return a.second < b.second;
};
priority_queue<pii, vector<pii>, decltype(cmp)> maxHeap(cmp);

maxHeap.push({0, dp[0]});

auto findInWindow = [](int minIdx, decltype(maxHeap)& window) {
bool foundInWin = false;
while(!window.empty()) {
auto node = window.top();
if(minIdx <= node.first) {
// window.pop();
return max(0, node.second);
} else {
window.pop();
}
}
return 0;
};

for(int i = 1; i < n; ++i) {
dp[i] = nums[i] + findInWindow(max(0, i - k), maxHeap);
curRes = max(curRes, dp[i]);
// cout << i << "-> " << dp[i] << endl;
maxHeap.push({i, dp[i]});
}
return curRes;
}
}

1499findMaxOfEquationInVec 满足不等式的最大值

1 题目

https://leetcode.cn/problems/max-value-of-equation/

2 解题思路

  • 1 解题思路:
    • 1.1 首先能想到的是,对于数字point[i]的xy,我们可以维护一个window,其中放了所有xi,yi,满足|xi - x| <= k,然后我们遍历这个窗口的所有值,找到一个答案,但是这样复杂度是nk有点大了
    • 1.2 我们考虑一下,是不是需要考虑point[i]的左右距离为k的值?不需要,因为上面显然存在重复计算,比如x坐标为1,3,5的三个点,k = 100好了,对于1,考虑了3,5,对于3考虑了1,5,然而13的组合在x=1和这个点已经考虑过了,所以我们可以只考虑坐标的左半边或者右半边,我们以考虑左半边为例子
    • 1.3 由于是左半边,我们遍历的点称之为xj,那么xi就是左边的所有点,我们如何快速拿到结果呢?
      • res = for i < j && xj - xi <= k: max(yi + yj + |xi - xj|) = max(yj + xj + yi - xi),去掉了绝对值
      • 然后可以看出来对于一个j,不就是求它左边窗口(窗口内的值需要满足:xj - xi <= k)中yi - xi的最大值吗?那不就是用priority_queue存起来就行了
      • 注意一点的是:当窗口中的xi, xj - xi > k可以放心pop,因为若当前的j都距离xi太远了,那后面的只会更加的遥远,于是可以pop
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
class Solution {
public:

using pii = pair<int, int>;

static constexpr auto cmp = [](const pii& a, const pii& b) {
return a.second < b.second;
};

int findMaxValueOfEquation(vector<vector<int>>& points, int k) {
// first: yi + yj + |xi - xj| == yj + xj + yi - xi
// so: for each j, we shall found the biggest yi - xi and |xj - xi| <= k
// using a priority_queue to store those yi - xi

int n = points.size();
// priority_queue<pii, vector<pii>, decltype(cmp)> maxHeapOri(cmp);
priority_queue<pii, vector<pii>, decltype(cmp)> maxHeap(cmp);
maxHeap.push({points[0][0], points[0][1] - points[0][0]}); // init it

int curRes = INT_MIN;
for(int j = 1; j < n; ++j) {
// |xj - xi| <= k, we should consider xj - xi <= k or xi - xj <= k
// but we can only consider those xj > xi, cause, when we try to consider xj < xi,
// the bigger J after j will be like xi, so all cases coverd
int xj = points[j][0];
int yj = points[j][1];
// cout << "dealing: " << xj << " " << yj << endl;
// auto maxHeap = maxHeapOri;
while(1) {
if(maxHeap.empty()) {
break;
}

auto t = maxHeap.top();
int xi = t.first;
int delatI = t.second;
if(xi != xj ) {
if(xj - xi > k) {
// cout << "pop: " << xi << endl;
maxHeap.pop();
} else {
curRes = max(curRes, yj + xj + delatI);
break;
}
} else {
break;
}
}
maxHeap.push({points[j][0], points[j][1] - points[j][0]});
}
return curRes;

}
}

1610visiblePoints 可见顶点的最大数目

1 题目

https://leetcode.cn/problems/maximum-number-of-visible-points/

2 解题思路

  • 1 解题思路:
    • 1.1 算出极角,然后排序,然后用一个窗口,范围是angle,从最小移动到最大即可
    • 1.2 注意循环:比如-179和179这两个数字爱得很近,所以我们把排序好的点的极角加上360加入到点的数组中,滑动窗口就行
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
67
68
69
70
71
72
73
74
class Solution {
public:
#define PI 3.14159265

using Point = pair<pair<int, int>, double>;

int visiblePoints(vector<vector<int>>& points, int angle, vector<int>& location) {

vector<Point> calPoints;
int n = points.size();
int dupWithOriCnt = 0;
// start form the negx: from -179.999 to 180
auto calDegreeForPoints = [&](vector<int>& xy, vector<Point>& calPoints) {
int x = xy[0] - location[0];
int y = xy[1] - location[1];
if(0 == x && 0 == y) {
dupWithOriCnt += 1;
return;
}
double result;
result = atan2 (y, x) * 180 / PI;
calPoints.emplace_back(Point{{x, y}, result});
};

for(auto& p : points) {
calDegreeForPoints(p, calPoints);
}

// all duplicated
if(0 == calPoints.size()) {
return dupWithOriCnt;
}

// sort by the angle
sort(calPoints.begin(), calPoints.end(), [](const Point& a, const Point& b) {
return a.second < b.second;
});
for(int i = 0; i < n; ++i) { // solve the circle problem
auto p = calPoints[i];
p.second += 360.0;
calPoints.push_back(p);
}

// init window
int st = 0;
int ed = 0;

double dAngle = angle;

while(ed < calPoints.size() && calPoints[ed].second - calPoints[st].second <= dAngle) {
++ed;
}
--ed;
int finRes = ed - st + 1;

// mv window
while(ed < calPoints.size()) {
// mv st
++st;
while(ed < calPoints.size() && calPoints[ed].second - calPoints[st].second <= dAngle) {
++ed;
}
if(ed == calPoints.size()) {
finRes = max(finRes, static_cast<int>(calPoints.size()) - st);
} else {
--ed;
finRes = max(finRes, ed - st + 1);
}

}

return finRes + dupWithOriCnt;
}
}

2302coountSunarrysScoreLessThanK 统计得分小于 K 的子数组数目

1 题目

https://leetcode.cn/problems/count-subarrays-with-score-less-than-k/

2 解题思路

  • 1 解题思路:
    • 1.1 使用st,ed记录当前窗口,将窗口扩展至最大的满足分数小于k的条件
    • 1.2 ++st,找到下一个st
    • 1.3 对于每个窗口如何计数:
      • 计数:以st开始的区间
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
class Solution {
public:
long long countSubarrays(vector<int>& nums, long long k) {
// alway keep st,ed as the max status:
// score(st, ed) <= k, ed cannot be bigger for each st
// then we statistic for each st, how many ways start with st

// moving window score = getScore(nums[st:ed])
long long st = 0;
long long ed = 0;
long long n = nums.size();
long long curScore = 0;
long long cnt = 0;
while(st < n) {
bool edMoved = false;
while(ed < n && (curScore + nums[ed])*(ed - st + 1) < k) {
curScore += nums[ed];
++ed;
edMoved = true;
}

// add cnt start with cur st
if(curScore*(ed - st) < k) {
cnt += ed - st;
}

curScore -= nums[st];
++st;
}
return cnt;
}
};