复数及单位根

复数的定义大概就是:\(i^2=-1\),其中\(i\)就是虚数单位。

那么,在复数意义下,对于方程:

\[x^n=1
\]

就必定有\(n\)个解,这\(n\)个解的分布一定是在复平面上,以圆点为圆心,半径为\(1\)的圆的\(n\)等分点。

由于欧拉公式:

\[e^{i\theta}=\cos\theta+i\cdot \sin\theta
\]

把\(2\pi\)带入:

\[e^{2i\pi}=1
\]

比较一下这个和上面的方程,设:

\[\omega_n=e^{2i\pi/n}
\]

那么可以得到上面方程的\(n\)个解分别为:

\[\forall i\in[0,n-1],x_i=\omega_n^i
\]

那么,我们称这\(n\)个解为\(n\)次单位根。

关于单位根,有以下性质:

\[\omega_n^x=-\omega_n^{x+\frac{n}{2}},w_n^2=w_{\frac{n}{2}}
\]

这些性质的证明都很简单。

点值表达式

考虑到,一个多项式可以看做是一个\(n\)次的函数,如果已知这个函数的\(n+1\)个点,那么就可以确定这个多项式。

任取\(n+1\)个不同的数\(x_i\),知道了多项式的结果\(F(x_i)\),这个称作多项式的点值表达式

离散傅里叶变换(Discrete Fourier Transform, DFT)

对于一个\(n-1\)次多项式,取\(n\)个数\(w_n^0,w_n^1...w_n^{n-1}\),得到一个点值表达式,称作离散傅里叶变换

先把这个多项式凑成\(n=2^x\)的形式,高位补\(0\)。

对于\(F(\omega_n^{k})\),显然可以得到:

\[F(\omega_n^k)=\sum_{i=0}^{n-1}(\omega_n^k)^i\cdot A_i
\]

其中\(A_i\)为系数。

然后对这个进行奇偶分类,可得:

\[\begin{align}
F(\omega_n^k)&=\sum_{i=0}^{n/2-1}(\omega_n^{k})^{2i}\cdot A_{2i}+\sum_{i=0}^{n/2-1}(\omega_n^k)^{2i+1}\cdot A_{2i+1}\\
&=\sum_{i=0}^{n/2-1}(\omega_{n/2}^{k})^i\cdot A_{2i}+\omega_n^k\cdot \sum_{i=0}^{n/2-1}(\omega_{n/2}^k)^{i}\cdot A_{2i+1}
\end{align}
\]

设\(F_0(x)\)为偶数项的系数构成的多项式,\(F_1(x)\)为奇数项,这个显然是一个子问题。

那么:

\[F(\omega_n^k)=F_0(\omega_{n/2}^k)+w_n^k\cdot F_1(\omega_{n/2}^k)
\]

所以,令\(k\leqslant n/2\),则有:

\[F(\omega_n^{k+n/2})=F_0(\omega_{n/2}^k)+w_n^{k+n/2}\cdot F_1(\omega_{n/2}^k)
\]

即:

\[F(\omega_n^{k})=F_0(\omega_{n/2}^k)+w_n^{k}\cdot F_1(\omega_{n/2}^k)
\\F(\omega_n^{k+n/2})=F_0(\omega_{n/2}^k)-w_n^{k}\cdot F_1(\omega_{n/2}^k)
\]

递归计算即可,复杂度:

\[T(n)=2 \cdot T(\frac{n}{2})+O(n)=O(n\log n)
\]

离散傅里叶逆变换(Inverse Discrete Fourier Transform, IDFT)

对于离散傅里叶变换,写成矩阵的形式就是:

\[\begin{bmatrix}
(\omega_n^0)^0&(\omega_n^0)^1&\cdots & (\omega_n^0)^{n-1}\\
(\omega_n^1)^0&(\omega_n^1)^1&\cdots & (\omega_n^1)^{n-1}\\
\vdots&\vdots&\ddots&\vdots\\
(\omega_n^{n-1})^0&(\omega_n^{n-1})^1&\cdots & (\omega_n^{n-1})^{n-1}\\
\end{bmatrix}
\times
\begin{bmatrix}
A_0\\A_1\\\vdots\\A_{n-1}
\end{bmatrix}
=
\begin{bmatrix}
F(\omega_n^0)\\F(\omega_n^1)\\\vdots\\F(\omega_n^{n-1})
\end{bmatrix}
\]

现在,我们是知道了等号右边的\(F\),要求等号左边的\(A\)。

设上面的系数矩阵为\(s\),考虑下面这个矩阵,设为\(t\)。

\[t=\begin{bmatrix}
(\omega_n^{-0})^0&(\omega_n^{-0})^1&\cdots & (\omega_n^{-0})^{n-1}\\
(\omega_n^{-1})^0&(\omega_n^{-1})^1&\cdots & (\omega_n^{-1})^{n-1}\\
\vdots&\vdots&\ddots&\vdots\\
(\omega_n^{-(n-1)})^0&(\omega_n^{-(n-1)})^1&\cdots & (\omega_n^{-(n-1)})^{n-1}\\
\end{bmatrix}
\]

考虑矩阵\(v=t\times s\):

对于\(v_{i,j}\),根据矩阵乘法规则,它会等于:

\[v_{i,j}=\sum_{k=0}^{n-1}(\omega_n^{-i})^{k}\cdot (\omega_{n}^{k})^{j}=\sum_{k=0}^{n-1}\omega_n^{k(j-i)}
\]

若\(i=j\),则:

\[v_{i,j}=n
\]

否则:

\[v_{i,j}=\sum_{k=0}^{n-1}\omega_n^{k(j-i)}=\frac{1-(\omega_n^{j-i})^n}{1-\omega_n^{j-i}}
\]

注意到:

\[\omega_n^n=0
\]

所以:

\[v_{i,j}=0
\]

然后把这个矩阵写出来:

\[v=\begin{bmatrix}
n&0&\cdots&0\\
0&n&\cdots&0\\
\vdots&\vdots&\ddots&\vdots\\
0&0&\cdots&n
\end{bmatrix}
\]

然后可以发现,这个就是单位矩阵的\(n\)倍,即:

\[t\times s=n\cdot \epsilon
\]

然后考虑第一个矩阵的式子,等式两边同时左乘一个\(t\),可得:

\[n\cdot
\begin{bmatrix}
A_0\\A_1\\\vdots\\A_{n-1}
\end{bmatrix}
=
\begin{bmatrix}
(\omega_n^{-0})^0&(\omega_n^{-0})^1&\cdots & (\omega_n^{-0})^{n-1}\\
(\omega_n^{-1})^0&(\omega_n^{-1})^1&\cdots & (\omega_n^{-1})^{n-1}\\
\vdots&\vdots&\ddots&\vdots\\
(\omega_n^{-(n-1)})^0&(\omega_n^{-(n-1)})^1&\cdots & (\omega_n^{-(n-1)})^{n-1}\\
\end{bmatrix}
\times
\begin{bmatrix}
F(\omega_n^0)\\F(\omega_n^1)\\\vdots\\F(\omega_n^{n-1})
\end{bmatrix}
\]

所以,\(IDFT\)的时候直接照搬\(DFT\),然后把\(\omega_n^k\)改成\(\omega_n^{-k}\),最后在除个\(n\)就好了。

迭代实现

由于上面的递归实现常数过大,不是很优秀,这里有一种迭代的实现方法。

考虑我们把递归过程改成迭代,那么显然我们需要把顺序重新排列一下,然后每次把相邻的\(2^k\)个数合并就好了。

令\(n=2^m\),考虑第\(i\)次递归的时候,二进制下第\(i\)为\(0\)的放左边,为\(1\)的放右边,那么可以发现,左边的所有数新位置的编号第\(m-i+1\)位都为\(0\),右边的为\(1\),这个可以自己画下图理解下。

那么,设\(rev(x)\)表示把\(x\)的二进制翻转的结果,即第\(i\)位和第\(m-i+1\)位交换。

对于原序列第\(i\)个数,他在新序列的位置就应该是\(rev(i)\)。

代码就比较好写了:

  1. #include<cmath>
  2. #include<cstdio>
  3. #include<iostream>
  4. #include<algorithm>
  5. using namespace std;
  6. void read(int &x) {
  7. x=0;int f=1;char ch=getchar();
  8. for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
  9. for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
  10. }
  11. void print(int x) {
  12. if(x<0) putchar('-'),x=-x;
  13. if(!x) return ;print(x/10),putchar(x%10+48);
  14. }
  15. void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
  16. const int maxn = 4e6+10;
  17. #define lf double
  18. const lf pi = acos(-1);
  19. struct complex {
  20. lf real,imag;
  21. complex () {}
  22. complex (lf _real,lf _imag) {real=_real,imag=_imag;}
  23. complex conj() {return complex(real,-imag);} //共轭复数
  24. complex operator = (const int &rhs) {real=rhs;return *this;}
  25. complex operator + (const complex &rhs) const {return complex(real+rhs.real,imag+rhs.imag);}
  26. complex operator - (const complex &rhs) const {return complex(real-rhs.real,imag-rhs.imag);}
  27. complex operator * (const complex &rhs) const {return complex(real*rhs.real-imag*rhs.imag,imag*rhs.real+real*rhs.imag);}
  28. }; //手写的一个复数类
  29. complex es[maxn],ces[maxn],a[maxn],b[maxn];
  30. int n,m,N,pos[maxn],bit;
  31. void init() {
  32. for(int i=0;i<N;i++) es[i]=complex(cos(2*pi/N*i),sin(2*pi/N*i));
  33. for(int i=0;i<N;i++) ces[i]=es[i].conj(); //预处理单位根
  34. for(int i=1;i<N;i++) pos[i]=pos[i>>1]>>1|((i&1)<<(bit-1)); //pos[x]表示rev(x)
  35. }
  36. void fft(complex *r,complex *w) {
  37. for(int i=0;i<N;i++) if(pos[i]>i) swap(r[i],r[pos[i]]); //调整位置
  38. for(int i=1;i<N;i<<=1)
  39. for(int j=0;j<N;j+=(i<<1))
  40. for(int k=0;k<i;k++) {
  41. complex x=r[j+k],y=w[N/(i<<1)*k]*r[j+k+i]; //迭代实现
  42. r[j+k]=x+y,r[i+j+k]=x-y;
  43. }
  44. }
  45. int main() {
  46. read(n),read(m);
  47. for(int i=0,x;i<=n;i++) read(x),a[i]=x;
  48. for(int i=0,x;i<=m;i++) read(x),b[i]=x;
  49. N=1;while(N<=n+m) N<<=1,bit++;
  50. init();fft(a,es),fft(b,es);
  51. for(int i=0;i<=N;i++) a[i]=a[i]*b[i];fft(a,ces);
  52. for(int i=0;i<=n+m;i++) printf("%d ",(int)(a[i].real/N+0.5));puts(""); //记得答案要除N,这个其实应该写在fft函数里面。。
  53. return 0;
  54. }

这份代码在洛谷的模板P3803 【模板】多项式乘法(FFT)提交可以通过。

快速数论变换(Fast Number-Theoretic Transform,FNT)

这玩意其实一般叫做\(NTT\)。

考虑到上面\(FFT\)的过程用到了单位根的哪些性质:

  1. \(\omega_n^0,\omega_n^1...\omega_n^{n-1}\)互不相同,这保证了点值表达式可以成立。
  2. \(\omega_n^2=\omega_{n/2}\),\(\omega_n^{k+n/2}=-\omega_n^k\)。
  3. \(\omega_n^n=1\),这保证了IDFT的正确性。

对于模数\(p=k\cdot 2^s+1\),且\(p\)为质数,设它的原根为\(g\),那么我们可以令\(\omega_n=g^{(p-1)/n}\)。

由于原根的性质,第一条显然是满足的。

对于第二条:

\[\omega_n^2=g^{2(p-1)/n}=g^{(p-1)/(n/2)}=\omega_{n/2}
\]

并且:

\[\omega_n^{n/2}=g^{(p-1)/2}=-1
\]

也比较显然。

对于第三点,其实就是费马小定理,显然满足,所以我们可以用这个来替代\(\omega_n\),进行数论变换,代码也差不多。

注意,对于质数\(p=k\cdot 2^s+1\),它能处理的数据范围是\(n\leqslant 2^s\)。

模板:题目和上题相同

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. void read(int &x) {
  4. x=0;int f=1;char ch=getchar();
  5. for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
  6. for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
  7. }
  8. void print(int x) {
  9. if(x<0) putchar('-'),x=-x;
  10. if(!x) return ;print(x/10),putchar(x%10+48);
  11. }
  12. void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
  13. const int maxn = 4e6+10;
  14. const int mod = 998244353;
  15. int n,m,N=1,bit,pos[maxn],es[maxn],ces[maxn],a[maxn],b[maxn];
  16. int qpow(int aa,int x) {
  17. int res=1;
  18. for(;x;x>>=1,aa=1ll*aa*aa%mod) if(x&1) res=1ll*res*aa%mod;
  19. return res;
  20. }
  21. void ntt(int *r,int f) {
  22. for(int i=0;i<N;i++) if(pos[i]>i) swap(r[i],r[pos[i]]);
  23. for(int i=1;i<N;i<<=1) {
  24. int wn=qpow(f==1?3:qpow(3,mod-2),(mod-1)/(i<<1));
  25. for(int j=0,w=1;j<N;j+=(i<<1),w=1)
  26. for(int k=0;k<i;k++,w=1ll*w*wn%mod) {
  27. int x=r[j+k],y=1ll*w*r[i+j+k]%mod;
  28. r[j+k]=(x+y)%mod,r[i+j+k]=(x-y)%mod;
  29. }
  30. }
  31. }
  32. int main() {
  33. read(n),read(m);
  34. for(int i=0;i<=n;i++) read(a[i]);
  35. for(int i=0;i<=m;i++) read(b[i]);
  36. while(N<=n+m) N<<=1,bit++;
  37. for(int i=0;i<N;i++) pos[i]=pos[i>>1]>>1|((i&1)<<(bit-1));
  38. ntt(a,1),ntt(b,1);
  39. for(int i=0;i<=N;i++) a[i]=1ll*a[i]*b[i]%mod;
  40. ntt(a,-1);int inv=qpow(N,mod-2);
  41. for(int i=0;i<=n+m;i++) printf("%d ",((1ll*a[i]*inv%mod)+mod)%mod);puts("");
  42. return 0;
  43. }

任意模数NTT(MTT)

设现在要算的是多项式\(A\times B\),模数可能不满足\(p=k\cdot 2^s+1\),甚至可以不是个质数。

如果直接\(FFT\)的话,显然会爆精度,现在考虑如何优化精度。

设\(r=\lceil\sqrt{p}\rceil\),那么对于多项式的每一项,设系数为\(s\),显然可以写成\(s=a\cdot r+b\)的形式。.

那么对于\(s\cdot t\),设\(s=a\cdot r+b,t=c\cdot r+d\),那么\(s\cdot t=ac\cdot r^2+(ad+bc)\cdot r+bd\)。

所以,可以把一个多项式拆成两个,分别做\(FFT\),这样精度一般是不会爆的。

然后正反一共做\(8\)遍\(FFT\)就好了。

好像有只需要做4遍FFT的方法,以后填坑。。

代码:题目出自【模板】任意模数NTT

  1. #include<cmath>
  2. #include<cstdio>
  3. #include<iostream>
  4. #include<algorithm>
  5. using namespace std;
  6. void read(int &x) {
  7. x=0;int f=1;char ch=getchar();
  8. for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
  9. for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
  10. }
  11. void print(int x) {
  12. if(x<0) putchar('-'),x=-x;
  13. if(!x) return ;print(x/10),putchar(x%10+48);
  14. }
  15. void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
  16. #define lf double
  17. const int maxn = 4e5+10;
  18. typedef long long ll;
  19. struct complex {
  20. lf r,i;
  21. complex () {}
  22. complex (lf _r,lf _i) {r=_r,i=_i;}
  23. complex conj() {return complex(r,-i);}
  24. complex operator = (const int &rhs) {r=rhs;return *this;}
  25. complex operator - (const complex &rhs) const {return complex(r-rhs.r,i-rhs.i);}
  26. complex operator + (const complex &rhs) const {return complex(r+rhs.r,i+rhs.i);}
  27. complex operator * (const complex &rhs) const {return complex(r*rhs.r-i*rhs.i,r*rhs.i+i*rhs.r);}
  28. }w1[maxn],w2[maxn],a[maxn],b[maxn],c[maxn],d[maxn];
  29. int N,bit,n,m,s[maxn],t[maxn],mod,p,ans[maxn],pos[maxn];
  30. const lf pi = acos(-1);
  31. void init() {
  32. N=1,bit=0;while(N<=n+m) N<<=1,bit++;
  33. for(int i=0;i<N;i++) pos[i]=pos[i>>1]>>1|((i&1)<<(bit-1));
  34. w1[0]=1;for(int i=1;i<N;i++) w1[i]=complex(cos(pi*2*i/N),sin(pi*2*i/N));
  35. for(int i=0;i<N;i++) w2[i]=w1[i].conj();
  36. }
  37. void fft(complex *r,complex *w,int f) {
  38. for(int i=0;i<N;i++) if(pos[i]>i) swap(r[i],r[pos[i]]);
  39. for(int i=1;i<N;i<<=1)
  40. for(int j=0;j<N;j+=(i<<1))
  41. for(int k=0;k<i;k++) {
  42. complex x=r[j+k],y=w[N/(i<<1)*k]*r[i+j+k];
  43. r[j+k]=x+y,r[i+j+k]=x-y;
  44. }
  45. if(f==-1) for(int i=0;i<N;i++) r[i].r/=N,r[i].i=0;
  46. }
  47. void mul(int *A,int *B,int *C) {
  48. for(int i=0;i<=n;i++) a[i]=A[i]/p,b[i]=A[i]%p;
  49. for(int i=0;i<=m;i++) c[i]=B[i]/p,d[i]=B[i]%p;
  50. init();
  51. fft(a,w1,1),fft(b,w1,1),fft(c,w1,1),fft(d,w1,1);
  52. for(int i=0;i<N;i++) {
  53. complex tmpa=a[i],tmpb=b[i],tmpc=c[i],tmpd=d[i];
  54. a[i]=tmpa*tmpc,b[i]=tmpa*tmpd+tmpb*tmpc,c[i]=tmpb*tmpd;
  55. }
  56. fft(a,w2,-1),fft(b,w2,-1),fft(c,w2,-1);
  57. for(int i=0;i<N;i++) {
  58. ll tmpa=ll(a[i].r+0.5),tmpb=ll(b[i].r+0.5),tmpc=ll(c[i].r+0.5);
  59. C[i]=(tmpa%mod*p%mod*p%mod+tmpb%mod*p%mod-mod+tmpc)%mod;
  60. }
  61. }
  62. int main() {
  63. read(n),read(m),read(mod);p=sqrt(mod)+1;
  64. for(int i=0;i<=n;i++) read(s[i]),s[i]%=mod;
  65. for(int i=0;i<=m;i++) read(t[i]),t[i]%=mod;
  66. mul(s,t,ans);
  67. for(int i=0;i<=n+m;i++) printf("%d ",(ans[i]+mod)%mod);puts("");
  68. return 0;
  69. }

浅谈FFT&NTT的更多相关文章

  1. 浅谈FFT、NTT和MTT

    前言 \(\text{FFT}\)(快速傅里叶变换)是 \(O(n\log n)\) 解决多项式乘法的一个算法,\(\text{NTT}\)(快速数论变换)则是在模域下的,而 \(\text{MTT} ...

  2. 浅谈FFT(快速傅里叶变换)

    前言 啊摸鱼真爽哈哈哈哈哈哈 这个假期努力多更几篇( 理解本算法需对一些< 常 用 >数学概念比较清楚,如复数.虚数.三角函数等(不会的自己查去(其实就是懒得写了(¬︿̫̿¬☆) 整理了一 ...

  3. 浅谈FFT(快速傅里叶变换)

    本文主要简单写写自己在算法竞赛中学习FFT的经历以及一些自己的理解和想法. FFT的介绍以及入门就不赘述了,网上有许多相关的资料,入门的话推荐这篇博客:FFT(最详细最通俗的入门手册),里面介绍得很详 ...

  4. 浅谈FFT(快速博立叶变换)&学习笔记

    0XFF---FFT是啥? FFT是一种DFT的高效算法,称为快速傅立叶变换(fast Fourier transform),它根据离散傅氏变换的奇.偶.虚.实等 特性,对离散傅立叶变换的算法进行改进 ...

  5. 浅谈范德蒙德(Vandermonde)方阵的逆矩阵的求法以及快速傅里叶变换(FFT)中IDFT的原理

    浅谈范德蒙德(Vandermonde)方阵的逆矩阵与拉格朗日(Lagrange)插值的关系以及快速傅里叶变换(FFT)中IDFT的原理 标签: 行列式 矩阵 线性代数 FFT 拉格朗日插值 只要稍微看 ...

  6. FFT/NTT复习笔记&多项式&生成函数学习笔记Ⅰ

    众所周知,tzc 在 2019 年(12 月 31 日)就第一次开始接触多项式相关算法,可到 2021 年(1 月 1 日)才开始写这篇 blog. 感觉自己开了个大坑( 多项式 多项式乘法 好吧这个 ...

  7. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

  8. 浅谈 LayoutInflater

    浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...

  9. 浅谈Java的throw与throws

    转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...

随机推荐

  1. JS中的eval函数

           最近开始慢慢学习前端的脚本了,上次碰到了一个问题,需要通过一个对象的属性名称来获得这个对象这个属性的值.如果在C#中,那么直接通过反射就可以了.而在js中,也有类似的函数,那就是eval ...

  2. 【redis的链接】redis的两种连接方法

    执行redis-server /etc/redis.conf开启服务 方法一: [root@zhangmeng ~]# redis-cli > > quit 方法二: [root@zhan ...

  3. Unity —— 通过鼠标点击控制物体移动

    //ClickMove - - 通过鼠标点击控制物体移动 using System.Collections; using System.Collections.Generic; using Unity ...

  4. SQL Server临时表漫谈

    SQL Server是微软的关系型数据库,对于刚入门的我是一个非常友好的开发工具.可视化界面的安装与操作,非常适合刚入门的我. 其实大家要找这方面的资料,在网上一搜一大堆,这里我就不赘述那些了,基本都 ...

  5. Java实现网上商城

    // 第一个JavaWeb项目 //练手项目没有使用框架 github下载 https://github.com/dejavudwh/Online-Shopping 项目截图 1.基本实现了购物网站该 ...

  6. PHPCMS V9 的手机门户wap绑定单页面

    当前的Phpcms V9手机网站的设置还有点弱,绑定的栏目不能设置选择模板,而且不能绑定单页面page.不过可以自定义做到绑定单页面page这一个功能:1.修改phpcms\modules\wap\i ...

  7. mongodb4简明笔记

    就一数据库,掌握基本用法,其他的现学现卖就行了. 所以要把握基本套路. 创建数据库=>使用数据库=>创建集合=>使用集合=>创建文档=>使用文档 1.数据库 mongod ...

  8. 2019CSUST集训队选拔赛题解(三)

    PY学长的放毒题 Description 下面开始PY的香港之行,PY有n个要去的小吃店,这n个小吃店被m条路径联通起来. PY有1个传送石和n−1个传送石碎片. PY可以用传送石标记一个小吃店作为根 ...

  9. 创建image

    摘要: 本节演示如何通过 Web GUI 和 CLI 两种方法创建 Image. 本节演示如何通过 Web GUI 和 CLI 两种方法创建 Image. OpenStack 为终端用户提供了 Web ...

  10. vue之指令篇 ps简单的对比angular

    这两天在开始vue的大型项目,发现和ng还是有许多不同,这里对比下两者的指令系统 难度系数:ng的指令难度大于vue:至少vue上暂时没发现@&=:require,compile,precom ...