@[T
机器学习
-
计算语义相似度:DSSM(Deep Structured Semantic Models,有监督),可解决query-doc结果对的召回问题(“快递软件”-“菜鸟裹裹”,“机票”-“携程”),已经语义顺序或细微不同的词引起的语义变化(小宝宝生病怎么办-狗宝宝生病怎么办,深度学习-学习深度)
把搜索引擎里的Query和Title转为低维向量。
输入层-----表示层(词袋模型,转为向量)----匹配层(计算距离) -
梯度消失的解决办法:LSTM-门机制,ResNet残差网络(有多条梯度更新的路径),ReLu,highway(空洞卷积)。
-
梯度爆炸:权重变为NaN,或者每个节点和层的误差梯度持续超过1.0。
解决办法:梯度截断(即设置阈值限制梯度大小),权重正则化。 -
Relu的好处:导数是常数,避免梯度消失;导数是常数,易于求导,加速计算;稀疏激活:负半轴的激活值为0。
-
降低过拟合风险(正则化):
① 数据增强:图平移,机器翻译重新生成,GAN生成新数据
② 降低模型复杂度:NN:减少层数和神经元个数,决策树:剪枝,降低树的深度
③ 权值约束:L1L2正则化
④ 集成学习:dropout、随机森林、GBDT
⑤ 提前终止 -
RNN会有梯度消失问题,LSTM引入输入门、遗忘们和输出门来解决这个问题,长期记忆:输入门为0,遗忘门为1(忽略当前信息,记忆之前的信息。)。短期记忆:输入门为1,遗忘门为0(忽略之前信息,记忆当前信息)。GRU则是更新门,重置门。(LSTM只能考虑当前时刻之前的词)
-
CNN:提取局部特征,只能考虑窗口附近的词,所以需要滑动窗口。相对于RNN不会梯度消失。
-
Attention机制,保存全局信息,可以把两个任意模块关联起来,比如把第100个词和第1个词放在一起考虑,而且attention矩阵可视化,也可解决encoder decoder框架中中间变量长度固定的问题。
-
改进SGD:1.动量 2.自适应学习率RMSprop 、Adam(带有动量项的RMSprop)
-
Seq2Seq:Encode、Decode
-
交叉熵:https://blog.youkuaiyun.com/red_stone1/article/details/80735068
给定输入x,预测标签y=1的概率为t, 即P(y=1|x)=t
,那么预测标签y=0的概率为1-t, 即P(y=0|x)=1-t
那么P(y|x)=t(y)×(1-t)(1-y)
我们希望P(y|x)越大越好。则先对其引入log,因为log不会改变函数本身的单调性。
则-logP(y|x)=-ylogt-(1-y)log(1-t)
这就是交叉熵损失函数。
在神经网络中的应用:神经网络的原始输出不是一个概率值,实质上只是输入的数值做了复杂的加权和与非线性处理之后的一个值而已,那么如何将这个输出变为概率分布?Softmax。
交叉熵刻画的是实际输出(概率)与期望输出(概率)的距离,也就是交叉熵的值越小,两个概率分布就越接近。假设概率分布p为期望输出,概率分布q为实际输出,H(p,q)为交叉熵,则 -
GBDT和XGBoost区别
① 传统的GBDT以CART树作为基学习器,XGBoost还支持线性分类器,这个时候XGBoost相当于L1和L2正则化的逻辑斯蒂回归(分类)或者线性回归(回归);
② 传统的GBDT在优化的时候只用到一阶导数信息,XGBoost则对代价函数进行了二阶泰勒展开,得到一阶和二阶导数;
③ XGBoost在代价函数中加入了正则项,用于控制模型的复杂度。从权衡方差偏差来看,它降低了模型的方差,使学习出来的模型更加简单,防止过拟合,这也是XGBoost优于传统GBDT的一个特性; -
词嵌入:相当于把单词嵌入到n维的坐标系中(n是特征向量的长度)。是一种分散式表示。包含了外部的信息。
-
Word2vec窗口随机1-5,增加随机性。
Trick:hierarchical softmax(降低复杂度, 本质是把 N 分类问题变成 log(N)次二分类)negative sampling(降低复杂度,本质是预测总体类别的一个子集)
15 样本不均衡的解决办法:
① 采样:采样分为上采样(Oversampling)和下采样(Undersampling),上采样是把小众类复制多份,下采样是从大众类中剔除一些样本,或者说只从大众类中选取部分样本。
② 合成新数据
③ 加权,对不同类别分错的代价不同 -
kmeas的计算方法:随机选取k个中心点,然后遍历所有数据点,把它们都分配给最近的中心点,再重新计算每个聚类的平均值作为新的中心点。
-
基尼指数类似熵,是一种不确定性的度量,越大表示包含的类别越杂乱,所以在CART中选择那些使基尼指数更小的特征。
-
特征生成 ① 组合特征:几个已有特性,相加相乘。
② 分离特征:比如数字中的小数部分。 -
① SVM优点:基于结构风险最小化原则,有更好的泛化性能。是凸二次规划函数,可以求得全局最优解。
SVM缺点:对于每个高维空间在此空间的映射F,如何确定F也就是核函数,现在还没有合适的方法,所以对于一般的问题,SVM只是把高维空间的复杂性的困难转为了求核函数的困难.而且即使确定核函数以后,在求解问题分类时,要求解函数的二次规划,这就需要大量的存储空间.这也是SVM的一个问题。
② 函数间隔:y(wx+b),如果成比例改变w,x,虽然超平面不会变,但是函数间隔会变大。所以要最优化几何间隔,也就是函数间隔/||w||。
③ 支持向量:距离超平面最近的且满足一定条件的几个训练样本点被称为支持向量。
④ 硬间隔最大化: 最小化1/2 ||w||^2
软间隔最大化:
C是惩罚因子,越大表示对目标函数的损失越大,当C无限大,退化为硬间隔最大化,因为一个离群点都不能容忍。
22朴素贝叶斯的缺点:
① 理论上,NBC模型与其他分类方法相比具有最小的误差率。但是实际上并非总是如此,这是因为NBC模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的(可以考虑用聚类算法先将相关性较大的属性聚类),这给NBC模型的正确分类带来了一定影响。在属性个数比较多或者属性之间相关性较大时,NBC模型的分类效率比不上决策树模型。而在属性相关性较小时,NBC模型的性能最为良好。
② 需要知道先验概率。
23.逻辑回归(判别模型)虽然sigmoid函数是非线性的,但决策边界也就是wx+b是线性的。但逻辑回归也能解决非线性分类,需要引入核函数。 应用场景:特征很多的时候。
- 决策树:ID3信息增益
C4.5 信息增益比 (ID3的信息增益偏向于选择取值较多的特征,所以需要信息增益比)
CART(分类回归树) 分类指标:基尼系数 回归指标:最小化平方误差
优点:(1)可解释性强 (2)不需要任何参数假设 (3) 分类速度快
缺点: (1) 容易过拟合 (2) 忽略属性之间的相关性 (3)受噪声影响大 (4)基于贪心,难以找到全局最优解。
适用场景:特征数量少。
25: 1.为什么多次小卷积核要好于一次大卷积核? -增强非线性映射。
26. 神经网络存在退化现象(degradation),即会随着深度增加达到饱和后,再持续增加深度准确率下降(测试集和训练集准确率均下降,故不是过拟合).
为了解决这个问题,引入了残差单元,一个残差单元学习的目标为输入和输出的差别H(x)-x,而不是完整的输入H(x)。
-
推荐算法:①基于内容的推荐,喜欢物品a的用户,可能也会喜欢和物品a相似的东西。 优点:没有冷启动问题。 缺点:可能推荐重复。
②协同过滤:协同过滤推荐算法的前提假设是:如果用户a与用户b均对一系列相同的物品表示喜欢,那么a极有可能也喜欢b用户喜欢的其他物品。先找和用户a品位相似的top n用户,并把top n用户喜欢的东西推荐给a。 优点:个性化程度高、容易让用户发现新的兴趣点。 缺点:基于目标用户的历史行为,存在冷启动问题。
③ 基于规则的推荐:比如基于最多用户点击,最多用户浏览。缺点:个性化程度很低,规则难以定义。 -
hinge loss(折页损失函数)
https://blog.youkuaiyun.com/fendegao/article/details/79968994 -
偏差与方差分别是用于衡量一个模型泛化误差的两个方面;
模型的偏差,指的是模型预测的期望值与真实值之间的差,用于描述模型的拟合能力。引起偏差的可能原因是做了错误的假设,或者模型不够复杂。(欠拟合)
模型的方差,指的是模型预测的期望值与预测值之间的差平方和,用于描述模型的稳定性。
引起方差过大的可能原因是模型的复杂度相对于训练集过高,导致过拟合。 -
Attention和transformer:
https://baijiahao.baidu.com/s?id=1622064575970777188&wfr=spider&for=pc -
RNN网络的当前输出和前面的输出是相关的,也就是说网络会对前面的信息进行记忆并在当前输出的计算中利用前面的信息,其网络的隐藏层之间节点相互连接,隐藏层的输入不仅包括输入层输出而且包括前面隐藏层的输出。
-
神经网络局限性:不论如何划分训练集和测试集,他们的数据分布都是一致的。所以我们不知道训练的是信号还是噪声。所以过拟合难以避免。
-
VAE结合了深度学习和统计学习。
-
(面试必考) 线性回归中,是要预测y=kx+b中,k和b的值就是我们得到的输出。
-
softmax函数加exp是为了让神经元的输出非负。
-
反向传播的学习率一般在1e-3到1e-4之间,切记宁可小,不要大。常用的动态学习方法是adam。
-
权重矩阵w一般是随机初始化的。
-
sigmoid的值域是0到1,tanh的值域是-1到1。 一般来说,tanh更经常被使用为激活函数。因为:
sigmoid的导数的峰值是0.25,所以更新效率比较低,而且,当x的值远离0时,会出现梯度消失的问题。梯度消失,意味着这个神经元的更新信息无法回传了。 -
全连接会提取全局的信息,包括相似或重复的信息。
-
sgd会陷入鞍点。
-
文本输入方式有两种,一种是one-hot,假设词之间没有联系,也就是没有任何额外的背景信息,特征完全由神经网络训练提供。AE(自编码器)的输入一般就是one-hot。
第二种是词嵌入,能体现出词之间的关联,而且跟one-hot比起来没那么稀疏。 -
稀疏自编码是有dropout的,防止过拟合。
-
CNN主要特征是局部连接(也可以叫稀疏连接)和参数(权值)共享,可以大大减少参数规模,加快训练速度。参数共享指的是,使用同一个卷积核的神经元,往下一层传的时候权值是一样的。卷积后一般会作最大池化,而不是平均值池化。
-
CTM(correlational topic model)跟LDA类模型不同,主题模型之间是有相关性的。
45.从深层网络角度来讲,不同的层学习的速度差异很大,表现为网络中靠近输出的层学习的情况很好,靠近输入的层学习的很慢,有时甚至训练了很久,前几层的权值和刚开始随机初始化的值差不多。因此,梯度消失、爆炸,其根本原因在于反向传播训练法则,属于先天不足。 -
为什么要用padding? 为了让卷积核多次处理边界上的信息。 也可以用于调整输出的规模。
-
batch-epoch更新机制:假如有1w张图片,一次更新耗时太大。于是可以选择20张图片为一个batch,更新500个batch,(batch数也可以理解为参数更新的次数)步长设置得小一些。
-
NER(Name Entity Recognition)命名实体识别,可以用RNN做(多输入多输出)。
一般是用双向LSTM+CRF(条件随机场),后者有标签转移概率。
数据结构 -
顺序表和链表各自的优缺点?
① 基于空间的比较:顺序表的内存是静态分配的,链表是动态分配的。顺序表的储存密度=1,链表的储存密度<1(密度=数据容量/结构容量,链表结构包括数据
② 基于时间的比较:顺序表支持随机存取,而链表只支持顺序存取。顺序表的插入和删除平均需要移动近一半的元素,而链表插入删除不需要移动元素,只需要修改指针。顺序表的查找要比链表快。 -
在堆中的变量:动态分配内存的变量,new,malloc的变量。
在栈中的变量:局部变量(比如函数中声明的非静态变量,函数形参等)。
程序为堆变量分配动态内存,在程序结束时为栈变量清除内存,但是堆变量不会被清除,需要自己手动delete,否则会造成内存泄露。
18 哈希:哈希表中的元素是由哈希函数确定的,映射关系。常见哈希函数:
直接地址法: H(key)=key 或 H(key)=a·key+b
除留余数法: H(key)=key MOD p,p<=m (m为哈希表的长度)
解决冲突:开放地址法,再哈希法,链接地址法 -
STL底层实现: vector-数组
list-双向链表
deque-一个中央控制器和多个缓冲区(合并了vector和list),支持首尾快速增删,但中间不行,可以随机访问
Stack和queue: list或deque(封闭头部)
priority_queue的底层数据结构一般为vector为底层容器,堆heap为处理规则来管理底层容器实现
map和set的底层实现都是红黑树,map的查找效率是O(logn)。
拓展:红黑树是一种平衡二叉树,根节点和叶子节点都是黑色的,每个红色节点的两个子节点也是黑色的,从任意节点到每个叶子的所有路径都包含相同的黑色节点。 -
不用中间变量交换两个数:
加减法: a=a+b; b=a-b; a=a-b;
异或法: a=a^b; b=a^b; a=a^b;
21.八大排序:
冒泡排序可以优化成O(n),在序列本身有序时。优化方法:通过设置标志位来确认后面的序列是否已经有序,来决定是否跳出外层循环,
堆排序中,建堆的复杂度是O(n),排序重建堆的复杂度是O(nlgn)。
选择排序的比较次数是固定的,和序列的初始状态无关。
快速排序的优化:取基准值时,用三数取中法,即取首元素,尾元素,中间元素的中间值。 当子序列长度小于一定值时,改用插入排序法。
22 顺序结构即可以储存线性结构(数组,链表等),可以储存非线性结构(树,图-比如邻接矩阵)。
归并排序:将两个各有N个元素的有序表归并成一个有序表,其最少的比较次数是N,假设第一个表的元素均小于第二个表的第一个元素,那么只要经过比较N个第一个表中的元素,然后直接取出第二个表中的所有元素。最多的比较次数是2N-1.
-
最小生成树(包括所有节点,但不需要包括所有的边)的树形可能不唯一,因为可能存在权重相等的边。
Prim算法:从一个点开始连通地找最小权重的边。
Kruskal算法:找遍所有权重最小的边。 -
AVL树调整: http://blog.youkuaiyun.com/zhanghaotian2011/article/details/8459281
25.拓扑排序:
①定义:对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。
拓扑排序对应施工的流程图具有特别重要的作用,它可以决定哪些子工程必须要先执行,哪些子工程要在某些工程执行后才可以执行。为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行。通常,我们把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。
一个AOV网应该是一个有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行(对于数据流来说就是死循环)。在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。
②实现步骤:1.在有向图中选择一个没有入边的顶点输出。
2.从有向图中删除该顶点以及所有和它有关的边。
3. 重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断一个图是否有环。
- AOE(Activity on Edge Network)网
AOV-网是优先考虑顶点的思路,而我们也同样可以优先考虑边,这个就是AOE-网的思路。
若在带权的有向无环图中,以顶点表示事件(能被触发,两特征属性:最早发生时间Ve(j);最晚发生时间Vl(j)),以有向边表示活动(能被开始,两特征属性:最早开始时间e(i);最晚开始时间l(i)),边上的权值表示活动的开销(如该活动持续的时间),则此带权的有向无环图称为AOE网。记住AOE-网只是比AOV-网多了一个边的权重,而且AOV-网一般是设计一个庞大的工程各个子工程实施的先后顺序,而我们的AOE-网就是不仅仅关系整个工程中各个子工程的实施的先后顺序,同时也关系整个工程完成最短时间。
AOE-网还有一个特点就是:只有一个起点(入度为0的顶点)和一个终点(出度为0的顶点),并且AOE-网有两个待研究的问题:完成整个工程需要的时间
哪些活动是影响工程进度的关键。
关键路径:从起点到终点长度最长的路径,不一定只有一条。
关键活动:关键路径上的边
两条原则:
Ø 只有某顶点所代表的事件发生后,从该顶点出发的各活动才能开始
Ø 只有进入某顶点的各活动都结束,该顶点所代表的事件才能发生
则我们称从v0到vi的最长路径的长度为vi的最早发生时间,同时,vi的最早发生时间也是所有以vi为尾的弧所表示的活动的最早开始时间。
一个事件的最迟开始时间为以该事件为尾的弧的活动最迟开始时间与该活动的持续时间的和。
如果一个活动的最早开始时间=最晚开始时间,就称其为关键活动。
27.哈夫曼树:
定义节点的带权路径长度=从根节点算起的路径长度*节点权重。
树的带权路径长度=树中所有叶子节点的带权路径长度之和。
带权路径最短的树即为哈夫曼树(最优二叉树)。
哈夫曼树的构造:①1.根据给定的n个权值{w1,w2,…,wn}构成二叉树集合F={T1,T2,…,Tn},其中每棵二叉树Ti中只有一个带权为wi的根结点,其左右子树为空.
②在F中选取两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为左右子树根结点的权值之和.
④ 在F中删除这两棵树,同时将新的二叉树加入F中.
⑤ 重复2、3,直到F只含有一棵树为止.(得到哈夫曼树),例:
哈夫曼编码属于哈夫曼树在通信中的应用之一,用于数据文件压缩。在通信业务中一般用二进制编码来表示字符序列,但面临两个问题:编码的唯一性问题和编码总长度最短问题。
对于唯一性问题,我们可以用二叉树设计二进制前缀编码,以电文中的字符作为叶子结点构造二叉树。然后将二叉树中结点引向其左孩子的分支标 ‘0’,引向其右孩子的分支标 ‘1’; 每个字符的编码即为从根到每个叶子的路径上得到的 0, 1 序列。如:
A:0 C:10 B:110 D:111
这样任何字符的编码都不会是其他字符的前缀。
对于总长度最短问题:把每个字符的出现频率作为权重,构造哈夫曼树,即可得到每个字符的哈夫曼编码。
-
关于二叉树:设度为1的结点数为n1,二叉树中总结点数为N,因为二叉树中所有结点均小于或等于2,所以有:N=n0+n1+n2 (1)
再看二叉树中的分支数,除根结点外,其余结点都有一个进入分支,设B为二叉树中的分支总数,则有:N=B+1。(可以理解为边的个数)
由于这些分支都是由度为1和2的结点射出的,所以有:B=n1+2n2;
N=B+1=n1+2n2+1(2)
由式(1)和(2)得到:n0+n1+n2=n1+2*n2+1; n0=n2+1 -
2-3查找树:为了保证查找树的平衡性,我们需要一些灵活性,因此在这里我们允许树中的一个结点保存多个键。
2-结点:含有一个键(及值)和两条链接,左链接指向的2-3树中的键都小于该结点,右链接指向的2-3树中的键都大于该结点。
3-结点:含有两个键(及值)和三条链接,左链接指向的2-3树中的键都小于该结点,中链接指向的2-3树中的键都位于该结点的两个键之间,右链接指向的2-3树中的键都大于该结点。
(2-3指的是2叉-3叉的意思)如图:
一颗完美平衡的2-3查找树中的所有空链接到根结点的距离都是相同的。
红黑树就是用红色表示3-结点的2-3树。
C++\C python Java
- ①虚函数:虚函数的作用主要是实现了“运行时多态”的机制。虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。如果不声明为虚函数,企图通过基类指针调用派生类的非虚函数是不行的。这就是多态性,对同一消息,不同对象有不同的响应方式。
父类中提供虚函数的实现,为子类提供默认的函数实现。子类可以重写父类的虚函数实现子类的特殊化。
一般父类的虚函数,和一般函数无异,只是在函数前面有一个virtual关键字。但是是动态编译的。 普通函数是静态编译的。
②纯虚函数没有函数体,只在函数后面加个=0,例如:
virtual void out()=0;
包含纯虚函数的类叫作抽象类,抽象类是不允许实例化对象的。而对于抽象类的子类来说,只有把抽象类中的纯虚函数全部实现之后,那么这个子类才可以实例化对象。为什么要定义虚函数? 在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
③ 虚析构,是为了防止内存泄漏的。具体地说,如果派生类中申请了内存空间,并在其析构函数中对这些内存空间进行释放。假设基类中采用的是非虚析构函数,当删除基类指针指向的派生类对象时就不会触发动态绑定,因而只会调用基类的析构函数,而不会调用派生类的析构函数。那么在这种情况下,派生类中申请的空间就得不到释放从而产生内存泄漏。析构顺序是先析构子类,再析构父类。析构函数不建议抛出异常,因为抛出异常后可能不会释放内存。
④ 虚表:只有每个建立了虚函数的类才有虚表,只要父类中声明为了virtual的函数,在父类和子类的虚表中,都有这个函数(如果子类中有重写)。虚表记录的是函数的地址。虚表在声明了类之后就已经创建了。
⑤ 虚指针是类示例对象指向虚表的指针,是每个对象的隐藏成员,在对象头部,大小为4个字节。对象通过虚指针来调用虚函数。
构造函数不能为虚函数的原因?
虚函数表在对象实例化之后才能被调用,但在构造对象之前是无法调用的。
2. Python垃圾回收:
引用计数法:每个对象维护一个ob_ref字段,用来记录该对象当前被引用的次数,每当新的引用指向该对象时,它的引用计数ob_ref加1,每当该对象的引用失效时计数ob_ref减1,一旦对象的引用计数为0,该对象立即被回收,对象占用的内存空间将被释放。它的缺点是需要额外的空间维护引用计数,这个问题是其次的,不过最主要的问题是它不能解决对象的“循环引用”,因此,也有很多语言比如Java并没有采用该算法做来垃圾的收集机制。
什么是循环引用?A和B相互引用而再没有外部引用A与B中的任何一个,它们的引用计数虽然都为1,但显然应该被回收。为了解决对象的循环引用问题,Python引入了标记-清除(Mark-Sweep)和分代回收两种GC机制。
标记清除:第一阶段是标记阶段,GC会把所有的『活动对象』打上标记,第二阶段是把那些没有标记的对象『非活动对象』进行回收。对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。
在上图中,我们把小黑圈视为全局变量,也就是把它作为root object,从小黑圈出发,对象1可直达,那么它将被标记,对象2、3可间接到达也会被标记,而4和5不可达,那么1、2、3就是活动对象,4和5是非活动对象会被GC回收。不过,这种简单粗暴的标记清除算法也有明显的缺点:清除非活动的对象前它必须顺序扫描整个堆内存。
分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。同时,分代回收是建立在标记清除技术基础之上。
3. 一门编程语言的效率包括编码效率和运行效率。Python和C++比起来运行效率低,是因为:
第一:python是动态语言
一个变量所指向对象的类型在运行时才确定,编译器做不了任何预测,也就无从优化。举一个简单的例子: r = a + b。 a和b相加,但a和b的类型在运行时才知道,对于加法操作,不同的类型有不同的处理,所以每次运行的时候都会去判断a和b的类型,然后执行对应的操作。而在静态语言如C++中,编译的时候就确定了运行时的代码。
另外一个例子是属性查找,关于具体的查找顺序在《python属性查找》中有详细介绍。简而言之,访问对象的某个属性是一个非常复杂的过程,而且通过同一个变量访问到的python对象还都可能不一样(参见Lazy property的例子)。而在C语言中,访问属性用对象的地址加上属性的偏移就可以了。
第二:python是解释执行,而不是编译执行。
第三:python中一切都是对象,每个对象都需要维护引用计数,增加了额外的工作。
第四:垃圾回收。
-
单例模式:确保一个类只有一个实例被建立,且提供了一个对对象的全局访问指针。
懒汉式:饿汉式,定义的时候就创建了实例,本来就是线程安全的:
线程安全懒汉,定义互斥量,需要包含头文件<pthread.h>:
- 枚举类:
enum 枚举名{
标识符[=整型常数],
标识符[=整型常数],
…
标识符[=整型常数],
} 枚举变量;
如果枚举没有初始化, 即省掉"=整型常数"时, 则从第一个标识符开始,
依次赋给标识符0, 1, 2, …。但当枚举中的某个成员赋值后, 其后的成员按依次
加1的规则确定其值。 注意标识符是字符串时不需要双引号。
6 public:public表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用。
private:private表示私有,私有的意思就是除了class自己的成员函数之外,任何人都不可以直接使用,包括本类的对象也不能直接使用。
protected:protected对于子女、朋友的成员函数来说,可以自由使用,没有任何限制,而对于其他的外部class,prote