居然没人说这个在GitHub上拿到34k星的项目?难道知乎上的程序员都只逛百度,不逛GitHub?
这个项目堪称传奇,在GitHub上被无数人称赞为新手必学项目,却偏偏又可以无需任何修改,就在任何平台、版本的IDE上运用,真正的0成本接入,堪称有史以来最伟大的工程,没有之一!
为了尊重原作者,请大家去项目中查看,绝对是计算机历史上的一座丰碑。
我上面的话,可不是我自己评价的,都是网友们的留言:
这也是我见过第一款,被中外老哥用花式语言夸奖的项目,比如Book思议(不可思议),甚至很多人用:道可道,非常道、无极、凝视深渊这里很有哲理的话来夸奖。
整个GitHub,甚至往大了说,全网你都找不到第二个这样的项目了!
项目名称:nocode
项目地址:https://github.com/kelseyhightower/nocode
等你心平气和,仔细看完项目就会明白,这个项目贯彻了git这个词的本意。
而且程序员其实有三重境界:
1.眼中有码,敲键盘如有神助。
2.心中有码,无码也有码,对于代码的理解近乎于道。
3.心中无码,有码也无码,他就是道。
只有到达第三重境界才能迈过35岁这个坎
我看完这个项目之后的感想:
《冰雪奇缘》没有真人出演,预算却高达1.5亿美元,每一秒的镜头都是经费在燃烧。一般人想用电脑做出CG特效简直不可想象。
然而,最近一位来自中国的MIT博士,开发了物理模拟编程语言:Taichi
大大降低了成本。
计算机图形学知名学者、北大教授陈宝权给出很高的评价:
import taichi as ti quality = 1 # Use a larger value for higher-res simulations n_particles, n_grid = 9000 * quality ** 2, 128 * quality dx, inv_dx = 1 / n_grid, float(n_grid) dt = 1e-4 / quality p_vol, p_rho = (dx * 0.5)**2, 1 p_mass = p_vol * p_rho E, nu = 0.1e4, 0.2 # Young's modulus and Poisson's ratio mu_0, lambda_0 = E / (2 * (1 + nu)), E * nu / ((1+nu) * (1 - 2 * nu)) # Lame parameters x = ti.Vector(2, dt=ti.f32, shape=n_particles) # position v = ti.Vector(2, dt=ti.f32, shape=n_particles) # velocity C = ti.Matrix(2, 2, dt=ti.f32, shape=n_particles) # affine velocity field F = ti.Matrix(2, 2, dt=ti.f32, shape=n_particles) # deformation gradient material = ti.var(dt=ti.i32, shape=n_particles) # material id Jp = ti.var(dt=ti.f32, shape=n_particles) # plastic deformation grid_v = ti.Vector(2, dt=ti.f32, shape=(n_grid, n_grid)) # grid node momemtum/velocity grid_m = ti.var(dt=ti.f32, shape=(n_grid, n_grid)) # grid node mass ti.cfg.arch = ti.cuda # Try to run on GPU @ti.kernel def substep(): for i, j in ti.ndrange(n_grid, n_grid): grid_v[i, j] = [0, 0] grid_m[i, j] = 0 for p in range(n_particles): # Particle state update and scatter to grid (P2G) base = (x[p] * inv_dx - 0.5).cast(int) fx = x[p] * inv_dx - base.cast(float) # Quadratic kernels [http://mpm.graphics Eqn. 123, with x=fx, fx-1,fx-2] w = [0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1), 0.5 * ti.sqr(fx - 0.5)] F[p] = (ti.Matrix.identity(ti.f32, 2) + dt * C[p]) @ F[p] # deformation gradient update h = ti.exp(10 * (1.0 - Jp[p])) # Hardening coefficient: snow gets harder when compressed if material[p] == 1: # jelly, make it softer h = 0.3 mu, la = mu_0 * h, lambda_0 * h if material[p] == 0: # liquid mu = 0.0 U, sig, V = ti.svd(F[p]) J = 1.0 for d in ti.static(range(2)): new_sig = sig[d, d] if material[p] == 2: # Snow new_sig = min(max(sig[d, d], 1 - 2.5e-2), 1 + 4.5e-3) # Plasticity Jp[p] *= sig[d, d] / new_sig sig[d, d] = new_sig J *= new_sig if material[p] == 0: # Reset deformation gradient to avoid numerical instability F[p] = ti.Matrix.identity(ti.f32, 2) * ti.sqrt(J) elif material[p] == 2: F[p] = U @ sig @ V.T() # Reconstruct elastic deformation gradient after plasticity stress = 2 * mu * (F[p] - U @ V.T()) @ F[p].T() + ti.Matrix.identity(ti.f32, 2) * la * J * (J - 1) stress = (-dt * p_vol * 4 * inv_dx * inv_dx) * stress affine = stress + p_mass * C[p] for i, j in ti.static(ti.ndrange(3, 3)): # Loop over 3x3 grid node neighborhood offset = ti.Vector([i, j]) dpos = (offset.cast(float) - fx) * dx weight = w[i][0] * w[j][1] grid_v[base + offset] += weight * (p_mass * v[p] + affine @ dpos) grid_m[base + offset] += weight * p_mass for i, j in ti.ndrange(n_grid, n_grid): if grid_m[i, j] > 0: # No need for epsilon here grid_v[i, j] = (1 / grid_m[i, j]) * grid_v[i, j] # Momentum to velocity grid_v[i, j][1] -= dt * 50 # gravity if i < 3 and grid_v[i, j][0] < 0: grid_v[i, j][0] = 0 # Boundary conditions if i > n_grid - 3 and grid_v[i, j][0] > 0: grid_v[i, j][0] = 0 if j < 3 and grid_v[i, j][1] < 0: grid_v[i, j][1] = 0 if j > n_grid - 3 and grid_v[i, j][1] > 0: grid_v[i, j][1] = 0 for p in range(n_particles): # grid to particle (G2P) base = (x[p] * inv_dx - 0.5).cast(int) fx = x[p] * inv_dx - base.cast(float) w = [0.5 * ti.sqr(1.5 - fx), 0.75 - ti.sqr(fx - 1.0), 0.5 * ti.sqr(fx - 0.5)] new_v = ti.Vector.zero(ti.f32, 2) new_C = ti.Matrix.zero(ti.f32, 2, 2) for i, j in ti.static(ti.ndrange(3, 3)): # loop over 3x3 grid node neighborhood dpos = ti.Vector([i, j]).cast(float) - fx g_v = grid_v[base + ti.Vector([i, j])] weight = w[i][0] * w[j][1] new_v += weight * g_v new_C += 4 * inv_dx * weight * ti.outer_product(g_v, dpos) v[p], C[p] = new_v, new_C x[p] += dt * v[p] # advection import random group_size = n_particles // 3 for i in range(n_particles): x[i] = [random.random() * 0.2 + 0.3 + 0.10 * (i // group_size), random.random() * 0.2 + 0.05 + 0.32 * (i // group_size)] material[i] = i // group_size # 0: fluid 1: jelly 2: snow v[i] = [0, 0] F[i] = [[1, 0], [0, 1]] Jp[i] = 1 import numpy as np gui = ti.GUI("Taichi MLS-MPM-99", res=512, background_color=0x112F41) for frame in range(20000): for s in range(int(2e-3 // dt)): substep() colors = np.array([0x068587, 0xED553B, 0xEEEEF0], dtype=np.uint32) gui.circles(x.to_numpy(), radius=1.5, color=colors[material.to_numpy()]) gui.show() # Change to gui.show(f'{frame:06d}.png') to write images to disk
这段代码用Python 3运行。运行前要根据操作系统和CUDA版本(如果有CUDA的话)安装taichi:
# CPU 版本 (支持Linux, OS X and Windows) python3 -m pip install taichi-nightly # GPU (CUDA 10.0) (只支持Linux) python3 -m pip install taichi-nightly-cuda-10-0 # GPU (CUDA 10.1) (只支持Linux) python3 -m pip install taichi-nightly-cuda-10-1
这99行代码虽然很短,其背后的故事却很长。
虽然语法看起来是Python,其计算部分却会被一整套编译系统接管,最后输出可执行的x86_64或者PTX instructions,能够在CPU/GPU上高效运行。
https://www.zhihu.com/video/1200720718734069760项目地址:https://github.com/yuanming-hu/difftaichi
延伸阅读:
如果你想了解更多有趣、有观点、有知识的内容,别忘记点击 @东来 关注我,一个有趣的人在默默的等着你!
我还写了不少“宝藏”内容, 可以来看看我19年的高赞内容,横跨编程、旅行、体育三大板块,好几个是单篇数百万阅读的硬核科普内容。
前面的回答有提到FFT,这个算法也很牛逼,另外我觉得的PID算法也很强,特别是在工业控制中,使用非常广泛,下面是我之前学习PID写的笔记,可以参考一下;
控制系统通常根据有没有反馈会分为开环系统和闭环系统,在闭环系统的控制中,PID算法非常强大,其三个部分分别为;
PID算法可以自动对控制系统进行准确且迅速的校正,因此被广泛地应用于工业控制系统。
首先来看开环控制系统,如下图所示,隆哥蒙着眼,需要走到虚线旗帜所表示的目标位置,由于缺少反馈(眼睛可以感知当前距离和位置,由于眼睛被蒙上没有反馈,所以这也是一个开环系统),最终隆哥会较大概率偏离预期的目标,可能会运行到途中实线旗帜所表示的位置。
开环系统的整体结构如下所示;
这里做一个不是很恰当的比喻;
看来没有反馈的存在,很难准确到达目标位置。
所以为了准确到达目标位置,这里就需要引入反馈,具体如下图所示;
在这里继续举个不怎么恰当的比喻;隆哥重获光明之后,基本可以看到目标位置了;
虽然在反馈系统下,隆哥最终到达目标位置,但是现在又来了新的任务,就是又快又准地到达目标位置。所以这里隆哥开始采用PID Controller,只要适当调整P,I和D的参数,就可以到达目标位置,具体如下图所示;
隆哥为了最短时间内到达目标位置,进行了不断的尝试,分别出现了以下几种情况;
经过不断的尝试,终于找到了最佳的方式,其过程大概如下图所示;
这里依然举一个不是很恰当的比喻;
P比例则是给定一个速度的大致范围,满足下面这个公式;
因此比例作用相当于某一时刻的偏差(err)与比例系数的乘积,具体如下所示;
绿色线为上述例子中从初始位置到目标位置的距离变化; 红色线为上述例子中从初始位置到目标位置的偏差变化,两者为互补的关系;
I积分则是误差在一定时间内的和,满足以下公式;
如下图所示;
红色曲线阴影部分面积即为积分作用的结果,其不断累积的误差,最终乘以积分系数就得到了积分部分的输出;
D微分则是误差变化曲线某处的导数,或者说是某一点的斜率,因此这里需要引入微分;
从图中可知,当偏差变化过快,微分环节会输出较大的负数,作为抑制输出继续上升,从而抑制过冲。
综上,,分别增加其中一项参数会对系统造成的影响总结如下表所示;
上面扯了这么多,无非是为了初步理解PID在负反馈系统中的调节作用,下面开始推导一下算法实现的具体过程;PID控制器的系统框图如下所示;
因此不难得出输入和输出的关系;
是比例增益; 是积分增益; 是微分增益;
在数字系统中进行PID算法控制,需要对上述算法进行离散化;假设系统采样时间为 则将输入序列化得到;
将输出序列化得到;
所以最终可以得到式①,也就是网上所说的位置式PID:
将式①再做一下简化;
最终得到增量式PID的离散公式如下:
这里简单总结一下位置式PID实现的伪算法;
previous_error := 0 //上一次偏差 integral := 0 //积分和 //循环 //采样周期为dt loop: //setpoint 设定值 //measured_value 反馈值 error := setpoint − measured_value //计算得到偏差 integral := integral + error × dt //计算得到积分累加和 derivative := (error − previous_error) / dt //计算得到微分 output := Kp × error + Ki × integral + Kd × derivative //计算得到PID输出 previous_error := error //保存当前偏差为下一次采样时所需要的历史偏差 wait(dt) //等待下一次采用 goto loop
这里是位置式PID算法的C语言实现;
pid.cpp
#ifndef _PID_SOURCE_ #define _PID_SOURCE_ #include <iostream> #include <cmath> #include "pid.h" using namespace std; class PIDImpl { public: PIDImpl( double dt, double max, double min, double Kp, double Kd, double Ki ); ~PIDImpl(); double calculate( double setpoint, double pv ); private: double _dt; double _max; double _min; double _Kp; double _Kd; double _Ki; double _pre_error; double _integral; }; PID::PID( double dt, double max, double min, double Kp, double Kd, double Ki ) { pimpl = new PIDImpl(dt,max,min,Kp,Kd,Ki); } double PID::calculate( double setpoint, double pv ) { return pimpl->calculate(setpoint,pv); } PID::~PID() { delete pimpl; } /** * Implementation */ PIDImpl::PIDImpl( double dt, double max, double min, double Kp, double Kd, double Ki ) : _dt(dt), _max(max), _min(min), _Kp(Kp), _Kd(Kd), _Ki(Ki), _pre_error(0), _integral(0) { } double PIDImpl::calculate( double setpoint, double pv ) { // Calculate error double error = setpoint - pv; // Proportional term double Pout = _Kp * error; // Integral term _integral += error * _dt; double Iout = _Ki * _integral; // Derivative term double derivative = (error - _pre_error) / _dt; double Dout = _Kd * derivative; // Calculate total output double output = Pout + Iout + Dout; // Restrict to max/min if( output > _max ) output = _max; else if( output < _min ) output = _min; // Save error to previous error _pre_error = error; return output; } PIDImpl::~PIDImpl() { } #endif
pid.h
#ifndef _PID_H_ #define _PID_H_ class PIDImpl; class PID { public: // Kp - proportional gain // Ki - Integral gain // Kd - derivative gain // dt - loop interval time // max - maximum value of manipulated variable // min - minimum value of manipulated variable PID( double dt, double max, double min, double Kp, double Kd, double Ki ); // Returns the manipulated variable given a setpoint and current process value double calculate( double setpoint, double pv ); ~PID(); private: PIDImpl *pimpl; }; #endif
pid_example.cpp
#include "pid.h" #include <stdio.h> int main() { PID pid = PID(0.1, 100, -100, 0.1, 0.01, 0.5); double val = 20; for (int i = 0; i < 100; i++) { double inc = pid.calculate(0, val); printf("val:% 7.3f inc:% 7.3f
", val, inc); val += inc; } return 0; }
编译并测试;
g++ -c pid.cpp -o pid.o # To compile example code: g++ pid_example.cpp pid.o -o pid_example
本文总结了PID控制器算法在闭环系统中根据偏差变化的具体调节作用,每个环节可能对系统输出造成什么样的变化,给出了位置式和离散PID算法的推导过程,并给出了位置式算法的C++程序实现。文章中只是最简易的PID算法,并没做优化,在实际的应用中,还需要做积分抗饱和,微分环节的优化,具体需要参考实际系统的情况来定。
由于作者能力和水平有限,文中难免存在错误和纰漏,请不吝赐教。
手动码字,感觉走心的话,点赞,关注支持一下吧; @极客小麦
快速傅里叶变换(FFT),核心算法用递归法几十行搞定。
计算机应用最为重要的算法之一,广泛应用于各种各样的工业和科学实践中。第一次看到递归算出快速傅里叶的时候,惊了很久。后来经过数学家的很多努力,有了新算法将复杂度从N^2,降到N*log(N),觉得可以称之为人类智慧的精华了。
有空可以贴下源代码。
2020.1.15 科研狗这几天忙着科研,大家别急,今天一定抽空把算法和源代码贴出来。
第一次更新(1.15):
众所周知,数学中有一个傅里叶变换,是将函数在实空间的定义域变换到倒空间的定义域上,这个变换非常重要,因为一些实空间不易发现的信息,在倒空间中会表现的非常明显,所以傅里叶变换是很多信号、物理的分析基础。它的形式如下:
以及其逆变换:
但是如果在计算机想要编程实现傅里叶变换就要不可避免地面临两个近似:第一,现实中我们无法将积分的上下界延伸到无穷,所以这里一定是一个有界积分;第二,无论什么连续的公式,只要是想要被计算机实现,就必须进行离散化,所以这里的连续积分( )就近似为离散求和( )。而基于这两种朴素近似下的,就是所谓的“离散傅里叶变换”(Discrete Fourier Transform,简称DFT),它的形式也很直观:
看到这里,其实你如果利用DFT简单地编程,其实已经可以实现傅里叶变换了。但是我们为什么不用这个算法呢?是因为复杂度。
复杂度是算法中的一个概念,简单来说就是复杂度越高,那么这个算法计算大体系就越困难,那有多困难呢?我举个例子,现在算法基础库中对于矩阵的对角化应该是最基础和重要的了,矩阵的对角化的计算复杂度是 ,就是说对角化的时间就随着矩阵维数的三次方增加,这是十分恐怖的增长。目前市面的通用的对角化库,比如 lapcak,对角化的极限维数一般也就在 10000, 这个量级。那DFT的复杂度是多少呢?你可以从公式上看到,如果你现在要对一个包含N个数的数组进行DFT,那你每算其中一个变换后的数,都要进行N次的运算,那这个算法的复杂度也就是 ,也就是说,用DFT最多也就只能处理大概一个拥有大概 个数的数组。但是在生产科研实践中,这实在不是一个大的上限,所以这个复杂度的硬伤一直制约着DFT的发展,而这就是属于当时制约整个人类文明进步的“卡脖子”的难题,如果FFT没有出现,那我们的世界现在绝对不是现在这个样子。
时间来到1965年,James Cooley 和 John Tukey 发现了一种算法,这种的算法的复杂度降低为了 ,而这种算法就称之为“快速傅里叶变换”(Fast Fourier transform, 简称FFT)。但其实,就这个问题,Danielson 和 Lanczos 在1942年就已经开始了相关的讨论。他们发现任何一个DFT其实可以重新改写成两个DFT的和:
数学家进一步发现这一分为二的DFT组,一个是原来是偶数项(even,第0,2,4,6...项)组成的,而另一个是由原来的奇数项(odd)组成的。如果你只是看到这一步,你已经发现了一个大事情,就是原来用这个公式来算的话,求一个傅里叶变换不再需要进行N次相乘,而只需要odd组的N/2次运算,even组的直接不用做任何运算直接拉下来就好了。那这样计算量不就减少一半了吗?但是其实事情并没有那么简单,我们刚才证明了任何一个DFT组都能被一分为二,而且计算量减半。但是我们现在一分为二得到的even组和odd组,他们不又是DFT组吗?我们可以继续之前的操作,将他们分别再次分为两个数组的求和!
一分为二,二分为四,四分为八,一直分到每个数组只剩一个数字为止!而且每进行一次操作,就能使得其中一半的计算量被减少。这样我们就把计算量从 降到了 。你可能现在对这个算法的突破性还没有概念,我现在来给你构建下概念,
随着N的增加,FFT算法对于DFT的优势会不断显现,一开始1024个数,FFT比DFT快100倍;32768个数,就快了2000多倍,到了我们之前提到的极限1百万个数,快了近5万倍!FFT将我们的计算分析能力成万倍的提升。提一句,现在FFT最稳定最快的库是FFTW,这个W是“in West”,就是说“西方的FFT”。我当时刚看懂FFT时,不服气,一心想写个东方版,FFTE(FFT in East),后来和我自己的程序和FFTW性能一比,我人都傻了,果然好的FFT程序还是超级难写的。
这个算法关键的关键就是将DFT组不断奇偶细分,细分到最后时,如何确定每个数组前面的相位系数。我希望今天可以在第二次更新中把如何确定细分后的序列,以及蝶形算法更完。
我先把我当时研一第一次接触到FFT编写的Fortran源码贴出来吧。你品,细品。短短几十行,如果你能花几天的时间理解这个递归程序,那么这将是2020年获得的第一个受益终身的知识。
! Cooley-Tukey FFT recursive subroutine fft(x,sgn) implicit none integer, intent(in) :: sgn complex(8), dimension(:), intent(inout) :: x complex(8) :: t integer :: N integer :: i complex(8), dimension(:), allocatable :: even, odd N=size(x) if(N .le. 1) return allocate(odd((N+1)/2)) allocate(even(N/2)) ! divide odd =x(1:N:2) even=x(2:N:2) ! conquer call fft(odd, sgn) call fft(even, sgn) ! combine do i=1,N/2 t=exp(cmplx(0.0d0,sgn*2.0d0*pi*(i-1)/N))*even(i) x(i) = odd(i) + t x(i+N/2) = odd(i) - t end do deallocate(odd) deallocate(even) end subroutine fft
有的,比如 洗牌算法:这个算法真的很牛逼很经典,而且代码很少。
评论区好多大佬,膜拜ing。。
先来思考一个问题:有一个大小为 100 的数组,里面的元素是从 1 到 100 按顺序排列,怎样随机的从里面选择 1 个数?
最简单的方法是利用系统的方法 Math.random() * 100
,这样就可以拿到一个 0 到 99 的随机数,然后去数组找对应的位置就即可。
接下来在思考一个问题: 有一个大小为100的数组,里面的元素是从 1 到 100 按顺序排列,怎样随机的从里面选择 50 个数?
注意数字不能重复!
注意数字不能重复!
注意数字不能重复!
如果根据上面的思路,你第一想法是:随机 50 次不就行了?
但是,这样做有个很明显的 bug :数字是会重复的。
修改一下?
弄一个数组,把每一次随机的数都放到数组里,下一次随机就看这个数组里面有没有这数,有的话就继续随机,直到这个数组里面有 50 个数字就停止。
这样是可以的!
但,还是有个小问题,考虑一下极端情况:有一个大小为100的数组,里面的元素是从 1 到 100 按顺序排列,怎样随机的从里面选择 99 个数。
如果按照上面的方法操作,越往后选择的数字跟前面已经挑选的数字重复的概率越高,这就会造成如果数组很大,选择的数字数目也很大的话,重复次数在量级上会很大。
这个时候就需要换一个思路,如果先将数组里面的元素打乱,那么按顺序选择前 50 个不就可以了?
是的!
但我们得注意什么叫乱?
一副扑克有 54 张牌,有 54! 种排列方式。所谓的打乱指的是,你所执行的操作,应该能够 等概率地生成 这 54! 种结果中的一种。
洗牌算法就能做到这一点。
Fisher–Yates shuffle 算法由 Ronald Fisher 和 Frank Yates 于 1938 年提出,在 1964 年由 Richard Durstenfeld 改编为适用于电脑编程的版本。
这个算法很牛逼却很好理解,通俗的解释就是:将最后一个数和前面任意 n-1 个数中的一个数进行交换,然后倒数第二个数和前面任意 n-2 个数中的一个数进行交换。。。
for (var i = this.rowCount * this.colCount - 1; i >= 0 ; i--){ var iX = parseInt(i / this.colCount); var iY = i % this.colCount; var randNumber = this.rangeRandom(0, i + 1); var randX = parseInt(randNumber / this.colCount); var randY = randNumber % this.colCount; //交换两个位置 var temp = tmpMineMap[iX][iY]; tmpMineMap[iX][iY] = tmpMineMap[randX][randY]; tmpMineMap[randX][randY] = temp; }
在整个过程中,这个算法保证了每一个元素出现在每一个位置的概率是相等的。
这个算法真的很牛逼很经典,而且代码很少。
我做了一个视频进行辅助理解。
洗牌算法:) https://www.zhihu.com/video/1220804449213698048
为了避免知乎大佬觉得我吹逼,先贴一下自己的 GitHub 地址,目前 70,000 star,全球排名 51 名。
https:// github.com/MisterBooo
算法是一种技能,是可以通过科学合理的方式训练出来的能力。
在想刷题之前,得从心里认识到接受刷题很重要,才能坚持去刷题。
江湖有个传言:国内刷 LeetCode,最多够你吃 1 年老本;湾区刷 LeetCode ,够你吃 10 年老本了。
为什么湾区的刷题性价比这么高呢?
你想想,电面考 4 道题,一道题值 5 万!单位是 Dollar !
刷到就是赚到!!
想想是不是很刺激,有没有动力开始刷题了!可以提速刷题了!
就目前互联网的情况来说,无论是面国外大厂还是面国内大厂,如果想换工作都要去刷题,一面二面不丢你几道 Hard 题,都对不住你偷偷摸摸找个会议室假装开会实则面试的鸡贼。
同时,还得认识到一点,面试能力和你平时的工作能力其实差别挺大的。
有些人技术挺厉害的,但没有刷题,一面二面都过不了,而某些小镇刷题家,还真就靠刷题拿下了 Google、微软、脸书等大厂offer。
国内大厂也有这种趋势,比如字节,一大半都是面试题。
要不是他提前先看视频刷题,妥妥得凉凉。
所以,刷题很重要。
(PS:感谢大家耐心的阅读,算法是程序员的重中之重,必须攻克,大厂面试必考,顺便送一份阿里大佬刷Leetcode总结的算法笔记,如果你能吃透,那我相信80%的技术面试都会不在话下:
BAT大佬写的Leetcode刷题笔记,看完秒杀80%的算法题!
这本书的目录,非常经典:
刷题大概可以分为 4 个阶段。
1、纯小白,不知道怎么刷题,对很多概念都很陌生,各种数据结构和知识点几乎完全不懂,打开 LeetCode 第一题,满头问号。
有人相爱、有人夜里开车看海、有人 LeetCode 第一题都做不出来。
2、算法上基本已经入门,Easy 可以做出来,Medium 纠结半天也能有头绪,但基础不牢,比如字符转字符串还得 Google 一下。
3、刷了几百道题后,总结了自己的解题模板,参加周赛有时候甚至可以全部完成。
4、开始以 beat 100% 作为 AC 的目标了。
就目前的算法面试大环境来说,能达到第二阶段,中小公司可以应付过去了,到达第三阶段,字节、腾讯算法面试环节妥妥没问题了。
怎么样到达第三阶段?
给一下我的一些小建议吧。
1、如果目标是国内大厂,那么一定要刷足够的题,不需要把 LeetCode 上 2500 道算法题都刷完,但至少刷 200 道算法高频题,这些高频题我都写了题解同时也录制了视频,
在这个链接总结了:https://www.algomooc.com/1659.html
2、面试前一周以看题为主,因为刷题也刷不了几题,多看看自己总结或者别人总结的模板,比如回溯算法模板,掌握后,几十道回溯题都不在话下。
一些模板:
3、刷题过程需要注意难度要循序渐进,算法训练是一个系统工程,需要循序渐进,太过于急功近利,反而容易因做不出难题而产生挫败感,带来反效果。
如果你本身有基础,熟练度高,那你刷简单的 LeetCode 应该是几分钟一题,几分钟一题的,花不了你多少时间。
如果你刷简单都花费很长时间,说明熟练度不够,就更应该从简单开始,然后过度到中等,再过度到困难。
并且,目前国内大厂的算法考察,基本不会超过 LeetCode 中等难度,上限难度基本都是 LeetCode 中等题里面的中等难度,所以不要太去纠结难题怪题偏题。
把高频题掌握就行了:https://www.algomooc.com/1659.html
再退一步,如果你觉得 LeetCode 的题目太难,可以先从《剑指 Offer》上的算法题开始学起。
为了帮助大家更好的入门学习算法,经过半年的积累,我给大家卷了《剑指 Offer》系列的三十道题目,结合动画的形式录制了视频,相信能帮助你更好的刷题。
领取地址:
4、按算法分类来选题,比如一个时间段,只刷链表题,刷得差不多的时候,接下来再刷二叉树的题。
这样做有几个很明显的好处。
一、持续地刷同个类型的题目,可以不断地巩固和加深理解,可以总结出自己的思考路径或者解题模板。
比如链表题目,就会去思考虚拟头节点、双指针、快慢指针。
二、可以更全面地接触这个数据结构,算法的各个变种,这会促使你对这个数据结构,算法的理解更加全面和深刻,学习的效率会更高。
我一直认为读书是世界上性价比最高的成长方式,书很便宜但分量很重,是让我们摆脱平庸走向卓越的方式之一。
对于计算机专业的学生而言,读计算机经典书籍不光能让你快速提升知识和能力,更会让你在校招之际如虎添翼。
书籍下载:计算机必看经典书籍(含下载方式)
下面这是一个高赞回答合集,建议大家点赞&收藏,Mark住别丢了,大学期间绝对用得上。
1、怎么学好数据结构,看下面这个回答,已经获得了 21000+ 的赞和 50000+的收藏。
2、如何系统地学习算法,看下面这个回答,已经获得了 11000+ 的赞和 26000+的收藏。
3、新手该如何使用 GitHub,看下面这个回答,如果在大学期间就知道使用 GitHub ,那么能力远超同龄人。
4、想成为一名优秀的程序员,那么这些程序员平时都喜欢逛的论坛怎么说你也得收藏一些吧。
5、无论别人怎么说,我都是坚定不移的选择计算机专业。
6、如何系统地学习 C++ ,这个回答能帮你找到路线。
7、想要准备 Java 面试,那么这些面试题必须掌握。
赶紧点赞和收藏吧~
没有人提王垠的40行代码吗?
简单地说,这段代码做了两件事,一件事是CPS,也就是自动尾递归,第二件事则是用Scheme语言写了一个Scheme的解释器。通过他给出的cps函数,我可以用Scheme这个语言的符号系统重新定义所有Scheme的关键字,并执行正确的程序语义。换言之,它可以让这个语言自己解释自己。本质上,他的代码是在模仿当初 John McCarthy 发明 Lisp 语言时给出的代码,但用了Scheme风格重写了一遍。
-----------------------------------
关于这段代码的内容大家可以到这个问题下去看
王垠的「40 行代码」真如他说的那么厉害吗? - 知乎 https://www.zhihu.com/question/20822815
也欢迎大家到我的公众号不恰柠檬来玩呀~
机器学习十大算法之一:EM算法。
能评得上十大之一,让人听起来觉得挺NB的。什么是NB啊,我们一般说某个人很NB,是因为他能解决一些别人解决不了的问题。那么EM算法能解决什么问题呢?或者说EM算法是因为什么而来到这个世界上,还吸引了那么多世人的目光。
进入正题之前先给大家推荐一本书--《Python机器学习及实践-从零开始通往KAGGLE竞赛之路》,对于想系统学习机器学习的同学,推荐去看一下,电子版下载链接如下:
链接:https://pan.baidu.com/s/1rzaFErwbtQ6CQdTSRnaURg
提取码:5gou
进入正题:
(1)写出似然函数;
(2)对似然函数取对数,并整理;
(3)求导数,令导数为0,得到似然方程;
(4)解似然方程,得到的参数即为所求;
的期望,(考虑到E(X)=∑x*p(x),f(X)是X的函数,则E(f(X))=∑f(x)*p(x)),又,所以就可以得到公式(3)的不等式了(若不明白,请拿起笔,呵呵):
(因为Q是随机变量z(i)的概率密度函数),则可以得到:分子的和等于c(分子分母都对所有z(i)求和:多个等式分子分母相加不变,这个认为每个样例的两个概率比值都是c),则:
更多计算机相关学习资料可以去我的个人网站:https://tanqingbo.cn/CSBook001/
我是一名基层派出所民警。
可以说当今中国警察普遍羡慕美国警察可以采取暴力手段绝对的镇压不法分子。
但是,不得不说,这次这位美国警察,太过分了,不仅是过分,而且我的理解是那已经构成了犯罪行为。那黑人已经制服了就可以正常上拷带走了,没必要一直压着脖子压那么长时间。没能置身其中不知现场那美国警察的所思所想,反正我个人挺不理解他为啥那样干的。
只能说无论什么地方,无论什么行业,只要是人的社会,都有像样的也有操蛋的吧。
_________此处为分割线 _________
以下为统一答复评论中有些人质疑的我所讲的羡慕二字。
能够出现这种质疑在我料想之中,因为中国警察也有过过分的时代,据我所知就是在七十八十九十年代,就如同地痞流氓,看谁不顺眼就能打谁对老百姓而言没王法可讲,那时候的警察说好听点可以说是威风凛凛说难听点儿是横行霸道。
但我想表明的是,时过境迁,现在的中国警察无论是受舆论约束还是因为法治社会建设制度规范都已经变得逐步文明与规范起来,起码我认为从我们现在开始从公安司法院校毕业参加公务员考试考进来的新一代警察已经具备新的面目,当然不可否认的是在这个行业内目前仍然存有历史的顽疾,仍然存在着臭虫,但我已经讲过无论什么行业都有操蛋的吧,这是个人问题,不是群体问题。相比之下,拍拍良心看,现在的整个警察队伍比照曾经确实过分的年代是不是已经是天地之别,问问曾经真正挨过曾经年代老警察欺负的中老年人就知道了。
为何会说起羡慕,因为警察每天面对的人群,大多是三教九流之辈,没有武力加身,很多事情在处理上警察显得软弱无能,说白了,好人谁没事儿上派出所转悠啊都忙着自己的生活呢,警察打人这句话,我们常常听到,但是但凡有点脑袋的人都能想明白,警察会闲着没事儿干把那在家里消停待着的遵纪守法的人抓起来暴揍一顿吗?
以上言辞不免更会有人质疑,请允许我解释,武力,当然不可滥用,我所说的羡慕不是羡慕美国警察的随意滥用武力,而是在合法范围内准许在对方不听从警察指令时动用武力,现在确实有人民警察法赋予了相关权力,但实践中现在的中国警察并不能或者说不敢执行人民警察法里的所有权力。拿防疫工作举例,卡口的工作人员在让出入的人员扫码登记时,就会有不愿意配合的人,然而这些不愿意配合的人可会知道工作人员的所做所为是为了整个社区的稳定安全,因为这整个社区包括了这名不愿意配合的人啊,在这个时候是否应当对其进行武力控制来保障其他居民的安全呢。同理,警察盘查也好,调查也好,总会有那些不愿意配合的人,自我感觉良好认为自己没问题所以警察不必要对其进行盘查所以就不配合,而警察当看到对方不配合时会以什么视角审视,难道要说谢谢您的不配合吗,万一这不愿配合的人真背着案子呢,那便是对更多的人民群众的不负责任。因此,我要说,民众的素质如果真正达到了人人互相敬重路不拾遗夜不闭户的文明程度,要求警察绝对文明不要有暴力举动,一点问题没有,一味强调了警察不该暴力执法而分毫不过问被执法对象自身是否存在问题,是不是看问题的角度些微的片面了些。
请注意,我说羡慕里的那句话尾巴实际已经表明了,羡慕的是暴力手段对不法分子的镇压,可不是对遵纪守法的百姓也要肆意妄为。例如像给群众办个身份证居住证之类的业务,警察当然应该热心服务。但当面对泼皮无赖时,还要笑脸相迎,得来的只有蹬鼻子上脸,警察都不怕了,您们认为这些无赖还有谁管得了。
列位存有异议的同志们,谢谢您们的教诲。言辞中犀利的同志们,谢谢您们的敦促。
让我知道当警察,需要吾日三省吾身。
还想要质疑甚或是骂的您们,若是能让您舒服,骂两句无妨。我不算您辱骂警察。不过是,道不同不相为谋罢了吧。
_____分割线
2020年6月5日22:53 出警在路上
我是一名基层派出所民警。
可以说当今中国警察普遍羡慕美国警察可以采取暴力手段绝对的镇压不法分子。
但是,不得不说,这次这位美国警察,太过分了,不仅是过分,而且我的理解是那已经构成了犯罪行为。那黑人已经制服了就可以正常上拷带走了,没必要一直压着脖子压那么长时间。没能置身其中不知现场那美国警察的所思所想,反正我个人挺不理解他为啥那样干的。
只能说无论什么地方,无论什么行业,只要是人的社会,都有像样的也有操蛋的吧。
_________此处为分割线 _________
以下为统一答复评论中有些人质疑的我所讲的羡慕二字。
能够出现这种质疑在我料想之中,因为中国警察也有过过分的时代,据我所知就是在七十八十九十年代,就如同地痞流氓,看谁不顺眼就能打谁对老百姓而言没王法可讲,那时候的警察说好听点可以说是威风凛凛说难听点儿是横行霸道。
但我想表明的是,时过境迁,现在的中国警察无论是受舆论约束还是因为法治社会建设制度规范都已经变得逐步文明与规范起来,起码我认为从我们现在开始从公安司法院校毕业参加公务员考试考进来的新一代警察已经具备新的面目,当然不可否认的是在这个行业内目前仍然存有历史的顽疾,仍然存在着臭虫,但我已经讲过无论什么行业都有操蛋的吧,这是个人问题,不是群体问题。相比之下,拍拍良心看,现在的整个警察队伍比照曾经确实过分的年代是不是已经是天地之别,问问曾经真正挨过曾经年代老警察欺负的中老年人就知道了。
为何会说起羡慕,因为警察每天面对的人群,大多是三教九流之辈,没有武力加身,很多事情在处理上警察显得软弱无能,说白了,好人谁没事儿上派出所转悠啊都忙着自己的生活呢,警察打人这句话,我们常常听到,但是但凡有点脑袋的人都能想明白,警察会闲着没事儿干把那在家里消停待着的遵纪守法的人抓起来暴揍一顿吗?
以上言辞不免更会有人质疑,请允许我解释,武力,当然不可滥用,我所说的羡慕不是羡慕美国警察的随意滥用武力,而是在合法范围内准许在对方不听从警察指令时动用武力,现在确实有人民警察法赋予了相关权力,但实践中现在的中国警察并不能或者说不敢执行人民警察法里的所有权力。拿防疫工作举例,卡口的工作人员在让出入的人员扫码登记时,就会有不愿意配合的人,然而这些不愿意配合的人可会知道工作人员的所做所为是为了整个社区的稳定安全,因为这整个社区包括了这名不愿意配合的人啊,在这个时候是否应当对其进行武力控制来保障其他居民的安全呢。同理,警察盘查也好,调查也好,总会有那些不愿意配合的人,自我感觉良好认为自己没问题所以警察不必要对其进行盘查所以就不配合,而警察当看到对方不配合时会以什么视角审视,难道要说谢谢您的不配合吗,万一这不愿配合的人真背着案子呢,那便是对更多的人民群众的不负责任。因此,我要说,民众的素质如果真正达到了人人互相敬重路不拾遗夜不闭户的文明程度,要求警察绝对文明不要有暴力举动,一点问题没有,一味强调了警察不该暴力执法而分毫不过问被执法对象自身是否存在问题,是不是看问题的角度些微的片面了些。
请注意,我说羡慕里的那句话尾巴实际已经表明了,羡慕的是暴力手段对不法分子的镇压,可不是对遵纪守法的百姓也要肆意妄为。例如像给群众办个身份证居住证之类的业务,警察当然应该热心服务。但当面对泼皮无赖时,还要笑脸相迎,得来的只有蹬鼻子上脸,警察都不怕了,您们认为这些无赖还有谁管得了。
列位存有异议的同志们,谢谢您们的教诲。言辞中犀利的同志们,谢谢您们的敦促。
让我知道当警察,需要吾日三省吾身。
还想要质疑甚或是骂的您们,若是能让您舒服,骂两句无妨。我不算您辱骂警察。不过是,道不同不相为谋罢了吧。
_____分割线
2020年6月5日22:53 出警在路上
我是一名基层派出所民警。
可以说当今中国警察普遍羡慕美国警察可以采取暴力手段绝对的镇压不法分子。
但是,不得不说,这次这位美国警察,太过分了,不仅是过分,而且我的理解是那已经构成了犯罪行为。那黑人已经制服了就可以正常上拷带走了,没必要一直压着脖子压那么长时间。没能置身其中不知现场那美国警察的所思所想,反正我个人挺不理解他为啥那样干的。
只能说无论什么地方,无论什么行业,只要是人的社会,都有像样的也有操蛋的吧。
_________此处为分割线 _________
以下为统一答复评论中有些人质疑的我所讲的羡慕二字。
能够出现这种质疑在我料想之中,因为中国警察也有过过分的时代,据我所知就是在七十八十九十年代,就如同地痞流氓,看谁不顺眼就能打谁对老百姓而言没王法可讲,那时候的警察说好听点可以说是威风凛凛说难听点儿是横行霸道。
但我想表明的是,时过境迁,现在的中国警察无论是受舆论约束还是因为法治社会建设制度规范都已经变得逐步文明与规范起来,起码我认为从我们现在开始从公安司法院校毕业参加公务员考试考进来的新一代警察已经具备新的面目,当然不可否认的是在这个行业内目前仍然存有历史的顽疾,仍然存在着臭虫,但我已经讲过无论什么行业都有操蛋的吧,这是个人问题,不是群体问题。相比之下,拍拍良心看,现在的整个警察队伍比照曾经确实过分的年代是不是已经是天地之别,问问曾经真正挨过曾经年代老警察欺负的中老年人就知道了。
为何会说起羡慕,因为警察每天面对的人群,大多是三教九流之辈,没有武力加身,很多事情在处理上警察显得软弱无能,说白了,好人谁没事儿上派出所转悠啊都忙着自己的生活呢,警察打人这句话,我们常常听到,但是但凡有点脑袋的人都能想明白,警察会闲着没事儿干把那在家里消停待着的遵纪守法的人抓起来暴揍一顿吗?
以上言辞不免更会有人质疑,请允许我解释,武力,当然不可滥用,我所说的羡慕不是羡慕美国警察的随意滥用武力,而是在合法范围内准许在对方不听从警察指令时动用武力,现在确实有人民警察法赋予了相关权力,但实践中现在的中国警察并不能或者说不敢执行人民警察法里的所有权力。拿防疫工作举例,卡口的工作人员在让出入的人员扫码登记时,就会有不愿意配合的人,然而这些不愿意配合的人可会知道工作人员的所做所为是为了整个社区的稳定安全,因为这整个社区包括了这名不愿意配合的人啊,在这个时候是否应当对其进行武力控制来保障其他居民的安全呢。同理,警察盘查也好,调查也好,总会有那些不愿意配合的人,自我感觉良好认为自己没问题所以警察不必要对其进行盘查所以就不配合,而警察当看到对方不配合时会以什么视角审视,难道要说谢谢您的不配合吗,万一这不愿配合的人真背着案子呢,那便是对更多的人民群众的不负责任。因此,我要说,民众的素质如果真正达到了人人互相敬重路不拾遗夜不闭户的文明程度,要求警察绝对文明不要有暴力举动,一点问题没有,一味强调了警察不该暴力执法而分毫不过问被执法对象自身是否存在问题,是不是看问题的角度些微的片面了些。
请注意,我说羡慕里的那句话尾巴实际已经表明了,羡慕的是暴力手段对不法分子的镇压,可不是对遵纪守法的百姓也要肆意妄为。例如像给群众办个身份证居住证之类的业务,警察当然应该热心服务。但当面对泼皮无赖时,还要笑脸相迎,得来的只有蹬鼻子上脸,警察都不怕了,您们认为这些无赖还有谁管得了。
列位存有异议的同志们,谢谢您们的教诲。言辞中犀利的同志们,谢谢您们的敦促。
让我知道当警察,需要吾日三省吾身。
还想要质疑甚或是骂的您们,若是能让您舒服,骂两句无妨。我不算您辱骂警察。不过是,道不同不相为谋罢了吧。
_____分割线
2020年6月5日22:53 出警在路上
我是一名基层派出所民警。
可以说当今中国警察普遍羡慕美国警察可以采取暴力手段绝对的镇压不法分子。
但是,不得不说,这次这位美国警察,太过分了,不仅是过分,而且我的理解是那已经构成了犯罪行为。那黑人已经制服了就可以正常上拷带走了,没必要一直压着脖子压那么长时间。没能置身其中不知现场那美国警察的所思所想,反正我个人挺不理解他为啥那样干的。
只能说无论什么地方,无论什么行业,只要是人的社会,都有像样的也有操蛋的吧。
_________此处为分割线 _________
以下为统一答复评论中有些人质疑的我所讲的羡慕二字。
能够出现这种质疑在我料想之中,因为中国警察也有过过分的时代,据我所知就是在七十八十九十年代,就如同地痞流氓,看谁不顺眼就能打谁对老百姓而言没王法可讲,那时候的警察说好听点可以说是威风凛凛说难听点儿是横行霸道。
但我想表明的是,时过境迁,现在的中国警察无论是受舆论约束还是因为法治社会建设制度规范都已经变得逐步文明与规范起来,起码我认为从我们现在开始从公安司法院校毕业参加公务员考试考进来的新一代警察已经具备新的面目,当然不可否认的是在这个行业内目前仍然存有历史的顽疾,仍然存在着臭虫,但我已经讲过无论什么行业都有操蛋的吧,这是个人问题,不是群体问题。相比之下,拍拍良心看,现在的整个警察队伍比照曾经确实过分的年代是不是已经是天地之别,问问曾经真正挨过曾经年代老警察欺负的中老年人就知道了。
为何会说起羡慕,因为警察每天面对的人群,大多是三教九流之辈,没有武力加身,很多事情在处理上警察显得软弱无能,说白了,好人谁没事儿上派出所转悠啊都忙着自己的生活呢,警察打人这句话,我们常常听到,但是但凡有点脑袋的人都能想明白,警察会闲着没事儿干把那在家里消停待着的遵纪守法的人抓起来暴揍一顿吗?
以上言辞不免更会有人质疑,请允许我解释,武力,当然不可滥用,我所说的羡慕不是羡慕美国警察的随意滥用武力,而是在合法范围内准许在对方不听从警察指令时动用武力,现在确实有人民警察法赋予了相关权力,但实践中现在的中国警察并不能或者说不敢执行人民警察法里的所有权力。拿防疫工作举例,卡口的工作人员在让出入的人员扫码登记时,就会有不愿意配合的人,然而这些不愿意配合的人可会知道工作人员的所做所为是为了整个社区的稳定安全,因为这整个社区包括了这名不愿意配合的人啊,在这个时候是否应当对其进行武力控制来保障其他居民的安全呢。同理,警察盘查也好,调查也好,总会有那些不愿意配合的人,自我感觉良好认为自己没问题所以警察不必要对其进行盘查所以就不配合,而警察当看到对方不配合时会以什么视角审视,难道要说谢谢您的不配合吗,万一这不愿配合的人真背着案子呢,那便是对更多的人民群众的不负责任。因此,我要说,民众的素质如果真正达到了人人互相敬重路不拾遗夜不闭户的文明程度,要求警察绝对文明不要有暴力举动,一点问题没有,一味强调了警察不该暴力执法而分毫不过问被执法对象自身是否存在问题,是不是看问题的角度些微的片面了些。
请注意,我说羡慕里的那句话尾巴实际已经表明了,羡慕的是暴力手段对不法分子的镇压,可不是对遵纪守法的百姓也要肆意妄为。例如像给群众办个身份证居住证之类的业务,警察当然应该热心服务。但当面对泼皮无赖时,还要笑脸相迎,得来的只有蹬鼻子上脸,警察都不怕了,您们认为这些无赖还有谁管得了。
列位存有异议的同志们,谢谢您们的教诲。言辞中犀利的同志们,谢谢您们的敦促。
让我知道当警察,需要吾日三省吾身。
还想要质疑甚或是骂的您们,若是能让您舒服,骂两句无妨。我不算您辱骂警察。不过是,道不同不相为谋罢了吧。
_____分割线
2020年6月5日22:53 出警在路上