每周完成一个 ARTS:
Algorithm: 每周至少做一个 LeetCode 的算法题
Review: 阅读并点评至少一篇英文技术文章
Tips: 学习至少一个技术技巧
Share: 分享一篇有观点和思考的技术文章


Contents:


Algorithm

两数之和

题目:1. Two Sum

难度:Easy

题意:给定一个整数数组 nums,找出其中两个和为给定目标的数字,并返回其下标。假定每个输入都只有一个确定的答案,但是要求数字的下标不能重复。

例子:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

解法:

  1. 使用穷举法,两遍嵌套的 for 循环找出对应的 i 和 j 使得 nums[i] + nums[j] == target 即可。
for i in range(len(nums) - 1):
    for j in range(i + 1, len(nums)):
        if nums[i] + nums[j] == target:
            return [i, j]

很明显,这样的做法是非常耗时的,两遍嵌套循环的时间复杂度为 O(n^2),如果想减小时间复杂度,应该考虑使用牺牲空间换取时间的方法。

  1. 使用两遍哈希表(Python 中为字典)。创建一个名为 values 的空字典,第一遍遍历 nums,存储 nums 中所有的值和它们对应的位置,第二遍在 values 中寻找 target - nums[i],并且判断符合条件的两个值的坐标是否相同(比如输入 nums = [2, 7, 11, 15], target = 4,那么 result 不应为 [0, 0]),如果相同就 continue
values = {}
    for i in range(len(nums)):
        values[nums[i]] = i
    for i in range(len(nums)):
        if target - nums[i] in values:
            if i == values[target - nums[i]]:
                continue
            else:
                return[i, values[target - nums[i]]]

从 OJ 的结果来看还是相对比较耗时,在第一步我们存入了所有的数字,那么空间复杂度是 O(n),牺牲了空间去换时间(这也是为什么提交的答案在「内存消耗」上之只超过了 5.08% 的用户的原因,因为大部分人都是使用的暴力解法,而暴力解法的空间复杂度是 O(1))。

  1. 一遍哈希。在查找过程中,每查找一个 nums[i],如果不在 values 中,那么就将 target - nums[i] 存进 values 中,那么如果下一次查找,nums[i] 正好在 values 中,即之前存进来过的 nums[某个i],那么直接返回。这样不用遍历所有的数字就可找出答案。返回的时候,应先返回存进 values 中的值,再返回当前的 i
values = {}
    for i in range(len(nums)):
        if nums[i] not in values:
            values[target - nums[i]] = i
        else:
            return [values[nums[i]], i]

这是我在 LeetCode 上做的第一道算法题,其实一开始并不知道如何去选择题目,就从第一题开始做了。在做完这道题目之后,我发现自己数据结构的基础非常不好,应该有意识地去针对性做练习,于是我找到了这个网站,它对 LeetCode 的题目进行了总结和排列。我打算之后按照 array,string,tree,linkedlist,math 的顺序来做题。请问大家是如何利用 LeetCode 进行练习的呢?


Review

Common Machine Learning Algorithms and their applications

这周的文章我选择的是 CohortPlus 上的一篇讲述常用的机器学习算法和应用的文章。

作者在文章开头就指出,拥有机器学习技术的工程师在市场上非常受欢迎,机器学习工程师和数据科学家拥有非常有竞争力的薪水。作者所有的工程师都应该尝试学习掌握机器学习技术。

那么对于初学者来说,应该掌握哪些常用的机器学习算法呢?对于一个实际问题,应该如何有针对性地选择高效的机器学习算法呢?对于某个确定的“过程”(即算法)来说,它又有哪些特点、优点和缺点呢?作者在后文给出了答案。

常用的机器学习算法分为四大类:

  1. 有监督学习 Supervised Learning

    有监督学习算法包含了典型的分类(classification)和回归(regression)问题。从标注数据(训练集)中我们推测出(infer)一个合适的函数或模型,用它对输入数据(即测试集或者真实待求的数据)进行赋值(labeling):如果结果为一个真实的数字,那么这个过程被称为回归;反之如果结果来自一些特定值的集合(set of values),这个过程被称为分类

  2. 无监督学习 Unsupervised Learning

    和有监督学习正相反,在无监督学习中,我们并没有标注好的数据。我们对数据的相似性进行观察,并找出一些拥有相似数据的集合,将他们分为合适的聚类(cluster),当某些数据无法被分到某个类中时,将它们分为异常点或离群点(anomalies or outlines)。

  3. 半监督学习 Semi-supervised Learning

    半监督学习算法将有监督学习和无监督学习结合,使用很少的标注数据和一些未标注数据共同对模型进行训练,可以极大地提高训练的准确性。

  4. 强化学习 Reinforcement Learning

    和上面三种算法不同,在强化学习中,智能体(agent)根据奖赏(reward, positive reinforcement)或惩罚(penalty, negative reinforcement)进行学习并决定在某种情况下采取何种行为。

接下下作者列出了一些常用的机器学习算法并进行了简要的介绍:

  1. 线性回归 Linear Regression: 回归算法中最简单的机器学习算法,在大型数据集上表现非常好,可以使用梯度下降法来降低计算量。

  2. 逻辑回归 Logistic Regression: 用于二分类问题,逻辑回归算法计算每个数据属于某一类的概率,再根据概率进行分类。从某种角度来说,逻辑回归属于神经网络的一个非常小的例子。

  3. 决策树 Decision Tree: 决策树是一个非常系统的机器学习算法,决策的过程非常直观,很容易理解。决策树既可以做分类也可以做回归,分别使用交叉熵(或基尼纯度)或最小二乘法进行计算。

  4. 神经网络 Neural Network: 神经网络模仿人类大脑的结构,在层与层之间进行线性的连接,这赋予了网络非线性特征。对于图像和视频,深度卷积神经网络(Deep Convolutional Neural Networks)给出了最好的结果,对于自然语言处理(Natural Language Processing)问题,循环神经网络(Recurrent Neural Network)的表现最好。

  5. 支持向量机 Support Vector Machine: 支持向量机是一个非常经典的分类算法(当然也可以用于回归),它将待分类数据通过核函数(Kernel Function)映射到一个更高的维度,这样在更高的维度上数据就是线性可分的,使用一个超平面就可以对数据进行分类。

  6. K 近邻算法 K-Nearest Neighbour: K 近邻算法是另一个既可以做分类又可以做回归的机器学习算法,K 近邻算法通过来自数据点的 K 个最近邻居的多数表决来识别新的分类。K 近邻算法的计算量非常大(computationally expensive),所以在使用过程中应该尽量使数据最小化。

  7. 朴素贝叶斯模型 Naïve Bayes Model: 朴素贝叶斯模型是一个非常简单的分类算法,它使用经典的贝叶斯公式,对每一个数据属于某个类的概率进行计算(条件概率或后验概率),再根据概率进行分类判断。“朴素”的含义是,该算法假定数据的特征之间是相互独立的。

最后作者对何种情况应选择何种算法进行了总结:

  1. 在数据量很大时,我们倾向于使用线性回归模型处理回归问题,因为其他的算法容易受到过拟合的影响;我们倾向使用逻辑回归来处理二分类问题。

  2. 当计算量不是一个问题的时候,这也就意味着我们对模型的精细度有了更高的要求,那么神经网络是一个非常好的选择(理论上只要有足够多的神经元,神经网络就可以以任意精度逼近任意复杂度的连续函数)。

  3. 当已知数据的特征是相互独立的时候,尽管数据集很大或者很复杂,我们依然可以选择朴素贝叶斯模型。


Tips

利用 ssh 传输文件

在 Linux 下一般用 scp 这个命令来通过 ssh 传输文件。

  1. 从服务器上下载文件 scp username@servername:/path/filename /var/www/local_dir

    例如:scp root@192.168.0.101:/var/www/test.txt /var/www/local_dir

    把 192.168.0.101 上的 /var/www/test.txt 的文件下载到 /var/www/local_dir(本地目录)。

  2. 上传本地文件到服务器

    scp /path/filename username@servername:/path

    例如:scp /var/www/test.php root@192.168.0.101:/var/www/

    把本机 /var/www/ 目录下的 test.php 文件上传到 192.168.0.101 这台服务器上的 /var/www/ 目录中。

  3. 从服务器下载整个目录

    scp -r username@servername:/var/www/remote_dir/ /var/www/local_dir/

    例如:

    scp -r root@192.168.0.101:/var/www/test/ /var/www/

  4. 上传目录到服务器

    scp -r local_dir username@servername:remote_dir

    例如:

    scp -r test root@192.168.0.101:/var/www/

    把当前目录下的 test 目录上传到服务器的 /var/www/ 目录。

注:目标服务器要开启写入权限。


Share

一篇只完成了一半的旧文,是我自己学习《数据分析实战 45 讲》第 3 讲的笔记,对 Python 基础部分进行了不少补充,算是自己的一个复习过程。

 针对数据分析工作的 Python 入门简介(上)