【省选难度】
[pdf-embedder url=”https://mhy12345.xyz/wp-content/uploads/securepdfs/2018/07/网络流.pdf” title=”网络流 v1.0″]
只愿面朝大海,春暖花开
【省选难度】
[pdf-embedder url=”https://mhy12345.xyz/wp-content/uploads/securepdfs/2018/07/网络流.pdf” title=”网络流 v1.0″]
这个数据结构讲稿是NOIP普及组-提高组难度的。下放附了部分常用的数据结构的代码实现。
[pdf-embedder url=”https://mhy12345.xyz/wp-content/uploads/2018/07/数据结构-讲义.pdf” title=”数据结构 讲义”]
[pdf-embedder url=”https://mhy12345.xyz/wp-content/uploads/2018/07/数据结构.pdf” download=’on’]
#include<iostream> #include<cstdio> using namespace std; #define MAXN 10000 int stack[MAXN],tops=-1; void push_stack(int x) { stack[++tops] = x; } int pop_stack() { return stack[tops--]; } int main() { push_stack(1); push_stack(2); cout<<pop_stack()<<endl; cout<<pop_stack()<<endl; }
#include<iostream> #include<cstdio> using namespace std; #define MAXN 10000 int queue[MAXN],head=-1,tail=-1; //++tail 表示把tail值加1,然后返回tail值 //tail++ 表示把tail值加1,然后返回之前的tail值 void push_queue(int x) { queue[++head] = x; } int pop_queue() { return queue[++tail]; } bool is_empty() { return head == tail; } int main() { }
#include<iostream> #include<algorithm> #include<queue> using namespace std; int main() { priority_queue<int> PQ; PQ.push(1); PQ.push(3); PQ.push(2); cout<<PQ.top()<<endl;PQ.pop(); cout<<PQ.top()<<endl;PQ.pop(); cout<<PQ.top()<<endl;PQ.pop(); }
#include<iostream> #include<cstdio> using namespace std; #define MAXV 10000 //最大点数 #define MAXE 200000 //最大边数 struct Edge { int np;//下一个顶点的编号 int val;//边权 Edge *next;//链表里面的指针 }E[MAXE],*V[MAXV]; int tope = -1; void add_edge(int x,int y,int v) { E[++tope].val = v; E[tope].next = V[x]; E[tope].np = y; V[x] = &E[tope]; } // V[1] = (np=3,val=3) -> (np=2,val=2) -> NULL // V[2] = (np=3,val=1) -> NULL // V[3] = (np=1,val=3) -> NULL int main() { freopen("input.txt","r",stdin); int n;//点数 int m;//边数 scanf("%d %d",&n,&m); for (int i=0;i<m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); add_edge(x,y,z); } for (int now=1;now<=n;now++){ for (Edge *ne=V[now];ne;ne=ne->next){ cout<<"EDGE "<<now<<"->"<<ne->np<<"("<<ne->val<<")"<<endl; } } }
#include<iostream> using namespace std; //0x12341234 //........................................... // aaaa // ^ // bbbbbbbb // ^ // ^ // cccc // // struct aaa{ int val,a2; }sa; int main() { int a,c; int b[2]; b[0] = 123; b[1] = 312; a = 1; c = 2; int *ptr_a = &a; cout<<"PTR_A = "<<ptr_a<<endl; cout<<"A = "<<a<<endl; cout<<"*PTR_A = "<<(*ptr_a)<<endl; (*ptr_a) = -1; cout<<"*PTR_A = "<<(*ptr_a)<<endl; cout<<"A = "<<a<<endl; cout<<"B = "<<b<<endl; cout<<"*B = "<<*b<<endl; cout<<"B[1] = "<<b[1]<<endl; cout<<"*(B+1) = "<<*(b+1)<<endl; cout<<"(A+1) = "<<*(ptr_a+1)<<endl; //一个struct的指针可以使用->访问成员变量 aaa *ptr_sa = &sa; cout<<"(*ptr_sa).val"<<((*ptr_sa).val)<<endl; cout<<"ptr_sa -> val"<<ptr_sa->val<<endl; }
#include<iostream> using namespace std; #define MAXN 100000 int jump[MAXN][20]; int a[MAXN]; int ff[MAXN]; // a << b // a >> b // // 00000000000011 // 00000000000001 // 00000000000110 // 00000000001100 int main() { scanf("%d",&n); for (int i=0;i<n;i++) scanf("%d",a+i); memset(ff,-1,sizeof(ff)); for (int i=0;(1<<i)<MAXN;i++) ff[1<<i] = 1<<i; for (int i=1;i<MAXN;i++) if (ff[i] == -1) ff[i] = ff[i-1]; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // . . . . . . . . . . . . . . . . . . // 1 2 4 8 16 // - 1 2 2 4 4 4 4 8 8 8 8 8 8 8 8 16 16 for (int i=0;i<n;i++) jump[i][0] = a[i]; for (int i=1;i<20;i++) for (int j=0;j+(1<<i)-1<n;j++) jump[j][i] = max(jump[j][i-1],jump[j+(1<<(i-1))][i-1]); //jump[i][0]->[i,i] //jump[i][j] //[3,3+2^4-1] //[3,3+2^3-1] [3+2^3,3+2^4-1] // // . . . . . . . . . . q q q q q q q q q q q . . . . . // . . . . . . . . . . a a a a a a a a // . . . . . . . . . . . . . a a a a a a a a . . . . . scanf("%d",&m); for (int i=0;i<m;i++) { scanf("%d%d",&l,&r); int len = r-l+1; int k = ff[len]; int ans = max(jump[l][k],jump[r-(1<<k)+1][r]); printf("%d\n",ans); } } //jump[i][j] 表示区间[i,i+2^j-1]的最大值
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MAXN 100000 #define lowbit(x) ((x) & (-(x))) int tarr[MAXN];//树状数组 void AddTarr(int pos,int v) { while (pos < MAXN) { tarr[pos] += v; pos += lowbit(pos); } } int QryTarr(int pos) { int res = 0; while (pos) { res += tarr[pos]; pos -= lowbit(pos); } return res; } int main() { int n; scanf("%d",&n); for (int i=1;i<=n;i++) { int x; scanf("%d",&x); AddTarr(i,x); } int m; scanf("%d",&m); for (int i=0;i<m;i++) { int cid; scanf("%d",&cid); if (cid == 0) {//单点加操作 int x,v; scanf("%d%d",&x,&v); AddTarr(x,v); }else if (cid == 1) {//区间和查询 int l,r; scanf("%d%d",&l,&r); printf("%d\n",QryTarr(r) - QryTarr(l-1)); } } }
#include<iostream> #include<cstdio> using namespace std; #define MAXS 10000 #define MAXT 100000 char buffer[MAXS]; struct TrieNode { int total; int nxt[26]; }trie[MAXT]; int topt = 1; int root = 1; void AddTrie(char* ptr) { int now = root; while (*ptr) { //*ptr-'a' if (!trie[now].nxt[*ptr-'a']) trie[now].nxt[*ptr-'a'] = ++topt; now = trie[now].nxt[*ptr-'a']; trie[now].total ++; ptr++; } } int QryTrie(char *ptr) { int now = root; while (*ptr) { if (!trie[now].nxt[*ptr-'a']) return 0; now = trie[now].nxt[*ptr-'a']; ptr++; } return trie[now].total; } int main() { freopen("input.txt","r",stdin); int n,m; scanf("%d%d\n",&n,&m); for (int i=0;i<n;i++) { scanf("%s\n",buffer); AddTrie(buffer); } for (int i=0;i<m;i++) { scanf("%s\n",buffer); cout<<QryTrie(buffer)<<endl; } }
#include<iostream> using namespace std; #define MAXN 101000 int uf[MAXN]; void init() { for (int i=0;i<n;i++) uf[i] = i; } int get_uf(int x) { return uf[x] == x ? x : uf[x] = get_uf(uf[x]); } bool join(int x,int y) { if (get_uf(x) == get_uf(y)) return false; uf[get_uf(x)] = get_uf(y); return true; } int main() { }
#include<iostream> #include<algorithm> #include<cassert> #include<cstdio> #include<cstring> using namespace std; #define MAXN 100000 #define MAXT MAXN*4 #define root 1 #define lch ((now<<1)) #define rch ((now<<1)^1) #define smid ((l+r)>>1) //(l+r)/2 //堆式线段树 struct sgt_node { int sum; int l,r; }sgt[MAXT]; int a[MAXN]; void update(int now) { sgt[now].sum = sgt[lch].sum + sgt[rch].sum; } void build_sgt(int now,int l,int r) {//建立一个以now为根的线段树,这个线段树对应区间l,r sgt[now].l = l; sgt[now].r = r; if (l == r) { sgt[now].sum = a[l]; return ; } build_sgt(lch,l,smid); build_sgt(rch,smid+1,r); update(now); } int query_sgt(int now,int l,int r,int x,int y) {//查询以now为根的线段树,线段树now节点对应区间为[l,r],而查询区间为[x,y] assert(l == sgt[now].l && r == sgt[now].r); if (l == x && r == y) return sgt[now].sum; if (y <= smid) return query_sgt(lch,l,smid,x,y); else if (smid < x) // smid+1 <= x return query_sgt(rch,smid+1,r,x,y); else return query_sgt(lch,l,smid,x,smid) + query_sgt(rch,smid+1,r,smid+1,y); } void modify_sgt(int now,int l,int r,int pos,int val) {//修改位置pos的值为val assert(l == sgt[now].l && r == sgt[now].r); if (l == r) { //l == r && r == pos sgt[now].sum = val; return ; } if (pos <= smid) modify_sgt(lch,l,smid,pos,val); else modify_sgt(rch,smid+1,r,pos,val); update(now); } int main() { freopen("input.txt","r",stdin); int n; scanf("%d",&n); for (int i=0;i<n;i++) scanf("%d",a+i); build_sgt(root,0,n-1); int m; scanf("%d",&m); for (int i=0;i<m;i++) { int cid; scanf("%d",&cid); if (cid == 1) { int x,y; scanf("%d%d",&x,&y);x--,y--; int res = query_sgt(root,0,n-1,x,y); printf("%d\n",res); }else if (cid == 2) { int p,v; scanf("%d%d",&p,&v);p--; modify_sgt(root,0,n-1,p,v); } } }
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MAXN 100000 #define MAXT MAXN*4 #define smid ((l+r)>>1) int a[MAXN]; struct sgt_node { int lch,rch; int sum; int pls; }sgt[MAXT]; int topt = 0; void update(int now) { sgt[now].sum = sgt[sgt[now].lch].sum + sgt[sgt[now].rch].sum; } void do_plus(int &now,int l,int r,int v) {//给now整棵子树都加上v if (!now)now = ++topt; sgt[now].pls += v; sgt[now].sum += (r-l+1)*v; } void push_down(int now,int l,int r) { if (sgt[now].pls) { do_plus(sgt[now].lch,l,smid,sgt[now].pls); do_plus(sgt[now].rch,smid+1,r,sgt[now].pls); sgt[now].pls = 0; } } int query_sgt(int &now,int l,int r,int x,int y) { if (!now)now=++topt; if (l == x && r == y) return sgt[now].sum; push_down(now,l,r); if (y <= smid) return query_sgt(sgt[now].lch,l,smid,x,y); else if (smid < x) return query_sgt(sgt[now].rch,smid+1,r,x,y); else return query_sgt(sgt[now].lch,l,smid,x,smid) + query_sgt(sgt[now].rch,smid+1,r,smid+1,y); } void modify_sgt(int &now,int l,int r,int pos,int v) { if (!now)now=++topt; if (l == r) { sgt[now].sum = v; return ; } push_down(now,l,r); if (pos <= smid) modify_sgt(sgt[now].lch,l,smid,pos,v); else modify_sgt(sgt[now].rch,smid+1,r,pos,v); update(now); } void plus_sgt(int &now,int l,int r,int x,int y,int v) { if (!now)now=++topt; if (l == x && r == y) { do_plus(now,l,r,v); return ; } push_down(now,l,r); if (y <= smid) plus_sgt(sgt[now].lch,l,smid,x,y,v); else if (smid < x) plus_sgt(sgt[now].rch,smid+1,r,x,y,v); else plus_sgt(sgt[now].lch,l,smid,x,smid,v),plus_sgt(sgt[now].rch,smid+1,r,smid+1,y,v); update(now); } int main() { freopen("input.txt","r",stdin); int n; scanf("%d",&n); int root = 0; int m; scanf("%d",&m); for (int i=0;i<m;i++) { int cid; scanf("%d",&cid); if (cid == 1) { int x,y; scanf("%d%d",&x,&y);x--,y--; int res = query_sgt(root,0,n-1,x,y); printf("%d\n",res); }else if (cid == 2) { int p,v; scanf("%d%d",&p,&v);p--; modify_sgt(root,0,n-1,p,v); }else if (cid == 3) { int x,y,v; scanf("%d%d%d",&x,&y,&v);x--,y--; plus_sgt(root,0,n-1,x,y,v); } } }
[pdf-embedder url=”https://mhy12345.xyz/wp-content/uploads/securepdfs/2018/07/数论-讲义.pdf” title=”数论 讲义v1.0″]
[pdf-embedder url=”https://mhy12345.xyz/wp-content/uploads/securepdfs/2018/07/数论-课件.pdf” title=”数论 课件 v1.0″]
bool Miller_Rabbin(int n,int a)//a属于[2,n) { if (n<2)return false; if (!(n%a))return false; int r=0,d=n-1; while (!(d&1)) { d>>=1; r++; } int k=pow_mod(a,d,n); if (k==1)return true; for (int i=0;i<r;i++) { if (k==n-1)return true; k=k*k%n; } return false; }
typedef long long qword; qword ext_gcd(qword a,qword b,qword &x,qword &y) { if (a%b==0) { //a*x+b*y==b x=0;y=1; return y; } qword ret=ext_gcd(b,a%b,x,y); qword tx=x,ty=y; x=ty; y=tx-a/b*ty; return ret; }
#include<iostream> using namespace std; #define MAXN 1010 bool pflag[MAXN]; int prime[MAXN],topp=-1; void init() { for (int i=2;i<MAXN;i++) { if (!pflag[i]) prime[++topp] = i; for (int j=0;j<=topp && prime[j]*i<MAXN;j++) { pflag[i*prime[j]] = true; if (i%prime[j] == 0)break; } } for (int i=0;i<=topp;i++) cout<<prime[i]<<endl; } int main() { init(); }
#include<iostream> using namespace std; #define MAXN 100 #define MOD 1000000007 bool pflag[MAXN]; int prime[MAXN],topp=-1; int inv[MAXN]; int phi[MAXN]; int h[MAXN];//存最小的那个质数所在的括号 int hh[MAXN];//存其他括号的乘积 int g[MAXN];//存最小那个质数所在的括号有多少个数 int gg[MAXN];//存其他括号的个数的乘积 long long pow_mod(long long a,long long b) { long long res = 1; while (b) { if (b&1) res = res * a%MOD; a = a*a%MOD; b>>=1; } return res; } //(1+5+25)*(1+3) * (1+2) //(1+5+25)*(1+3) * (1+2+4) // hh[i] h[i] // //(1+2)*2+1 = (1+2+4) //(1+2+4)*2+1 = (1+2+4+8) // //(1+5+25) * (1+3) //(1+5+25)*(1+3) * (1+2) void init() { inv[1] = 1; for (int i=2;i<MAXN;i++) { if (!pflag[i]) { prime[++topp] = i; inv[i] = pow_mod(i,MOD-2); phi[i] = i-1; h[i] = i+1; hh[i] = 1; g[i] = 2; gg[i] = 1; } for (int j=0;j<=topp && i*prime[j]<MAXN;j++) { pflag[i*prime[j]] = true; inv[i*prime[j]] = (long long)inv[i] * inv[prime[j]] %MOD; if (i%prime[j] == 0) { phi[i*prime[j]] = phi[i] * prime[j]; h[i*prime[j]] = h[i] * prime[j] + 1; hh[i*prime[j]] = hh[i]; g[i*prime[j]] = g[i]+1; gg[i*prime[j]] = gg[i]; break; }else { phi[i*prime[j]] = phi[i] * (prime[j]-1); h[i*prime[j]] = prime[j] + 1; hh[i*prime[j]] = h[i] * hh[i]; g[i*prime[j]] = 2; gg[i*prime[j]] = g[i] * gg[i]; } } } } int main() { init(); /* for (int i=0;i<=topp;i++) cout<<prime[i]<<"\t";*/ for (int i=2;i<MAXN;i++) { cout<<i<<" INV="<<inv[i]<<" PHI="<<phi[i]<<" H="<<h[i]*hh[i]<<" G="<<g[i]*gg[i]<<endl; } }
[pdf-embedder url=”https://mhy12345.xyz/wp-content/uploads/securepdfs/2018/07/数学-讲义-v1.1.pdf” title=”数学 讲义 v1.1″]
[pdf-embedder url=”https://mhy12345.xyz/wp-content/uploads/securepdfs/2018/07/数学-习题.pdf” title=”数学 习题”]
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cassert> using namespace std; #define MOD 100000007 struct matrix { static const int maxn = 2; int n,m; int mat[maxn][maxn]; matrix() { memset(mat,0,sizeof(mat)); } void initEye(int n) { this->n = this->m = n; memset(mat,0,sizeof(mat)); for (int i=0;i<n;i++) mat[i][i] = 1; } void initStart() { this->n = 2; this->m = 1; memset(mat,0,sizeof(mat)); mat[0][0] = mat[1][0] = 1; } void initFib() { this->n = this->m = 2; mat[0][0] = 0; mat[1][0] = mat[0][1] = mat[1][1] = 1; } void print() { cout<<"MATRIX "<<n<<" "<<m<<endl; for (int i=0;i<n;i++) { for (int j=0;j<m;j++) { cout<<mat[i][j]<<"\t"; } cout<<endl; } } }; matrix operator* (const matrix& m1,const matrix& m2) { assert(m1.m == m2.n); matrix res; res.n = m1.n; res.m = m2.m; for (int i=0;i<m1.n;i++) { for (int j=0;j<m2.m;j++) { for (int k=0;k<m1.m;k++) { res.mat[i][j] = (res.mat[i][j] + (long long)m1.mat[i][k]*m2.mat[k][j])%MOD; } } } return res; } matrix pow_mod(matrix x,int y) { matrix res; res.initEye(2); while(y) { if (y&1) res = res * x; x = x*x; y>>=1; } return res; } int main() { matrix x; matrix T; x.initStart(); x.print(); T.initFib(); T.print(); (pow_mod(T,60000000)*x).print(); while (1); }
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<cassert> #include<cmath> using namespace std; #define MOD 100000007 #define eps 1e-7 struct matrix { static const int maxn = 20; int n,m; double mat[maxn][maxn]; matrix() { memset(mat,0,sizeof(mat)); } void print() { cout<<"MATRIX "<<n<<" "<<m<<endl; for (int i=0;i<n;i++) { for (int j=0;j<m;j++) { cout<<mat[i][j]<<"\t"; } cout<<endl; } } void random(int n) { this->n = n; this->m = n; for (int i=0;i<n;i++) for (int j=0;j<n;j++) mat[i][j] = rand()%100; } void initSquare() { this->n = 4; this->m = 4; memset(mat,0,sizeof(mat)); mat[0][1] = mat[0][3] = 1; mat[1][0] = mat[1][2] = 1; mat[2][1] = mat[2][3] = 1; mat[3][0] = mat[3][2] = 1; mat[0][0] = mat[1][1] = mat[2][2] = mat[3][3] = -2; this->n --;//去一行 this->m --;//去一列 } double gauss() { double ans = 1; for (int i=0;i<n;i++) { int sid = -1; for (int j=i;j<n;j++) if (abs(mat[j][i]) > eps) { sid = j; break; } if (sid == -1) continue; if (sid != i) { for (int j=0;j<n;j++) { swap(mat[sid][j],mat[i][j]); ans = - ans; } } for (int j=i+1;j<n;j++) { double ratio = mat[j][i]/mat[i][i]; for (int k=0;k<n;k++) { mat[j][k] -= mat[i][k] * ratio; } } //print(); } for (int i=0;i<n;i++) ans *= mat[i][i]; return abs(ans); } }; int main() { srand(1); matrix T; //T.random(2); T.initSquare(); T.print(); double ans = T.gauss(); T.print(); cout<<ans<<endl; }
网站访问速度是搜索引擎衡量一个网站的核心因素,因为这在另一方面意味着用户点选这个词条的用户体验。因此,我们十分有必要优化Wordpress的速度。这里,我使用了WP Super Cache和Jetpack插件,WP Super Cache可以缓存你的博客页面,特别是对于匿名用户,不论如何,网站对匿名用户返回的始终是同一个页面,因此将他们缓存下来可以达到很好的效果(从原来的2s变成了几乎看不到延迟)。同时Jetpack插件提供了免费的CDN服务。也可以优化图片以及视频的加载速度。
之前曾经提到百度将我的博客列为“用户不友好网站”,追溯其原因。回想之前对网站做的危险操作,不外乎变更子域名了。最初我的博客使用的是blog.mhy12345.xyz,提交了百度收录之后,我又心血来潮将站点的域名改变成了mhy12345.xyz。当时的想仅仅是,也许我改变了域名,搜索引擎自己会直接迁移过去blablabla,但是可能并不是这样。
从搜索引擎视角来看,首先出现了一个blog.mhy12345.xyz网站,接着有出现了一个冒充blog.mhy12345.xyz网站的另一个网站,内容完全相似。直接判定为抄袭站点。接下来发生的事情就更好解释了,百度只收录blog.mhy12345.xyz,我一怒之下,删去了对于blog.mhy12345.xyz的解析,剩下了一个mhy12345.xyz,而这个网站已经被搜索引擎判定为了恶意站点。然后……就没有然后了……(等等,为啥google就没有判断出错呢? 唔,毕竟百度不是世界一流互联网公司吧)
自己写博客,特别是技术类博客,当然希望能够有其他人能够看到。我的第一篇正式文章Ubuntu 16.04 & Docker 搭建 WordPress 写于2017年4月,而到现在已经一年多了。大大小小杂文有七十于篇(怎么会那么多!?)自己认为很有用的也有大约10篇了。
有了足够的干货,当然希望更多人能够看到,这就依赖于搜索引擎了。然而搜索引擎却貌似并不买我的账。怎么说呢,google是个好公司啊,因为至少在我一点一点写博客这个时间段,google索引量在逐渐增加的,google给我带来了95%的搜索引擎索引量。而百度,甚至将我的博客列为了“用户不友好网站”==。想一想其实不无道理,google面向国外,有非常多的优质个人站点,所以理所当然赋予了个人站点更高的权值,反观百度的用户主体,国内,随随便便点开一个小站,各种广告网站满天飞,是我也想一枪毙掉所有的个人网站了。
这是真实感图像渲染系列的第十篇文章。
这里写一写个人在编写过程中发现的一些坑,分享出来以免之后再踩吧。
这是真实感图像渲染系列的第九篇文章。
不同的相机模型中,有一个人眼位置和一个屏幕,对于屏幕中每一个像素,连接人眼和该像素位置得到一条射出光线。其2D示意图如下:
首先,一直屏幕像素坐标,以及屏幕本身在场景中的位置,我们可以计算出该像素的位置,如图中红点所述。接着,连接人眼和像素的光线就可以用于计算该光线的颜色。这一部分挺容易理解的。
在普通相机模型的屏幕和人眼位置之外,带景深的相机模型额外存在一个焦平面。如下图所示
通过普通的相机模型,我们可以求得普通相机的光线的方向。
普通相机的光线一定会和焦平面在某个位置碰撞,为了达到景深效果,一方面,我们需要保证同一个像素对应的光线一定会击打到焦平面上的同一个点,另一方面,在焦平面之外的其他位置,同一个像素对应的光线应该击打到不同的点。
解决方案如下:我们求得在普通相机模型中,光线和焦平面的焦点,将该焦点景深相机模型的焦点。接下来,我们对光线的起始位置做一个微小扰动,并且修改光线方向使得光线恒定过焦平面上的点。这样,无论初始光线如何扰动,焦平面上的点都可以最清晰的表现出来。而不在焦平面上的点,会产生随机的模糊效果。
这是真实感图像渲染系列的第八篇文章。
贝塞尔曲线是一个由多个控制点组合而成的曲线。在本文探讨的贝塞尔曲线,是由多条简单的4阶贝塞尔曲线拼接而成。绕z轴旋转贝塞尔曲线,我们可以得到一个旋转体曲面,该曲面就是待渲染的参数曲面。本文将探讨如何使用牛顿迭代法与直线求交。求交过程中
贝塞尔曲线是一个由多个控制点组合而成的曲线,n阶贝塞尔曲线定义为
(1)
式(1)定义了该曲线的关于参数
的参数方程,其中
为n个曲线的控制点。当我看到这个公式的时候,整个人都已经懵掉了。实际上理解起来并不复杂。
函数由参数
定义了一个二维向量,其中
与
相互独立。只需要理解其中一个的产生式即可。考虑函数
,发现这
实际上是一个和为1的数列,数列在杨辉三角形中定义。换句话来说,
实际上可以得到一个权值的分配,使用
对于所有的控制点
加权平均,就是贝塞尔曲线尝试完成的事情。当
权值全部分配给了
,因此贝塞尔曲线的起点(
)和
重合。
在三维空间中,我们通过旋转贝塞尔曲线得到一个曲面。曲面强制对z轴旋转对称。(注:在代码中是对x轴旋转对称,不过z轴对称相对好理解一些)。
(2)
一个贝塞尔曲线旋转体要能够成功渲染,其核心就是需要支持与光线的求交。光线我们使用表示,
表示光线起点,
表示光线方向,是一个单位向量。则光线上一点可表示为
,满足:
(3)
倘若直线与
相交,则有
(4)
其中,为
的函数,要求
且
,原问题可转化为求函数零点问题。函数零点问题使用牛顿迭代解决。
(5)
其中为函数
的Jacobian矩阵,定义为:
(6)
要试图计算出式(\ref(eq:jacobian)),我们需要分别求出对于
偏导数的每一个分量的值(共九个)。结果为:
(7)
其中是对于式子(1)按照最原始的求导法则求导的结果。
如图
共6条4阶贝塞尔曲线,每一条由绿蓝红绿四个控制点定义。