2024年2月

那么作为门外汉,如何快速了解一个行业。可以从四个层面系统性地去了解

1、行业了解的目的

一般来说,从企业角度出发做行业分析的目的通常有三个:

  1. 了解所属行业的发展现状、竞争优劣、行业前景等,现在这个行业里竞争环境如何。
  2. 挖掘行业机会点,明确优势,看清劣势,寻找与领先企业的差距,改善资源配置,扬长避短。
  3. 分析市场产品布局,找到企业产品层面的突破,为新产品做行业调研、市场分析、行业的发展动态,竞品格局,市场情况,使用何种技术和做出哪一类型的产品可以让企业最具竞争力。

2、资料收集

刚开始分析的重点是快,而不是深

资料搜集对于整体行业的了解至关重要
。如果在第一环节搜集到的资料过少,进而导致整个行业研究流产。为此,我们会通过多维度进行资料的搜集,其中包括:

  • 一手信息
    :从身边的资料开始,在公司或者认识的人中是否有涉足过该行业的人进行调研访谈。不过要从浅水区到深水区,就不太好走,信息不那么容易获取。要知道行业里面的干货,就需要和行业内部人士交流。
  • 二手信息:
    整理和分析第三方机构发布的数据,了解所在行业/市场。以下是几个常用的搜索互联网行业信息的渠道。

搜索引擎

行业报告

行业资讯

皮书数据库:
https://www.pishu.com.cn

3、行业分析

在资料采集阶段,获取大量信息,那么需要消化和对比这些资料,提出我们了解这个市场的分析框架。我们可以从四个方面进行梳理:

  1. 行业发展历程
  2. 宏观经济PEST分析
  3. 行业细分领域
  4. 标杆企业

1)行业的发展史与现状

行业生命周期理论
通过
行业增长率、市场集中度、竞争状况、市场规模、利润率、技术成熟度
等指标,将一个行业从兴起到衰落分成幼稚期、成长期、成熟期和衰退期四个发展阶段

例如K12在线教育行业的目前的发展阶段在成熟期:

2)宏观经济PEST分析

PEST是从
政治
(Politics)、
经济
(Economic)、
社会
(Society)、
技术
(Technology)四个方面,基于公司战略的眼光来分析企业外部宏观环境的一种方法。

公司战略的制定离不开宏观环境,而PEST分析法能从各个方面比较好的把握宏观环境的现状及变化的趋势,有利于企业对生存发展的机会加以利用,对环境可能带来的威胁及早发现避开。

3) 这个行业的细分领域

行业细分是按照一定的方法划分经营领域的过程,并在细分的基础上,把行业分析的方法进一步运用于经营领域,从而为企业经营战略的制定提供依据。

行业细分是行业内部结构分析的一种方法,可以帮助企业选择特定的经营领域。行业细分的实质是企业根据自身战略制定的需要,将整个行业的生产领域(产品或服务)和市场领域(顾客或用户)分别按照若干特定的变量划分后再组合。

例如中国教育行业细分领域:早教幼儿领域、K12教育领域、高等教育领域和职业培训领域。在互联网上,K12、职业教育和语言培训三个教育细分领域受到非常高的关注。通过分析用户上网路径过程及获取信息过程的数据,我们发现用户对于这三个细分领域的需求较大。

在线教育行业的细分领域之下,可了解每个细分领域中的典型竞品

4)供给分析

行业产业链包括原材料采购、将它们转换成产品、运送至顾客和提供终端用户最终产品等,价值链描述了从原材料到最终消费者的所有活动

5)竞争分析

1、行业集中度

我们主要看CR4和CR8。
一般来说,行业集中度越高,对新入局的玩家越不利,因为头部掌握了消费者心智以及行业核心资源。

2、主要竞争者及成功要素

找到对标的竞争者之后,可以从
品牌力、产品力、渠道力、营销力、资本力
五个要点,列出五维雷达图的得分,帮助我们分析。

3、行业壁垒

如果行业竞争壁垒比较低,那么竞争的玩家也就比较多。

以敏感肌行业为例的话,主要包含品牌壁垒、规模化经营壁垒、人才壁垒、产品质量壁垒、技术壁垒和行业壁垒六大板块。

  • 品牌壁垒

    消费者在购买化妆品过程中受品牌形象、广告营销等因素较大。
  • 规模化经营壁垒
    :于供应商、经销商而言,议价处于优势;销售渠道更为便利、快捷;保证研发、生产制造的持续投入。
  • 人才壁垒
    :化妆品企业注重品牌策划、市场营销、销售渠道、产品研发等领域的建设。
  • 产品质量壁垒
    :化妆品由于直接作用于皮肤表面,其产品质量直接关系到消费者身体健康;随着消费理念的逐渐成熟,消费者对于化妆品的安全性、专业性的关 注度逐渐提高。
  • 技术壁垒(
    敏感肌):敏感肌肤人群的皮肤对于环境因素、季节变化等外部刺激的敏感性更强,因此对于护肤品的安全性要求更高。
  • 行业壁垒
    (敏感肌):敏感肌人群选择谨慎,适用皮肤后一般不轻易更改,因此产品的客户粘性较强

4、替代品威胁

在直接行业产品之外,是否存在满足消费者同一需求的替代品,从而形成一定的竞争关系。

举个例子,猪肉价格涨的时候,大家就会转向鸡鸭肉。

从行业下放到公司层面,从事这个行业的知名企业有哪些,我们对行业的了解也要细化到公司针对的细分市场。行业内定位不同的公司有着不同的需求结构。也可以了解这个领域的知名专家有哪些,他们有什么观点,有什么著作。

①读企业报表

研究财务报表,不是看赚了多少利润,这个属于结果的范畴。研究一件事情,要看的是过程,而不是结果。结果可以造假,过程无法粉饰。

所以了解一个行业,要通过龙头公司的报表,连续一段时间看它的财务投入在哪些地方、是否真的在扩张、是否有业绩,这至关重要。

换句话说如果龙头公司都没有任何战略布局,只是通过单纯的数字游戏,或者重组除了主营业务之外的领域,说明这个行业就是迷茫的。

公司数据信息收集网站

管理信息

财报信息

投融资信息

②挖知名创始人

一个好公司,一定会有战略眼光清晰的创始人。比如在线教育市场,通过了解市场参与者背景,直接百度或者通过一些搜索渠道,看看他说话靠不靠谱就行了。如果他出口就是梦想,空话套话一大堆,那这个行业就要小心了。

4. 总结结论

通过资料收集获取很多基本材料之后,我们会对这些材料进行结构化分析,从而快速建立起对这个行业的整体认知:对这个行业的定义、这个行业上下游、从事这个行业的机构、行业的受众用户这些基本问题。通过从行业分析、市场分析和商业模式多个角度,对我们前面搜集到的信息进行分析,通过结构化分析最终能够帮助我们全面深刻地理解所研究的行业。

我们了解的这些信息是碎片化的,还需要站在我们自己的专业领域的角度,结合这些的想法,可以把这些信息加工成一个完成的故事,去给其他人分享,通过这种输出的方式,更加有助于我们的了解。

原文 | Richard Lander

翻译 | 郑子铭

轻量级功能

嗯……但是如果我们使用 Wasm 更像是一个典型的功能而不是一个应用程序,我们可能不会计算一百万个单词,而是做一些更轻量级的事情。让我们重新运行比较,但使用最小的文件。

通过 Wasm,使用我们的解释器:

$ time WASMTIME_NEW_CLI=0 wasmtime run --mapdir /text::/home/rich/git/convenience/wordcount count.wasm $* /text/Clarissa_Harlowe/summary.md
        9     153    1044 /text/Clarissa_Harlowe/summary.md
Elapsed time (ms): 21
Elapsed time (us): 21020.8

real    0m0.098s
user    0m0.083s
sys 0m0.014s

使用 Wasm 和原生 AOT:

$ time WASMTIME_NEW_CLI=0 wasmtime run --mapdir /text::/home/rich/git/convenience/wordcount count.wasm $* /text/Clarissa_Harlowe/summary.md
        9     153    1044 /text/Clarissa_Harlowe/summary.md
Elapsed time (ms): 0
Elapsed time (us): 825.3

real    0m0.048s
user    0m0.035s
sys 0m0.014s

再次,使用 CoreCLR:

$ time ./app/count ../Clarissa_Harlowe/summary.md 
        9     153    1044 ../Clarissa_Harlowe/summary.md
Elapsed time (ms): 16
Elapsed time (us): 16100

real    0m0.063s
user    0m0.027s
sys 0m0.019s

此图表显示了上下文中的所有结果,这次是针对较小的文档。

有趣的。对于较小的工作负载,其中一些选项之间的性能差异开始缩小。我们还可以看到运行时启动成本的差异。现在说还为时过早,但这些动态可能是这项技术的关键考虑因素。一个重要的警告是,字数统计只是一种情况,其他情况可能会产生完全不同的结果。目前,该示例已经提供了足够的预期内容。

这一切都还处于早期阶段。随着我们走得更远,我们将想要测试更有趣的场景,以形成更具代表性的理解。我也确信其中一些性能数字将会提高。

功能上略有改进

WASI 的承诺是能够依赖一组具有丰富功能的接口(和匹配的实现)。
SpiderLighting
兑现了这一承诺。

SpiderLightning:一组 WIT 接口,抽象分布式应用程序功能和运行时 CLI,用于运行使用这些功能的 Wasm 应用程序。

如前所述,WASI 旨在定义与 System 命名空间相同的平台功能(但只是其中的一小部分)。您可以在 SpiderLighting
wit
目录中看到该接口的接口定义(至少一种版本)。

如果你有智慧,那就发挥你的智慧

您应该能够走到任何 WIT 界面,引用它,查看其完整的类型形状,并开始使用它进行编码。我们距离最终体验还有几步之遥,但这就是愿景。

SpiderLighting 提供了一个名为 slim 的便捷 CLI 工具,它可以连接 wasmtime、您的应用程序、
WASI SDK
以及您的应用程序所需的任何 WIT 实现(如在
slimfile.toml
中声明的那样)。

SpiderLighting 团队告诉我们,他们构建了 slim(及相关组件)作为工具来帮助他们开发
wasi-cloud-core
规范,以实现无服务器功能。在不久的将来,我们预计其他应用程序主机(例如
Fermyon Spin
)将使用 wasi-cloud-core 接口,然后我们将使用其中一台主机,而不是轻微的。

我们有一组
Spiderlight
样本。以下示例创建 WASI 键值存储,然后打印到控制台。请注意,dotnet run 使用 light 作为实现细节。

using SpiderLightning;

using var keyValue = new KeyValue("placeholder-name");
keyValue.Set("somekey", "Hello from .NET. This value is from a SpiderLightning key-value store.");

Console.WriteLine(keyValue.GetString("somekey"));

请记住,KeyValue 不是 C# 类型,而是投射到 C# 中的 WASI 接口。

这是代码运行时的样子。

$ pwd
/home/rich/git/spiderlightning-dotnet
$ docker run --rm -it -v $(pwd):/source -w /source/sample/ConsoleApp wasi-sdk dotnet run -c Release
Hello from .NET. This value is from a SpiderLightning key-value store.

我正在一个安装了所有必需依赖项的
容器
中运行该应用程序。容器和WASI可以一起使用吗?一定。

您可以在
有限的环境
中构建依赖 WASI SDK 的应用程序,并在
更大的环境
中运行它们。 Windows 和 macOS Arm64 的支持似乎最差。随着时间的推移,这肯定会改变。

网页场景

对 WASI 的很多兴趣在于能够托管小型且便携式的 Wasm 功能和应用程序。其中一个关键方面是使用某种形式的网络编程模型。目前,我们还没有启用 WASI 的 ASP.NET Core。目前,我们已经公开了
http-server
WASI 类型。

它启用
以下模式

HttpServer.OnIncomingRequest(request =>
{
    return new HttpResponse(200)
        .WithBody($"<h1>Hello!</h1> You did a {request.Method} request to {request.Uri} with {request.Headers.Count} headers")
        .WithHeaders(new[] { KeyValuePair.Create("content-type", "text/html") });
});

这有点低级了。
委托
也不是异步友好的。以下是异步最终如何工作的
一些提示

我尝试
用这个 API 编写一个更大的示例
。它目前被阻止,因为我们没有办法调用 https 端点。我可以通过在本地复制所有必需的 JSON 文件来解决这个问题,但不会那么引人注目。

这个领域是最有趣的,但也是定义最少的。我们预计至少还需要一年的时间才能运行我们所认为的真正的网络应用程序和功能。我们的目标是建立一个模型,您无需更改太多代码即可使用 Wasm 作为部署目标。

实验

WASI 工作负载目前处于实验阶段,因此得名工作负载。至少在 WASI 本身发布稳定的 1.0 版本之前,它仍将是一个实验。我们无法确切地预测那会是什么时候。

有几个待办事项需要调查和解决:

  • 综合调试
  • 支持AOT
  • dotnet 使用 wasmtime CLI 参数运行
  • 支持更多 WASI 接口,可能通过更好的 witbindgen 支持

闭幕式

更高层次的故事是,我们已经能够使我们的 Blazor Wasm 实现(实际上是整个 .NET)适应便携式计算的新领域。正如这几个演示所证明的那样,很多功能已经发挥作用。

您可以使用 .NET 8 尝试在本文中阅读的所有内容(以及更多内容)。从以下命令开始,安装所需的软件。

dotnet workload install wasi-experimental

在接下来的一年里,我们将专注于提高当前实施的功能和用户体验,并跟随 WASI 的总体发展。我们还期待观察云团队如何在其服务中采用 WASI。迄今为止,我们一直在构建支持技术。随着我们的进一步发展,我们将考虑专注于与云服务相结合的更多定位体验。就目前而言,这一切都是面向未来的,就像 WASI 的其他部分一样。

简洁是智慧的灵魂。 - 威廉·莎士比亚

如果你读到这里,你就会明白,如果我说得简短一点,你就不会那么了解情况了。但鉴于这篇文章的篇幅和细节,我已经无计可施了。

原文链接

Extending WebAssembly to the Cloud with .NET

知识共享许可协议

本作品采用
知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接:
http://www.cnblogs.com/MingsonZheng/
),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

如有任何疑问,请与我联系 (
MingsonZheng@outlook.com
)

服务的发现

发现的方式

服务的发现说的就是svc的ip只有集群内的资源可以访问到,比如集群内的节点,pod
到底说的是什么意思呢?我们可以来看看LNMP架构(Linux + Nginx + Mysql + php)搭建的wordpress
首先wordpress是需要对接数据库的,那么wordpress部署在集群里如何去对接后端的数据库存储数据呢?这也就是服务的发现
也就是说你要通过什么方式去让前端的wordpress去对接后端的数据库

1. ClusterIP

每个pod被创建出来之后,都会被分配一个IP地址,但是这个IP地址只有集群内部可以访问到,那么既然他都有IP地址了,为什么要通过这个ClusterIP呢?
我们来仔细分析这句话,每个Pod都会被分配IP地址,那万一这个Pod是被deployment管理的,这个pod被意外删除了,那么控制器会重新启一个pod,那么这个pod会有一个新的IP,这个时候你的wordpress指定的数据库地址还是之前的那个地址,然而那个地址已经不存在,那么你的wordpress就会报数据库连接失败的错误
我们来看看是不是这样

# 我们创建一个控制器,副本数为1,看看IP,然后我们手动删除他创建出来的那个pod,然后我们再看看这个新的Pod的IP
[root@master ~]# kubectl create deployment test01 --image nginx
deployment.apps/test01 created
[root@master ~]# kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
test01-994bb76cb-2qd5g   1/1     Running   0          33s   10.244.104.59   node2   <none>           <none>
# 我们现在看到,这个Pod的IP是 10.244.104.59,删除这个pod
[root@master ~]# kubectl delete pod/test01-994bb76cb-2qd5g 
pod "test01-994bb76cb-2qd5g" deleted
[root@master ~]# kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
test01-994bb76cb-4vtkw   1/1     Running   0          25s   10.244.166.182   node1   <none>           <none>

我们通过这个小实验可以看到,IP是会变动的,那么我们使用以前的IP访问现在的Pod肯定是不行的,那么有没有一种办法就是给他们一个浮动IP呢,我每次只需要访问这个浮动IP,他会自动帮我把流量转发到pod下面呢?有的
这个就是ClusterIP的作用
再来看一个实验 还是这个控制器,我们给这个控制器创建一个svc,然后通过svc的浮动IP去访问pod

# 由于刚刚的deployment并没有删除,那我现在接着做
[root@master ~]# kubectl expose deployment test01 --port 80 --target-port 80
# 这里的--port 是svc暴露的端口,可以随意修改、
# --target-port 是指的pod内的服务端口,你的pod开放什么端口这里就要写什么,写错了服务就访问不到的
[root@master ~]# kubectl expose deployment test01 --port 80 --target-port 80 
service/test01 exposed
[root@master ~]# kubectl get svc
# 我不是在默认的命名空间下,如果你是在默认的命名空间下还会有一个默认的Kubernets的svc
NAME     TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
test01   ClusterIP   10.98.54.158   <none>        80/TCP    3s
# 我们通过这个ip地址来访问一下nginx看看
[root@master ~]# curl 10.98.54.158
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
…… 省略部分信息
# 可以访问到nginx的首页,我们删除这个pod,然后再来使用这个IP地址看看
[root@master ~]# kubectl delete pods/test01-994bb76cb-4vtkw 
pod "test01-994bb76cb-4vtkw" deleted
[root@master ~]# kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
test01-994bb76cb-ntwjn   1/1     Running   0          15s   10.244.104.55   node2   <none>           <none>
# 他的pod的IP地址已经变了,我们来看看svc的地址能否继续访问到
[root@master ~]# curl 10.98.54.158
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
……省略部分信息

通过这个实验我们看到了,我们可以通过这这种方式来给这些个pod一个浮动的IP,无论你Pod的IP地址怎么变动,我只要访问你的浮动IP就可以了。并不需要知道或者固定你的IP

2. 通过变量

学习这个之前你得先能搞懂ClusterIP!!!
在K8S集群里面,每个被创建出来的pod都会去加载当前命名空间下的所有的SVC并注册成环境变量,注意,是所有,大概的流程就是这样的
pod1 pod2 pod3

svc1 svc2 svc3
----------------------------------->时间线
这个图的意思就是在创建一个pod1的时候,他会把svc1注册成他的一个环境变量,创建pod2的时候会把svc1和svc2都注册,pod3则会把svc1,svc2和svc3全部注册到环境变量里面
pod1不会加载svc2和svc3,因为在创建pod1的时候svc2和svc3还没有被创建出来
大概就是这样一个流程,我们来看看是不是这样

[root@master wordpress]# kubectl get svc
NAME     TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
test01   ClusterIP   10.98.54.158   <none>        80/TCP    16m
# 我们先进入刚刚的nginx看看环境变量
[root@master wordpress]# kubectl exec -it pods/test01-994bb76cb-ntwjn -- bash
root@test01-994bb76cb-ntwjn:/# env |grep -i test01
HOSTNAME=test01-994bb76cb-ntwjn
TEST01_SERVICE_PORT=80
TEST01_PORT_80_TCP_PROTO=tcp
TEST01_PORT_80_TCP_ADDR=10.98.54.158
TEST01_PORT=tcp://10.98.54.158:80
TEST01_PORT_80_TCP=tcp://10.98.54.158:80
TEST01_SERVICE_HOST=10.98.54.158
TEST01_PORT_80_TCP_PORT=80

我们可以看到他确实有这些变量,而且有一个变量是TEST01_SERVICE_HOST 这个不就是浮动IP地址吗
我们再来创建一个新的pod,叫做pod02,给这个pod也创建一个svc,最后我们创建一个pod03,进入到pod03里面看看是不是有svc1和svc2的变量

[root@master wordpress]# kubectl run pod02 --image nginx --image-pull-policy IfNotPresent
pod/pod02 created
[root@master wordpress]# kubectl expose pod pod02 --port 80 --target-port 80
service/pod02 exposed
[root@master wordpress]# kubectl get svc
NAME     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
pod02    ClusterIP   10.105.166.129   <none>        80/TCP    10s
test01   ClusterIP   10.98.54.158     <none>        80/TCP    21m
# 现在有pod02的svc和test01的svc
# 创建新的pod,直接进去里面看看环境变量
[root@master wordpress]# kubectl run pod03 --image nginx --image-pull-policy IfNotPresent 
pod/pod03 created
[root@master wordpress]# kubectl exec -it pods/pod03 -- bash
root@pod03:/# env |grep -i pod02
POD02_PORT_80_TCP_PROTO=tcp
POD02_PORT_80_TCP_ADDR=10.105.166.129
POD02_PORT=tcp://10.105.166.129:80
POD02_SERVICE_HOST=10.105.166.129
POD02_PORT_80_TCP_PORT=80
POD02_PORT_80_TCP=tcp://10.105.166.129:80
POD02_SERVICE_PORT=80
# pod02是有的,再来看看test01
root@pod03:/# env |grep -i test01
TEST01_SERVICE_PORT=80
TEST01_PORT_80_TCP_PROTO=tcp
TEST01_PORT_80_TCP_ADDR=10.98.54.158
TEST01_PORT=tcp://10.98.54.158:80
TEST01_PORT_80_TCP=tcp://10.98.54.158:80
TEST01_SERVICE_HOST=10.98.54.158
TEST01_PORT_80_TCP_PORT=80

我们可以发现,确实跟我们说的一样,他会pod创建之前就存在的svc全都注册到环境变量里面
那么通过这一特性,我们可以在给pod传参的时候就不用写IP地址了,直接写上变量,这种方式的场景是什么呢?
是这样的,虽然我们现在有浮动IP了,那万一这个svc被删除了又被重新创建出来了呢,浮动IP是不是变动了,那么就可以使用这个方式

# 我直接拿wordpress的一段代码过来给你们对比一下
# 之前的方式
containers:
      - image: wordpress
        imagePullPolicy: IfNotPresent
        name: wordpress
        resources: {}
        volumeMounts:
        - name: web
          mountPath: /var/www/html
        env:
        - name: WORDPRESS_DB_HOST
          value: "10.110.98.28"

变量的方式

containers:
      - image: wordpress
        imagePullPolicy: IfNotPresent
        name: wordpress
        resources: {}
        volumeMounts:
        - name: web
          mountPath: /var/www/html
        env:
        - name: WORDPRESS_DB_HOST
          value: $(TEST01_SERVICE_HOST)

我们直接在这个地方指定他去获取环境变量,那么他自动就能拿到svc的浮动IP了,并且只要SVC的名字不变,他是能一直拿到的,当然,这个pod要重新创建

3. 通过DNS(推荐)

这种方式更加简单,我们使用控制器创建出来的pod,然后再创建一个pod使用busybox,我们直接使用wget + svc名字

# 我们先看看svc
[root@master wordpress]# kubectl get svc
NAME     TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
test01   ClusterIP   10.98.54.158   <none>        80/TCP    65m
# 我们使用busybox来看看
[root@master wordpress]# kubectl run busybox --image  busybox -- sleep 3600
pod/busybox created
# 进入pod
[root@master wordpress]# kubectl exec -it busybox -- sh
/ # 
/ # wget test01
Connecting to test01 (10.98.54.158:80)
saving to 'index.html'
index.html           100% |**************************************************************|   615  0:00:00 ETA
'index.html' saved
/ # ls
bin         etc         index.html  lib64       root        tmp         var
dev         home        lib         proc        sys         usr

看到这里我们直接使用wget+ svc的名字他就把index.html文件下载了,说明是能访问到的
这种方式只能方式同一命名空间下的svc,如果要哦访问不同的命令空间下的svc也很简单
只需要在svc名字后面加上. 命名空间名字

比如
我现在需要访问hehe命令空间下的svc01,那么就是这样的
wget svc01.hehe 这样就好了

洛谷试炼场的题目确实很具有代表性,但是近几年以来,又有许多经典题目出现在 OI 界中,这个大题单就是作为洛谷试炼场的扩展和补充。

新版本食用指南

本次版本更新变更较大,建议您仔细阅读下面的内容!

在刚刚更新的 2.0 版本中,我们改变了原来按知识难度排列知识点的目录结构,改为按照专题大类组织目录结构。
这些大类都整合到了数个小题单当中,方便大家使用。

为了方便按知识难度刷题的用户,这里给出一些建议:

  • 对于初学者,建议先完成 Part 1,2 两部分内容,为接下来的学习打好基础。
  • 对于要参加 CSP-S 的选手,建议在前面的基础上优先完成 Part 3.1-3.4, 4.1-4.4, 6.1-6.5, 7.1-7.8, 8.1-8.7 的内容(具体内容见下),在此基础上继续完成其他内容。
  • 每个专题下的题目先给出模板,剩下的题目均按照难度递增顺序排序,部分难度较高的综合性题目建议达到一定能力后再尝试解决。

$\\ $

更新日志

\(\color{blue} \small \text{【NEW】3.0.3 2024/1/26:}\)

  1. \(\color{blue} \tt \small 把各部分(Part)整和到了很多个小题单里面。\)
  2. \(\color{blue} \tt \small 增添了\color{gold} \ 智能判断系统\color{blue}\ 。\)

3.0.2 2020/2/28:

  1. 添加了少量比赛题目;
  2. 移除了一些做法重复的题目。

3.0.1 2019/12/8:

  1. 添加了 CSP2019 和一些公开赛的题目;
  2. 跟进洛谷域名更换,将题目链接全部更新。

3.0 2019/10/13:

  1. 新增专题:回文自动机,K-D Tree,自适应辛普森法,左偏树,置换群,离线算法,构造,DLX,三分法,珂朵莉树。
  2. 添加了一些最近的公开比赛题目,部分专题补充了一些优质题目。
  3. 移除了部分重复题目。
  4. 对之前没有介绍的专题补充了介绍。

更早版本的更新日志请点击这里查看

$\\ $

题单

希望这份题单能够帮助到你!

Part 0 试机题

三道试机题目。

Part 1 入门阶段

  • Part 1.1 从零开始

  • Part 1.2 数组基础

  • Part 1.3 字符串基础

  • Part 1.4 函数,递归及递推

Part 2 基础算法

  • Part 2.1 模拟

  • Part 2.2 排序算法

  • Part 2.3 二分答案

  • Part 2.4 分治

  • Part 2.5 贪心

  • Part 2.6 构造

  • Part 2.7 高精度

  • Part 2.8 前缀和 & 差分

Part 3 搜索

  • Part 3.1 深度优先搜索

  • Part 3.2 广度优先搜索

  • Part 3.3 记忆化搜索

  • Part 3.4 搜索的剪枝

  • Part 3.5 双向搜索

  • Part 3.6 A*

  • Part 3.7 IDA*

  • Part 3.8 DLX

Part 4 动态规划

  • Part 4.1-4.4 动态规划


    • Part 4.1 线性动态规划

    • Part 4.2 背包动态规划

    • Part 4.3 区间动态规划

    • Part 4.4 树形动态规划

  • Part 4.5-4.12 动态规划


    • Part 4.5 状态压缩动态规划

    • Part 4.6 倍增优化动态规划

    • Part 4.7 数据结构优化动态规划

    • Part 4.8 单调队列优化动态规划

    • Part 4.9 斜率优化动态规划

    • Part 4.10 决策单调性优化动态规划

    • Part 4.11 数位统计类动态规划

    • Part 4.12 轮廓线动态规划

Part 5 字符串

Part 6 数学

  • Part 6.1-6.4 数学


    • Part 6.1 位运算

    • Part 6.2 整除相关


      • Part 6.2.1 素数

      • Part 6.2.2 最大公约数

      • Part 6.2.3 欧拉函数

    • Part 6.3 同余方程


      • Part 6.3.1 线性同余方程&乘法逆元

      • Part 6.3.2 中国剩余定理

      • Part 6.3.3 高次同余方程

    • Part 6.4 博弈论

  • Part 6.5-6.6 数学


    • Part 6.5 概率与期望

    • Part 6.6 组合数学


      • Part 6.6.1 排列组合

      • Part 6.6.2 卡特兰数&斯特林数

      • Part 6.6.3 容斥原理

  • Part 6.7-6.8 数学


    • Part 6.7 线性代数


      • Part 6.7.1 矩阵

      • Part 6.7.2 高斯消元

      • Part 6.7.3 线性基

    • Part 6.8 多项式

  • Part 6.9-6.13 数学


    • Part 6.9 莫比乌斯反演

    • Part 6.10 筛法

    • Part 6.11 线性规划

    • Part 6.12 数值方法


      • Part 6.12.1 三分法

      • Part 6.12.2 自适应辛普森法

    • Part 6.13 置换群

Part 7 数据结构

  • Part 7.1-7.7 数据结构


    • Part 7.1 链表

    • Part 7.2 栈

    • Part 7.3 队列

    • Part 7.4 并查集

    • Part 7.5 二叉堆

    • Part 7.6 ST表

    • Part 7.7 树状数组

  • Part 7.8-7.12 数据结构


    • Part 7.8 线段树

    • Part 7.9 分块

    • Part 7.10 可并堆

    • Part 7.11 主席树

    • Part 7.12 平衡树

  • Part 7.13-7.18 数据结构


    • Part 7.13 树链剖分

    • Part 7.14 树套树

    • Part 7.15 动态树

    • Part 7.16 可持久化数据结构

    • Part 7.17 K-D Tree

    • Part 7.18 珂朵莉树

Part 8 图论

  • Part 8.1-8.6 图论


    • Part 8.1 图的存储与遍历

    • Part 8.2 最短路问题

    • Part 8.3 树上问题


      • Part 8.3.1 二叉树

      • Part 8.3.2 树的直径

      • Part 8.3.3 最近公共祖先

    • Part 8.4 生成树

    • Part 8.5 拓扑排序

    • Part 8.6 差分约束

  • Part 8.7-8.9.2 图论


    • Part 8.7 图的连通性相关

    • Part 8.8 二分图

    • Part 8.9 网络流


      • Part 8.9.1 最大流

      • Part 8.9.2 最小割

  • Part 8.9.3-8.13 图论


    • Part 8.9 网络流


      • Part 8.9.3 费用流

      • Part 8.9.4 上下界网络流

    • Part 8.10 2-SAT

    • Part 8.11 点分治

    • Part 8.12 虚树

    • Part 8.13 矩阵树定理

Part 9 计算几何

  • Part 9.1 凸包

  • Part 9.2 旋转卡壳

  • Part 9.3 半平面交

Part 10 杂项

  • Part 10.1 模拟退火

  • Part 10.2 0/1 分数规划

  • Part 10.3 离线算法


    • Part 10.3.1 CDQ 分治

    • Part 10.3.2 整体二分

    • Part 10.3.3 莫队

  • Part 10.4 奇怪的题目

  • Part 10.5 非传统题


    • Part 10.5.1 提交答案题

C++ map自定义比较函数遵守严格弱序

问题背景及定位

背景:这个问题是在将
tablesaw
(一个Java的数据处理项目)迁移到C++时出现的。

问题位置:SplitOn()函数,在数据流水线中的aggregate阶段。

问题描述:使用google/benchmark进行了批量化的性能测试,在测试中出现偶发性段错误,几率大约在万分之一到十万分之一之间。

问题定位:由于开发环境为受限环境,无法使用GDB调试查看堆栈定位,只能使用打印日志的方式处理

定位问题出现在如下代码处:

struct ByteArrayCompare {
    bool operator()(const ByteArray &a, const ByteArray &b) const {
        for (int i = 0; i < min(a.byteArray.size(), b.byteArray.size()); i++)
		{
			if (a.byteArray[i] != b.byteArray[i])
				return a.byteArray[i] < b.byteArray[i];
		}
		return true;
    }
    typedef ByteArray value_type;
};

......

map<ByteArray, Selection, ByteArrayCompare> selectionMap;

......

selectionMap[instanceByteArray] = std::move(selection); # crash here

至此,我个人百思不得其解,按照常理来说,应该是没有问题的。在没有段错误的情况下,测试用例能够顺利通过。

刚开始以为是class Selection的右值引用问题,有内存分配/释放没有构造/析构好,或者是移动构造出现问题,经过思考和检查排除以上问题。

因此定位问题出现在map自定义的ByteArrayCompare函数上。

map定义参见文档:
https://cplusplus.com/reference/map/map/

template < class Key,                                   //map::key_tpe
           class T,                                     //map::mapped_type
           class Compare = less<Key>,                   //map::key_compare
           class Alloc = allocator<pair<const Key, T>>  //map::allocator_type
           > class map;

由以上代码可见,map是可以自定义Compare比较函数和Alloc分配器的,此处就使用了自定义的Compare比较函数,应用于ByteArray数据类型。

题外话:unordered_map可以自定义hash和equal函数,这也体现了STL对于两种数据结构的不同实现方式,此处不再展开。

问题原因及解决方案

这里我们需要一个概念
strict_weak_order(严格弱序)

本篇文章在数学和语义上阐述了严格弱序的意义,值得一看:
https://zhuanlan.zhihu.com/p/378294506

抛开复杂的逻辑不谈,简单来说,该性质要求比较函数对于两个
不同的
key,改变输入顺序不会改变比较结果。

例:(a, b)形式输入,输出结果为a < b(假设为false),(b, a)形式输入,输出结果应该为true,若为仍false则会出现问题。

具体到我们此处的代码:此时我们已经遍历完成了a和b中较短的那个,但是对于剩余长度,没有进行比较,而是直接返回true,因此出现了上述的非严格弱序问题。

修改后代码:

struct ByteArrayCompare {
    bool operator()(const ByteArray &a, const ByteArray &b) const {
        for (int i = 0; i < min(a.byteArray.size(), b.byteArray.size()); i++)
		{
			if (a.byteArray[i] != b.byteArray[i])
				return a.byteArray[i] < b.byteArray[i];
		}
		return a.byteArray.size() < b.byteArray.size();
    }
    typedef ByteArray value_type;
};

......

map<ByteArray, Selection, ByteArrayCompare> selectionMap;

......

selectionMap[instanceByteArray] = std::move(selection); # crash here

至此,再进行测试后不会出现上述段错误问题,问题解决。