-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlocal-search.xml
More file actions
1219 lines (584 loc) · 975 KB
/
local-search.xml
File metadata and controls
1219 lines (584 loc) · 975 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>How To Write</title>
<link href="/2022/03/17/HowToWrite/"/>
<url>/2022/03/17/HowToWrite/</url>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h1 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h1><h2 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h2><figure class="highlight bash"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><pre><code class="hljs bash">$ hexo new <span class="hljs-string">"My New Post"</span><br></code></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h2 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo server<br></code></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h2 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo generate<br></code></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h2 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ hexo deploy<br></code></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
<tags>
<tag>Hexo</tag>
<tag>Fluid</tag>
</tags>
</entry>
<entry>
<title>SSE/SSE2/AVX/AVX2 Notes</title>
<link href="/2019/12/04/AVX-AVX2-Notes/"/>
<url>/2019/12/04/AVX-AVX2-Notes/</url>
<content type="html"><![CDATA[<h1 id="AVX-x2F-AVX2-x2F-SSE-x2F-SSE2指令集"><a href="#AVX-x2F-AVX2-x2F-SSE-x2F-SSE2指令集" class="headerlink" title="AVX/AVX2/SSE/SSE2指令集"></a>AVX/AVX2/SSE/SSE2指令集</h1><h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><table><thead><tr><th>数据类型</th><th>描述</th></tr></thead><tbody><tr><td>__m128</td><td>包含4个float类型数字的向量</td></tr><tr><td>__m128d</td><td>包含2个double类型数字的向量</td></tr><tr><td>__m128i</td><td>包含若干整形数字的向量</td></tr><tr><td>__m256</td><td>包含8个float类型数字的向量</td></tr><tr><td>__m256d</td><td>包含4个float类型数字的向量</td></tr><tr><td>__m256i</td><td>包含若干整形数字的向量</td></tr></tbody></table><ul><li>每一种类型,2个下划线开头,接着一个m,然后是vector的长度</li><li>若向量类型是d结尾,则向量存的double类型的数字。若无后缀则为float类型的数字。</li><li>整型的向量可以包含各种类型的整型数,如char,short,unsigned long long。也即, __m256i可以包含32个char,16个short,8个int,4个long类型。这些整型数可以是有符号和无符号类型。</li></ul><h2 id="函数名约定"><a href="#函数名约定" class="headerlink" title="函数名约定"></a>函数名约定</h2><p><code>_mm<bit_width>_<name>_<data_type></code></p><ul><li><p><code><bit_width></code>:向量长度,对于128位向量,参数为空,256位向量,参数为256</p></li><li><p><code><name></code>:内联函数的算数操作简述</p></li><li><p><code><data_type></code>:函数主参数的数据类型</p><ul><li><code>p/s</code>:分别为<code>packed</code>和<code>scalar</code>。packed指令是对整个向量暂存器的所有数据都进行计算。而scalar只计算向量暂存器的低位中的数据进行计算。</li><li><code>s/d</code>:s为float类型,d为double类型</li></ul><blockquote><p><strong>ps:</strong>包含float类型的向量<br><strong>pd:</strong>包含double类型的向量<br><strong>epi8/epi16/epi32/epi64:</strong>包含8位/16位/32位/64位的<strong>有符号整数</strong><br><strong>epu8/epu16/epu32/epu64:</strong>包含8位/16位/32位/64位的<strong>无符号整数</strong><br><strong>si128/si256:</strong>未指定的128位或者256位向量<br><strong>m128/m128i/m128d/m256/m256i/m256d:</strong>当输入向量类型与返回向量的类型不同时,标识输入向量类型</p></blockquote></li></ul><p>示例1:<code>_mm256_srlv_epi64</code>,即使不知道<code>srlv</code>的含义,<code>_mm256</code>前缀说明该函数返回一个<strong>256-bit</strong>向量,后缀<code>_epi64</code>表示参数包含多个<strong>64-bit</strong>整型数。</p><p>示例2:<code>_mm_testnzc_ps</code>,其中<code>_mm</code>意味着该函数返回一个<strong>128-bit</strong>向量,后缀<code>ps</code>表示该参数包含float类型。</p><h2 id="写一个AVX程序"><a href="#写一个AVX程序" class="headerlink" title="写一个AVX程序"></a>写一个AVX程序</h2><p>首先需要包含<code>immintrin.h</code>头文件。</p><p>hello_avx.cpp</p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><immintrin.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><isotream></span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{<br> <span class="hljs-comment">// Initialize the two argument vectors</span><br> __m256 evens = _mm256_set_ps(<span class="hljs-number">2.0</span>,<span class="hljs-number">4.0</span>,<span class="hljs-number">6.0</span>,<span class="hljs-number">8.0</span>,<span class="hljs-number">10.0</span>,<span class="hljs-number">12.0</span>,<span class="hljs-number">14.0</span>,<span class="hljs-number">16.0</span>);<br> __m256 odds = _mm256_set_ps(<span class="hljs-number">1.0</span>, <span class="hljs-number">3.0</span>, <span class="hljs-number">5.0</span>, <span class="hljs-number">7.0</span>, <span class="hljs-number">9.0</span>, <span class="hljs-number">11.0</span>, <span class="hljs-number">13.0</span>, <span class="hljs-number">15.0</span>);<br> <br> <span class="hljs-comment">// Compute the difference between the two vectors</span><br> __m256 result = _mm256_sub_ps(evens, odds);<br> <br> <span class="hljs-comment">// Show the elements of the result vector</span><br> <span class="hljs-type">float</span> *f = (<span class="hljs-type">float</span>*)&result;<br> cout << f[<span class="hljs-number">0</span>] << f[<span class="hljs-number">0</span>] << f[<span class="hljs-number">0</span>] << f[<span class="hljs-number">0</span>] << f[<span class="hljs-number">0</span>] << f[<span class="hljs-number">0</span>] << f[<span class="hljs-number">0</span>] << f[<span class="hljs-number">0</span>] << endl;<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><h2 id="函数初始化"><a href="#函数初始化" class="headerlink" title="函数初始化"></a>函数初始化</h2><h3 id="标量初始化函数"><a href="#标量初始化函数" class="headerlink" title="标量初始化函数"></a>标量初始化函数</h3><table><thead><tr><th>数据类型</th><th>描述</th></tr></thead><tbody><tr><td>_mm256_setzero_ps/pd</td><td>返回一个全0的float/double类型向量</td></tr><tr><td>_mm256_setzero_si256</td><td>返回一个全0的整型向量</td></tr><tr><td>_mm256_set1_ps/pd</td><td>用一个float类型数填充向量</td></tr><tr><td>_mm256_set1_epi8/epi16/epi32/epi64x</td><td>用整型数填充向量</td></tr><tr><td>_mm256_set_epi8/epi16/epi32/epi64x</td><td>用一个整形数初始化向量</td></tr><tr><td>_mm256_set_ps/pd</td><td>用8个float或4个double类型数字初始化向量</td></tr><tr><td>_mm256_set_m128/m128d/m128i</td><td>用2个128位的向量初始化一个256位向量</td></tr><tr><td>_mm256_setr_ps/pd</td><td>用8个float或者4个double的转置顺序初始化向量</td></tr><tr><td>_mm256_setr_epi8/epi16/epi32/epi64x</td><td>用若干个整形数的转置顺序初始化向量</td></tr></tbody></table><h4 id="setzero"><a href="#setzero" class="headerlink" title="setzero"></a>setzero</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><immintrin.h></span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">setzero</span><span class="hljs-params">()</span> </span>{<br><span class="hljs-comment">//单精度</span><br>__m256 float_vec = _mm256_setzero_ps();<br><span class="hljs-type">float</span> *flo = (<span class="hljs-type">float</span>*)&float_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"float:\t%f, %f, %f, %f, %f, %f, %f, %f\n"</span>, flo[<span class="hljs-number">0</span>], flo[<span class="hljs-number">1</span>], flo[<span class="hljs-number">2</span>], flo[<span class="hljs-number">3</span>], flo[<span class="hljs-number">4</span>], flo[<span class="hljs-number">5</span>], flo[<span class="hljs-number">6</span>], flo[<span class="hljs-number">7</span>]);<br><br><span class="hljs-comment">//双精度</span><br>__m256d double_vec = _mm256_setzero_pd();<br><span class="hljs-type">double</span> *dou = (<span class="hljs-type">double</span>*)&double_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"double:\t%lf, %lf, %lf, %lf\n"</span>, dou[<span class="hljs-number">0</span>], dou[<span class="hljs-number">1</span>], dou[<span class="hljs-number">2</span>], dou[<span class="hljs-number">3</span>]);<br><br><span class="hljs-comment">//整型</span><br>__m256i int_vec = _mm256_setzero_si256();<br><span class="hljs-type">int</span>* i = (<span class="hljs-type">int</span>*)&int_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"int:\t%d, %d, %d, %d, %d, %d, %d, %d\n"</span>, i[<span class="hljs-number">0</span>], i[<span class="hljs-number">1</span>], i[<span class="hljs-number">2</span>], i[<span class="hljs-number">3</span>], i[<span class="hljs-number">4</span>], i[<span class="hljs-number">5</span>], i[<span class="hljs-number">6</span>], i[<span class="hljs-number">7</span>]);<br><br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>输出:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">float: 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000<br>double: 0.000000, 0.000000, 0.000000, 0.000000<br>int: 0, 0, 0, 0, 0, 0, 0, 0<br></code></pre></td></tr></table></figure><h4 id="set1"><a href="#set1" class="headerlink" title="set1"></a>set1</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><immintrin.h></span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">set1</span><span class="hljs-params">()</span> </span>{<br><span class="hljs-comment">//单精度</span><br>__m256 float_vec = _mm256_set1_ps(<span class="hljs-number">1.0</span>);<br><span class="hljs-type">float</span> *flo = (<span class="hljs-type">float</span>*)&float_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"float:\t\t%f, %f, %f, %f, %f, %f, %f, %f\n"</span>, flo[<span class="hljs-number">0</span>], flo[<span class="hljs-number">1</span>], flo[<span class="hljs-number">2</span>], flo[<span class="hljs-number">3</span>], flo[<span class="hljs-number">4</span>], flo[<span class="hljs-number">5</span>], flo[<span class="hljs-number">6</span>], flo[<span class="hljs-number">7</span>]);<br><br><span class="hljs-comment">//双精度</span><br>__m256d double_vec = _mm256_set1_pd(<span class="hljs-number">2.0</span>);<br><span class="hljs-type">double</span> *dou = (<span class="hljs-type">double</span>*)&double_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"double:\t\t%lf, %lf, %lf, %lf\n"</span>, dou[<span class="hljs-number">0</span>], dou[<span class="hljs-number">1</span>], dou[<span class="hljs-number">2</span>], dou[<span class="hljs-number">3</span>]);<br><br><span class="hljs-comment">//8-bit整型</span><br>__m256i char_vec = _mm256_set1_epi8(<span class="hljs-number">3</span>);<br><span class="hljs-type">char</span>* c = (<span class="hljs-type">char</span>*)&char_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"char:\t\t%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n"</span>, c[<span class="hljs-number">0</span>], c[<span class="hljs-number">1</span>], c[<span class="hljs-number">2</span>], c[<span class="hljs-number">3</span>], c[<span class="hljs-number">4</span>], c[<span class="hljs-number">5</span>], c[<span class="hljs-number">6</span>], c[<span class="hljs-number">7</span>], c[<span class="hljs-number">8</span>], c[<span class="hljs-number">9</span>], c[<span class="hljs-number">10</span>], c[<span class="hljs-number">11</span>], c[<span class="hljs-number">12</span>], c[<span class="hljs-number">13</span>], c[<span class="hljs-number">14</span>], c[<span class="hljs-number">15</span>], c[<span class="hljs-number">16</span>], c[<span class="hljs-number">17</span>], c[<span class="hljs-number">18</span>], c[<span class="hljs-number">19</span>], c[<span class="hljs-number">20</span>], c[<span class="hljs-number">21</span>], c[<span class="hljs-number">22</span>], c[<span class="hljs-number">23</span>], c[<span class="hljs-number">24</span>], c[<span class="hljs-number">25</span>], c[<span class="hljs-number">26</span>], c[<span class="hljs-number">27</span>], c[<span class="hljs-number">28</span>], c[<span class="hljs-number">29</span>], c[<span class="hljs-number">30</span>], c[<span class="hljs-number">31</span>]);<br><br><span class="hljs-comment">//16-bit整型</span><br>__m256i short_vec = _mm256_set1_epi16(<span class="hljs-number">4</span>);<br><span class="hljs-type">short</span> *sho = (<span class="hljs-type">short</span>*)&short_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"short:\t\t%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n"</span>, sho[<span class="hljs-number">0</span>], sho[<span class="hljs-number">1</span>], sho[<span class="hljs-number">2</span>], sho[<span class="hljs-number">3</span>], sho[<span class="hljs-number">4</span>], sho[<span class="hljs-number">5</span>], sho[<span class="hljs-number">6</span>], sho[<span class="hljs-number">7</span>], sho[<span class="hljs-number">8</span>], sho[<span class="hljs-number">9</span>], sho[<span class="hljs-number">10</span>], sho[<span class="hljs-number">11</span>], sho[<span class="hljs-number">12</span>], sho[<span class="hljs-number">13</span>], sho[<span class="hljs-number">14</span>], sho[<span class="hljs-number">15</span>]);<br><br><span class="hljs-comment">//32-bit整型</span><br>__m256i int_vec = _mm256_set1_epi32(<span class="hljs-number">5</span>);<br><span class="hljs-type">int</span> *i = (<span class="hljs-type">int</span>*)&int_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"int:\t\t%d, %d, %d, %d, %d, %d, %d, %d\n"</span>, i[<span class="hljs-number">0</span>], i[<span class="hljs-number">1</span>], i[<span class="hljs-number">2</span>], i[<span class="hljs-number">3</span>], i[<span class="hljs-number">4</span>], i[<span class="hljs-number">5</span>], i[<span class="hljs-number">6</span>], i[<span class="hljs-number">7</span>]);<br><br><span class="hljs-comment">//64-bit整数</span><br>__m256i long_vec = _mm256_set1_epi64x(<span class="hljs-number">6</span>);<br><span class="hljs-type">long</span> <span class="hljs-type">long</span> *lo = (<span class="hljs-type">long</span> <span class="hljs-type">long</span>*)&long_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"long long:\t%lld, %lld, %lld, %lld\n"</span>, lo[<span class="hljs-number">0</span>], lo[<span class="hljs-number">1</span>], lo[<span class="hljs-number">2</span>], lo[<span class="hljs-number">3</span>]);<br><br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>输出:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs shell">float: 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000, 1.000000<br>double: 2.000000, 2.000000, 2.000000, 2.000000<br>char: 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3<br>short: 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4<br>int: 5, 5, 5, 5, 5, 5, 5, 5<br>long long: 6, 6, 6, 6<br></code></pre></td></tr></table></figure><h4 id="set"><a href="#set" class="headerlink" title="set"></a>set</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><immintrin.h></span><span class="hljs-comment">//AVX/AVX2</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><xmmintrin.h></span><span class="hljs-comment">//SSE</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><emmintrin.h></span><span class="hljs-comment">//SSE2</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br><span class="hljs-comment">//单精度</span><br>__m256 float_vec = _mm256_set_ps(<span class="hljs-number">1.0</span>, <span class="hljs-number">2.0</span>, <span class="hljs-number">3.0</span>, <span class="hljs-number">4.0</span>, <span class="hljs-number">5.0</span>, <span class="hljs-number">6.0</span>, <span class="hljs-number">7.0</span>, <span class="hljs-number">8.0</span>);<br><span class="hljs-type">float</span> *flo = (<span class="hljs-type">float</span> *)&float_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"float:\t\t%f, %f, %f, %f, %f, %f, %f, %f\n"</span>, flo[<span class="hljs-number">0</span>], flo[<span class="hljs-number">1</span>], flo[<span class="hljs-number">2</span>], flo[<span class="hljs-number">3</span>], flo[<span class="hljs-number">4</span>], flo[<span class="hljs-number">5</span>], flo[<span class="hljs-number">6</span>], flo[<span class="hljs-number">7</span>]);<br><br><span class="hljs-comment">//双精度</span><br>__m256d double_vec = _mm256_set_pd(<span class="hljs-number">9.0</span>, <span class="hljs-number">10.0</span>, <span class="hljs-number">11.0</span>, <span class="hljs-number">12.0</span>);<br><span class="hljs-type">double</span> *dou = (<span class="hljs-type">double</span>*)&double_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"double:\t\t%lf, %lf, %lf, %lf\n"</span>, dou[<span class="hljs-number">0</span>], dou[<span class="hljs-number">1</span>], dou[<span class="hljs-number">2</span>], dou[<span class="hljs-number">3</span>]);<br><br><span class="hljs-comment">//8-bit整型</span><br>__m256i char_vec = _mm256_set_epi8(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">10</span>, <span class="hljs-number">11</span>, <span class="hljs-number">12</span>, <span class="hljs-number">13</span>, <span class="hljs-number">14</span>, <span class="hljs-number">15</span>, <span class="hljs-number">16</span>, <span class="hljs-number">17</span>, <span class="hljs-number">18</span>, <span class="hljs-number">19</span>, <span class="hljs-number">20</span>, <span class="hljs-number">21</span>, <span class="hljs-number">22</span>, <span class="hljs-number">23</span>, <span class="hljs-number">24</span>, <span class="hljs-number">25</span>, <span class="hljs-number">26</span>, <span class="hljs-number">27</span>, <span class="hljs-number">28</span>, <span class="hljs-number">29</span>, <span class="hljs-number">30</span>, <span class="hljs-number">31</span>, <span class="hljs-number">32</span>);<br><span class="hljs-type">char</span> *c = (<span class="hljs-type">char</span>*)&char_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"char:\t\t%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n"</span>, c[<span class="hljs-number">0</span>], c[<span class="hljs-number">1</span>], c[<span class="hljs-number">2</span>], c[<span class="hljs-number">3</span>], c[<span class="hljs-number">4</span>], c[<span class="hljs-number">5</span>], c[<span class="hljs-number">6</span>], c[<span class="hljs-number">7</span>], c[<span class="hljs-number">8</span>], c[<span class="hljs-number">9</span>], c[<span class="hljs-number">10</span>], c[<span class="hljs-number">11</span>], c[<span class="hljs-number">12</span>], c[<span class="hljs-number">13</span>], c[<span class="hljs-number">14</span>], c[<span class="hljs-number">15</span>], c[<span class="hljs-number">16</span>], c[<span class="hljs-number">17</span>], c[<span class="hljs-number">18</span>], c[<span class="hljs-number">19</span>], c[<span class="hljs-number">20</span>], c[<span class="hljs-number">21</span>], c[<span class="hljs-number">22</span>], c[<span class="hljs-number">23</span>], c[<span class="hljs-number">24</span>], c[<span class="hljs-number">25</span>], c[<span class="hljs-number">26</span>], c[<span class="hljs-number">27</span>], c[<span class="hljs-number">28</span>], c[<span class="hljs-number">29</span>], c[<span class="hljs-number">30</span>], c[<span class="hljs-number">31</span>]);<br><br><span class="hljs-comment">//16-bit整型</span><br>__m256i short_vec = _mm256_set_epi16(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">10</span>, <span class="hljs-number">11</span>, <span class="hljs-number">12</span>, <span class="hljs-number">13</span>, <span class="hljs-number">14</span>, <span class="hljs-number">15</span>, <span class="hljs-number">16</span>);<br><span class="hljs-type">short</span> *sho = (<span class="hljs-type">short</span> *)&short_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"short:\t\t%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n"</span>, sho[<span class="hljs-number">0</span>], sho[<span class="hljs-number">1</span>], sho[<span class="hljs-number">2</span>], sho[<span class="hljs-number">3</span>], sho[<span class="hljs-number">4</span>], sho[<span class="hljs-number">5</span>], sho[<span class="hljs-number">6</span>], sho[<span class="hljs-number">7</span>], sho[<span class="hljs-number">8</span>], sho[<span class="hljs-number">9</span>], sho[<span class="hljs-number">10</span>], sho[<span class="hljs-number">11</span>], sho[<span class="hljs-number">12</span>], sho[<span class="hljs-number">13</span>], sho[<span class="hljs-number">14</span>], sho[<span class="hljs-number">15</span>]);<br><br><span class="hljs-comment">//32-bit整型</span><br>__m256i int_vec = _mm256_set_epi32(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>);<br><span class="hljs-type">int</span> *i = (<span class="hljs-type">int</span>*)&int_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"int:\t\t%d, %d, %d, %d, %d, %d, %d, %d\n"</span>, i[<span class="hljs-number">0</span>], i[<span class="hljs-number">1</span>], i[<span class="hljs-number">2</span>], i[<span class="hljs-number">3</span>], i[<span class="hljs-number">4</span>], i[<span class="hljs-number">5</span>], i[<span class="hljs-number">6</span>], i[<span class="hljs-number">7</span>]);<br><br><span class="hljs-comment">//64-bit整型</span><br>__m256i long_vec = _mm256_set_epi64x(<span class="hljs-number">9</span>, <span class="hljs-number">10</span>, <span class="hljs-number">11</span>, <span class="hljs-number">12</span>);<br><span class="hljs-type">long</span> <span class="hljs-type">long</span> *lo = (<span class="hljs-type">long</span> <span class="hljs-type">long</span>*)&long_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"long long:\t%lld, %lld, %lld, %lld\n"</span>, lo[<span class="hljs-number">0</span>], lo[<span class="hljs-number">1</span>], lo[<span class="hljs-number">2</span>], lo[<span class="hljs-number">3</span>]);<br><br><span class="hljs-comment">//从128-bit单精度向量初始化值</span><br>__m128 float_vec_128_0 = _mm_set_ps(<span class="hljs-number">1.0</span>, <span class="hljs-number">2.0</span>, <span class="hljs-number">3.0</span>, <span class="hljs-number">4.0</span>);<br>__m128 float_vec_128_1 = _mm_set_ps(<span class="hljs-number">5.0</span>, <span class="hljs-number">6.0</span>, <span class="hljs-number">7.0</span>, <span class="hljs-number">8.0</span>);<br>__m256 float_vec_256 = _mm256_set_m128(float_vec_128_1, float_vec_128_0);<br><span class="hljs-type">float</span> *flo_256 = (<span class="hljs-type">float</span>*)&float_vec_256;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"float:\t\t%f, %f, %f, %f, %f, %f, %f, %f\n"</span>, flo_256[<span class="hljs-number">0</span>], flo_256[<span class="hljs-number">1</span>], flo_256[<span class="hljs-number">2</span>], flo_256[<span class="hljs-number">3</span>], flo_256[<span class="hljs-number">4</span>], flo_256[<span class="hljs-number">5</span>], flo_256[<span class="hljs-number">6</span>], flo_256[<span class="hljs-number">7</span>]);<br><br><span class="hljs-comment">//从128-bit双精度向量初始化值</span><br>__m128d double_vec_128_0 = _mm_set_pd(<span class="hljs-number">9.0</span>, <span class="hljs-number">10.0</span>);<br>__m128d double_vec_128_1 = _mm_set_pd(<span class="hljs-number">11.0</span>, <span class="hljs-number">12.0</span>);<br>__m256d double_vec_256 = _mm256_set_m128d(double_vec_128_1, double_vec_128_0);<br><span class="hljs-type">double</span> *dou_256 = (<span class="hljs-type">double</span>*)&double_vec_256;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"double:\t\t%lf, %lf, %lf, %lf\n"</span>, dou_256[<span class="hljs-number">0</span>], dou_256[<span class="hljs-number">1</span>], dou_256[<span class="hljs-number">2</span>], dou_256[<span class="hljs-number">3</span>]);<br><br><span class="hljs-comment">//从128-bit整型向量初始化值</span><br>__m128i int_vec_128_0 = _mm_set_epi32(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>);<br>__m128i int_vec_128_1 = _mm_set_epi32(<span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>);<br>__m256i int_vec_256 = _mm256_set_m128i(int_vec_128_1, int_vec_128_0);<br><span class="hljs-type">int</span> *i_256 = (<span class="hljs-type">int</span>*)&int_vec_256;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"int:\t\t%d, %d, %d, %d, %d, %d, %d, %d\n"</span>, i_256[<span class="hljs-number">0</span>], i_256[<span class="hljs-number">1</span>], i_256[<span class="hljs-number">2</span>], i_256[<span class="hljs-number">3</span>], i_256[<span class="hljs-number">4</span>], i_256[<span class="hljs-number">5</span>], i_256[<span class="hljs-number">6</span>], i_256[<span class="hljs-number">7</span>]);<br><br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>输出:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs shell">float: 8.000000, 7.000000, 6.000000, 5.000000, 4.000000, 3.000000, 2.000000, 1.000000<br>double: 12.000000, 11.000000, 10.000000, 9.000000<br>char: 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1<br>short: 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1<br>int: 8, 7, 6, 5, 4, 3, 2, 1<br>long long: 12, 11, 10, 9<br>float: 4.000000, 3.000000, 2.000000, 1.000000, 8.000000, 7.000000, 6.000000, 5.000000<br>double: 10.000000, 9.000000, 12.000000, 11.000000<br>int: 4, 3, 2, 1, 8, 7, 6, 5<br></code></pre></td></tr></table></figure><h4 id="setr"><a href="#setr" class="headerlink" title="setr"></a>setr</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><immintrin.h></span><span class="hljs-comment">//AVX/AVX2</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><xmmintrin.h></span><span class="hljs-comment">//SSE</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><emmintrin.h></span><span class="hljs-comment">//SSE2</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br><span class="hljs-comment">//单精度 </span><br>__m256 float_vec = _mm256_setr_ps(<span class="hljs-number">1.0</span>, <span class="hljs-number">2.0</span>, <span class="hljs-number">3.0</span>, <span class="hljs-number">4.0</span>, <span class="hljs-number">5.0</span>, <span class="hljs-number">6.0</span>, <span class="hljs-number">7.0</span>, <span class="hljs-number">8.0</span>);<br><br><span class="hljs-type">float</span>* flo = (<span class="hljs-type">float</span>*)&float_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"float:\t\t%f, %f, %f, %f, %f, %f, %f, %f\n"</span>, flo[<span class="hljs-number">0</span>], flo[<span class="hljs-number">1</span>], flo[<span class="hljs-number">2</span>], flo[<span class="hljs-number">3</span>], flo[<span class="hljs-number">4</span>], flo[<span class="hljs-number">5</span>], flo[<span class="hljs-number">6</span>], flo[<span class="hljs-number">7</span>]);<br><br><span class="hljs-comment">//双精度</span><br>__m256d double_vec = _mm256_setr_pd(<span class="hljs-number">9.0</span>, <span class="hljs-number">10.0</span>, <span class="hljs-number">11.0</span>, <span class="hljs-number">12.0</span>);<br><br><span class="hljs-type">double</span>* dou = (<span class="hljs-type">double</span>*)&double_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"double:\t\t%lf, %lf, %lf, %lf\n"</span>, dou[<span class="hljs-number">0</span>], dou[<span class="hljs-number">1</span>], dou[<span class="hljs-number">2</span>], dou[<span class="hljs-number">3</span>]);<br><br><span class="hljs-comment">//32-bit整型</span><br>__m256i int_vec = _mm256_setr_epi32(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>);<br><br><span class="hljs-type">int</span>* i = (<span class="hljs-type">int</span>*)&int_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"int:\t\t%d, %d, %d, %d, %d, %d, %d, %d\n"</span>, i[<span class="hljs-number">0</span>], i[<span class="hljs-number">1</span>], i[<span class="hljs-number">2</span>], i[<span class="hljs-number">3</span>], i[<span class="hljs-number">4</span>], i[<span class="hljs-number">5</span>], i[<span class="hljs-number">6</span>], i[<span class="hljs-number">7</span>]);<br><br><span class="hljs-comment">//64-bit整型</span><br>__m256i long_vec = _mm256_setr_epi64x(<span class="hljs-number">9</span>, <span class="hljs-number">10</span>, <span class="hljs-number">11</span>, <span class="hljs-number">12</span>);<br><br><span class="hljs-type">long</span> <span class="hljs-type">long</span>* lo = (<span class="hljs-type">long</span> <span class="hljs-type">long</span>*)&long_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"long long:\t%d, %d, %d, %d\n"</span>, lo[<span class="hljs-number">0</span>], lo[<span class="hljs-number">1</span>], lo[<span class="hljs-number">2</span>], lo[<span class="hljs-number">3</span>]);<br><br><span class="hljs-comment">//16-bit整型</span><br>__m256i short_vec = _mm256_setr_epi16(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">10</span>, <span class="hljs-number">11</span>, <span class="hljs-number">12</span>, <span class="hljs-number">13</span>, <span class="hljs-number">14</span>, <span class="hljs-number">15</span>, <span class="hljs-number">16</span>);<br><br><span class="hljs-type">short</span>* sho = (<span class="hljs-type">short</span>*)&short_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"short:\t\t%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n"</span>, sho[<span class="hljs-number">0</span>], sho[<span class="hljs-number">1</span>], sho[<span class="hljs-number">2</span>], sho[<span class="hljs-number">3</span>], sho[<span class="hljs-number">4</span>], sho[<span class="hljs-number">5</span>], sho[<span class="hljs-number">6</span>], sho[<span class="hljs-number">7</span>], sho[<span class="hljs-number">8</span>], sho[<span class="hljs-number">9</span>], sho[<span class="hljs-number">10</span>], sho[<span class="hljs-number">11</span>], sho[<span class="hljs-number">12</span>], sho[<span class="hljs-number">13</span>], sho[<span class="hljs-number">14</span>], sho[<span class="hljs-number">15</span>]);<br><br><span class="hljs-comment">//8-bit整型</span><br>__m256i char_vec = _mm256_setr_epi8(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">10</span>, <span class="hljs-number">11</span>, <span class="hljs-number">12</span>, <span class="hljs-number">13</span>, <span class="hljs-number">14</span>, <span class="hljs-number">15</span>, <span class="hljs-number">16</span>, <span class="hljs-number">17</span>, <span class="hljs-number">18</span>, <span class="hljs-number">19</span>, <span class="hljs-number">20</span>, <span class="hljs-number">21</span>, <span class="hljs-number">22</span>, <span class="hljs-number">23</span>, <span class="hljs-number">24</span>, <span class="hljs-number">25</span>, <span class="hljs-number">26</span>, <span class="hljs-number">27</span>, <span class="hljs-number">28</span>, <span class="hljs-number">29</span>, <span class="hljs-number">30</span>, <span class="hljs-number">31</span>, <span class="hljs-number">32</span>);<br><br><span class="hljs-type">char</span>* c = (<span class="hljs-type">char</span>*)&char_vec;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"char:\t\t%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n"</span>, c[<span class="hljs-number">0</span>], c[<span class="hljs-number">1</span>], c[<span class="hljs-number">2</span>], c[<span class="hljs-number">3</span>], c[<span class="hljs-number">4</span>], c[<span class="hljs-number">5</span>], c[<span class="hljs-number">6</span>], c[<span class="hljs-number">7</span>], c[<span class="hljs-number">8</span>], c[<span class="hljs-number">9</span>], c[<span class="hljs-number">10</span>], c[<span class="hljs-number">11</span>], c[<span class="hljs-number">12</span>], c[<span class="hljs-number">13</span>], c[<span class="hljs-number">14</span>], c[<span class="hljs-number">15</span>], c[<span class="hljs-number">16</span>], c[<span class="hljs-number">17</span>], c[<span class="hljs-number">18</span>], c[<span class="hljs-number">19</span>], c[<span class="hljs-number">20</span>], c[<span class="hljs-number">21</span>], c[<span class="hljs-number">22</span>], c[<span class="hljs-number">23</span>], c[<span class="hljs-number">24</span>], c[<span class="hljs-number">25</span>], c[<span class="hljs-number">26</span>], c[<span class="hljs-number">27</span>], c[<span class="hljs-number">28</span>], c[<span class="hljs-number">29</span>], c[<span class="hljs-number">30</span>], c[<span class="hljs-number">31</span>]);<br><br><span class="hljs-comment">// Set value from 128-bit single-precision vectors</span><br>__m128 float_vec_128_0 = _mm_setr_ps(<span class="hljs-number">1.0</span>, <span class="hljs-number">2.0</span>, <span class="hljs-number">3.0</span>, <span class="hljs-number">4.0</span>);<br>__m128 float_vec_128_1 = _mm_setr_ps(<span class="hljs-number">5.0</span>, <span class="hljs-number">6.0</span>, <span class="hljs-number">7.0</span>, <span class="hljs-number">8.0</span>);<br><br>__m256 float_vec_256 = _mm256_setr_m128(float_vec_128_1, float_vec_128_0);<br><span class="hljs-type">float</span>* flo_256 = (<span class="hljs-type">float</span>*)&float_vec_256;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"float:\t\t%f, %f, %f, %f, %f, %f, %f, %f\n"</span>, flo_256[<span class="hljs-number">0</span>], flo_256[<span class="hljs-number">1</span>], flo_256[<span class="hljs-number">2</span>], flo_256[<span class="hljs-number">3</span>], flo_256[<span class="hljs-number">4</span>], flo_256[<span class="hljs-number">5</span>], flo_256[<span class="hljs-number">6</span>], flo_256[<span class="hljs-number">7</span>]);<br><br><span class="hljs-comment">// Set value from 128-bit double-precision vectors</span><br>__m128d double_vec_128_0 = _mm_setr_pd(<span class="hljs-number">9.0</span>, <span class="hljs-number">10.0</span>);<br>__m128d double_vec_128_1 = _mm_setr_pd(<span class="hljs-number">11.0</span>, <span class="hljs-number">12.0</span>);<br><br>__m256d double_vec_256 = _mm256_setr_m128d(double_vec_128_1, double_vec_128_0);<br><span class="hljs-type">double</span>* dou_256 = (<span class="hljs-type">double</span>*)&double_vec_256;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"double:\t\t%lf, %lf, %lf, %lf\n"</span>, dou_256[<span class="hljs-number">0</span>], dou_256[<span class="hljs-number">1</span>], dou_256[<span class="hljs-number">2</span>], dou_256[<span class="hljs-number">3</span>]);<br><br><span class="hljs-comment">// Set value from 128-bit integer vectors</span><br>__m128i int_vec_128_0 = _mm_setr_epi32(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>);<br>__m128i int_vec_128_1 = _mm_setr_epi32(<span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>);<br><br>__m256i int_vec_256 = _mm256_setr_m128i(int_vec_128_1, int_vec_128_0);<br><span class="hljs-type">int</span>* i_256 = (<span class="hljs-type">int</span>*)&int_vec_256;<br><span class="hljs-built_in">printf</span>(<span class="hljs-string">"int:\t\t%d, %d, %d, %d, %d, %d, %d, %d\n"</span>, i_256[<span class="hljs-number">0</span>], i_256[<span class="hljs-number">1</span>], i_256[<span class="hljs-number">2</span>], i_256[<span class="hljs-number">3</span>], i_256[<span class="hljs-number">4</span>], i_256[<span class="hljs-number">5</span>], i_256[<span class="hljs-number">6</span>], i_256[<span class="hljs-number">7</span>]);<br><br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>输出:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs shell">float: 1.000000, 2.000000, 3.000000, 4.000000, 5.000000, 6.000000, 7.000000, 8.000000<br>double: 9.000000, 10.000000, 11.000000, 12.000000<br>int: 1, 2, 3, 4, 5, 6, 7, 8<br>long long: 9, 10, 11, 12<br>short: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16<br>char: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32<br>float: 5.000000, 6.000000, 7.000000, 8.000000, 1.000000, 2.000000, 3.000000, 4.000000<br>double: 11.000000, 12.000000, 9.000000, 10.000000<br>int: 5, 6, 7, 8, 1, 2, 3, 4<br></code></pre></td></tr></table></figure><h3 id="从内存中加载数据"><a href="#从内存中加载数据" class="headerlink" title="从内存中加载数据"></a>从内存中加载数据</h3><table><thead><tr><th>数据类型</th><th>描述</th></tr></thead><tbody><tr><td>_mm256_load_ps/pd</td><td>从对齐的内存地址加载float/double向量</td></tr><tr><td>_mm256_load_si256</td><td>从对齐的内存地址加载整形向量</td></tr><tr><td>_mm256_loadu_ps/pd</td><td>从未对齐的内存地址加载浮点向量</td></tr><tr><td>_mm256_loadu_si256</td><td>从未对齐的内存地址加载整形向量</td></tr><tr><td>_mm_maskload_ps/pd</td><td>根据掩码加载128位浮点向量的部分</td></tr><tr><td>_mm256_maskload_ps/pd</td><td>根据掩码加载256位浮点向量的部分</td></tr><tr><td>_mm_maskload_epi32/64(只在avx2中支持)</td><td>根据掩码加载128位整形向量的部分</td></tr><tr><td>_mm256_maskload_epi32/64(只在avx2中支持)</td><td>根据掩码加载256位整形向量的部分</td></tr></tbody></table><p>调用:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">float</span> *aligned_floats = (<span class="hljs-type">float</span>*)<span class="hljs-built_in">aligned_alloc</span>(<span class="hljs-number">32</span>, <span class="hljs-number">64</span>*<span class="hljs-built_in">sizeof</span>(<span class="hljs-type">float</span>));<br><span class="hljs-comment">// Initialize data</span><br>__m256 vec = _mm256_load_ps(aligned_floats);<br><br><span class="hljs-type">float</span> *unaligned_floats = (<span class="hljs-type">float</span>*)<span class="hljs-built_in">malloc</span>(<span class="hljs-number">64</span>*<span class="hljs-built_in">sizeof</span>(<span class="hljs-type">float</span>));<br><span class="hljs-comment">// Initialize data</span><br>__m256 vec = _mm256_loadu_ps(unaligned_floats);<br></code></pre></td></tr></table></figure><p>若要处理的float数组长度为11不能被8整除。那么最后5个浮点数需要置0。或者使用上表中的<code>_maskload_</code>。</p><p><code>_maskload_</code>函数有两个参数:内存地址、相同元素数目的整型向量作为返回。整型向量中每个元素的最高位为1,返回的向量中对应元素是从内存中读取的。若整型向量中每个元素最高位为0,则返回的向量对应元素置0。</p><p>mask_load.cpp</p><p>读入8个int到向量,最后3个应该置0。使用了<code>_mm256_maskload_epi32</code>,第二个参数应为<code>__m256i</code>mask向量。该mask向量包含5个整型,其最高位是1,剩下3个整型的最高位置0.</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><immintrin.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostram></span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span>{<br> <span class="hljs-type">int</span> int_array[<span class="hljs-number">8</span>] = {<span class="hljs-number">100</span>,<span class="hljs-number">200</span>,<span class="hljs-number">300</span>,<span class="hljs-number">400</span>,<span class="hljs-number">500</span>,<span class="hljs-number">600</span>,<span class="hljs-number">700</span>,<span class="hljs-number">800</span>};<br> <span class="hljs-comment">// Initialize the mask vector</span><br> __m256i mask = _mm256_setr_epi32(<span class="hljs-number">-20</span>, <span class="hljs-number">-70</span>, <span class="hljs-number">-48</span>, <span class="hljs-number">-9</span>, <span class="hljs-number">-100</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>);<br> <span class="hljs-comment">// or</span><br> __m256i mask_1 = _mm256_set_epi32(<span class="hljs-number">3</span>,<span class="hljs-number">5</span>,<span class="hljs-number">8</span>,<span class="hljs-number">-100</span>,<span class="hljs-number">-9</span>,<span class="hljs-number">-48</span>,<span class="hljs-number">-70</span>,<span class="hljs-number">-20</span>);<br> <br> <span class="hljs-comment">// Selectively load data to the vector</span><br> __m256i result = _mm256_maskload_epi32(int_array, mask);<br> <br> <span class="hljs-comment">// Selectively load data into reuslt vector</span><br> <span class="hljs-type">int</span> *res = (<span class="hljs-type">int</span>*)&result;<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"%d %d %d %d %d %d %d %d\n"</span>, <br> res[<span class="hljs-number">0</span>], res[<span class="hljs-number">1</span>], res[<span class="hljs-number">2</span>], res[<span class="hljs-number">3</span>], res[<span class="hljs-number">4</span>], res[<span class="hljs-number">5</span>], res[<span class="hljs-number">6</span>], res[<span class="hljs-number">7</span>]);<br> <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>输出:</p><figure class="highlight basic"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs basic"><span class="hljs-symbol">100 </span><span class="hljs-number">200</span> <span class="hljs-number">300</span> <span class="hljs-number">400</span> <span class="hljs-number">500</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span><br></code></pre></td></tr></table></figure><p>说明:</p><ul><li>负整型的最高位总为1。所以mask vector选用5个负数,3个正数</li><li><code>_mm256_maskload_epi32</code>是AVX2函数,因此用gcc编译加<code>-mavx2</code>参数</li></ul><h2 id="算术本质"><a href="#算术本质" class="headerlink" title="算术本质"></a>算术本质</h2><h3 id="加减法"><a href="#加减法" class="headerlink" title="加减法"></a>加减法</h3><table><thead><tr><th>数据类型</th><th>描述</th></tr></thead><tbody><tr><td>_mm256_add_ps/pd</td><td>对两个浮点向量做加法</td></tr><tr><td>_mm256_sub_ps/pd</td><td>对两个浮点向量做减法</td></tr><tr><td>(2)_mm256_add_epi8/16/32/64</td><td>对两个整形向量做加法</td></tr><tr><td>(2)_mm256_sub_epi8/16/32/64</td><td>对两个整形向量做减法</td></tr><tr><td>(2)_mm256_adds_epi8/16 (2)_mm256_adds_epu8/16</td><td>两个整数向量相加且考虑内存饱和问题</td></tr><tr><td>(2)_mm256_subs_epi8/16 (2)_mm256_subs_epu8/16</td><td>两个整数向量相减且考虑内存饱和问题</td></tr><tr><td>_mm256_hadd_ps/pd</td><td>水平方向上对两个float类型向量做加法</td></tr><tr><td>_mm256_hsub_ps/pd</td><td>垂直方向上最两个float类型向量做减法</td></tr><tr><td>(2)_mm256_hadd_epi16/32</td><td>水平方向上对两个整形向量做加法</td></tr><tr><td>(2)_mm256_hsub_epi16/32</td><td>水平方向上最两个整形向量做减法</td></tr><tr><td>(2)_mm256_hadds_epi16</td><td>对两个包含short类型的向量做加法且考虑内存饱和的问题</td></tr><tr><td>(2)_mm256_hsubs_epi16</td><td>对两个包含short类型的向量做减法且考虑内存饱和的问题</td></tr><tr><td>_mm256_addsub_ps/pd</td><td>加上和减去两个float类型的向量、(在偶数位置减去,奇数位置加上,获最后得目标向量。)</td></tr></tbody></table><p>前面有一个(2),代表函数只在AVX2中支持。</p><p><code>_add_/_sub_</code>函数和<code>_adds_/_subs_</code>函数的区别在于。<code>s</code>表示饱和,即当结果需要更多的内存来保存结果也能存下。</p><p><strong>例1:</strong>一个向量包含signed bytes,因此每个元素最大值是127(0x7F).若有98加85的操作,结果是183(0xB7).</p><ul><li>若用<code>_mm256_add_epi8</code>,溢出部分会被忽略存储结果为**-73(0xB7)**</li><li>若用<code>_mm256_adds_epi8</code>,结果会被限制在最大值即<strong>127(0x7F)</strong></li></ul><p><strong>例2:</strong>两个signed short整型向量,最小值为-32768。若计算-18000-19000,结果为-37000(0xFFFF6F78 as a 32-bit integer)</p><ul><li>若用<code>_mm256_sub_epi16</code>,溢出会被忽略存储结果为 <strong>28536(0x6F78)</strong></li><li>若用<code>_mm256_subs_epi16</code>,结果会被限制在最小值**-32768(0x8000)**</li></ul><p><code>_hadd_/_hsub_</code>函数为水平加减。即向量相邻元素做加减,而不是向量间做加减。</p><p>而在水平方向上做加减法的意思如下图:</p><p><img src="https://www.codeproject.com/KB/cpp/874396/Fig1.jpg"></p><h3 id="乘除法"><a href="#乘除法" class="headerlink" title="乘除法"></a>乘除法</h3><table><thead><tr><th>数据类型</th><th>描述</th></tr></thead><tbody><tr><td>_mm256_mul_ps/pd</td><td>对两个float类型的向量进行相乘</td></tr><tr><td>(2)_mm256_mul_epi32 (2)_mm256_mul_epu32</td><td>将包含32位整数的向量的最低四个元素相乘</td></tr><tr><td>(2)_mm256_mullo_epi16/32</td><td>Multiply integers and store low halves</td></tr><tr><td>(2)_mm256_mulhi_epi16 (2)_mm256_mulhi_epu16</td><td>Multiply integers and store high halves</td></tr><tr><td>(2)_mm256_mulhrs_epi16</td><td>Multiply 16-bit elements to form 32-bit elements</td></tr><tr><td>_mm256_div_ps/pd</td><td>对两个float类型的向量进行想除</td></tr></tbody></table><p><img src="https://img-blog.csdnimg.cn/20190701164948547.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2p1c3Rfc29ydA==,size_16,color_FFFFFF,t_70"></p><p><img src="https://img-blog.csdnimg.cn/2019070116512981.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2p1c3Rfc29ydA==,size_16,color_FFFFFF,t_70"></p><h3 id="复合运算"><a href="#复合运算" class="headerlink" title="复合运算"></a>复合运算</h3><table><thead><tr><th>数据类型</th><th>描述</th></tr></thead><tbody><tr><td>(2)_mm_fmadd_ps/pd/ (2)_mm256_fmadd_ps/pd</td><td>将两个向量相乘,再将积加上第三个。(res=a*b+c)</td></tr><tr><td>(2)_mm_fmsub_ps/pd/ (2)_mm256_fmsub_ps/pd</td><td>将两个向量相乘,然后从乘积中减去一个向量。(res=a*b-c)</td></tr><tr><td>(2)_mm_fmadd_ss/sd</td><td>将向量中最低的元素相乘并相加(res[0]=a[0]*b[0]+c[0])</td></tr><tr><td>(2)_mm_fmsub_ss/sd</td><td>将向量中最低的元素相乘并相减(res[0]=a[0]*b[0]-c[0])</td></tr><tr><td>(2)_mm_fnmadd_ps/pd (2)_mm256_fnmadd_ps/pd</td><td>将两个向量相乘,并将负积加到第三个。(res = -(a * b) + c)</td></tr><tr><td>(2)_mm_fnmsub_ps/pd/ (2)_mm256_fnmsub_ps/pd</td><td>将两个向量相乘,并将负积加到第三个 (res = -(a * b) - c)</td></tr><tr><td>(2)_mm_fnmadd_ss/sd</td><td>将两个向量的低位相乘,并将负积加到第三个向量的低位。(res[0] = -(a[0] * b[0]) + c[0])</td></tr><tr><td>(2)_mm_fnmsub_ss/sd</td><td>将最低的元素相乘,并从求反的积中减去第三个向量的最低元素。(res[0] = -(a[0] * b[0]) - c[0])</td></tr><tr><td>(2)_mm_fmaddsub_ps/pd/ (2)_mm256_fmaddsub_ps/pd</td><td>将两个矢量相乘,然后从乘积中交替加上和减去(res=a*b+/-c)</td></tr><tr><td>(2)_mm_fmsubadd_ps/pd/ (2)_mmf256_fmsubadd_ps/pd</td><td>将两个向量相乘,然后从乘积中交替地进行减法和加法(res=a*b-/+c)(奇数次方,偶数次方)</td></tr></tbody></table><h2 id="unpack、permute、shuffle、blend"><a href="#unpack、permute、shuffle、blend" class="headerlink" title="unpack、permute、shuffle、blend"></a>unpack、permute、shuffle、blend</h2><h3 id="unpack"><a href="#unpack" class="headerlink" title="unpack"></a>unpack</h3><img src="https://www.officedaytime.com/tips/simdimg/unpack.png" style="zoom:150%;" /><h3 id="permute"><a href="#permute" class="headerlink" title="permute"></a>permute</h3><table><thead><tr><th>数据类型</th><th>描述</th></tr></thead><tbody><tr><td>_mm_permute_ps/pd _mm256_permute_ps/pd</td><td>根据8位控制值从输入向量中选择元素</td></tr><tr><td>(2)_mm256_permute4x64_pd/ (2)_mm256_permute4x64_epi64</td><td>根据8位控制值从输入向量中选择64位元素</td></tr><tr><td>_mm256_permute2f128_ps/pd</td><td>基于8位控制值从两个输入向量中选择128位块</td></tr><tr><td>_mm256_permute2f128_si256</td><td>基于8位控制值从两个输入向量中选择128位块</td></tr><tr><td>_mm_permutevar_ps/pd _mm256_permutevar_ps/pd</td><td>根据整数向量中的位从输入向量中选择元素</td></tr><tr><td>(2)_mm256_permutevar8x32_ps (2)_mm256_permutevar8x32_epi32</td><td>使用整数向量中的索引选择32位元素(浮点和整数)</td></tr></tbody></table><img src="https://www.officedaytime.com/tips/simdimg/shuffle.png" style="zoom:150%;" /><p><img src="https://img-blog.csdnimg.cn/20190701173134557.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2p1c3Rfc29ydA==,size_16,color_FFFFFF,t_70"></p><h3 id="shuffle"><a href="#shuffle" class="headerlink" title="shuffle"></a>shuffle</h3><table><thead><tr><th>数据类型</th><th>描述</th></tr></thead><tbody><tr><td>_mm256_shuffle_ps/pd</td><td>根据8位值选择浮点元素</td></tr><tr><td>_mm256_shuffle_epi8/ _mm256_shuffle_epi32</td><td>根据8位值选择整数元素</td></tr><tr><td>(2)_mm256_shufflelo_epi16/ (2)_mm256_shufflehi_epi16</td><td>基于8位控制值从两个输入向量中选择128位块</td></tr></tbody></table><p>对于_mm256_shuffle_pd,只使用控制值的高4位。如果输入向量包含int或float,则使用所有控制位。对于_mm256_shuffle_ps,前两对位从第一个矢量中选择元素,第二对位从第二个矢量中选择元素。</p><img src="https://www.officedaytime.com/tips/simdimg/shuffle.png" style="zoom:150%;" /><p><img src="https://img-blog.csdnimg.cn/20190701173738368.png"></p><h3 id="blend"><a href="#blend" class="headerlink" title="blend"></a>blend</h3><img src="https://www.officedaytime.com/tips/simdimg/blend.png" style="zoom:150%;" /><h1 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h1><p><a href="https://software.intel.com/sites/landingpage/IntrinsicsGuide/">https://software.intel.com/sites/landingpage/IntrinsicsGuide/</a></p><p><a href="https://software.intel.com/en-us/articles/introduction-to-intel-advanced-vector-extensions/">https://software.intel.com/en-us/articles/introduction-to-intel-advanced-vector-extensions/</a></p><p><a href="https://www.codeproject.com/Articles/874396/Crunching-Numbers-with-AVX-and-AVX">https://www.codeproject.com/Articles/874396/Crunching-Numbers-with-AVX-and-AVX</a></p><p><a href="https://blog.triplez.cn/avx-avx2-learning-notes/#Shuffling">https://blog.triplez.cn/avx-avx2-learning-notes/#Shuffling</a></p><p><a href="https://github.com/Triple-Z/AVX-AVX2-Example-Code">https://github.com/Triple-Z/AVX-AVX2-Example-Code</a></p><p><a href="https://blog.csdn.net/just_sort/article/details/94393506">https://blog.csdn.net/just_sort/article/details/94393506</a></p><p><a href="https://www.jianshu.com/p/64ef4d304e17">https://www.jianshu.com/p/64ef4d304e17</a></p><p><a href="https://www.officedaytime.com/tips/simd.html">https://www.officedaytime.com/tips/simd.html</a></p><p><a href="https://github.com/microsoft/SPTAG/blob/master/AnnService/inc/Core/Common/DistanceUtils.h">https://github.com/microsoft/SPTAG/blob/master/AnnService/inc/Core/Common/DistanceUtils.h</a></p><p>CPU指令集介绍<br><a href="https://blog.csdn.net/gengshenghong/article/details/7006817">https://blog.csdn.net/gengshenghong/article/details/7006817</a></p><p>在C/C++代码中使用SSE等指令集的指令(1)介绍<br><a href="https://blog.csdn.net/gengshenghong/article/details/7007100">https://blog.csdn.net/gengshenghong/article/details/7007100</a><br>在C/C++代码中使用SSE等指令集的指令(2)参考手册<br><a href="https://blog.csdn.net/gengshenghong/article/details/7008682">https://blog.csdn.net/gengshenghong/article/details/7008682</a><br>在C/C++代码中使用SSE等指令集的指令(3)SSE指令集基础<br><a href="https://blog.csdn.net/gengshenghong/article/details/7008704">https://blog.csdn.net/gengshenghong/article/details/7008704</a><br>在C/C++代码中使用SSE等指令集的指令(4)SSE指令集Intrinsic函数使用<br><a href="https://blog.csdn.net/gengshenghong/article/details/7010615">https://blog.csdn.net/gengshenghong/article/details/7010615</a><br>在C/C++代码中使用SSE等指令集的指令(5)SSE进行加法运算简单的性能测试<br><a href="https://blog.csdn.net/gengshenghong/article/details/7011373">https://blog.csdn.net/gengshenghong/article/details/7011373</a></p><p>Writing C++ Wrappers for SIMD Intrinsics (1-5)<br><a href="https://johanmabille.github.io/blog/2014/10/09/writing-c-plus-plus-wrappers-for-simd-intrinsics-1/">https://johanmabille.github.io/blog/2014/10/09/writing-c-plus-plus-wrappers-for-simd-intrinsics-1/</a><br><a href="https://johanmabille.github.io/blog/2014/10/10/writing-c-plus-plus-wrappers-for-simd-intrinsics-2/">https://johanmabille.github.io/blog/2014/10/10/writing-c-plus-plus-wrappers-for-simd-intrinsics-2/</a><br><a href="https://johanmabille.github.io/blog/2014/10/10/writing-c-plus-plus-wrappers-for-simd-intrinsics-3/">https://johanmabille.github.io/blog/2014/10/10/writing-c-plus-plus-wrappers-for-simd-intrinsics-3/</a><br><a href="https://johanmabille.github.io/blog/2014/10/13/writing-c-plus-plus-wrappers-for-simd-intrinsics-4/">https://johanmabille.github.io/blog/2014/10/13/writing-c-plus-plus-wrappers-for-simd-intrinsics-4/</a><br><a href="https://johanmabille.github.io/blog/2014/10/25/writing-c-plus-plus-wrappers-for-simd-intrinsics-5/">https://johanmabille.github.io/blog/2014/10/25/writing-c-plus-plus-wrappers-for-simd-intrinsics-5/</a><br>Performance Considerations About SIMD Wrappers<br><a href="https://johanmabille.github.io/blog/2014/11/20/performance-considerations-about-simd-wrappers/">https://johanmabille.github.io/blog/2014/11/20/performance-considerations-about-simd-wrappers/</a><br>Aligned Memory Allocator<br><a href="https://johanmabille.github.io/blog/2014/12/06/aligned-memory-allocator/">https://johanmabille.github.io/blog/2014/12/06/aligned-memory-allocator/</a></p><p>Ubuntu SSE指令集 编程实例—复数乘法与共轭乘法</p><p><a href="https://blog.csdn.net/jxwxg/article/details/53091376">https://blog.csdn.net/jxwxg/article/details/53091376</a></p><p>AVX2整数向量运算</p><p><a href="https://blog.csdn.net/tigerisland45/article/details/54671536">https://blog.csdn.net/tigerisland45/article/details/54671536</a></p>]]></content>
<tags>
<tag>Base</tag>
</tags>
</entry>
<entry>
<title>Delaunay三角剖分</title>
<link href="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/"/>
<url>/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/</url>
<content type="html"><![CDATA[<h1 id="Delaunay-Triangle"><a href="#Delaunay-Triangle" class="headerlink" title="Delaunay Triangle"></a>Delaunay Triangle</h1><p>Delaunay三角剖分特点:</p><blockquote><p><strong>最小角最大</strong>:在不出现奇异性的情况下,Delaunay三角剖分最小角之和均大于任何非 Delaunay剖分所形成三角形最小角之和 ,三角形的最小内角之和最大 ,从而使得划分的三角形不会出现某个内角过小的情况 ,比较有利于有限元的后续计算。<br><strong>空外接圆</strong>:Delaunay三角剖分中任意三角形的外接圆内不包括其他结点。因此 ,在各种二维三角剖分中 ,只有 Delaunay三角剖分才同时满足全局和局部最优。</p></blockquote><p>Delaunay三角剖分优点:</p><blockquote><p>1.<strong>最接近</strong>:以最近的三点组成三角形,且三角形各边皆不相交。<br>2.<strong>唯一性</strong>:不论从何处区域开始构建,最终结果唯一。<br>3.<strong>最优解</strong>:任意两个相邻三角形形成的凸四边形的对角线若可以互换,那么两个三角形六个内角中最小的角度不会变大。<br>4.<strong>最规则</strong>:若将三角网中的每个三角形的最小角进行升序排列,则Delaunay三角网的排列得到的数值最大。<br>5.<strong>区域性</strong>:新增、删除、移动某一个顶点时只会影响临近的三角形。<br>6.具有<strong>凸多边形的外壳</strong>:三角网最外层的边界形成一个凸多边形的外壳。</p></blockquote><h1 id="Voronoi图"><a href="#Voronoi图" class="headerlink" title="Voronoi图"></a>Voronoi图</h1><p>离散点集P的Delaunay三角剖分和离散点集P的Voronoi图为<a href="https://en.wikipedia.org/wiki/Dual_graph">对偶图</a>的关系。</p><p>Delaunay三角形的外心是Voronoi图的顶点。 </p><blockquote><p>对偶图:设G是平面图,在图G的每个面中指定一个新节点,对两个面公共的边,指定一条新边与其<strong>相交</strong>。由这些新结点和新边组成的图为G的对偶图。</p></blockquote><p>图中的蓝色虚线的图是红色实线的对偶图:</p><p><img src="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/1280px-Duals_graphs.svg.png" alt="Duals_graphs"></p><p>Delaunay三角形和其外接圆与圆心:</p><p><img src="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/337px-Delaunay_circumcircles_centers.svg.png" alt="Delaunay_circumcircles_centers"></p><p>连接外接圆圆心生成Voronoi图:</p><p><img src="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/441px-Delaunay_Voronoi.svg.png" alt="Delaunay_Voronoi"></p><h1 id="Delaunay三角剖分算法"><a href="#Delaunay三角剖分算法" class="headerlink" title="Delaunay三角剖分算法"></a>Delaunay三角剖分算法</h1><p>Delaunay三角剖分常见算法:翻边算法、分割归并法、逐点插入算法、三角网增长法。本文从常见的逐点插入的Bowyer-Watson算法来理解。</p><p>在<a href="http://paulbourke.net/papers/triangulate/">《Triangulate》</a>里对该方法进行了分析,给出了伪代码:</p><figure class="highlight pgsql"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></div></td><td class="code"><pre><code class="hljs pgsql">subroutine triangulate<br><span class="hljs-keyword">input</span> : vertex list<br>output : triangle list<br> initialize the triangle list<br> determine the supertriangle<br> <span class="hljs-keyword">add</span> supertriangle vertices <span class="hljs-keyword">to</span> the <span class="hljs-keyword">end</span> <span class="hljs-keyword">of</span> the vertex list<br> <span class="hljs-keyword">add</span> the supertriangle <span class="hljs-keyword">to</span> the triangle list<br> <span class="hljs-keyword">for</span> <span class="hljs-keyword">each</span> sample <span class="hljs-type">point</span> <span class="hljs-keyword">in</span> the vertex list<br> initialize the edge buffer<br> <span class="hljs-keyword">for</span> <span class="hljs-keyword">each</span> triangle currently <span class="hljs-keyword">in</span> the triangle list<br> calculate the triangle circumcircle center <span class="hljs-keyword">and</span> radius<br> <span class="hljs-keyword">if</span> the <span class="hljs-type">point</span> lies <span class="hljs-keyword">in</span> the triangle circumcircle <span class="hljs-keyword">then</span><br> <span class="hljs-keyword">add</span> the three triangle edges <span class="hljs-keyword">to</span> the edge buffer<br> remove the triangle <span class="hljs-keyword">from</span> the triangle list<br> endif<br> endfor<br> <span class="hljs-keyword">delete</span> <span class="hljs-keyword">all</span> doubly specified edges <span class="hljs-keyword">from</span> the edge buffer<br> this leaves the edges <span class="hljs-keyword">of</span> the enclosing <span class="hljs-type">polygon</span> <span class="hljs-keyword">only</span><br> <span class="hljs-keyword">add</span> <span class="hljs-keyword">to</span> the triangle list <span class="hljs-keyword">all</span> triangles formed <span class="hljs-keyword">between</span> the <span class="hljs-type">point</span> <br> <span class="hljs-keyword">and</span> the edges <span class="hljs-keyword">of</span> the enclosing <span class="hljs-type">polygon</span><br> endfor<br> remove <span class="hljs-keyword">any</span> triangles <span class="hljs-keyword">from</span> the triangle list that use the supertriangle vertices<br> remove the supertriangle vertices <span class="hljs-keyword">from</span> the vertex list<br><span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure><p>因该算法效率不高,因此有网上给的优化后的伪代码:</p><figure class="highlight glsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs glsl">input: 顶点列表(<span class="hljs-keyword">vertices</span>) <span class="hljs-comment">//vertices为外部生成的随机或乱序顶点列表</span><br>output:已确定的三角形列表(<span class="hljs-keyword">triangles</span>)<br> 初始化顶点列表<br> 创建索引列表(indices = new Array(<span class="hljs-keyword">vertices</span>.<span class="hljs-built_in">length</span>)) <span class="hljs-comment">//indices数组中的值为0,1,2,3,......,vertices.length-1</span><br> 基于<span class="hljs-keyword">vertices</span>中的顶点x坐标对indices进行sort <span class="hljs-comment">//sort后的indices值顺序为顶点坐标x从小到大排序(也可对y坐标,本例中针对x坐标)</span><br> 确定超级三角形<br> 将超级三角形保存至未确定三角形列表(temp <span class="hljs-keyword">triangles</span>)<br> 将超级三角形push到<span class="hljs-keyword">triangles</span>列表<br> 遍历基于indices顺序的<span class="hljs-keyword">vertices</span>中每一个点 <span class="hljs-comment">//基于indices后,则顶点则是由x从小到大出现</span><br> 初始化边缓存数组(edge <span class="hljs-keyword">buffer</span>)<br> 遍历temp <span class="hljs-keyword">triangles</span>中的每一个三角形<br> 计算该三角形的圆心和半径<br> 如果该点在外接圆的右侧<br> 则该三角形为Delaunay三角形,保存到<span class="hljs-keyword">triangles</span><br> 并在temp里去除掉<br> 跳过<br> 如果该点在外接圆外(即也不是外接圆右侧)<br> 则该三角形为不确定 <span class="hljs-comment">//后面会在问题中讨论</span><br> 跳过<br> 如果该点在外接圆内<br> 则该三角形不为Delaunay三角形<br> 将三边保存至edge <span class="hljs-keyword">buffer</span><br> 在temp中去除掉该三角形<br> 对edge <span class="hljs-keyword">buffer</span>进行去重<br> 将edge <span class="hljs-keyword">buffer</span>中的边与当前的点进行组合成若干三角形并保存至temp <span class="hljs-keyword">triangles</span>中<br> 将<span class="hljs-keyword">triangles</span>与temp <span class="hljs-keyword">triangles</span>进行合并<br> 除去与超级三角形有关的三角形<br>end<br></code></pre></td></tr></table></figure><h1 id="Bowyer-Watson算法"><a href="#Bowyer-Watson算法" class="headerlink" title="Bowyer-Watson算法"></a>Bowyer-Watson算法</h1><p>基本思想:</p><blockquote><p>1.构造一个超级三角形,包含所有的散点,存入三角形链表<br>2.将点集中的离散点依次插入,在三角形链表中找出外接圆包含插入点的三角形(称为该点的影响三角形),删除影响三角形的<strong>公共边</strong>,将插入点同影响三角形的全部顶点连接起来,完成一个点在Delaunay三角形链表中的插入。<br>3.根据优化准则对局部新形成的三角形优化。将形成的三角形放入Delaunay三角形链表。<br>4.循环执行步骤2,直到所有离散点插入完毕。</p></blockquote><p>图示:(来自<a href="https://baike.baidu.com/item/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86%E7%AE%97%E6%B3%95">百度百科</a>)</p><p><img src="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/d009b3de9c82d1582789d194800a19d8bc3e4288.jpg" alt="d009b3de9c82d1582789d194800a19d8bc3e4288.jpg"></p><p>借助三个点理解一遍。</p><p><img src="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/Snipaste_2019-11-28_09-57-39.png" alt="Snipaste_2019-11-28_09-57-39.png"></p><p>根据离散点的最大分布得到一个随机超级三角形(即该三角形包含了整个离散点集P),并将超级三角形放入temp triangles中</p><p><img src="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/Snipaste_2019-11-28_10-01-38.png" alt="Snipaste_2019-11-28_10-01-38.png"></p><p>接下来对temp triangle中的三角形遍历画外接圆,这时先对左边第一个点a进行判断,其在圆内所以该三角形不为Delaunay三角形,将其三边保存至edge buffer中,temp triangles中删除该三角形</p><p><img src="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/Snipaste_2019-11-28_10-03-32.png" alt="Snipaste_2019-11-28_10-03-32.png"></p><p>将a点与edge buffer中的每一个边相连,组成三个三角形,存入temp triangles中</p><p><img src="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/Snipaste_2019-11-28_10-05-40.png" alt="Delaunay三角剖分/Snipaste_2019-11-28_10-05-40.png"></p><p>再将重复对temp triangles的遍历并画外接圆,这时使用的是b点来进行判断</p><ul><li>b点在三角形1外接圆右侧,则表示左侧三角形为Delaunay三角形,将该三角形保存至triangles中</li><li>b点在三角形2外接圆外侧,为不确定三角形,跳过(后面处理),但并不在temp triangles中删除</li><li>b点在三角形3外接圆内侧,则这时向清空后的edge buffer加入该三角形的三条边,并用b点与edge buffer中的三角边进行组合,组合成了三个三角形并加入到temp triangles中</li></ul><p>这时,temp buffer 中有六条边,triangles中有两个三角形,temp triangles中有1个三角形</p><p>对temp buffer中的六条边进行去重,得到五条边,将该点与这五条边组合成五个三角形并加入到temp triangles 中,这时temp triangles中有6个三角形</p><p><img src="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/Snipaste_2019-11-28_10-13-35.png" alt="Delaunay三角剖分/Snipaste_2019-11-28_10-13-35.png"></p><p>由于三个点已经遍历结束,到了不会再对第三个点形成的三角形做外接圆,这时则将triangles与temp triangles合并,合并后的数组表示包含已经确定的Delaunay三角形和剩下的三角形</p><p>这时除去合并后数组中的和超级三角形三个点有关的所有三角形,即进行数组坐标的限定,则得到了最后的结果:</p><p><img src="/2019/11/28/Delaunay%E4%B8%89%E8%A7%92%E5%89%96%E5%88%86/Snipaste_2019-11-28_10-18-12.png" alt="Snipaste_2019-11-28_10-18-12.png"></p><h1 id="Code"><a href="#Code" class="headerlink" title="Code"></a>Code</h1><p><a href="https://github.com/loopvoid/delaunay">Delaunay-Cpp</a></p><h1 id="References"><a href="#References" class="headerlink" title="References"></a>References</h1><p><a href="http://www.cs.cmu.edu/~quake/triangle.html">http://www.cs.cmu.edu/~quake/triangle.html</a><br><a href="https://people.sc.fsu.edu/~jburkardt/c_src/triangle/triangle.html">https://people.sc.fsu.edu/~jburkardt/c_src/triangle/triangle.html</a><br><a href="https://www.cnblogs.com/zhiyishou/p/4430017.html">https://www.cnblogs.com/zhiyishou/p/4430017.html</a><br><a href="https://en.wikipedia.org/wiki/Delaunay_triangulation">https://en.wikipedia.org/wiki/Delaunay_triangulation</a><br><a href="http://local.wasp.uwa.edu.au/~pbourke/papers/triangulate/index.html">http://local.wasp.uwa.edu.au/~pbourke/papers/triangulate/index.html</a><br><a href="http://local.wasp.uwa.edu.au/~pbourke/papers/triangulate/cpp.zip">http://local.wasp.uwa.edu.au/~pbourke/papers/triangulate/cpp.zip</a><br><a href="https://github.com/obviousjim/ofxDelaunay">https://github.com/obviousjim/ofxDelaunay</a><br><a href="https://github.com/delfrrr/delaunator-cpp/blob/master/include/delaunator.hpp">https://github.com/delfrrr/delaunator-cpp/blob/master/include/delaunator.hpp</a></p>]]></content>
<tags>
<tag>Algorithm</tag>
</tags>
</entry>
<entry>
<title>正则表达式</title>
<link href="/2019/11/17/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/"/>
<url>/2019/11/17/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/</url>
<content type="html"><![CDATA[<h1 id="推荐"><a href="#推荐" class="headerlink" title="推荐"></a>推荐</h1><ul><li>将JS正则可视化的工具:<a href="https://regexper.com/">regexper.com</a></li><li>在线练习:<a href="https://regex101.com/">regex101.com</a></li><li>正则教程:<a href="https://github.com/ziishaned/learn-regex/blob/master/translations/README-cn.md">Learn Regex the easy way</a></li></ul><h1 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h1><h2 id="1-元字符"><a href="#1-元字符" class="headerlink" title="1.元字符"></a>1.元字符</h2><table><thead><tr><th>元字符</th><th>说明</th></tr></thead><tbody><tr><td>.</td><td>匹配除换行符以外的任意字符</td></tr><tr><td>\w</td><td>匹配<strong>字母、数字、下划线、汉字</strong></td></tr><tr><td>\s</td><td>匹配任意的<strong>空白符</strong></td></tr><tr><td>\d</td><td>匹配<strong>数字</strong></td></tr><tr><td>\b</td><td>匹配单词的<strong>开始</strong>或<strong>结束</strong></td></tr><tr><td>[]</td><td>匹配方括号内的任意字符</td></tr><tr><td>[^]</td><td>匹配除了方括号里的任意字符集</td></tr><tr><td>(xyz)</td><td>匹配与xyz完全相等的字符串</td></tr><tr><td>^</td><td>匹配字符串的<strong>开始</strong></td></tr><tr><td>$</td><td>匹配字符串的<strong>结束</strong></td></tr></tbody></table><p>根据元字符,可以写一些简单的正则:</p><p>1.匹配所有ab开头的字符串</p><figure class="highlight cos"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><pre><code class="hljs cos">\bab 或者 <span class="hljs-symbol">^ab</span><br></code></pre></td></tr></table></figure><p>2.匹配9位数字的QQ号:</p><figure class="highlight moonscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs moonscript">^\d\d\d\d\d\d\d\d\d$<br></code></pre></td></tr></table></figure><p>3.匹配1开头的7位数字的电话号码</p><figure class="highlight moonscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs moonscript">^<span class="hljs-number">1</span>\d\d\d\d\d\d$<br></code></pre></td></tr></table></figure><p>4.匹配<code>The car parked in the garage.</code>中的<code>The</code>和<code>the</code>:</p><figure class="highlight prolog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs prolog">正则:<br>[<span class="hljs-symbol">Th</span>]he<br><br>结果:<br>[<span class="hljs-symbol">The</span>] car parked in [the] garage.<br></code></pre></td></tr></table></figure><p>5.匹配<code>The car parked in the garage.</code>中不是car的其他结果:</p><figure class="highlight prolog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs prolog">正则:<br>[^c]ar<br><br>结果:<br><span class="hljs-symbol">The</span> car [par]ked in the [gar]age.<br></code></pre></td></tr></table></figure><h2 id="2-重复限定符"><a href="#2-重复限定符" class="headerlink" title="2.重复限定符"></a>2.重复限定符</h2><p>用元字符能写不少正则表达式,但是向上面的一串<code>\d</code>还不够简洁。<br>为了处理重复的问题,正则提供了重复限定符号:</p><table><thead><tr><th>语法</th><th>说明</th></tr></thead><tbody><tr><td>*</td><td>重复<strong>0次</strong>或<strong>多次</strong></td></tr><tr><td>+</td><td>重复<strong>1次</strong>或<strong>多次</strong></td></tr><tr><td>?</td><td>重复<strong>0次</strong>或<strong>1次</strong></td></tr><tr><td>{n}</td><td>重复<strong>n次</strong></td></tr><tr><td>{n,}</td><td>重复<strong>n次</strong>或<strong>更多次</strong></td></tr><tr><td>{n,m}</td><td>重复<strong>n到m次</strong></td></tr></tbody></table><p>有了这些限定符之后,就能对元字符进行改造:</p><p>1.匹配9位数字的QQ号:</p><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs dust"><span class="language-xml">^\d</span><span class="hljs-template-variable">{8}</span><span class="language-xml">$</span><br></code></pre></td></tr></table></figure><p>2.匹配1开头的7位数字的电话号码:</p><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs dust"><span class="language-xml">^1\d</span><span class="hljs-template-variable">{6}</span><span class="language-xml">$</span><br></code></pre></td></tr></table></figure><p>3.匹配14~18位的银行卡号:</p><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs dust"><span class="language-xml">^\d</span><span class="hljs-template-variable">{14,18}</span><span class="language-xml">$</span><br></code></pre></td></tr></table></figure><p>4.匹配以a开头的,0个或多个b结尾的字符串</p><figure class="highlight cos"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cos"><span class="hljs-symbol">^ab</span>*$<br></code></pre></td></tr></table></figure><h2 id="3-分组"><a href="#3-分组" class="headerlink" title="3.分组"></a>3.分组</h2><p>从上面例4中看到,*限定符是作用在与他左边最近的一个字符,那么如果我想要ab同时被*限定那怎么办呢?</p><blockquote><p>正则表达式中用小括号()来做分组,也就是括号中的内容作为一个整体。</p></blockquote><p>因此当我们要匹配多个ab时,我们可以这样</p><p>如:匹配字符串中包含0到多个ab开头:</p><figure class="highlight gcode"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gcode">^<span class="hljs-comment">(ab)</span>*<br></code></pre></td></tr></table></figure><h2 id="4-转义"><a href="#4-转义" class="headerlink" title="4.转义"></a>4.转义</h2><p>正则提供了转义的方式,也就是要把这些元字符、限定符或者关键字转义成普通的字符,需要在要转义的字符前面加个斜杠,也就是\即可。如将分组所使用的括号转义为匹配对象:</p><figure class="highlight taggerscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs taggerscript">^(<span class="hljs-symbol">\(</span>ab<span class="hljs-symbol">\)</span>)*<br></code></pre></td></tr></table></figure><h2 id="5-条件或"><a href="#5-条件或" class="headerlink" title="5.条件或"></a>5.条件或</h2><p>国内手机号码它们都有属于自己的号段,比如联通有130/131/132/155/156/185/186/145/176等号段,假如匹配一个联通的号码就要用到一些并列的条件,也就是“或”。</p><blockquote><p>正则用符号 | 来表示或,也叫做分支条件,当满足正则里的分支条件的任何一种条件时,都会当成是匹配成功。</p></blockquote><p>那么我们就可以用或条件来处理这个问题:</p><figure class="highlight coq"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs coq">^(<span class="hljs-number">130</span>|<span class="hljs-type">131</span>|<span class="hljs-type">132</span>|<span class="hljs-type">155</span>|<span class="hljs-type">156</span>|<span class="hljs-type">185</span>|<span class="hljs-type">186</span>|<span class="hljs-type">145</span>|<span class="hljs-type">176</span>)\d{<span class="hljs-number">8</span>}$<br></code></pre></td></tr></table></figure><h2 id="6-区间"><a href="#6-区间" class="headerlink" title="6.区间"></a>6.区间</h2><blockquote><p>正则提供一个元字符中括号 [] 来表示区间条件。</p></blockquote><p>1.限定0到9 可以写成[0-9]</p><p>2.限定A-Z 写成[A-Z]</p><p>3.限定某些数字 [165]</p><p>那上面的正则我们还改成这样:</p><figure class="highlight coq"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs coq">^((<span class="hljs-number">13</span>[<span class="hljs-number">0</span><span class="hljs-number">-2</span>])|<span class="hljs-type">(15</span>[<span class="hljs-number">56</span>])|<span class="hljs-type">(18</span>[<span class="hljs-number">5</span><span class="hljs-number">-6</span>])|<span class="hljs-type">145</span>|<span class="hljs-type">176</span>)\d{<span class="hljs-number">8</span>}$<br></code></pre></td></tr></table></figure><h1 id="进阶"><a href="#进阶" class="headerlink" title="进阶"></a>进阶</h1><h2 id="1-零宽断言"><a href="#1-零宽断言" class="headerlink" title="1.零宽断言"></a>1.零宽断言</h2><p>1.<strong>断言</strong>:俗话的断言就是“我断定什么什么”,而正则中的断言,就是说正则可以指明在指定的内容的前面或后面会出现满足指定规则的内容,意思正则也可以像人类那样断定什么什么,比如”ss1aa2bb3”,正则可以用断言找出aa2前面有bb3,也可以找出aa2后面有ss1.<br>2.<strong>零宽</strong>:就是没有宽度,在正则中,断言只是匹配位置,不占字符,也就是说,匹配结果里是不会返回断言本身。</p><table><thead><tr><th>符号</th><th>描述</th><th>解释</th></tr></thead><tbody><tr><td>?=pattern</td><td>正先行断言-存在</td><td>匹配pattern<strong>前面</strong>的内容</td></tr><tr><td>?<=pattern</td><td>正后发断言-存在</td><td>匹配pattern<strong>后面</strong>的内容</td></tr><tr><td>?!pattern</td><td>负先行断言-排除</td><td>匹配<strong>非</strong>pattern<strong>前面</strong>的内容</td></tr><tr><td>?<!pattern</td><td>负后发断言-排除</td><td>匹配<strong>非</strong>pattern<strong>后面</strong>的内容</td></tr></tbody></table><blockquote><p>正先行断言-存在: “(T|t)he(?=\sfat)” => <strong>The</strong> fat cat sat on the mat.</p><p>正后发断言-存在: “(?<=(T|t)he\s)(fat|mat)” => The <strong>fat</strong> cat sat on the <strong>mat</strong>. </p><p>负先行断言-排除: “(T|t)he(?!\sfat)” => The fat cat sat on <strong>the</strong> mat.</p><p>负后发断言-排除: “(?<!(T|t)he\s)(cat)” => The cat sat on <strong>cat</strong>.</p></blockquote><p>例子:假设我们要用爬虫抓取csdn里的文章阅读量。通过查看源代码可以看到文章阅读量这个内容是这样的结构</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"read-count"</span>></span>阅读数:641<span class="hljs-tag"></<span class="hljs-name">span</span>></span><br></code></pre></td></tr></table></figure><p>其中也就‘641’这个是变量,也就是说不同文章不同的值,当我们拿到这个字符串时,需要获得这里边的‘641’有很多种办法,但如果正则应该怎么匹配呢?</p><p><strong>1).正向先行断言(正前瞻)</strong>:</p><ul><li>语法:**(?=pattern)**</li><li>作用:匹配pattern表达式<strong>前面</strong>的内容,<strong>不返回本身</strong></li></ul><p>要取到阅读量,在正则表达式中就意味着要能匹配到<code></span></code>前面的数字内容:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs xml">内容:<br><span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"read-count"</span>></span>阅读数:641<span class="hljs-tag"></<span class="hljs-name">span</span>></span><br><br>正则:<br>.+(?=<span class="hljs-tag"></<span class="hljs-name">span</span>></span>)<br><br>匹配结果:<br><span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"read-count"</span>></span>阅读数:641<br></code></pre></td></tr></table></figure><p>我们要的只是前面的数字呀,那也简单咯,匹配数字 \d,那可以改成:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs xml">内容:<br><span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"read-count"</span>></span>阅读数:641<span class="hljs-tag"></<span class="hljs-name">span</span>></span><br><br>正则:<br>\d+(?=<span class="hljs-tag"></<span class="hljs-name">span</span>></span>)<br><br>匹配结果:<br>641<br></code></pre></td></tr></table></figure><p><strong>2).正向后行断言(正后顾)</strong>:</p><ul><li>语法:**(?<=pattern)**</li><li>作用:匹配pattern表达式<strong>后面</strong>的内容,<strong>不返回本身</strong></li></ul><p><strong>先行</strong>是匹配<strong>前面</strong>的内容,那<strong>后行</strong>就是匹配<strong>后面</strong>的内容。<br>上面的栗子,也可以用后行断言来处理:</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs routeros">内容:<br><span <span class="hljs-attribute">class</span>=<span class="hljs-string">"read-count"</span>>阅读数:641</span><br><br>正则:<br>(?<=<span <span class="hljs-attribute">class</span>=<span class="hljs-string">"read-count"</span>>阅读数:)\d+<br><br>匹配结果:<br>641<br></code></pre></td></tr></table></figure><p>**3).负向先行断言(负前瞻)**:</p><ul><li>语法:**(?!pattern)**</li><li>作用:匹配<strong>非</strong>pattern表达式<strong>前面</strong>的内容,<strong>不返回本身</strong>,即匹配<strong>结果不包含pattern</strong>表达式</li></ul><p>例如对<code>regex represents regular expression</code>,要想匹配除<strong>re</strong>gex和<strong>re</strong>gular之外的re:</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs stylus">内容:<br>regex represents regular expression<br><br>正则:<br><span class="hljs-function"><span class="hljs-title">re</span><span class="hljs-params">(?!g)</span></span><br><br>匹配结果:<br>regex <span class="hljs-selector-attr">[re]</span><span class="hljs-selector-tag">p</span><span class="hljs-selector-attr">[re]</span>sents regular exp<span class="hljs-selector-attr">[re]</span>ssion<br></code></pre></td></tr></table></figure><h2 id="2-捕获和非捕获"><a href="#2-捕获和非捕获" class="headerlink" title="2.捕获和非捕获"></a>2.捕获和非捕获</h2><p>单纯说到捕获,他的意思是匹配表达式,但捕获通常和分组联系在一起,也就是“捕获组”</p><blockquote><p>捕获组:匹配子表达式的内容,把匹配结果保存到内存中中数字编号或显示命名的组里,以深度优先进行编号,之后可以通过序号或名称来使用这些匹配结果。</p></blockquote><p>而根据命名方式的不同,又可以分为两种组:</p><p><strong>1).数字编号捕获组:</strong></p><ul><li>语法:**(exp)**</li><li>解释:从表达式左侧开始,每出现一个左括号和它对应的右括号之间的内容为一个分组,在分组中,第0组为整个表达式,第一组开始为分组。</li></ul><p>比如固定电话的:<strong>020-85653333</strong><br>他的正则表达式为:**(0\d{2})-(\d{8})**<br>按照左括号的顺序,这个表达式有如下分组:</p><table><thead><tr><th>序号</th><th>编号</th><th>分组</th><th>内容</th></tr></thead><tbody><tr><td>0</td><td>0</td><td>(0\d{2})-(\d{8})</td><td>020-85653333</td></tr><tr><td>1</td><td>1</td><td>(0\d{2})</td><td>020</td></tr><tr><td>2</td><td>2</td><td>(\d{8})</td><td>85653333</td></tr></tbody></table><p><strong>2).命名编号捕获组:</strong></p><ul><li>语法:**(?<name>exp)**</li><li>解释:分组的命名由表达式中的name指定</li></ul><p>比如区号也可以这样写:<strong>(?<quhao>\0\d{2})-(?<haoma>\d{8})</strong></p><p>按照左括号的顺序,这个表达式有如下分组:</p><table><thead><tr><th>序号</th><th>编号</th><th>分组</th><th>内容</th></tr></thead><tbody><tr><td>0</td><td>0</td><td>(0\d{2})-(\d{8})</td><td>020-85653333</td></tr><tr><td>1</td><td>quhao</td><td>(0\d{2})</td><td>020</td></tr><tr><td>2</td><td>haoma</td><td>(\d{8})</td><td>85653333</td></tr></tbody></table><p><strong>3).非捕获组</strong>:</p><ul><li>语法:**(?:exp)**</li><li>解释:和捕获组刚好相反,它用来标识那些不需要捕获的分组,通俗讲,就是可以根据需要去保存你的分组。</li></ul><p>比如上面的正则表达式,程序不需要用到第一个分组,那就可以这样写:</p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs scss">(?:\<span class="hljs-number">0</span>\d{<span class="hljs-number">2</span>})<span class="hljs-built_in">-</span>(\d{<span class="hljs-number">8</span>})<br></code></pre></td></tr></table></figure><table><thead><tr><th>序号</th><th>编号</th><th>分组</th><th>内容</th></tr></thead><tbody><tr><td>0</td><td>0</td><td>(0\d{2})-(\d{8})</td><td>020-85653333</td></tr><tr><td>1</td><td>1</td><td>(\d{8})</td><td>85653333</td></tr></tbody></table><h2 id="3-反向引用"><a href="#3-反向引用" class="headerlink" title="3.反向引用"></a>3.反向引用</h2><p><strong>捕获会返回一个捕获组,这个分组是保存在内存中,不仅可以在正则表达式外部通过程序进行引用,也可以在正则表达式内部进行引用,这种引用方式就是反向引用</strong></p><p>根据捕获组的命名规则,反向引用可分为:</p><p>1).数字编号组反向引用:<code>\k</code>或<code>\number</code></p><p>2).命名编号组反向引用:<code>\k</code>或<code>\name</code></p><p>例:</p><p>比如要查找一串字母<code>aabbbbgbddesddfiid</code>里成对的字母</p><p>思路:</p><ul><li>a. 匹配到一个字母</li><li>b. 匹配下一个字母,检查是否和上一个字母一样</li><li>c. 若一样,匹配成功,否则失败</li></ul><p>这里的思路2中匹配下一个字母时,需要用到上一个字母,那怎么记住上一个字母呢?这下捕获就有用处啦,我们可以利用捕获把上一个匹配成功的内容用来作为本次匹配的条件.</p><p>首先匹配一个字母:<code>\w</code></p><p>需要将匹配的第一个字母做成分组才能捕获,因此:<code>(\w)</code></p><p>然后用这个捕获组作为条件,则:<code>(\w)\1</code></p><p><strong>在默认情况下都是以数字来命名,而且数字命名的顺序是从1开始的,\1表示第一个捕获组,也就是(\w)</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs bash">内容:<br>aabbbbgbddesddfiid<br><br>正则:<br>\(w)\1<br><br>结果:<br>aa<br>bb<br>bb<br><span class="hljs-built_in">dd</span><br><span class="hljs-built_in">dd</span><br>ii<br></code></pre></td></tr></table></figure><h2 id="4-替换"><a href="#4-替换" class="headerlink" title="4.替换"></a>4.替换</h2><p>有了引用那么我们可以根据捕获组进行直接对应内容的替换。</p><p>例如想要将</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs stylus"><span class="hljs-number">1</span><span class="hljs-selector-class">.Anne</span><br><span class="hljs-number">2</span>.John<br></code></pre></td></tr></table></figure><p>每行的编号替换成带引号的编号,即<code>"1","2"</code>这样替换就很方便了:</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs stylus">内容:<br><span class="hljs-number">1</span><span class="hljs-selector-class">.Anne</span><br><span class="hljs-number">2</span><span class="hljs-selector-class">.John</span><br><br>正则:<br> - 查找: ^\d<br> - 替换: <span class="hljs-string">"$1"</span><br><br>结果:<br><span class="hljs-string">"1"</span><span class="hljs-selector-class">.Anne</span><br><span class="hljs-string">"2"</span>.John<br></code></pre></td></tr></table></figure><h2 id="5-贪婪和非贪婪"><a href="#5-贪婪和非贪婪" class="headerlink" title="5.贪婪和非贪婪"></a>5.贪婪和非贪婪</h2><p><strong>1).贪婪</strong></p><ul><li>贪婪匹配:当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符,这匹配方式叫做贪婪匹配。</li><li>特性:一次性读入整个字符串进行匹配,每当不匹配就舍弃最右边一个字符,继续匹配,依次匹配和舍弃(这种匹配-舍弃的方式也叫做回溯),直到匹配成功或者把整个字符串舍弃完为止,因此它是一种最大化的数据返回,能多不会少。</li></ul><p>前面的重复限定符就是贪婪量词,比如表达式:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">\d{<span class="hljs-number">3</span>,<span class="hljs-number">6</span>}<br></code></pre></td></tr></table></figure><p>用来匹配3到6位数字,在这种情况下,它是一种贪婪模式的匹配,也就是假如字符串里有6个个数字可以匹配,那它就是全部匹配到。</p><figure class="highlight dns"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs dns">内容:<br><span class="hljs-number">61762828 176</span> <span class="hljs-number">2991 871</span><br><br>正则:<br>\d{<span class="hljs-number">3</span>,<span class="hljs-number">6</span>}<br><br>结果:<br><span class="hljs-number">617628</span><br><span class="hljs-number">176</span><br><span class="hljs-number">2991</span><br><span class="hljs-number">871</span><br></code></pre></td></tr></table></figure><p>由结果可见:本来字符串中的“61762828”这一段,其实只需要出现3个(617)就已经匹配成功了的,但是他并不满足,而是匹配到了最大能匹配的字符,也就是6个。</p><p>多个贪婪量词凑在一起,那他们是如何支配自己的匹配权的呢?</p><blockquote><p>多个贪婪在一起时,如果字符串能满足他们各自最大程度的匹配时,就互不干扰,但如果不能满足时,会根据深度优先原则,也就是从左到右的每一个贪婪量词,优先最大数量的满足,剩余再分配下一个量词匹配。</p></blockquote><figure class="highlight dns"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs dns">内容:<br><span class="hljs-number">61762828 176</span> <span class="hljs-number">2991 87321</span><br><br>正则:<br>(\d{<span class="hljs-number">1</span>,<span class="hljs-number">2</span>})(\d{<span class="hljs-number">3</span>,<span class="hljs-number">4</span>})<br><br>结果:<br><span class="hljs-number">617628</span><br><span class="hljs-number">2991</span><br><span class="hljs-number">87321</span><br></code></pre></td></tr></table></figure><p>“617628” 是前面的\d{1,2}匹配出了61,后面\d{3,4}匹配出了7628<br>“2991”是前面的\d{1,2}匹配出了29,后面\d{3,4}匹配出了91<br>“97321”是前面的\d{1,2}匹配出了97,后面\d{3,4}匹配出了321</p><p><strong>2).懒惰(非贪婪)</strong></p><ul><li>懒惰匹配:当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能少的字符,这匹配方式叫做懒惰匹配。</li><li>特性:从左到右,从字符串的最左边开始匹配,每次试图不读入字符匹配,匹配成功,则完成匹配,否则读入一个字符再匹配,依此循环(读入字符、匹配)直到匹配成功或者把字符串的字符匹配完为止。</li><li>懒惰量词是在贪婪量词后面加个<code>?</code></li></ul><table><thead><tr><th>代码</th><th>说明</th></tr></thead><tbody><tr><td>*?</td><td>重复任意次数,但尽可能少重复</td></tr><tr><td>+?</td><td>重复1次或更多次,但是尽可能少重复</td></tr><tr><td>??</td><td>重复0次或1次,但是尽可能少重复</td></tr><tr><td>{n,m}?</td><td>重复n到m次,但是尽可能少重复</td></tr><tr><td>{n,}?</td><td>重复n次以上,但是尽可能少重复</td></tr></tbody></table><figure class="highlight dns"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs dns">内容:<br><span class="hljs-number">61762828 176</span> <span class="hljs-number">2991 87321</span><br><br>正则:<br>(\d{<span class="hljs-number">1</span>,<span class="hljs-number">2</span>}?)(\d{<span class="hljs-number">3</span>,<span class="hljs-number">4</span>})<br><br>结果:<br><span class="hljs-number">61762</span><br><span class="hljs-number">2991</span><br><span class="hljs-number">87321</span><br></code></pre></td></tr></table></figure><p>“61762” 是前面的懒惰匹配\d{1,2}?匹配出了6,后面贪婪匹配\d{3,4}匹配出了1762<br>“2991”是前面的懒惰匹配\d{1,2}?匹配出了2,后面贪婪匹配\d{3,4}匹配出了991<br>“87321”是前面的\d{1,2}匹配出了8,后面\d{3,4}匹配出了7321</p><h2 id="6-反义"><a href="#6-反义" class="headerlink" title="6.反义"></a>6.反义</h2><p>若不想匹配某些内容,就可以使用反义元字符,反义元字符很大一部分就是元字符大写:</p><table><thead><tr><th>元字符</th><th>解释</th></tr></thead><tbody><tr><td>\W</td><td>匹配任意<strong>不是</strong>字母、数字、下划线、汉字的字符</td></tr><tr><td>\S</td><td>匹配任意<strong>不是</strong>空白符的字符</td></tr><tr><td>\D</td><td>匹配任意<strong>非</strong>数字的字符</td></tr><tr><td>\B</td><td>匹配不是单词开头或结束的位置</td></tr><tr><td>[^x]</td><td>匹配除了x<strong>以外</strong>的任意字符</td></tr><tr><td>[^aeiou]</td><td>匹配除了aeiou这几个字母以外的任意字符</td></tr></tbody></table>]]></content>
<tags>
<tag>regex</tag>
<tag>base</tag>
</tags>
</entry>
<entry>
<title>移动最小二乘的曲线曲面拟合</title>
<link href="/2019/10/21/%E7%A7%BB%E5%8A%A8%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98%E7%9A%84%E6%9B%B2%E7%BA%BF%E6%9B%B2%E9%9D%A2%E6%8B%9F%E5%90%88/"/>
<url>/2019/10/21/%E7%A7%BB%E5%8A%A8%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98%E7%9A%84%E6%9B%B2%E7%BA%BF%E6%9B%B2%E9%9D%A2%E6%8B%9F%E5%90%88/</url>
<content type="html"><![CDATA[<h1 id="LS-Approximation-Least-squares"><a href="#LS-Approximation-Least-squares" class="headerlink" title="LS Approximation(Least-squares)"></a>LS Approximation(Least-squares)</h1><p><strong>Question:</strong> 给定N个点$x_i\in\mathbb{R}^d,where\ i\in[1…N]$.希望得到$f(x)$能近似出点$x_i$处的$f_i$值。<br>误差函数为$J_{LS}=\sum_i||f(X_i)-f_i||^2$。那么得到最小化问题:<br>$$<br>min_{f_x\in\prod_m^d}\sum_i||f(\mathbb{x}_i)-f_i||^2<br>$$<br>$f$ 取自$\prod_m^d$,d维空间上为total degree为m的多项式空间,则可以写作:<br>$$<br>f(x)=b(x)^Tc=b(x)\cdot c<br>$$<br>其中:$\mathbb{b}(x)=[b_1(x),…,b_k(x)]^T$是多项式的基向量,$\mathbb{c}=[c_1,…,c_k]^T$是需要最小化求解的未知系数向量。</p><p>多项式的基:</p><ul><li>$(m=2,d=2),\mathbb{b(x)}=[1,x,y,x^2,xy,y^2]^T$</li><li>$\mathbb{R^3}(m=1,d=3)$的线性拟合,$\mathbb{b(x)}=[1,x,y,z]^T$</li><li>任意维度常数拟合,$\mathbb{b(x)}=[1]$</li></ul><p><strong>Solution:</strong> 误差函数$J_{LS}$的偏导为0.$\bigtriangledown J_{LS}=0,\bigtriangledown=[\partial/\partial_{c_1},…,\partial/\partial_{c_k}]$ </p><p>因此,可以得到一个线性方程组(<strong>LSE</strong>):<br>$$<br>\begin{align}<br>\partial J_{LS}/\partial c_1=0 &: \sum_i2b_1(\mathbb{x_i})[\mathbb{b(x_i)}^T\mathbb{c}-f_i]=0\\<br>\partial J_{LS}/\partial c_2=0 &: \sum_i2b_2(\mathbb{x_i})[\mathbb{b(x_i)}^T\mathbb{c}-f_i]=0\\<br>&\ \vdots\\<br>\partial J_{LS}/\partial c_k=0 &: \sum_i2b_k(\mathbb{x_i})[\mathbb{b(x_i)}^T\mathbb{c}-f_i]=0\\<br>\end{align}<br>$$<br>可以向量表示为:<br>$$<br>\begin{aligned}<br>& 2\sum_i\mathbb{[b(x_i)b(x_i)}^T\mathbb{c}-\mathbb{b(x_i)}f_i]=0 \\<br>& \Rightarrow \\<br>&c=[\sum_ib(x_i)b(x_i)^T]^{-1}\sum_ib(x_i)f_i<br>\end{aligned}<br>$$<br><strong>Example.</strong></p><p>在$\mathbb{R}^2$拟合一个二元二次多项式,i.e. $d=2,m=2$,那么有$\mathbb{b(x)}=[1,x,y,x^2,xy,y^2]^T$,那么线性方程组为:<br>$$<br>\begin{bmatrix}<br>1 & x_i & y_i & x_i^2 & x_iy_i & y_i^2 \\<br>x_i & x_i^2 & x_iy_i & x_i^3 & x_i^2y_i & x_iy_i^2 \\<br>y_i & x_iy_i & y_i^2 & x_i^2y_i & x_iy_i^2 & y_i^3 \\<br>x_i^2 & x_i^3 & x_i^2y_i & x_i^4 & x_i^3y_i & x_i^2y_i^2 \\<br>x_iy_i & x_i^2y_i & x_iy_i^2 & x_i^3y_i & x_i^2y_i^2 & x_iy_i^3 \\<br>y_i^2 & x_iy_i^2 & y_i^3 & x_i^2y_i^2 & x_iy_i^3 & y_i^4<br>\end{bmatrix}<br>\begin{bmatrix}<br>c_1\\c_2\\c_3\\c_4\\c_5\\c_6<br>\end{bmatrix}=<br>\sum_i<br>\begin{bmatrix}<br>1\\x_i\\y_i\\x_i^2\\x_iy_i\\y_i^2<br>\end{bmatrix}<br>f_i<br>$$<br>考虑一组二维平面上的点$P_i={(1,1),(1,-1),(-1,1),(-1,-1),(0,0),(1,0),(-1,0),(0,1),(0,-1)}$有两组对应的函数值$f_i^1={1.0, -0.5, 1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 0.0}$和$f_i^2={1.0, -1.0, 0.0, 0.0, 1.0, 0.0, -1.0, -1.0, 1.0}$。</p><p>图1为该数据的拟合:</p><p><img src="/2019/10/21/%E7%A7%BB%E5%8A%A8%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98%E7%9A%84%E6%9B%B2%E7%BA%BF%E6%9B%B2%E9%9D%A2%E6%8B%9F%E5%90%88/fig1.png" alt="fig1"></p><p>图1. 二元二次多相似局部拟合:第一行为2组9个数据点,第二行是LS拟合函数。对应的参数向量$[c_1,…,c_6]^T$分别左为$[.0.834,.0.25,0.75,0.25,0.375,0.75]^T$,右为$[0.334,0.167,0.0,.0.5,0.5,0.0]^T$.</p><hr><h1 id="WLS-Approximation-Weighted-Least-Squates"><a href="#WLS-Approximation-Weighted-Least-Squates" class="headerlink" title="WLS Approximation(Weighted Least Squates)"></a>WLS Approximation(Weighted Least Squates)</h1><p>在<strong>WLS</strong>方法中使用误差函数$J_{WLS}=\sum_i\theta(||\mathbb{\bar x-x_i}||)||f(\mathbb{x_i})-f_i||^2$,$\bar x \in \mathbb{R}^d$为固定点。因此需要最小化:<br>$$<br>min_{f_x\in\prod_m^d}\sum_i\theta(||\mathbb{\bar x-x_i}||)||f(\mathbb{x}_i)-f_i||^2\tag 1<br>$$<br>其中:权重$\theta(d)$由数据点$\mathbb{x_i}$到$\mathbb{\bar x}$之间的欧氏距离表示。</p><p>未知系数也与上述权重有关,则:</p><p>$$<br>\begin{aligned}<br>f_{\mathbb{\bar x}}(\mathbb{x})&=\mathbb{b(x)^Tc(\bar x)}=\mathbb{b(x)\cdot c(\bar x)},||x-\bar x||<h \\<br>b(x)&=[b_1(x),…,b_k(x)]^T \\<br>Gaussian:\theta (d)&=e^{-\frac{d^2}{h^2}} \\<br>Wendland:\theta (d)&=(1-\frac{d}{h})^4(4\frac{d}{h}+1) \\<br>c(\bar x)&=[\sum_i\theta (d_i)b(x_i)b(x_i)^T]^{-1}\sum_i\theta (d_i)b(x_i)f_i<br>\end{aligned}<br>$$</p><h1 id="MLS-Approximation-Moving-least-squares"><a href="#MLS-Approximation-Moving-least-squares" class="headerlink" title="MLS Approximation(Moving least squares)"></a>MLS Approximation(Moving least squares)</h1><p>与<strong>WLS</strong>很类似,不同点:</p><blockquote><p>n <strong>move</strong> this point ($\bar x$) over the entire parameter domain, where a weighted least squares fit is computed and evaluated for each point individually.</p></blockquote><p>因此:<br>$$<br>\begin{aligned}<br>f(x)&=f_x(x),min_{f_x\in\prod_m^d}\sum_i\theta(||x-x_i||)||f_x(x_i)-f_i||^2 \\<br>f_{\bar x}(x)&=b(x)\cdot c(\bar x) \\<br>c(\bar x)&=[\sum_i\theta (d_i)b(x_i)b(x_i)^T]^{-1}\sum_i\theta (d_i)b(x_i)f_i<br>\end{aligned}<br>$$</p><hr><h2 id="拟合函数的建立"><a href="#拟合函数的建立" class="headerlink" title="拟合函数的建立"></a>拟合函数的建立</h2><p>在拟合区域的局部子域上,拟合函数<br>$$<br>f(x)=\sum_{i=1}^m\alpha_{i}(x)p_i(x)=p^T(x)\alpha(x)\tag{1}<br>$$<br>其中:</p><p>$\alpha(x)=[\alpha_1(x),\alpha_2(x),…,\alpha_m(x)]^T$为待求系数,它是坐标x的函数</p><p>$p(x)=[p_1(x),p_2(x),…,p_m(x)]^T$称为基函数,它是一个$k$阶完备的多项式</p><p>$m$是基函数的项数</p><p>对于二维问题:</p><p>线性基:$p(x)=[1,x,y]^T,m=3$</p><p>二次基:$p(x)=[1,x,y,x^2,xy,y^2]^T,m=6$</p><p>考虑加权离散 $L_2$ 范式(向量 $\mathbf{x}=[x_1,x_2,…,x_n]$ 的 $L_2$ 范式 $L_2=(\sum_{i=1}^nx_i^2)^{1/2}$<br>$$<br>\begin{aligned}<br>J&=\sum_{i=1}^nw(x-x_i)[f(x)-y_i]^2 \\<br>&=\sum_{i=1}^nw(x-x_i)[p^T(x_i)\alpha(x)-y_i]^2<br>\end{aligned}\tag{3}<br>$$<br>其中:</p><p>$n$是影响区域内节点的数目</p><p>$f(x)$是拟合函数</p><p>$y_i$是$x=x_i$处的节点值,$y_i=y(x_i)$</p><p>$w(x-x_i)$是节点$x_i$的权函数</p><p>为确定系数$\alpha(x)$,$J$应取极小值。对$\alpha$求导:<br>$$<br>\frac{\partial J}{\partial \alpha}= A(x)\alpha(x)-B(x)y=0\tag{4}<br>$$<br>$$<br>\alpha(x)= A^{-1}(x)B(x)y\tag{5}<br>$$<br>其中:<br>$$<br>A(x)= \sum_{i=1}^nw(x-x_i)p(x_i)p^T(x_i)\tag{6} \\<br>$$</p><p>或者(线性二维曲线):<br>$$<br>\begin{aligned}<br>A(x)&= \sum_{i=1}^np^T(x_i)w(x-x_i)p(x_i) \\<br>&= \begin{bmatrix}<br>\sum_{i=1}^nw(x-x_i) & \sum_{i=1}^nx_iw(x-x_i) \\<br>\sum_{i=1}^nx_iw(x-x_i) & \sum_{i=1}^nx_i^2w(x-x_i)<br>\end{bmatrix}<br>\end{aligned}<br>$$<br>$$<br>B(x)= [w(x-x_1)p(x_1),w(x-x_2)p(x_2),…,w(x-x_n)p(x_n)]\tag{7}<br>$$<br>$$<br>y^T= [y_1,y_2,…,y_n]\tag{8}<br>$$</p><p>把式 <strong>(5)</strong> 代入式 <strong>(1)</strong> ,就可以得到 <strong>MLS</strong> 拟合函数:<br>$$<br>f(x)=\sum_{i=1}^n\Phi_i^k(x)y_i=O^k(x)y\tag{9}<br>$$<br>其中,$O^k(x)$叫 <strong>形函数</strong> ,$k$表示基函数的 <strong>阶数</strong> :<br>$$<br>O^k(x)=[\Phi_1^k,\Phi_2^k,…,\Phi_n^k]=p^T(x)A^{-1}(x)B(x)\tag{10}<br>$$<br>如果$k=0$,则基函数$p(x)={1}$,这时的形函数为Shepard函数:<br>$$<br>O_i^{Shepard}(x)=\frac{w(x-x_i)}{\sum_{j=1}^nw(x=x_j)}\tag{11}<br>$$</p><p>即使基函数$p(x)$为多项式,式 <strong>(9)</strong> 中的$f(x)$也不再是多项式。</p><p>若基函数$p\in C^r$,权函数$w\in C^s$,则拟合函数$f\in C^{min(r,s)}$。</p><h2 id="权函数"><a href="#权函数" class="headerlink" title="权函数"></a>权函数</h2><p>移动最小二乘中的权函数$w(x-x_i)$,其在$x$的一个子领域内不等于0,在这个子域外全为0,这个子域称为权函数的支持域(即$x$的影响区域)。</p><p>一般选择圆形作为权函数的支持域,其半径记为$s_{max}$</p><p>只有包含在支持域内的数据对$x$的取值有影响</p><p>权函数$w(x-x_i)$应该是<strong>非负</strong>的,并且随着$||x-x_i||_2$的增加单调递减。</p><p>权函数还应该具有一定的光滑性,因为拟合函数会继承权函数的连续性。</p><p>连续性:若权函数$w(x-x_i)$是$C^1$阶连续的,则拟合函数也是$C^1$阶连续的。</p><p>常用的权函数是<strong>样条函数</strong>,记$s=x-x_i,\bar s=\frac{s}{s_{max}}$则三次样条函数如式 <strong>(12)</strong> 所示:<br>$$<br>\begin{aligned}<br>w (\bar s) &= \frac{2}{3}-4\bar s^2 + 4\bar s^3 & (\bar s\le\frac{1}{2}) \\<br>w (\bar s) &= \frac{4}{3}-4\bar s+4\bar s^2-\frac{4}{3}\bar s^3 & (\frac{1}{2}<\bar s \le 1) \\<br>w (\bar s) &= 0 & (\bar s>1)<br>\end{aligned}<br>\tag{12}<br>$$</p><p>支持域应该包含足够多的节点,使得式 <strong>(6)</strong> 中$A(x)$可逆。</p><p>如果式 <strong>(1)</strong> 使用的是线性基函数,则 <strong>曲线拟合</strong> 的支持域内应该至少包含不重叠的 <strong>2个节点</strong>,<strong>曲面拟合</strong> 的支持域内应该至少包含不在同一条直线上的 <strong>3个节点</strong></p><h2 id="MLS拟合流程"><a href="#MLS拟合流程" class="headerlink" title="MLS拟合流程"></a>MLS拟合流程</h2><p>基本思想:先讲拟合区域网格化,然后用公式 <strong>(9)</strong> 求出网格上节点值,最后连接网格节点形成拟合曲线(曲面)。</p><ul><li>将拟合区域网格化;</li><li>对每个网格点x进行循环:<ul><li>确定网格点x的支持域(半径)的大小;</li><li>确定包含在x的影响区域内的节点;</li><li>计算行函数$O^k(x)$;</li><li>计算网格点x处的函数值</li></ul></li><li>结束网格点循环;</li><li>连接网格点形成拟合曲线(曲面)。</li></ul><h2 id="误差分析"><a href="#误差分析" class="headerlink" title="误差分析"></a>误差分析</h2><p>为分析<strong>曲面拟合</strong>误差,定义误差项:<br>$$<br>error=\int_U[f^{EXACT}(x,y)-f^{MLS}(x,y)]^2dU\tag{14}<br>$$<br>其中,$f^{EXACT}(x,y)$是精确值,$f^{MLS}(x,y)$是移动最小二乘的计算值。</p><hr><h1 id="Applications"><a href="#Applications" class="headerlink" title="Applications"></a>Applications</h1><p><img src="/2019/10/21/%E7%A7%BB%E5%8A%A8%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98%E7%9A%84%E6%9B%B2%E7%BA%BF%E6%9B%B2%E9%9D%A2%E6%8B%9F%E5%90%88/fit.png" alt="fit.png"></p><blockquote><p>The MLS surface of a point-set with varying density (the<br>density is reduced along the vertical axis from top to bottom). The<br>surface is obtained by applying the projection operation described<br>by Alexa et. al. [2003]. Image courtesy of Marc Alexa.</p></blockquote><hr><h1 id="Coding"><a href="#Coding" class="headerlink" title="Coding"></a>Coding</h1><ul><li><p><a href="https://github.com/loopvoid/mls">Matlab原理版本</a></p></li><li><p><a href="https://github.com/loopvoid/mls">C++版本</a></p></li></ul><hr><h1 id="References"><a href="#References" class="headerlink" title="References"></a>References</h1><p><a href="http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=2ahUKEwiHrcX4z7zlAhXDAYgKHeS_Cj8QFjAAegQIBBAC&url=http://www.nealen.de/projects/mls/asapmls.pdf&usg=AOvVaw0mQQFav7cDFU1l9-OYOf7y">asapmls</a><br><a href="https://blog.csdn.net/baidu_38127162/article/details/82380914">https://blog.csdn.net/baidu_38127162/article/details/82380914</a><br><a href="https://blog.csdn.net/liumangmao1314/article/details/54179526">https://blog.csdn.net/liumangmao1314/article/details/54179526</a><br><a href="https://blog.csdn.net/hjimce/article/details/46550001">https://blog.csdn.net/hjimce/article/details/46550001</a><br><a href="https://wenku.baidu.com/view/fe7a74976f1aff00bed51eb1.html">https://wenku.baidu.com/view/fe7a74976f1aff00bed51eb1.html</a><br><a href="https://blog.csdn.net/liumangmao1314/article/details/89421806">https://blog.csdn.net/liumangmao1314/article/details/89421806</a><br><a href="https://en.wikipedia.org/wiki/Moving_least_squares">https://en.wikipedia.org/wiki/Moving_least_squares</a><br><a href="http://vision.gel.ulaval.ca/~jflalonde/cours/4105/h14/tps/results/project/jingweicao/index.html">Final Project: Image Deformation Using Moving Least Squares</a></p>]]></content>
</entry>
<entry>
<title>MTCNN算法与代码理解</title>
<link href="/2019/10/14/MTCNN%E7%AE%97%E6%B3%95%E4%B8%8E%E4%BB%A3%E7%A0%81%E7%90%86%E8%A7%A3/"/>
<url>/2019/10/14/MTCNN%E7%AE%97%E6%B3%95%E4%B8%8E%E4%BB%A3%E7%A0%81%E7%90%86%E8%A7%A3/</url>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p><strong>原作主页</strong>:<a href="https://kpzhang93.github.io/MTCNN_face_detection_alignment/index.html">https://kpzhang93.github.io/MTCNN_face_detection_alignment/index.html</a></p><p><strong>arxiv论文</strong>:<a href="https://arxiv.org/abs/1604.02878">https://arxiv.org/abs/1604.02878</a></p><p><strong>代码</strong>:<a href="https://github.com/kpzhang93/MTCNN_face_detection_alignment">官方matlab版</a>、<a href="https://github.com/kpzhang93/MTCNN_face_detection_alignment">C++ caffe版</a> </p><p><strong>其他框架代码</strong>:<a href="https://github.com/TropComplique/mtcnn-pytorch">pytorch</a>、<a href="https://github.com/AITTSMD/MTCNN-Tensorflow">tensorflow</a>、<a href="https://github.com/Seanlinx/mtcnn">mxnet</a></p><p>MTCNN 《<a href="https://kpzhang93.github.io/MTCNN_face_detection_alignment/paper/spl.pdf">Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Networks</a>》,采用级联CNN结构,通过多任务学习,同时完成–人脸检测和人脸对齐,输出人脸的BoundingBox和人脸5个关键点(双眼、鼻子、双嘴角)的位置。</p><p>MTCNN在提出时在<a href="http://vis-www.cs.umass.edu/fddb/">FDDB</a>、<a href="http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/">WIDER FACE</a>和AFLW数据集上取得了当时(2016年4月)的<strong>SOTA</strong>速度又快,现在仍被广泛使用作为人脸识别的前端,如InsightFace和facenet。</p><p>论文提到,MTCNN的效果主要来自3个主要原因:</p><blockquote><p>1.精心设计的级联CNN结构(carefully designed cascaded CNNs architecture)</p><p>2.在线难样本挖掘策略(online hard sample mining strategy)</p><p>3.人脸检测与对其联合学习(joint face alignment learning)</p></blockquote><p> <strong>Tips:</strong>个人建议在看这篇文章的时候,对照着<a href="https://github.com/TropComplique/mtcnn-pytorch">pytorch</a>的实现代码理解,尤其是里面的 <a href="https://github.com/TropComplique/mtcnn-pytorch/blob/master/try_mtcnn_step_by_step.ipynb">try_mtcnn_step_by_step.ipynb</a>非常有助于理解整个文章的流程。 </p><h1 id="算法流程(概略)"><a href="#算法流程(概略)" class="headerlink" title="算法流程(概略)"></a>算法流程(概略)</h1><p><strong>Inference阶段</strong></p><p><strong>图像金字塔</strong></p><blockquote><p>1、对于给定的输入图像进行scale操作, 得到若干个不同scale的输入图像,这一步的目的是能够针对不同大小人脸进行候选框的检测。</p></blockquote><p><strong>P-Net</strong></p><blockquote><p>2.1、将不同scale的图像输入到P-Net中。</p><p>2.2、 设定阈值,根据face classification的结果,选出可能含有目标框的点 。</p><p>2.3、 将这些在不同尺度下可能是目标框的点映射到原始没有经过scale的图中,得到了很多候选区域。 </p><p>2.4、 使用nms算法对目标框进行筛选,并且根据P-Net中输出的offset(对应bounding box regression)对候选框进行微调,校准。 </p></blockquote><p><strong>R-Net</strong></p><blockquote><p>3、 将上一步得到所有候选框提取出来,并resize到24*24的大小,输入至R-Net中,并根据face classification的值,进一步对候选进行筛选,类似于2-4中的操作,对目标框位置进行筛选校准 </p></blockquote><p><strong>O-Net</strong></p><blockquote><p>4、 类似于第3步的操作,进一步对目标框进行筛选,并得到最终的输出结果,并且根据Facial landmark的输出得到5个landmark。 </p></blockquote><blockquote><p>PS: 在P-Net和R-Net中,都没有输出Facial landmark localization,只在最终的O-Net中输出了 。</p></blockquote><h1 id="算法流程(详解)"><a href="#算法流程(详解)" class="headerlink" title="算法流程(详解)"></a>算法流程(详解)</h1><p>MTCNN方法主要为:<strong>图像金字塔+3个级联CNN网络</strong>。</p><p>算法流程图如下所示:</p><p><img src="/2019/10/14/MTCNN%E7%AE%97%E6%B3%95%E4%B8%8E%E4%BB%A3%E7%A0%81%E7%90%86%E8%A7%A3/pipeline.png" alt="pipeline"></p><h2 id="图像金字塔"><a href="#图像金字塔" class="headerlink" title="图像金字塔"></a>图像金字塔</h2><p><strong>图像金字塔(<a href="https://en.wikipedia.org/wiki/Pyramid_(image_processing)">image pyramid</a>)</strong> :主要是保证了多尺度的人脸数据的训练和检测(其中也有很多的trick)</p><p>Q:请问为什么输入PNET为什么是图片金字塔吗?能简单解释一下吗? A:我的理解是:train的时候并不是图片金字塔,而是在predict或者说inference时是图片金字塔,因为训练的时候每个样本大小是12×12,而predict时是原图大小可能很大比如1920×1080,在图片中人脸占用像素可能200×200,这样使用训练好的模型去识别就识别不了,这时如果缩小图片使得人脸大概是12×12左右的话,就可以识别了。而由于我们并不知道实际原图中人脸大小是多少,也就不知道图片需要缩小多少倍,所以会按不同的比例缩放多种尺寸,形成图片金字塔,这样总有几种尺寸可以满足要求</p><p>3个阶段的级联的CNN网络作为主要的任务网络分别是<strong>P-Net,R-Net,O-Net</strong>,三个网络级联前一个网络的输出是后一个网络的输入,从而完成对人脸<strong>由粗到细(coarse-to-fine)<strong>的检测。并且可以看到随着</strong>网络层数逐渐加深</strong>,<strong>输入图像的尺寸(感受野)在逐渐变大12→24→48</strong>,<strong>最终输出的特征维数也在增加32→128→256</strong>,意味着利用的信息越来越多。</p><p><img src="/2019/10/14/MTCNN%E7%AE%97%E6%B3%95%E4%B8%8E%E4%BB%A3%E7%A0%81%E7%90%86%E8%A7%A3/nets.png" alt="nets"></p><p>其中 <code>P-Net:Fast Proposal Network</code>,<code>R-Net:Refinement Network</code>,<code>O-Net:Output Network</code>,<code>MP:max pooling</code>,<code>Conv:convolution</code></p><h2 id="P-Net"><a href="#P-Net" class="headerlink" title="P-Net"></a>P-Net</h2><p>该网络的主要任务是获得人脸区域的<strong>候选窗口</strong>和<strong>边界框的回归向量</strong>。并用该边界框做回归,对候选窗口进行校准,然后通过非极大值抑制(NMS)来合并高度重叠的候选框。该网络是整个网络的起始输入端,是一个**全卷积神经网络(<a href="https://www.dogedoge.com/rd/ewRpxetiNIuimF6YaBgH13FvU2Re6VBxbkb3XYTg8VhOLWAUP62tfXkOt32sV7BJyqqVSYQRgH6eklGRNjca31uN2Kvhjh2%2Bhw6gC9S7Hbg%3D">FNC</a>)<strong>,前向传播得到的特征图在每个位置是个32维的特征向量,用于判断每个位置处约12×12大小的区域内是否包含人脸,如果包含人脸,则回归出人脸的Bounding Box,进一步获得Bounding Box对应到原图中的区域,通过</strong><a href="http://www.vision.ee.ethz.ch/publications/papers/proceedings/eth_biwi_01126.pdf">NMS</a>**保留分数最高的Bounding box以及移除重叠区域过大的Bounding Box。</p><h3 id="网络结构"><a href="#网络结构" class="headerlink" title="网络结构"></a>网络结构</h3><p>P-Net的网络结构是一个<strong>全卷积神经网络</strong>的网络结构。</p><blockquote><p>FCN(全卷积神经网络)</p><p>全卷积网络就是去除了传统卷积网络的全连接层,然后对其进行反卷积对最后一个卷积层(或者其他合适的卷积层)的feature map进行上采样,使其恢复到原有图像的尺寸(或者其他),并对反卷积图像的每个像素点都可以进行一个类别的预测,同时保留了原有图像的空间信息。同时,在反卷积对图像进行操作的过程中,也可以通过提取其他卷积层的反卷积结果对最终图像进行预测,合适的选择会使得结果更好、更精细。</p><p><strong>输入(Input)可以是任意大小的图片</strong></p></blockquote><p>输入是一个$12\times 12$大小的图片,所以训练前需要把生成的训练数据(通过生成bounding box,然后把该bounding box 剪切成$12\times 12$大小的图片),转换成$12\times 12\times 3$的结构。</p><p>1.通过10个$3\times 3\times 3$的卷积核,$2\times 2$的Max Pooling(stride=2)操作,生成10个$5\times 5$的特征图。</p><p>2.通过16个$3\times 3\times 10$的卷积核,生成16个$3\times 3$的特征图。</p><p>3.通过32个$3\times 3\times 16$的卷积核,生成32个$1\times 1$的特征图</p><p>4.针对32个$1\times 1$的特征图</p><ul><li>通过2个$1\times 1\times 32$的卷积核,生成2个$1\times 1$的特征图用于人脸的分类;</li><li>通过4个$1\times 1\times 32$的卷积核,生成4个$1\times 1$的特征图用于回归框判断;</li><li>通过10个$1\times 1\times 32$的卷积核,生成10个$1\times 1$的特征图用于人脸关键点的判断。</li></ul><h3 id="模型训练"><a href="#模型训练" class="headerlink" title="模型训练"></a>模型训练</h3><p><strong>1、</strong>P-Net 的层数很浅,主要作用是尽可能多的把人脸框都选进来,宁愿错误拿来好多个,也不丢掉一个。 </p><p><strong>2、</strong>P-Net的训练数据主要由4部分组成:其中比例为$pos:part:neg:landmark=1:1:3:2$</p><ul><li><code>pos</code>正label数据(IoU>0.65,面部Landmark特征值为0)</li><li><code>neg</code>负label数据(IoU<0.40,面部Landmark特征值为0,回归框值为0)</li><li><code>part</code>中间数据(0.40<IoU<0.65,面部Landmark特征值为0)</li><li><code>landmark</code>面部Landmark数据(回归框值为0)</li></ul><p><strong>3、</strong>训练数据的由来:</p><p>pos,part,neg是随机和人脸的数据裁剪得到的,裁剪图片与人脸框最大的iou值大于0.65的为pos图像,大于0.4的为part图像,小于0.4的为neg图像,landmark截取的是带有关键点的图像。</p><ul><li><p>pos,part的label含有它们的类别1,-1还有人脸框相对于图像左上角的偏移量,偏移量除以图像大小做了归一化;</p></li><li><p>neg的label只含有类别0;</p></li><li><p>landmark的label含有类别-2和5个关键点的坐标偏移也是进行了归一化的。</p></li></ul><p> 这四种图像都resize成$12\times 12$作为PNet的输入,通过P-Net得到了是否有人脸的概率[batch,2],人脸框的偏移量[batch,4],关键点的偏移量[batch,10]。 </p><p><strong>4、</strong>四种不同数据的训练方式:</p><ul><li>对于是否存在人脸的类别损失只通过neg和pos数据来对参数进行更新,具体办法是通过label中的类别值做了一个遮罩来划分数据,只计算neg和pos的损失,不计算其他数据的损失; </li><li>人脸框的损失只计算pos和part数据的; </li><li>关键点的损失只计算landmark的 </li><li><strong>Tips: Online Hard Example Mining(OHEM)</strong> 在训练过程中,对每个mini-batch,取loss最大的70%进行反向传播,忽略那些简单的样本。说是模型准确率会有提升,在代码中也都有体现,具体实现可以参考代码。</li></ul><h2 id="R-Net"><a href="#R-Net" class="headerlink" title="R-Net"></a>R-Net</h2><p>该网络的主要任务还是通过边界框回归和NMS来去掉那些false-positive区域。该网络是单纯的卷积神经网络(CNN),先将P-Net认为可能包含人脸的Bounding Box <strong>双线性插值</strong>到24×24,输入<strong>R-Net</strong>,判断是否包含人脸,如果包含人脸,也回归出Bounding Box,同样经过NMS过滤。只是由于该网络结构和P-Net网络结构有差异,多了一个全连接层,所以会取得更好的抑制false-positive的作用。</p><h2 id="O-Net"><a href="#O-Net" class="headerlink" title="O-Net"></a>O-Net</h2><p>该网络的主要任务是对人脸区域进行了更多的监督,同时输出5个人脸landmark。该网络也是单纯的卷积神经网络(CNN),该网络比<strong>R-Net</strong>又多了一层卷基层,所以处理的结果会更加精细。作用和<strong>R-Net</strong>层作用一样,并输出5个人脸landmark。</p><h2 id="Face-classification"><a href="#Face-classification" class="headerlink" title="Face classification"></a>Face classification</h2><p>人脸的检测可以看作是一个局部二分类问题(该区域存在/不存在人脸)。</p><p>MTCNN采用的是交叉熵作为loss:</p><p>$$L_i^{det}=-(y_i^{det}log(p_i)+(1-y_i^{det})(1-log(p_i)))$$</p><p>其中,$p_i$为该网络表示样本$x_i$是人脸的概率,$y_i^{det}\in {0,1}$为该区域的真实标签。</p><h2 id="Bounding-box-regression"><a href="#Bounding-box-regression" class="headerlink" title="Bounding box regression"></a>Bounding box regression</h2><p>MTCNN通过每个样本$x_i$的欧氏距离作回归损失:</p><p>$$L_i^{box}=||\hat y_i^{box}-y_i^{box}||_2^2$$</p><p>其中,$\hat y_i^{box}$为对于第$i$个样本该网络预测得到的回归目标,$y_i^{box}$为第$i$个样本实际的真实的背景坐标。其中$y_i$为(左上角x,左上角y,长,宽)/(left,top,height,width)组成的四元组。</p><p>在训练过程中,$\hat y_i^{box}$和$y_i^{box}$的交并集IoU(<a href="https://loopvoid.github.io/2019/10/13/IoU-Intersection-over-Union/">Intersection-over-Union</a>)比例为:</p><table><thead><tr><th>训练样本比例</th><th>负样本:正样本:part样本:Landmark = 3:1:1:2</th></tr></thead><tbody><tr><td>非人脸</td><td>(0,0.3)</td></tr><tr><td>Part人脸</td><td>(0.4,0.65)</td></tr><tr><td>人脸</td><td>(0.65,1.00)</td></tr><tr><td>Landmark</td><td>(0.3,0.4)</td></tr></tbody></table><h2 id="Facial-Landmark-Localization"><a href="#Facial-Landmark-Localization" class="headerlink" title="Facial Landmark Localization"></a>Facial Landmark Localization</h2><p>与bounding box regression任务类似人脸特征点定位也是一个回归问题采用同样的欧式距离作为loss:</p><p>$$L_i^{landmark}=||\hat y_i^{landmark}-y_i^{landmark}||_2^2$$ </p><p>其中,$\hat y_i^{landmark}$为对于第$i$个样本该网络预测得到的回归目标,$y_i^{landmark}$为第$i$个样本实际的真实的坐标。人脸特征点包括(left eye, right eye, nose, left mouse corner, right mouse corner),即$y_i^{landmark}\in \mathbb{R}^{10}$.</p><h2 id="Multi-source-training"><a href="#Multi-source-training" class="headerlink" title="Multi-source training"></a>Multi-source training</h2><p>整个训练可以表示为:</p><p>$$min\sum _{i=1}^N\sum _{j\in {det,box,landmark}}\alpha _j\beta_i^jL_i^j,\beta_i^j\in{0,1}$$</p><p>$$P-Net,R-Net: (\alpha _{det}=1,\alpha _{box}=0.5,\alpha _{landmark}=0.5)$$</p><p>$$O-Net:(\alpha_{det}=1,\alpha_{box}=0.5,\alpha_{landmark}=1)$$</p><p>其中,<strong>N</strong>为训练样本数,$\alpha_j$为任务的重要性,$\beta_j$为样本标签,$L_j$为前面三个任务(face classification、bounding box regression、facial landmark localization)对应的loss函数。</p><p>在<strong>训练阶段</strong>,3个网络都会将关键点位置作为监督信号来引导网络的学习, 但在<strong>预测阶段</strong>,P-Net和R-Net仅做人脸检测,不输出关键点位置(因为这时人脸检测都是不准的),关键点位置仅在O-Net中输出。</p><p><strong>Bounding box</strong>和<strong>关键点</strong>输出均为<strong>归一化后的相对坐标</strong>,Bounding Box是相对待检测区域(R-Net和O-Net是相对输入图像),归一化是相对坐标除以检测区域的宽高,关键点坐标是相对Bounding box的坐标,归一化是相对坐标除以Bounding box的宽高,这里先建立起初步的印象,具体可以参看后面准备训练数据部分和预测部分的代码细节。</p><h1 id="优点-改进"><a href="#优点-改进" class="headerlink" title="优点(改进)"></a>优点(改进)</h1><ul><li>减少卷积核数量(每层内部)</li><li>将$5\times5$的卷积核替换为$3\times3$,并增加网络深度 </li><li>在线难样本挖掘(Online hard sample mining)<ul><li>传统的难样本的处理方法是检测过一次以后,手动检测哪些困难的样本无法被分类,本文采用online hard sample mining的方法。具体就是在每个mini-batch中,取loss最大的70%进行反向传播,忽略那些简单的样本。</li></ul></li><li>人脸检测和对齐联合学习(joint face detecion and alignment)</li></ul><h1 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h1><p><a href="https://github.com/loopvoid/mtcnn">mtcnn人脸检测</a></p><h1 id="实现效果"><a href="#实现效果" class="headerlink" title="实现效果"></a>实现效果</h1><p>环境:<br>Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz<br>Logitech HD Pro Webcam c920<br>图像大小:640*640<br>效果:(compressed gif)<br><img src="/2019/10/14/MTCNN%E7%AE%97%E6%B3%95%E4%B8%8E%E4%BB%A3%E7%A0%81%E7%90%86%E8%A7%A3/mtcnn.gif" alt="mtcnn"></p><hr><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://kpzhang93.github.io/MTCNN_face_detection_alignment/paper/spl.pdf">Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Networks</a><br><a href="https://www.cnblogs.com/shine-lee/p/10115582.html">https://www.cnblogs.com/shine-lee/p/10115582.html</a><br><a href="https://zhuanlan.zhihu.com/p/31913064">https://zhuanlan.zhihu.com/p/31913064</a><br><a href="https://www.cnblogs.com/shine-lee/p/10115582.html">https://www.cnblogs.com/shine-lee/p/10115582.html</a><br><a href="https://blog.csdn.net/qq_14845119/article/details/52680940">https://blog.csdn.net/qq_14845119/article/details/52680940</a><br><a href="http://www.sfinst.com/?p=1683">http://www.sfinst.com/?p=1683</a><br><a href="https://blog.csdn.net/qq_36782182/article/details/83624357">https://blog.csdn.net/qq_36782182/article/details/83624357</a></p><p>训练:<br><a href="https://github.com/dlunion/mtcnn/tree/master/train">https://github.com/dlunion/mtcnn/tree/master/train</a><br><a href="https://www.cnblogs.com/helloworld0604/p/9808795.html">https://www.cnblogs.com/helloworld0604/p/9808795.html</a><br><a href="https://joshua19881228.github.io/2018-09-11-training-mtcnn/">https://joshua19881228.github.io/2018-09-11-training-mtcnn/</a><br><a href="https://github.com/dlunion/mtcnn">https://github.com/dlunion/mtcnn</a><br><a href="https://github.com/BobLiu20/mtcnn_tf">https://github.com/BobLiu20/mtcnn_tf</a></p><p>实现<br><a href="https://github.com/imistyrain/MTCNN/blob/master/MTCNN-light/src/main.cpp">https://github.com/imistyrain/MTCNN/blob/master/MTCNN-light/src/main.cpp</a><br><a href="https://github.com/cpuimage/MTCNN">https://github.com/cpuimage/MTCNN</a><br><a href="https://github.com/AITTSMD/MTCNN-Tensorflow/blob/master/train_models/mtcnn_model.py">https://github.com/AITTSMD/MTCNN-Tensorflow/blob/master/train_models/mtcnn_model.py</a><br><a href="https://github.com/davidsandberg/facenet/blob/master/src/align/detect_face.py">https://github.com/davidsandberg/facenet/blob/master/src/align/detect_face.py</a><br><a href="https://github.com/TropComplique/mtcnn-pytorch">https://github.com/TropComplique/mtcnn-pytorch</a><br><a href="https://github.com/LeslieZhoa/tensorflow-MTCNN">https://github.com/LeslieZhoa/tensorflow-MTCNN</a><br><a href="https://github.com/imistyrain/MTCNN">https://github.com/imistyrain/MTCNN</a><br><a href="https://github.com/davidsandberg/facenet">https://github.com/davidsandberg/facenet</a><br><a href="https://github.com/AlphaQi/MTCNN-light">https://github.com/AlphaQi/MTCNN-light</a><br><a href="https://github.com/BobLiu20/mtcnn_tf">https://github.com/BobLiu20/mtcnn_tf</a><br><a href="https://github.com/LucyLu-LX/MTCNN_face_detection_caffe">https://github.com/LucyLu-LX/MTCNN_face_detection_caffe</a><br><a href="https://github.com/pangyupo/mxnet_mtcnn_face_detection">https://github.com/pangyupo/mxnet_mtcnn_face_detection</a><br><a href="https://github.com/Longqi-S/ncnn-mtcnn">https://github.com/Longqi-S/ncnn-mtcnn</a><br><a href="https://github.com/githublet/mtcnn/blob/master/caffemodel2txt/caffemodel2txt.py">https://github.com/githublet/mtcnn/blob/master/caffemodel2txt/caffemodel2txt.py</a> </p>]]></content>
</entry>
<entry>
<title>IoU(Intersection over Union)</title>
<link href="/2019/10/13/IoU-Intersection-over-Union/"/>
<url>/2019/10/13/IoU-Intersection-over-Union/</url>
<content type="html"><![CDATA[<h1 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h1><p>在目标检测中,我们需要定位出目标的位置,而我们的算法不可能百分百跟人工标注的数据完全匹配,因此需要一种衡量目标定位精度的标准。</p><p>IoU(Intersection over Union)是一种常见的用于衡量目标定位精度的标准,可以理解为重叠度,是一种简单的测量标准,只要是在输出中得出一个预测范围(bounding box)的任务都可以用IoU来进行测量。</p><p><img src="/2019/10/13/IoU-Intersection-over-Union/1.jpg" alt="1"></p><p>如上图所示,ground-truth和predicted的存在误差,绿色框是人为标记的正确结果,红色框是算法预测出来的结果,IoU要做的就是在这两个结果中测量算法的准确度,它定义了两个bounding box的重叠度 ,如下图所示:</p><p><img src="/2019/10/13/IoU-Intersection-over-Union/2.png" alt="2"></p><p>$$IoU=\frac{A\bigcap B}{A\bigcup B}=\frac{A\bigcap B}{A+B-A\bigcap B}$$</p><p>就是矩形框A、B的重叠面积,占$A\bigcup B$的编辑的比例。</p><p><strong>一般来说,IoU>0.5 就可以被认为一个不错的结果,IoU>0.7 结果就非常不错了,可以参见下图</strong></p><p><img src="/2019/10/13/IoU-Intersection-over-Union/3.png" alt="3"></p><h1 id="Implementation"><a href="#Implementation" class="headerlink" title="Implementation"></a>Implementation</h1><p>因为IoU的思想很简单,Python实现可以参考[1]:</p><figure class="highlight python"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></div></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">bb_intersection_over_union</span>(<span class="hljs-params">boxA, boxB</span>):<br><span class="hljs-comment"># determine the (x, y)-coordinates of the intersection rectangle</span><br>xA = <span class="hljs-built_in">max</span>(boxA[<span class="hljs-number">0</span>], boxB[<span class="hljs-number">0</span>])<br>yA = <span class="hljs-built_in">max</span>(boxA[<span class="hljs-number">1</span>], boxB[<span class="hljs-number">1</span>])<br>xB = <span class="hljs-built_in">min</span>(boxA[<span class="hljs-number">2</span>], boxB[<span class="hljs-number">2</span>])<br>yB = <span class="hljs-built_in">min</span>(boxA[<span class="hljs-number">3</span>], boxB[<span class="hljs-number">3</span>])<br> <br><span class="hljs-comment"># compute the area of intersection rectangle</span><br>interArea = (xB - xA + <span class="hljs-number">1</span>) * (yB - yA + <span class="hljs-number">1</span>)<br> <br><span class="hljs-comment"># compute the area of both the prediction and ground-truth</span><br><span class="hljs-comment"># rectangles</span><br>boxAArea = (boxA[<span class="hljs-number">2</span>] - boxA[<span class="hljs-number">0</span>] + <span class="hljs-number">1</span>) * (boxA[<span class="hljs-number">3</span>] - boxA[<span class="hljs-number">1</span>] + <span class="hljs-number">1</span>)<br>boxBArea = (boxB[<span class="hljs-number">2</span>] - boxB[<span class="hljs-number">0</span>] + <span class="hljs-number">1</span>) * (boxB[<span class="hljs-number">3</span>] - boxB[<span class="hljs-number">1</span>] + <span class="hljs-number">1</span>)<br> <br><span class="hljs-comment"># compute the intersection over union by taking the intersection</span><br><span class="hljs-comment"># area and dividing it by the sum of prediction + ground-truth</span><br><span class="hljs-comment"># areas - the interesection area</span><br>iou = interArea / <span class="hljs-built_in">float</span>(boxAArea + boxBArea - interArea)<br> <br><span class="hljs-comment"># return the intersection over union value</span><br><span class="hljs-keyword">return</span> iou<br></code></pre></td></tr></table></figure><h1 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h1><p>[1] <a href="https://www.pyimagesearch.com/2016/11/07/intersection-over-union-iou-for-object-detection">Intersection over Union (IoU) for object detection</a></p>]]></content>
</entry>
<entry>
<title>机器学习的数学基础</title>
<link href="/2019/09/26/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%9A%84%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80/"/>
<url>/2019/09/26/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%9A%84%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80/</url>
<content type="html"><![CDATA[<h1 id="机器学习的数学基础"><a href="#机器学习的数学基础" class="headerlink" title="机器学习的数学基础"></a>机器学习的数学基础</h1><h1 id="高等数学"><a href="#高等数学" class="headerlink" title="高等数学"></a>高等数学</h1><h2 id="导数定义:"><a href="#导数定义:" class="headerlink" title="导数定义:"></a><strong>导数定义:</strong></h2><p>导数和微分的概念</p><p>$f’(x_0)=lim_{\Delta x\to 0}\frac{f(x_0+\Delta x)-f(x_0)}{\Delta x}$</p><p>或者:</p><p>$f’(x_0)=lim_{x\to x_0}\frac{f(x)-f(x_0)}{x-x_0}$</p><h2 id="左右导数导数的几何意义和物理意义"><a href="#左右导数导数的几何意义和物理意义" class="headerlink" title="左右导数导数的几何意义和物理意义"></a><strong>左右导数导数的几何意义和物理意义</strong></h2><p>函数$f(x)$在$x_0$处的左、右导数分别定义为:</p><p>左导数:$f_-‘(x_0)=lim_{\Delta x\to 0^-}\frac{f(x_0+\Delta x)-f(x_0)}{\Delta x}=lim_{x\to x_0^-}\frac{f(x)-f(x_0)}{x-x_0},(x=x_0+\Delta x)$</p><p>右导数:$f_+’(x_0)=lim_{\Delta x\to 0^+}\frac{f(x_0+\Delta x)-f(x_0)}{\Delta x}=lim_{x\to x_0^+}\frac{f(x)-f(x_0)}{x-x_0}$</p><h2 id="函数的可导性与连续性之间的关系"><a href="#函数的可导性与连续性之间的关系" class="headerlink" title="函数的可导性与连续性之间的关系"></a><strong>函数的可导性与连续性之间的关系</strong></h2><p><strong>Th1:</strong> 函数$f(x)$在$x_0$处可微$\Leftrightarrow f(x)$在$x_0$处可导</p><p><strong>Th2:</strong> 若函数在点$x_0$处可导,则$y=f(x)$在点$x_0$处连续,反之则不成立。即函数连续不一定可导。</p><p><strong>Th3:</strong> $f’(x_0)$存在 $f’<em>{-}(x_0)=f’</em>{+}(x_0)$</p><h2 id="平面曲线的切线和法线"><a href="#平面曲线的切线和法线" class="headerlink" title="平面曲线的切线和法线"></a><strong>平面曲线的切线和法线</strong></h2><p>切线方程 : $y-y_0=f’(x_0)(x-x_0)$<br>法线方程:$y-y_0=-\frac{1}{f’(x_0)}(x-x_0),f’(x_0)\ne 0$</p><h2 id="四则运算法则"><a href="#四则运算法则" class="headerlink" title="四则运算法则"></a><strong>四则运算法则</strong></h2><p>设函数$u=u(x),v=v(x)$]在点$x$可导则<br>(1) $(u\pm v{)}’={u}’\pm {v}’ \ \ \ \ \ d(u\pm v)=du\pm dv$<br>(2)$(uv{)}’=u{v}’+v{u}’$ $d(uv)=udv+vdu$<br>(3) $(\frac{u}{v}{)}’=\frac{v{u}’-u{v}’}{v^2}(v\ne 0) \ \ \ \ \ d(\frac{u}{v})=\frac{vdu-udv}{v^2}$</p><h2 id="基本导数与微分表"><a href="#基本导数与微分表" class="headerlink" title="基本导数与微分表"></a><strong>基本导数与微分表</strong></h2><p>(1) $y=c$(常数) $y’=0$ $dy=0$</p><p>(2) $y=x^\alpha$($\alpha $为实数) ${y}’=\alpha x^{\alpha -1}$ $dy=\alpha x^{\alpha -1}dx$</p><p>(3) $y=a^x$ ${y}’=a^x\ln a$ $dy=a^x\ln adx$<br> 特例: $(e^x)’=e^x \ \ \ \ \ \ \ d(e^x)=e^xdx$</p><p>(4) $y=\log _ax$ $y’=\frac{1}{x\ln a}$<br>$dy=\frac{1}{x\ln a}dx$<br>特例:$y=\ln x \ \ \ \ \ \ (\ln x)’=\frac{1}{x} \ \ \ \ \ \ d(\ln x)=\frac{1}{x}dx$</p><p>(5) $y=\sin x$<br>${y}’=\cos x$ $d(\sin x)=\cos xdx$</p><p>(6) $y=\cos x$<br>${y}’=-\sin x \ \ \ \ \ d(\cos x)=-\sin xdx$</p><p>(7) $y=\tan x$<br>${y}’=\frac{1}{\cos ^2x}={\sec ^2}x \ \ \ \ \ d(\tan x)=\sec ^2xdx$</p><p>(8) $y=\cot x \ \ \ \ \ {y}’=-\frac{1}{\sin ^2x}=-\csc ^2x \ \ \ \ \ d(\cot x)=-\csc ^2xdx$</p><p>(9) $y=\sec x \ \ \ \ \ y’=\sec x\tan x$<br> $d(\sec x)=\sec x\tan xdx$</p><p>(10) $y=\csc x \ \ \ \ \ y’=-\csc x\cot x$<br>$d(\csc x)=-\csc x\cot xdx$</p><p>(11) $y=\arcsin x$<br>${y}’=\frac{1}{\sqrt{1-x^2}}$<br>$d(\arcsin x)=\frac{1}{\sqrt{1-x^2}}dx$</p><p>(12) $y=\arccos x$<br>${y}’=-\frac{1}{\sqrt{1-x^2}}$ $d(\arccos x)=-\frac{1}{\sqrt{1-x^2}}dx$</p><p>(13) $y=\arctan x$<br>$y’=\frac{1}{1+x^2}$ $d(\arctan x)=\frac{1}{1+x^2}dx$</p><p>(14) $y=\operatorname{arc}\cot x$<br>${y}’=-\frac{1}{1+x^2}$<br>$d(\operatorname{arc}\cot x)=-\frac{1}{1+x^2}dx$</p><p>(15) $y=shx$<br>${y}’=chx \ \ \ \ \ d(shx)=chxdx$</p><p>(16) $y=chx$<br>${y}’=shx \ \ \ \ \ d(chx)=shxdx$</p><h2 id="复合函数,反函数,隐函数以及参数方程所确定的函数的微分法"><a href="#复合函数,反函数,隐函数以及参数方程所确定的函数的微分法" class="headerlink" title="复合函数,反函数,隐函数以及参数方程所确定的函数的微分法"></a><strong>复合函数,反函数,隐函数以及参数方程所确定的函数的微分法</strong></h2><p>(1) 反函数的运算法则: 设$y=f(x)$在点$x$的某邻域内单调连续,在点$x$处可导且$f’(x)\ne 0$,则其反函数在点$x$所对应的$y$处可导,并且有$\frac{dy}{dx}=\frac{1}{\frac{dx}{dy}}$<br>(2) 复合函数的运算法则:若$\mu =\varphi (x)$在点$x$可导,而$y=f(\mu )$在对应点$\mu(\mu =\varphi (x))$可导,则复合函数$y=f(\varphi (x))$在点$x$可导,且$y’=f’(\mu )\cdot \varphi ‘(x)$<br>(3) 隐函数导数$\frac{dy}{dx}$的求法一般有三种方法:<br>1)方程两边对$x$求导,要记住$y$是$x$的函数,则$y$的函数是$x$的复合函数.例如$\frac{1}{y}$,$y^2$,$ln y$,$e^y$等均是$x$的复合函数.<br>对$x$求导应按复合函数连锁法则做.<br>2)公式法.由$F(x,y)=0$知 $\frac{dy}{dx}=-\frac{F’_x(x,y)}{F’_y(x,y)}$,其中,$F’_x(x,y)$,<br>$F’_y(x,y)$分别表示$F(x,y)$对$x$和$y$的偏导数<br>3)利用微分形式不变性</p><h2 id="常用高阶导数公式"><a href="#常用高阶导数公式" class="headerlink" title="常用高阶导数公式"></a><strong>常用高阶导数公式</strong></h2><p>(1)$(a^x)^{(n)}=(a^x){\ln }^na\quad (a>{0})\quad \quad (e^x)^{(n)}=e^{x}$<br>(2)$(\sin kx)^{(n)}=k^n\sin (kx+n\cdot \frac{\pi }{2})$<br>(3)$(\cos kx)^{(n)}=k^{n}\cos (kx+n\cdot \frac{\pi }{2})$<br>(4)$(x^m)^{(n)}=m(m-1)\cdots (m-n+1)x^{m-n}$<br>(5)$(\ln x)^n=(-1)^{(n-1)}\frac{(n-1)!}{x^n}$<br>(6)莱布尼兹公式:若$u(x),v(x)$均$n$阶可导,则<br> $(uv)^{(n)}=\sum\limits_{i=0}^nc_n^iu^{(i)}v^{(n-i)}$,其中$u^{(0)}=u$,$v^{(0)}=v$</p><h2 id="微分中值定理,泰勒公式"><a href="#微分中值定理,泰勒公式" class="headerlink" title="微分中值定理,泰勒公式"></a><strong>微分中值定理,泰勒公式</strong></h2><p><strong>Th1:</strong>(费马定理)</p><p>若函数$f(x)$满足条件:<br>(1)函数$f(x)$在$x_0$的某邻域内有定义,并且在此邻域内恒有<br>$f(x)\le f(x_0)$或$f(x)\ge f(x_0)$,</p><p>(2) $f(x)$在$x_0$处可导,则有 $f’(x_0)=0$</p><p><strong>Th2:</strong>(罗尔定理) </p><p>设函数$f(x)$满足条件:<br>(1)在闭区间$[a,b]$上连续;</p><p>(2)在$(a,b)$内可导;</p><p>(3)$f(a)=f(b)$;</p><p>则在$(a,b)$内一存在个$\xi $,使 $f’(\xi )=0$<br><strong>Th3:</strong> (拉格朗日中值定理) </p><p>设函数$f(x)$满足条件:<br>(1)在$[a,b]$上连续;</p><p>(2)在$(a,b)$内可导;</p><p>则在$(a,b)$内一存在个$\xi $,使 $\frac{f(b)-f(a)}{b-a}=f’(\xi )$</p><p><strong>Th4:</strong> (柯西中值定理)</p><p> 设函数$f(x)$,$g(x)$满足条件:<br>(1) 在$[a,b]$上连续;</p><p>(2) 在$(a,b)$内可导且$f’(x)$,$g’(x)$均存在,且$g’(x)\ne 0$</p><p>则在$(a,b)$内存在一个$\xi $,使 $\frac{f(b)-f(a)}{g(b)-g(a)}=\frac{f’(\xi )}{g’(\xi )}$</p><h2 id="洛必达法则"><a href="#洛必达法则" class="headerlink" title="洛必达法则"></a><strong>洛必达法则</strong></h2><p>法则Ⅰ ($\frac{0}{0}$型)<br>设函数$f(x),g(x)$满足条件:<br>$lim_{x\to x_0}f(x)=0,lim_{x\to x_0}g(x)=0$;<br>$f(x),g(x)$在$x_0$的邻域内可导,(在$x_0$处可除外)且$g’(x)\ne 0$;<br>$lim_{x\to x_0}\frac{f’(x)}{g’(x)}$存在(或$\infty $)。<br>则:<br>$lim_{x\to x_0}\frac{f(x)}{g(x)}=lim_{x\to x_0}\frac{f’(x)}{g’(x)}$。<br>法则$I’$ ($\frac{0}{0}$型)设函数$f(x),g(x)$满足条件:<br>$lim_{x\to \infty }f(x)=0,lim_{x\to \infty }g(x)=0$;<br>存在一个$X>0$,当$|x|>X$时,$f(x),g(x)$可导,且${g}’(x)\ne 0$;$lim_{x\to x_0}\frac{f’(x)}{g’(x)}$存在(或$\infty $)。<br>则:<br>$lim_{x\to x_0}\frac{f(x)}{g(x)}=lim_{x\to x_0}\frac{f’(x)}{g’(x)}$</p><p>法则Ⅱ($\frac{\infty }{\infty }$型)<br>设函数$f(x),g(x)$满足条件:<br>$lim_{x\to x_0}f(x)=\infty ,lim_{x\to x_0}g(x)=\infty $;<br>$f(x),g(x)$在$x_0$ 的邻域内可导(在$x_0$处可除外)且$g’(x)\ne 0$;$lim_{x\to x_0}\frac{f’(x)}{g’(x)}$存在(或$\infty $)。则<br>$lim_{x\to x_0}\frac{f(x)}{g(x)}=lim_{x\to x_0}\frac{f’(x)}{g’(x)}.$同理法则${II’}$($\frac{\infty }{\infty }$型)仿法则${I’}$可写出。</p><h2 id="泰勒公式"><a href="#泰勒公式" class="headerlink" title="泰勒公式"></a><strong>泰勒公式</strong></h2><p>设函数$f(x)$在点$x_0$处的某邻域内具有$n+1$阶导数,则对该邻域内异于$x_0$的任意点$x$,在$x_0$与$x$之间至少存在<br>一个$\xi $,使得:<br>$f(x)=f(x_0)+f’(x_0)(x-x_0)+\frac{1}{2!}f’’(x_0)(x-x_0)^2+\cdots +\frac{f^{(n)}(x_0)}{n!}(x-x_0)^n+R_n(x)$ 其中<br> $R_n(x)=\frac{f^{(n+1)}(\xi )}{(n+1)!}(x-x_0)^{n+1}$称为$f(x)$在点$x_0$处的$n$阶泰勒余项。<br>令$x_0=0$,则$n$阶泰勒公式<br>$f(x)=f(0)+f’(0)x+\frac{1}{2!}f’’(0)x^2+\cdots +\frac{f^{(n)}(0)}{n!}x^n+R_n(x) \ \ \ 麦克劳林公式$<br>其中 $R_n(x)=\frac{f^{(n+1)}(\xi )}{(n+1)!}x^{n+1}$,$\xi $在0与$x$之间.</p><p><strong>常用五种函数在${x_0}=0$处的泰勒公式</strong></p><p>(1) $e^x=1+x+\frac{1}{2!}x^2+\cdots +\frac{1}{n!}x^n+\frac{x^{n+1}}{(n+1)!}e^{\xi }$<br>或 $=1+x+\frac{1}{2!}x^2+\cdots +\frac{1}{n!}x^n+o(x^n)$</p><p>(2) $\sin x=x-\frac{1}{3!}x^3+\cdots +\frac{x^n}{n!}\sin \frac{n\pi }{2}+\frac{x^{n+1}}{(n+1)!}\sin (\xi +\frac{n+1}{2}\pi )$<br>或 $=x-\frac{1}{3!}x^3+\cdots +\frac{x^n}{n!}\sin \frac{n\pi }{2}+o(x^n)$</p><p>(3) $\cos x=1-\frac{1}{2!}x^2+\cdots +\frac{x^n}{n!}\cos \frac{n\pi }{2}+\frac{x^{n+1}}{(n+1)!}\cos (\xi +\frac{n+1}{2}\pi )$<br>或 $=1-\frac{1}{2!}x^2+\cdots +\frac{x^n}{n!}\cos \frac{n\pi }{2}+o(x^n)$</p><p>(4) $\ln (1+x)=x-\frac{1}{2}x^2+\frac{1}{3}x^3-\cdots +(-1)^{n-1}\frac{x^n}{n}+\frac{(-1)^nx^{n+1}}{(n+1)(1+\xi )^{n+1}}$<br>或 $=x-\frac{1}{2}x^2+\frac{1}{3}x^3-\cdots +(-1)^{n-1}\frac{x^n}{n}+o(x^n)$</p><p>(5) $(1+x)^m=1+mx+\frac{m(m-1)}{2!}x^2+\cdots +\frac{m(m-1)\cdots (m-n+1)}{n!}x^n+\frac{m(m-1)\cdots (m-n+1)}{(n+1)!}x^{n+1}(1+\xi )^{m-n-1}$<br>或 $(1+x)^m=1+mx+\frac{m(m-1)}{2!}x^2+\cdots $ $+\frac{m(m-1)\cdots (m-n+1)}{n!}x^n+o(x^n)$</p><h2 id="函数单调性的判断"><a href="#函数单调性的判断" class="headerlink" title="函数单调性的判断"></a><strong>函数单调性的判断</strong></h2><p><strong>Th1:</strong> 设函数$f(x)$在$(a,b)$区间内可导,如果对$\forall x\in (a,b)$,都有$f’(x)>0$(或$f’(x)<0$),则函数$f(x)$在$(a,b)$内是单调增加的(或单调减少)</p><p><strong>Th2:</strong> (取极值的必要条件)设函数$f(x)$在$x_0$处可导,且在$x_0$处取极值,则$f’(x_0)=0$。</p><p><strong>Th3:</strong> (取极值的第一充分条件)设函数$f(x)$在$x_0$的某一邻域内可微,且$f’(x_0)=0$(或$f(x)$在$x_0$处连续,但$f’(x_0)$不存在。)<br>(1)若当$x$经过$x_0$时,$f’(x)$由“+”变“-”,则$f(x_0)$为极大值;<br>(2)若当$x$经过$x_0$时,$f’(x)$由“-”变“+”,则$f(x_0)$为极小值;<br>(3)若$f’(x)$经过$x=x_0$的两侧不变号,则$f(x_0)$不是极值。</p><p><strong>Th4:</strong> (取极值的第二充分条件)设$f(x)$在点$x_0$处有$f’’(x)\ne 0$,且$f’(x_0)=0$,则 当$f’’(x_0)<0$时,$f(x_0)$为极大值;<br>当$f’’(x_0)>0$时,$f(x_0)$为极小值。<br>注:如果$f’’(x_0)<0$,此方法失效。</p><h2 id="渐近线的求法"><a href="#渐近线的求法" class="headerlink" title="渐近线的求法"></a><strong>渐近线的求法</strong></h2><p>(1)水平渐近线 若$lim_{x\to +\infty }f(x)=b$,或$lim_{x\to -\infty }f(x)=b$,则</p><p>$y=b$称为函数$y=f(x)$的水平渐近线。</p><p>(2)铅直渐近线 若$lim_{x\to x_0^-}f(x)=\infty $,或$lim_{x\to x_0^+}f(x)=\infty $,则</p><p>$x=x_0$称为$y=f(x)$的铅直渐近线。</p><p>(3)斜渐近线 若$a=lim_{x\to \infty }\frac{f(x)}{x},\quad b=lim_{x\to \infty }[f(x)-ax]$,则<br>$y=ax+b$称为$y=f(x)$的斜渐近线。</p><h2 id="函数凹凸性的判断"><a href="#函数凹凸性的判断" class="headerlink" title="函数凹凸性的判断"></a><strong>函数凹凸性的判断</strong></h2><p><strong>Th1:</strong> (凹凸性的判别定理)若在I上$f’’(x)<0$(或$f’’(x)>0$),则$f(x)$在I上是凸的(或凹的)。</p><p><strong>Th2:</strong> (拐点的判别定理1)若在$x_0$处$f’’(x)=0$,(或$f’’(x)$不存在),当$x$变动经过$x_0$时,$f’’(x)$变号,则$(x_0,f(x_0))$为拐点。</p><p><strong>Th3:</strong> (拐点的判别定理2)设$f(x)$在$x_0$点的某邻域内有三阶导数,且$f’’(x)=0$,$f’’’(x)\ne 0$,则$(x_0,f(x_0))$为拐点。</p><h2 id="弧微分"><a href="#弧微分" class="headerlink" title="弧微分"></a><strong>弧微分</strong></h2><p>$dS=\sqrt{1+y’^2}dx$</p><h2 id="曲率"><a href="#曲率" class="headerlink" title="曲率"></a><strong>曲率</strong></h2><p>曲线$y=f(x)$在点$(x,y)$处的曲率$k=\frac{|y’’|}{(1+y’^2)^{\tfrac{3}{2}}}$。</p><p>对于参数方程</p><p>$\begin{cases} x=\varphi (t)\\<br>y=\psi (t) \end{cases}$ $k=\frac{|\varphi(t)’\psi (t)’’-\varphi (t)’’\psi (t)’|}{[\varphi ‘^2(t)+\psi ‘^2(t)]^{\frac{3}{2}}}$</p><h2 id="曲率半径"><a href="#曲率半径" class="headerlink" title="曲率半径"></a><strong>曲率半径</strong></h2><p>曲线在点$M$处的曲率$k(k\ne 0)$与曲线在点$M$处的曲率半径$\rho $有如下关系:$\rho =\frac{1}{k}$。</p><h1 id="线性代数"><a href="#线性代数" class="headerlink" title="线性代数"></a>线性代数</h1><h2 id="行列式"><a href="#行列式" class="headerlink" title="行列式"></a>行列式</h2><p><strong>行列式按行(列)展开定理</strong></p><p>(1) 设$A=(a_{ij})<em>{n\times n}$,则:$a</em>{i1}A_{j1} +a_{i2}A_{j2} + \cdots + a_{in}A_{jn} = \begin{cases}|A|,i=j\\ 0,i \neq j\end{cases}$</p><p>或$a_{1i}A_{1j} + a_{2i}A_{2j} + \cdots + a_{ni}A_{nj} = \begin{cases}|A|,i=j\ 0,i \neq j\end{cases}$即 $AA^*=A^<em>A = |A|E,$其中:$A^</em>= \begin{pmatrix} A_{11} & A_{12} & \ldots & A_{1n} \ A_{21} & A_{22} & \ldots & A_{2n} \ \ldots & \ldots & \ldots & \ldots \ A_{n1} & A_{n2} & \ldots & A_{nn} \ \end{pmatrix} = (A_{ji}) = {(A_{ij})}^{T}$</p><p>$D_{n} = \begin{vmatrix} 1 & 1 & \ldots & 1 \ x_{1} & x_{2} & \ldots & x_{n} \ \ldots & \ldots & \ldots & \ldots \ x_{1}^{n - 1} & x_{2}^{n - 1} & \ldots & x_{n}^{n - 1} \ \end{vmatrix} = \prod_{1 \leq j < i \leq n}^{},(x_{i} - x_{j})$</p><p>(2) 设$A,B$为$n$阶方阵,则$\left| {AB} \right| = \left| A \right|\left| B \right| = \left| B \right|\left| A \right| = \left| {BA} \right|$,但$\left| A \pm B \right| = \left| A \right| \pm \left| B \right|$不一定成立。</p><p>(3) $\left| {kA} \right| = k^{n}\left| A \right|$,$A$为$n$阶方阵。</p><p>(4) 设$A$为$n$阶方阵,$|A^{T}| = |A|;|A^{- 1}| = |A|^{- 1}$(若$A$可逆),$|A^{*}| = |A|^{n - 1}$</p><p>$n \geq 2$</p><p>(5) $\left| \begin{matrix} & {A\quad O} \ & {O\quad B} \ \end{matrix} \right| = \left| \begin{matrix} & {A\quad C} \ & {O\quad B} \ \end{matrix} \right| = \left| \begin{matrix} & {A\quad O} \ & {C\quad B} \ \end{matrix} \right| =| A||B|$<br>,$A,B$为方阵,但$\left| \begin{matrix} {O} & A_{m \times m} \ B_{n \times n} & { O} \ \end{matrix} \right| = ({- 1)}^|A||B|$ 。</p><p>(6) 范德蒙行列式$D_{n} = \begin{vmatrix} 1 & 1 & \ldots & 1 \ x_{1} & x_{2} & \ldots & x_{n} \ \ldots & \ldots & \ldots & \ldots \ x_{1}^{n - 1} & x_{2}^{n 1} & \ldots & x_{n}^{n - 1} \ \end{vmatrix} = \prod_{1 \leq j < i \leq n}^{},(x_{i} - x_{j})$</p><p>设$A$是$n$阶方阵,$\lambda_{i}(i = 1,2\cdots,n)$是$A$的$n$个特征值,则<br>$|A| = \prod_{i = 1}^{n}\lambda_{i}$</p><h2 id="矩阵"><a href="#矩阵" class="headerlink" title="矩阵"></a>矩阵</h2><p>矩阵:$m \times n$个数$a_ij$排成$m$行$n$列的表格$\begin{bmatrix} a_{11}\quad a_{12}\quad\cdots\quad a_{1n} \ a_{21}\quad a_{22}\quad\cdots\quad a_{2n} \ \quad\cdots\cdots\cdots\cdots\cdots \ a_{m1}\quad a_{m2}\quad\cdots\quad a_{mn} \ \end{bmatrix}$ 称为矩阵,简记为$A$,或者$\left( a_{ij} \right)_{m \times n}$ 。若$m = n$,则称$A$是$n$阶矩阵或$n$阶方阵。</p><p><strong>矩阵的线性运算</strong></p><p><strong>1.矩阵的加法</strong></p><p>设$A = (a_{ij}),B = (b_{ij})$是两个$m \times n$矩阵,则$m \times n$ 矩阵$C = c_{ij} = a_{ij} + b_{ij}$称为矩阵$A$与$B$的和,记为$A + B = C$ 。</p><p><strong>2.矩阵的数乘</strong></p><p>设$A = (a_{ij})$是$m \times n$矩阵,$k$是一个常数,则$m \times n$矩阵$(ka_{ij})$称为数$k$与矩阵$A$的数乘,记为${kA}$。</p><p><strong>3.矩阵的乘法</strong></p><p>设$A = (a_{ij})$是$m \times n$矩阵,$B = (b_{ij})$是$n \times s$矩阵,那么$m \times s$矩阵$C = (c_{ij})$,其中$c_{ij} = a_{i1}b_{1j} + a_{i2}b_{2j} + \cdots + a_{in}b_{nj} = \sum_{k =1}^na_{ik}b_{kj}$称为${AB}$的乘积,记为$C = AB$ 。</p><p><strong>4.</strong> $\mathbf{A}^{\mathbf T}$<strong>、</strong>$\mathbf{A}^{\mathbf -1}$<strong>、</strong>$\mathbf A^{\mathbf \star}$<strong>三者之间的关系</strong></p><p>(1) $(A^T)^T = A,(AB)^T = B^TA^T,(kA)^T = kA^T,{(A \pm B)}^T = A^T \pm B^T$</p><p>(2) $(A^{- 1})^{- 1} = A,(AB)^{- 1} = B^{- 1}A^{- 1},({kA})^{- 1} = \frac{1}{k}A^{- 1},$</p><p>但 ${(A \pm B)}^{- 1} = A^{- 1} \pm B^{- 1}$不一定成立。</p><p>(3) $(A^\star )^\star = |A|^{n - 2}\ A\ \ (n \geq 3)$,$(AB)^\star = B^\star A^\star ,$ $({kA})^\star = k^{n -1}A^\star {\ \ }(n \geq 2)$</p><p>但$(A \pm B)^\star = A^\star \pm B^\star $不一定成立。</p><p>(4) $(A^{- 1})^T = (A^T)^{- 1},\ (A^{- 1})^\star =(AA^\star )^{- 1},(A^\star )^T = (A^T)^\star $</p><p><strong>5.有关</strong>$\mathbf A^{\mathbf \star}$<strong>的结论</strong></p><p>(1) $AA^\star = A^\star A = |A|E$</p><p>(2) $|A^\star | = |A|^{n -1}\ (n \geq 2),\ \ \ \ {(kA)}^\star = k^{n -1}A^\star ,\ \ (A^\star )^\star = |A|^{n - 2}A(n \geq 3)$</p><p>(3) 若$A$可逆,则$A^\star = |A|A^{-1},(A^\star )^\star = \frac{1}{|A|}A$(4) 若$A$为$n$阶方阵,则:</p><p>$r(A^*)=\begin{cases}n,\quad r(A)=n\ 1,\quad r(A)=n-1\ 0,\quad r(A)<n-1\end{cases}$</p><p><strong>6.有关</strong>$\mathbf{A}^{\mathbf{-1}}$<strong>的结论</strong></p><p>$A$可逆$\Leftrightarrow AB = E; \Leftrightarrow |A| \neq 0; \Leftrightarrow r(A) = n;$</p><p>$\Leftrightarrow A$可以表示为初等矩阵的乘积;$\Leftrightarrow A;\Leftrightarrow Ax = 0$。</p><p><strong>7.有关矩阵秩的结论</strong></p><p>(1) 秩$r(A)$=行秩=列秩;</p><p>(2) $r(A_{m \times n}) \leq \min(m,n);$</p><p>(3) $A \neq 0 \Rightarrow r(A) \geq 1$;</p><p>(4) $r(A \pm B) \leq r(A) + r(B);$</p><p>(5) 初等变换不改变矩阵的秩</p><p>(6) $r(A) + r(B) - n \leq r(AB) \leq \min(r(A),r(B)),$特别若$AB = O$<br>则:$r(A) + r(B) \leq n$</p><p>(7) 若$A^{-1}$存在$\Rightarrow r(AB) = r(B);$ 若$B^{-1}$存在<br>$\Rightarrow r(AB) = r(A);$<br>若$r(A_{m \times n}) = n \Rightarrow r(AB) = r(B);$ 若$r(A_{m \times s}) = n\Rightarrow r(AB) = r(A)$。</p><p>(8) $r(A_{m \times s}) = n \Leftrightarrow Ax = 0$只有零解</p><p><strong>8.分块求逆公式</strong></p><p>$\begin{pmatrix} A & O \ O & B \ \end{pmatrix}^{-1} = \begin{pmatrix} A^{-1} & O \ O & B^{-1} \ \end{pmatrix}$; $\begin{pmatrix} A & C \ O & B \\end{pmatrix}^{-1} = \begin{pmatrix} A^{-1}& - A^{-1}CB^{-1} \ O & B^{-1} \ \end{pmatrix}$;</p><p>$\begin{pmatrix} A & O \ C & B \ \end{pmatrix}^{-1} = \begin{pmatrix} A^{-1}&{O} \ - B^{-1}CA^{-1} & B^{-1} \\end{pmatrix}$; $\begin{pmatrix} O & A \ B & O \ \end{pmatrix}^{-1} =\begin{pmatrix} O & B^{-1} \ A^{-1} & O \ \end{pmatrix}$</p><p>这里$A$,$B$均为可逆方阵。</p><h2 id="向量"><a href="#向量" class="headerlink" title="向量"></a>向量</h2><p><strong>1.有关向量组的线性表示</strong></p><p>(1)$\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$线性相关$\Leftrightarrow$至少有一个向量可以用其余向量线性表示。</p><p>(2)$\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$线性无关,$\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$,$\beta$线性相关$\Leftrightarrow \beta$可以由$\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$唯一线性表示。</p><p>(3) $\beta$可以由$\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$线性表示<br>$\Leftrightarrow r(\alpha_{1},\alpha_{2},\cdots,\alpha_{s}) =r(\alpha_{1},\alpha_{2},\cdots,\alpha_{s},\beta)$ 。</p><p><strong>2.有关向量组的线性相关性</strong></p><p>(1)部分相关,整体相关;整体无关,部分无关.</p><p>(2) ① $n$个$n$维向量<br>$\alpha_{1},\alpha_{2}\cdots\alpha_{n}$线性无关$\Leftrightarrow \left|\left\lbrack \alpha_{1}\alpha_{2}\cdots\alpha_{n} \right\rbrack \right| \neq0$, $n$个$n$维向量$\alpha_{1},\alpha_{2}\cdots\alpha_{n}$线性相关<br>$\Leftrightarrow |\lbrack\alpha_{1},\alpha_{2},\cdots,\alpha_{n}\rbrack| = 0$</p><p>② $n + 1$个$n$维向量线性相关。</p><p>③ 若$\alpha_{1},\alpha_{2}\cdots\alpha_{S}$线性无关,则添加分量后仍线性无关;或一组向量线性相关,去掉某些分量后仍线性相关。</p><p><strong>3.有关向量组的线性表示</strong></p><p>(1) $\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$线性相关$\Leftrightarrow$至少有一个向量可以用其余向量线性表示。</p><p>(2) $\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$线性无关,$\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$,$\beta$线性相关$\Leftrightarrow\beta$ 可以由$\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$唯一线性表示。</p><p>(3) $\beta$可以由$\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$线性表示<br>$\Leftrightarrow r(\alpha_{1},\alpha_{2},\cdots,\alpha_{s}) =r(\alpha_{1},\alpha_{2},\cdots,\alpha_{s},\beta)$</p><p><strong>4.向量组的秩与矩阵的秩之间的关系</strong></p><p>设$r(A_{m \times n}) =r$,则$A$的秩$r(A)$与$A$的行列向量组的线性相关性关系为:</p><p>(1) 若$r(A_{m \times n}) = r = m$,则$A$的行向量组线性无关。</p><p>(2) 若$r(A_{m \times n}) = r < m$,则$A$的行向量组线性相关。</p><p>(3) 若$r(A_{m \times n}) = r = n$,则$A$的列向量组线性无关。</p><p>(4) 若$r(A_{m \times n}) = r < n$,则$A$的列向量组线性相关。</p><p><strong>5.</strong>$\mathbf{n}$<strong>维向量空间的基变换公式及过渡矩阵</strong></p><p>若$\alpha_{1},\alpha_{2},\cdots,\alpha_{n}$与$\beta_{1},\beta_{2},\cdots,\beta_{n}$是向量空间$V$的两组基,则基变换公式为:<br>$$<br>(\beta_{1},\beta_{2},\cdots,\beta_{n}) = (\alpha_{1},\alpha_{2},\cdots,\alpha_{n})\begin{bmatrix} c_{11}& c_{12}& \cdots & c_{1n} \ c_{21}& c_{22}&\cdots & c_{2n} \ \cdots & \cdots & \cdots & \cdots \ c_{n1}& c_{n2} & \cdots & c_{nn} \\end{bmatrix} = (\alpha_{1},\alpha_{2},\cdots,\alpha_{n})C<br>$$</p><p>其中$C$是可逆矩阵,称为由基$\alpha_{1},\alpha_{2},\cdots,\alpha_{n}$到基$\beta_{1},\beta_{2},\cdots,\beta_{n}$的过渡矩阵。</p><p><strong>6.坐标变换公式</strong></p><p>若向量$\gamma$在基$\alpha_{1},\alpha_{2},\cdots,\alpha_{n}$与基$\beta_{1},\beta_{2},\cdots,\beta_{n}$的坐标分别是<br>$X = {(x_{1},x_{2},\cdots,x_{n})}^{T}$,</p><p>$Y = \left( y_{1},y_{2},\cdots,y_{n} \right)^{T}$ 即: $\gamma =x_{1}\alpha_{1} + x_{2}\alpha_{2} + \cdots + x_{n}\alpha_{n} = y_{1}\beta_{1} +y_{2}\beta_{2} + \cdots + y_{n}\beta_{n}$,则向量坐标变换公式为$X = CY$ 或$Y = C^{- 1}X$,其中$C$是从基$\alpha_{1},\alpha_{2},\cdots,\alpha_{n}$到基$\beta_{1},\beta_{2},\cdots,\beta_{n}$的过渡矩阵。</p><p><strong>7.向量的内积</strong></p><p>$(\alpha,\beta) = a_{1}b_{1} + a_{2}b_{2} + \cdots + a_{n}b_{n} = \alpha^{T}\beta = \beta^{T}\alpha$</p><p><strong>8.Schmidt正交化</strong></p><p>若$\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$线性无关,则可构造$\beta_{1},\beta_{2},\cdots,\beta_{s}$使其两两正交,且$\beta_{i}$仅是$\alpha_{1},\alpha_{2},\cdots,\alpha_{i}$的线性组合$(i= 1,2,\cdots,n)$,再把$\beta_{i}$单位化,记$\gamma_{i} =\frac{\beta_{i}}{\left| \beta_{i}\right|}$,则$\gamma_{1},\gamma_{2},\cdots,\gamma_{i}$是规范正交向量组。其中<br>$\beta_{1} = \alpha_{1}$, $\beta_{2} = \alpha_{2} -\frac{(\alpha_{2},\beta_{1})}{(\beta_{1},\beta_{1})}\beta_{1}$ , $\beta_{3} =\alpha_{3} - \frac{(\alpha_{3},\beta_{1})}{(\beta_{1},\beta_{1})}\beta_{1} -\frac{(\alpha_{3},\beta_{2})}{(\beta_{2},\beta_{2})}\beta_{2}$ ,</p><p>…………</p><p>$\beta_{s} = \alpha_{s} - \frac{(\alpha_{s},\beta_{1})}{(\beta_{1},\beta_{1})}\beta_{1} - \frac{(\alpha_{s},\beta_{2})}{(\beta_{2},\beta_{2})}\beta_{2} - \cdots - \frac{(\alpha_{s},\beta_{s - 1})}{(\beta_{s - 1},\beta_{s - 1})}\beta_{s - 1}$</p><p><strong>9.正交基及规范正交基</strong></p><p>向量空间一组基中的向量如果两两正交,就称为正交基;若正交基中每个向量都是单位向量,就称其为规范正交基。</p><h2 id="线性方程组"><a href="#线性方程组" class="headerlink" title="线性方程组"></a>线性方程组</h2><p><strong>1.克莱姆法则</strong></p><p>线性方程组$\begin{cases} a_{11}x_{1} + a_{12}x_{2} + \cdots +a_{1n}x_{n} = b_{1} \ a_{21}x_{1} + a_{22}x_{2} + \cdots + a_{2n}x_{n} =b_{2} \ \quad\cdots\cdots\cdots\cdots\cdots\cdots\cdots\cdots\cdots \ a_{n1}x_{1} + a_{n2}x_{2} + \cdots + a_{nn}x_{n} = b_{n} \ \end{cases}$,如果系数行列式$D = \left| A \right| \neq 0$,则方程组有唯一解,$x_{1} = \frac{D_{1}}{D},x_{2} = \frac{D_{2}}{D},\cdots,x_{n} =\frac{D_{n}}{D}$,其中$D_{j}$是把$D$中第$j$列元素换成方程组右端的常数列所得的行列式。</p><p><strong>2.</strong> $n$阶矩阵$A$可逆$\Leftrightarrow Ax = 0$只有零解。$\Leftrightarrow\forall b,Ax = b$总有唯一解,一般地,$r(A_{m \times n}) = n \Leftrightarrow Ax= 0$只有零解。</p><p><strong>3.非奇次线性方程组有解的充分必要条件,线性方程组解的性质和解的结构</strong></p><p>(1) 设$A$为$m \times n$矩阵,若$r(A_{m \times n}) = m$,则对$Ax =b$而言必有$r(A) = r(A \vdots b) = m$,从而$Ax = b$有解。</p><p>(2) 设$x_{1},x_{2},\cdots x_{s}$为$Ax = b$的解,则$k_{1}x_{1} + k_{2}x_{2}\cdots + k_{s}x_{s}$当$k_{1} + k_{2} + \cdots + k_{s} = 1$时仍为$Ax =b$的解;但当$k_{1} + k_{2} + \cdots + k_{s} = 0$时,则为$Ax =0$的解。特别$\frac{x_{1} + x_{2}}{2}$为$Ax = b$的解;$2x_{3} - (x_{1} +x_{2})$为$Ax = 0$的解。</p><p>(3) 非齐次线性方程组${Ax} = b$无解$\Leftrightarrow r(A) + 1 =r(\overline{A}) \Leftrightarrow b$不能由$A$的列向量$\alpha_{1},\alpha_{2},\cdots,\alpha_{n}$线性表示。</p><p><strong>4.奇次线性方程组的基础解系和通解,解空间,非奇次线性方程组的通解</strong></p><p>(1) 齐次方程组${Ax} = 0$恒有解(必有零解)。当有非零解时,由于解向量的任意线性组合仍是该齐次方程组的解向量,因此${Ax}= 0$的全体解向量构成一个向量空间,称为该方程组的解空间,解空间的维数是$n - r(A)$,解空间的一组基称为齐次方程组的基础解系。</p><p>(2) $\eta_{1},\eta_{2},\cdots,\eta_{t}$是${Ax} = 0$的基础解系,即:</p><ol><li><p>$\eta_{1},\eta_{2},\cdots,\eta_{t}$是${Ax} = 0$的解;</p></li><li><p>$\eta_{1},\eta_{2},\cdots,\eta_{t}$线性无关;</p></li><li><p>${Ax} = 0$的任一解都可以由$\eta_{1},\eta_{2},\cdots,\eta_{t}$线性表出.<br>$k_{1}\eta_{1} + k_{2}\eta_{2} + \cdots + k_{t}\eta_{t}$是${Ax} = 0$的通解,其中$k_{1},k_{2},\cdots,k_{t}$是任意常数。</p></li></ol><h2 id="矩阵的特征值和特征向量"><a href="#矩阵的特征值和特征向量" class="headerlink" title="矩阵的特征值和特征向量"></a>矩阵的特征值和特征向量</h2><p><strong>1.矩阵的特征值和特征向量的概念及性质</strong></p><p>(1) 设$\lambda$是$A$的一个特征值,则 ${kA},{aA} + {bE},A^{2},A^{m},f(A),A^{T},A^{- 1},A^{*}$有一个特征值分别为<br>${kλ},{aλ} + b,\lambda^{2},\lambda^{m},f(\lambda),\lambda,\lambda^{- 1},\frac{|A|}{\lambda},$且对应特征向量相同($A^{T}$ 例外)。</p><p>(2)若$\lambda_{1},\lambda_{2},\cdots,\lambda_{n}$为$A$的$n$个特征值,则$\sum_{i= 1}^{n}\lambda_{i} = \sum_{i = 1}^{n}a_,\prod_{i = 1}^{n}\lambda_{i}= |A|$ ,从而$|A| \neq 0 \Leftrightarrow A$没有特征值。</p><p>(3)设$\lambda_{1},\lambda_{2},\cdots,\lambda_{s}$为$A$的$s$个特征值,对应特征向量为$\alpha_{1},\alpha_{2},\cdots,\alpha_{s}$,</p><p>若: $\alpha = k_{1}\alpha_{1} + k_{2}\alpha_{2} + \cdots + k_{s}\alpha_{s}$ ,</p><p>则: $A^{n}\alpha = k_{1}A^{n}\alpha_{1} + k_{2}A^{n}\alpha_{2} + \cdots +k_{s}A^{n}\alpha_{s} = k_{1}\lambda_{1}^{n}\alpha_{1} +k_{2}\lambda_{2}^{n}\alpha_{2} + \cdots k_{s}\lambda_{s}^{n}\alpha_{s}$ 。</p><p><strong>2.相似变换、相似矩阵的概念及性质</strong></p><p>(1) 若$A \sim B$,则</p><ol><li><p>$A^{T} \sim B^{T},A^{- 1} \sim B^{- 1},,A^{\star} \sim B^{\star}$</p></li><li><p>$|A| = |B|,\sum_{i = 1}^{n}A_ = \sum_{i =1}^{n}b_,r(A) = r(B)$</p></li><li><p>$|\lambda E - A| = |\lambda E - B|$,对$\forall\lambda$成立</p></li></ol><p><strong>3.矩阵可相似对角化的充分必要条件</strong></p><p>(1)设$A$为$n$阶方阵,则$A$可对角化$\Leftrightarrow$对每个$k_{i}$重根特征值$\lambda_{i}$,有$n-r(\lambda_{i}E - A) = k_{i}$</p><p>(2) 设$A$可对角化,则由$P^{- 1}{AP} = \Lambda,$有$A = {PΛ}P^{-1}$,从而$A^{n} = P\Lambda^{n}P^{- 1}$</p><p>(3) 重要结论</p><ol><li><p>若$A \sim B,C \sim D$,则$\begin{bmatrix} A & O \ O & C \\end{bmatrix} \sim \begin{bmatrix} B & O \ O & D \\end{bmatrix}$.</p></li><li><p>若$A \sim B$,则$f(A) \sim f(B),\left| f(A) \right| \sim \left| f(B)\right|$,其中$f(A)$为关于$n$阶方阵$A$的多项式。</p></li><li><p>若$A$为可对角化矩阵,则其非零特征值的个数(重根重复计算)=秩($A$)</p></li></ol><p><strong>4.实对称矩阵的特征值、特征向量及相似对角阵</strong></p><p>(1)相似矩阵:设$A,B$为两个$n$阶方阵,如果存在一个可逆矩阵$P$,使得$B =P^{- 1}{AP}$成立,则称矩阵$A$与$B$相似,记为$A \sim B$。</p><p>(2)相似矩阵的性质:如果$A \sim B$则有:</p><ol><li><p>$A^{T} \sim B^{T}$</p></li><li><p>$A^{- 1} \sim B^{- 1}$ (若$A$,$B$均可逆)</p></li><li><p>$A^{k} \sim B^{k}$ ($k$为正整数)</p></li><li><p>$\left| {λE} - A \right| = \left| {λE} - B \right|$,从而$A,B$<br>有相同的特征值</p></li><li><p>$\left| A \right| = \left| B \right|$,从而$A,B$同时可逆或者不可逆</p></li><li><p>秩$\left( A \right) =$秩$\left( B \right),\left| {λE} - A \right| =\left| {λE} - B \right|$,$A,B$不一定相似</p></li></ol><h2 id="二次型"><a href="#二次型" class="headerlink" title="二次型"></a>二次型</h2><p><strong>1.</strong>$\mathbf{n}$<strong>个变量</strong>$\mathbf{x}<em>{\mathbf{1}}\mathbf{,}\mathbf{x}</em>{\mathbf{2}}\mathbf{,\cdots,}\mathbf{x}_{\mathbf{n}}$<strong>的二次齐次函数</strong></p><p>$f(x_1,x_2,\cdots,x_n) = \sum_{i = 1}^n{\sum_{j =1}^n{a_{ij}x_iy_j}}$,其中$a_ = a_(i,j =1,2,\cdots,n)$,称为$n$元二次型,简称二次型. 若令$x = \ \begin{bmatrix}x_{1} \ x_{1} \ \vdots \ x_{n} \ \end{bmatrix},A = \begin{bmatrix} a_{11}& a_{12}& \cdots & a_{1n} \ a_{21}& a_{22}& \cdots & a_{2n} \ \cdots &\cdots &\cdots &\cdots \ a_{n1}& a_{n2} & \cdots & a_{nn} \\end{bmatrix}$,这二次型$f$可改写成矩阵向量形式$f =x^{T}{Ax}$。其中$A$称为二次型矩阵,因为$a_ =a_(i,j =1,2,\cdots,n)$,所以二次型矩阵均为对称矩阵,且二次型与对称矩阵一一对应,并把矩阵$A$的秩称为二次型的秩。</p><p><strong>2.惯性定理,二次型的标准形和规范形</strong></p><p>(1) 惯性定理</p><p>对于任一二次型,不论选取怎样的合同变换使它化为仅含平方项的标准型,其正负惯性指数与所选变换无关,这就是所谓的惯性定理。</p><p>(2) 标准形</p><p>二次型$f = \left( x_{1},x_{2},\cdots,x_{n} \right) =x^{T}{Ax}$经过合同变换$x = {Cy}$化为$f = x^{T}{Ax} =y^{T}C^{T}{AC}$</p><p>$y = \sum_{i = 1}^{r}{d_{i}y_{i}^{2}}$称为 $f(r \leq n)$的标准形。在一般的数域内,二次型的标准形不是唯一的,与所作的合同变换有关,但系数不为零的平方项的个数由$r(A)$唯一确定。</p><p>(3) 规范形</p><p>任一实二次型$f$都可经过合同变换化为规范形$f = z_{1}^{2} + z_{2}^{2} + \cdots z_{p}^{2} - z_{p + 1}^{2} - \cdots -z_{r}^{2}$,其中$r$为$A$的秩,$p$为正惯性指数,$r -p$为负惯性指数,且规范型唯一。</p><p><strong>3.用正交变换和配方法化二次型为标准形,二次型及其矩阵的正定性</strong></p><p>设$A$正定$\Rightarrow {kA}(k > 0),A^{T},A^{- 1},A^{*}$正定;$|A| >0$,$A$可逆;$a_ > 0$,且$|A_| > 0$</p><p>$A$,$B$正定$\Rightarrow A +B$正定,但${AB}$,${BA}$不一定正定</p><p>$A$正定$\Leftrightarrow f(x) = x^{T}{Ax} > 0,\forall x \neq 0$</p><p>$\Leftrightarrow A$的各阶顺序主子式全大于零</p><p>$\Leftrightarrow A$的所有特征值大于零</p><p>$\Leftrightarrow A$的正惯性指数为$n$</p><p>$\Leftrightarrow$存在可逆阵$P$使$A = P^{T}P$</p><p>$\Leftrightarrow$存在正交矩阵$Q$,使$Q^{T}{AQ} = Q^{- 1}{AQ} =\begin{pmatrix} \lambda_{1} & & \ \begin{matrix} & \ & \ \end{matrix} &\ddots & \ & & \lambda_{n} \ \end{pmatrix},$</p><p>其中$\lambda_{i} > 0,i = 1,2,\cdots,n.$正定$\Rightarrow {kA}(k >0),A^{T},A^{- 1},A^{*}$正定; $|A| > 0,A$可逆;$a_ >0$,且$|A_| > 0$ 。</p><h1 id="概率论和数理统计"><a href="#概率论和数理统计" class="headerlink" title="概率论和数理统计"></a>概率论和数理统计</h1><h2 id="随机事件和概率"><a href="#随机事件和概率" class="headerlink" title="随机事件和概率"></a>随机事件和概率</h2><p><strong>1.事件的关系与运算</strong></p><p>(1) 子事件:$A \subset B$,若$A$发生,则$B$发生。</p><p>(2) 相等事件:$A = B$,即$A \subset B$,且$B \subset A$ 。</p><p>(3) 和事件:$A\bigcup B$(或$A + B$),$A$与$B$中至少有一个发生。</p><p>(4) 差事件:$A - B$,$A$发生但$B$不发生。</p><p>(5) 积事件:$A\bigcap B$(或${AB}$),$A$与$B$同时发生。</p><p>(6) 互斥事件(互不相容):$A\bigcap B$=$\varnothing$。</p><p>(7) 互逆事件(对立事件):<br>$A\bigcap B=\varnothing ,A\bigcup B=\Omega ,A=\bar{B},B=\bar{A}$<br><strong>2.运算律</strong><br>(1) 交换律:$A\bigcup B=B\bigcup A,A\bigcap B=B\bigcap A$<br>(2) 结合律:$(A\bigcup B)\bigcup C=A\bigcup (B\bigcup C)$<br>(3) 分配律:$(A\bigcap B)\bigcap C=A\bigcap (B\bigcap C)$<br><strong>3.德$\centerdot $摩根律</strong></p><p>$\overline{A\bigcup B}=\bar{A}\bigcap \bar{B}$ $\overline{A\bigcap B}=\bar{A}\bigcup \bar{B}$<br><strong>4.完全事件组</strong> </p><p>$A_1A_2\cdots A_n$两两互斥,且和事件为必然事件,即$A_i\bigcap A_j=\varnothing, i\ne j ,\underset{i=1}{\overset{n}{\mathop \bigcup }},=\Omega $</p><p><strong>5.概率的基本公式</strong><br>(1)条件概率:<br> $P(B|A)=\frac{P(AB)}{P(A)}$,表示$A$发生的条件下,$B$发生的概率。<br>(2)全概率公式:<br>$P(A)=\sum\limits_{i=1}^{n}{P(A|B_i)P(B_i),B_iB_j}=\varnothing ,i\ne j,\underset{i=1}{\overset{n}{\mathop{\bigcup }}},B_i=\Omega $<br>(3) Bayes公式:</p><p>$P(B_j|A)=\frac{P(A|B_j)P(B_j)}{\sum\limits_{i=1}^{n}{P(A|B_i)P(B_i)}},j=1,2,\cdots ,n$<br>注:上述公式中事件$B_i$的个数可为可列个。<br>(4)乘法公式:<br>$P(A_1A_2)=P(A_1)P(A_2|A_1)=P(A_2)P(A_1|A_2)$<br>$P(A_1A_2\cdots A_n)=P(A_1)P(A_2|A_1)P(A_3|A_1A_2)\cdots P(A_n|A_1A_2\cdots A_{n-1})$</p><p><strong>6.事件的独立性</strong><br>(1)$A$与$B$相互独立$\Leftrightarrow P(AB)=P(A)P(B)$<br>(2)$A$,$B$,$C$两两独立<br>$\Leftrightarrow P(AB)=P(A)P(B)$;$P(BC)=P(B)P(C)$ ;$P(AC)=P(A)P(C)$;<br>(3)$A$,$B$,$C$相互独立<br>$\Leftrightarrow P(AB)=P(A)P(B)$; $P(BC)=P(B)P(C)$ ;<br>$P(AC)=P(A)P(C)$ ; $P(ABC)=P(A)P(B)P(C)$</p><p><strong>7.独立重复试验</strong> </p><p>将某试验独立重复$n$次,若每次实验中事件A发生的概率为$p$,则$n$次试验中$A$发生$k$次的概率为:<br>$P(X=k)=C_{n}^{k}p^k(1-p)^{n-k}$<br><strong>8.重要公式与结论</strong><br>$(1)P(\bar{A})=1-P(A)$<br>$(2)P(A\bigcup B)=P(A)+P(B)-P(AB)$<br> $P(A\bigcup B\bigcup C)=P(A)+P(B)+P(C)-P(AB)-P(BC)-P(AC)+P(ABC)$<br>$(3)P(A-B)=P(A)-P(AB)$<br>$(4)P(A\bar{B})=P(A)-P(AB),P(A)=P(AB)+P(A\bar{B}),$<br> $P(A\bigcup B)=P(A)+P(\bar{A}B)=P(AB)+P(A\bar{B})+P(\bar{A}B)$<br>(5)条件概率$P(\centerdot |B)$满足概率的所有性质,<br>例如:. $P(\bar{A}<em>{1}|B)=1-P(A_1|B)$<br>$P(A_1\bigcup A_2|B)=P(A_1|B)+P(A_2|B)-P(A_1A_2|B)$<br>$P(A_1A_2|B)=P(A_1|B)P(A_2|A_1B)$<br>(6)若$A_1,A_2,\cdots ,A_n$相互独立,则$P(\bigcap\limits</em>{i=1}^{n}{A_i})=\prod\limits_{i=1}^{n}{P(A_i)},$<br> $P(\bigcup\limits_{i=1}^{n}{A_i})=\prod\limits_{i=1}^{n}{(1-P(A_i))}$<br>(7)互斥、互逆与独立性之间的关系:<br>$A$与$B$互逆$\Rightarrow$ $A$与$B$互斥,但反之不成立,$A$与$B$互斥(或互逆)且均非零概率事件$\Rightarrow $$A$与$B$不独立.<br>(8)若$A_1,A_2,\cdots ,A_m,B_1,B_2,\cdots ,B_n$相互独立,则$f(A_1,A_2,\cdots ,A_m)$与$g(B_1,B_2,\cdots ,B_n)$也相互独立,其中$f(\centerdot ),g(\centerdot )$分别表示对相应事件做任意事件运算后所得的事件,另外,概率为1(或0)的事件与任何事件相互独立.</p><h2 id="随机变量及其概率分布"><a href="#随机变量及其概率分布" class="headerlink" title="随机变量及其概率分布"></a>随机变量及其概率分布</h2><p><strong>1.随机变量及概率分布</strong></p><p>取值带有随机性的变量,严格地说是定义在样本空间上,取值于实数的函数称为随机变量,概率分布通常指分布函数或分布律</p><p><strong>2.分布函数的概念与性质</strong></p><p>定义: $F(x) = P(X \leq x), - \infty < x < + \infty$</p><p>性质:(1)$0 \leq F(x) \leq 1$ </p><p>(2) $F(x)$单调不减</p><p>(3) 右连续$F(x + 0) = F(x)$ </p><p>(4) $F( - \infty) = 0,F( + \infty) = 1$</p><p><strong>3.离散型随机变量的概率分布</strong></p><p>$P(X = x_{i}) = p_{i},i = 1,2,\cdots,n,\cdots\quad\quad p_{i} \geq 0,\sum_{i =1}^{\infty}p_{i} = 1$</p><p><strong>4.连续型随机变量的概率密度</strong></p><p>概率密度$f(x)$;非负可积,且:</p><p>(1)$f(x) \geq 0,$ </p><p>(2)$\int_{- \infty}^{+\infty}{f(x){dx} = 1}$ </p><p>(3)$x$为$f(x)$的连续点,则:</p><p>$f(x) = F’(x)$分布函数$F(x) = \int_{- \infty}^{x}{f(t){dt}}$</p><p><strong>5.常见分布</strong></p><p>(1) 0-1分布:$P(X = k) = p^{k}(1 - p)^{1 - k},k = 0,1$</p><p>(2) 二项分布:$B(n,p)$: $P(X = k) = C_n^kp^k(1 - p)^{n - k},k =0,1,\cdots,n$</p><p>(3) <strong>Poisson</strong>分布:$p(\lambda)$: $P(X = k) = \frac{\lambda^{k}}{k!}e^{-\lambda},\lambda > 0,k = 0,1,2\cdots$</p><p>(4) 均匀分布$U(a,b)$:$f(x) = { \begin{matrix} & \frac{1}{b - a},a < x< b \ & 0, \ \end{matrix} $</p><p>(5) 正态分布:$N(\mu,\sigma^{2}):$ $\varphi(x) =\frac{1}{\sqrt{2\pi}\sigma}e^{- \frac{(x - \mu)^2}{2\sigma^2},\sigma > 0,\infty < x < + \infty$</p><p>(6)指数分布:$E(\lambda):f(x) ={ \begin{matrix} & \lambda e^{-{λx}},x > 0,\lambda > 0 \ & 0, \ \end{matrix} $</p><p>(7)几何分布:$G(p):P(X = k) = (1-p)^{k - 1}p,0 < p < 1,k = 1,2,\cdots.$</p><p>(8)超几何分布: $H(N,M,n):P(X = k) = \frac{C_{M}^{k}C_{N - M}^{n -k}}{C_{N}^{n}},k =0,1,\cdots,min(n,M)$</p><p><strong>6.随机变量函数的概率分布</strong></p><p>(1)离散型:$P(X = x_{1}) = p_{i},Y = g(X)$</p><p>则: $P(Y = y_{j}) = \sum_{g(x_{i}) = y_{i}}^{}{P(X = x_{i})}$</p><p>(2)连续型:$X\tilde{\ }f_{X}(x),Y = g(x)$</p><p>则:$F_{y}(y) = P(Y \leq y) = P(g(X) \leq y) = \int_{g(x) \leq y}^{}{f_{x}(x)dx}$, $f_{Y}(y) = F’_{Y}(y)$</p><p><strong>7.重要公式与结论</strong></p><p>(1) $X\sim N(0,1) \Rightarrow \varphi(0) = \frac{1}{\sqrt{2\pi}},\Phi(0) =\frac{1}{2},$ $\Phi( - a) = P(X \leq - a) = 1 - \Phi(a)$</p><p>(2) $X\sim N\left( \mu,\sigma^{2} \right) \Rightarrow \frac{X -\mu}{\sigma}\sim N\left( 0,1 \right),P(X \leq a) = \Phi(\frac{a -\mu}{\sigma})$</p><p>(3) $X\sim E(\lambda) \Rightarrow P(X > s + t|X > s) = P(X > t)$</p><p>(4) $X\sim G(p) \Rightarrow P(X = m + k|X > m) = P(X = k)$</p><p>(5) 离散型随机变量的分布函数为阶梯间断函数;连续型随机变量的分布函数为连续函数,但不一定为处处可导函数。</p><p>(6) 存在既非离散也非连续型随机变量。</p><h2 id="多维随机变量及其分布"><a href="#多维随机变量及其分布" class="headerlink" title="多维随机变量及其分布"></a>多维随机变量及其分布</h2><p><strong>1.二维随机变量及其联合分布</strong></p><p>由两个随机变量构成的随机向量$(X,Y)$, 联合分布为$F(x,y) = P(X \leq x,Y \leq y)$</p><p><strong>2.二维离散型随机变量的分布</strong></p><p>(1) 联合概率分布律 $P{ X = x_{i},Y = y_{j}} = p_{ij};i,j =1,2,\cdots$</p><p>(2) 边缘分布律 $p_{i \cdot} = \sum_{j = 1}^{\infty}p_{ij},i =1,2,\cdots$ $p_{\cdot j} = \sum_{i}^{\infty}p_{ij},j = 1,2,\cdots$</p><p>(3) 条件分布律 $P{ X = x_{i}|Y = y_{j}} = \frac{p_{ij}}{p_{\cdot j}}$<br>$P{ Y = y_{j}|X = x_{i}} = \frac{p_{ij}}{p_{i \cdot}}$</p><p><strong>3. 二维连续性随机变量的密度</strong></p><p>(1) 联合概率密度$f(x,y):$</p><ol><li><p>$f(x,y) \geq 0$ </p></li><li><p>$\int_{- \infty}^{+ \infty}{\int_{- \infty}^{+ \infty}{f(x,y)dxdy}} = 1$</p></li></ol><p>(2) 分布函数:$F(x,y) = \int_{- \infty}^{x}{\int_{- \infty}^{y}{f(u,v)dudv}}$</p><p>(3) 边缘概率密度: $f_{X}\left( x \right) = \int_{- \infty}^{+ \infty}{f\left( x,y \right){dy}}$ $f_{Y}(y) = \int_{- \infty}^{+ \infty}{f(x,y)dx}$</p><p>(4) 条件概率密度:$f_{X|Y}\left( x \middle| y \right) = \frac{f\left( x,y \right)}{f_{Y}\left( y \right)}$ $f_{Y|X}(y|x) = \frac{f(x,y)}{f_{X}(x)}$</p><p><strong>4.常见二维随机变量的联合分布</strong></p><p>(1) 二维均匀分布:$(x,y) \sim U(D)$ ,$f(x,y) = \begin{cases} \frac{1}{S(D)},(x,y) \in D \ 0,其他 \end{cases}$</p><p>(2) 二维正态分布:$(X,Y)\sim N(\mu_{1},\mu_{2},\sigma_{1}^{2},\sigma_{2}^{2},\rho)$,$(X,Y)\sim N(\mu_{1},\mu_{2},\sigma_{1}^{2},\sigma_{2}^{2},\rho)$</p><p>$f(x,y) = \frac{1}{2\pi\sigma_{1}\sigma_{2}\sqrt{1 - \rho^{2}}}.\exp\left{ \frac{- 1}{2(1 - \rho^{2})}\lbrack\frac{(x - \mu_{1})^{2}}{\sigma_{1}^{2}} - 2\rho\frac{(x - \mu_{1})(y - \mu_{2})}{\sigma_{1}\sigma_{2}} + \frac{(y - \mu_{2})^{2}}{\sigma_{2}^{2}}\rbrack \right}$</p><p><strong>5.随机变量的独立性和相关性</strong></p><p>$X$和$Y$的相互独立:$\Leftrightarrow F\left( x,y \right) = F_{X}\left( x \right)F_{Y}\left( y \right)$:</p><p>$\Leftrightarrow p_{ij} = p_{i \cdot} \cdot p_{\cdot j}$(离散型)<br>$\Leftrightarrow f\left( x,y \right) = f_{X}\left( x \right)f_{Y}\left( y \right)$(连续型)</p><p>$X$和$Y$的相关性:</p><p>相关系数$\rho_{XY} = 0$时,称$X$和$Y$不相关,<br>否则称$X$和$Y$相关</p><p><strong>6.两个随机变量简单函数的概率分布</strong></p><p>离散型: $P\left( X = x_{i},Y = y_{i} \right) = p_{ij},Z = g\left( X,Y \right)$ 则:</p><p>$P(Z = z_{k}) = P\left{ g\left( X,Y \right) = z_{k} \right} = \sum_{g\left( x_{i},y_{i} \right) = z_{k}}^{}{P\left( X = x_{i},Y = y_{j} \right)}$</p><p>连续型: $\left( X,Y \right) \sim f\left( x,y \right),Z = g\left( X,Y \right)$<br>则:</p><p>$F_{z}\left( z \right) = P\left{ g\left( X,Y \right) \leq z \right} = \iint_{g(x,y) \leq z}^{}{f(x,y)dxdy}$,$f_{z}(z) = F’_{z}(z)$</p><p><strong>7.重要公式与结论</strong></p><p>(1) 边缘密度公式: $f_{X}(x) = \int_{- \infty}^{+ \infty}{f(x,y)dy,}$<br>$f_{Y}(y) = \int_{- \infty}^{+ \infty}{f(x,y)dx}$</p><p>(2) $P\left{ \left( X,Y \right) \in D \right} = \iint_{D}{f\left( x,y \right){dxdy}}$</p><p>(3) 若$(X,Y)$服从二维正态分布$N(\mu_{1},\mu_{2},\sigma_{1}^{2},\sigma_{2}^{2},\rho)$<br>则有:</p><ol><li><p>$X\sim N\left( \mu_{1},\sigma_{1}^{2} \right),Y\sim N(\mu_{2},\sigma_{2}^{2}).$</p></li><li><p>$X$与$Y$相互独立$\Leftrightarrow \rho = 0$,即$X$与$Y$不相关。</p></li><li><p>$C_{1}X + C_{2}Y\sim N(C_{1}\mu_{1} + C_{2}\mu_{2},C_{1}^{2}\sigma_{1}^{2} + C_{2}^{2}\sigma_{2}^{2} + 2C_{1}C_{2}\sigma_{1}\sigma_{2}\rho)$</p></li><li><p>${\ X}$关于$Y=y$的条件分布为: $N(\mu_{1} + \rho\frac{\sigma_{1}}{\sigma_{2}}(y - \mu_{2}),\sigma_{1}^{2}(1 - \rho^{2}))$</p></li><li><p>$Y$关于$X = x$的条件分布为: $N(\mu_{2} + \rho\frac{\sigma_{2}}{\sigma_{1}}(x - \mu_{1}),\sigma_{2}^{2}(1 - \rho^{2}))$</p></li></ol><p>(4) 若$X$与$Y$独立,且分别服从$N(\mu_{1},\sigma_{1}^{2}),N(\mu_{1},\sigma_{2}^{2}),$<br>则:$\left( X,Y \right)\sim N(\mu_{1},\mu_{2},\sigma_{1}^{2},\sigma_{2}^{2},0),$</p><p>$C_{1}X + C_{2}Y\tilde{\ }N(C_{1}\mu_{1} + C_{2}\mu_{2},C_{1}^{2}\sigma_{1}^{2} C_{2}^{2}\sigma_{2}^{2}).$</p><p>(5) 若$X$与$Y$相互独立,$f\left( x \right)$和$g\left( x \right)$为连续函数, 则$f\left( X \right)$和$g(Y)$也相互独立。</p><h2 id="随机变量的数字特征"><a href="#随机变量的数字特征" class="headerlink" title="随机变量的数字特征"></a>随机变量的数字特征</h2><p><strong>1.数学期望</strong></p><p>离散型:$P\left{ X = x_{i} \right} = p_{i},E(X) = \sum_{i}{x_{i}p_{i}}$;</p><p>连续型: $X\sim f(x),E(X) = \int_{- \infty}^{+ \infty}{xf(x)dx}$</p><p>性质:</p><p>(1) $E(C) = C,E\lbrack E(X)\rbrack = E(X)$</p><p>(2) $E(C_{1}X + C_{2}Y) = C_{1}E(X) + C_{2}E(Y)$</p><p>(3) 若$X$和$Y$独立,则$E(XY) = E(X)E(Y)$ </p><p>(4)$\left\lbrack E(XY) \right\rbrack^{2} \leq E(X^{2})E(Y^{2})$</p><p><strong>2.方差</strong>:$D(X) = E\left\lbrack X - E(X) \right\rbrack^{2} = E(X^{2}) - \left\lbrack E(X) \right\rbrack^{2}$</p><p><strong>3.标准差</strong>:$\sqrt{D(X)}$,</p><p><strong>4.离散型:</strong>$D(X) = \sum_{i}^{}{\left\lbrack x_{i} - E(X) \right\rbrack^{2}p_{i}}$</p><p><strong>5.连续型:</strong>$D(X) = {\int_{- \infty}^{+ \infty}\left\lbrack x - E(X) \right\rbrack}^{2}f(x)dx$</p><p>性质:</p><p>(1)$\ D(C) = 0,D\lbrack E(X)\rbrack = 0,D\lbrack D(X)\rbrack = 0$</p><p>(2) $X$与$Y$相互独立,则$D(X \pm Y) = D(X) + D(Y)$</p><p>(3)$\ D\left( C_{1}X + C_{2} \right) = C_{1}^{2}D\left( X \right)$</p><p>(4) 一般有 $D(X \pm Y) = D(X) + D(Y) \pm 2Cov(X,Y) = D(X) + D(Y) \pm 2\rho\sqrt{D(X)}\sqrt{D(Y)}$</p><p>(5)$\ D\left( X \right) < E\left( X - C \right)^{2},C \neq E\left( X \right)$</p><p>(6)$D(X)=0 \Leftrightarrow P\left{ X = C \right}=1$</p><p><strong>6.随机变量函数的数学期望</strong></p><p>(1) 对于函数$Y = g(x)$</p><p>$X$为离散型:$P{ X = x_{i}} = p_{i},E(Y) = \sum_{i}^{}{g(x_{i})p_{i}}$;</p><p>$X$为连续型:$X\sim f(x),E(Y) = \int_{- \infty}^{+ \infty}{g(x)f(x)dx}$</p><p>(2) $Z = g(X,Y)$;$\left( X,Y \right)\sim P{ X = x_{i},Y = y_{j}} = p_{ij}$; $E(Z) = \sum_{i}^{}{\sum_{j}^{}{g(x_{i},y_{j})p_{ij}}}$ $\left( X,Y \right)\sim f(x,y)$;$E(Z) = \int_{- \infty}^{+ \infty}{\int_{- \infty}^{+ \infty}{g(x,y)f(x,y)dxdy}}$</p><p><strong>7.协方差</strong> </p><p>$Cov(X,Y) = E\left\lbrack (X - E(X)(Y - E(Y)) \right\rbrack$</p><p><strong>8.相关系数</strong></p><p> $\rho_{XY} = \frac{Cov(X,Y)}{\sqrt{D(X)}\sqrt{D(Y)}}$,$k$阶原点矩 $E(X^{k})$;<br>$k$阶中心矩 $E\left{ {\lbrack X - E(X)\rbrack}^k \right}$</p><p>性质:</p><p>(1)$\ Cov(X,Y) = Cov(Y,X)$</p><p>(2)$\ Cov(aX,bY) = abCov(Y,X)$</p><p>(3)$\ Cov(X_{1} + X_{2},Y) = Cov(X_{1},Y) + Cov(X_{2},Y)$</p><p>(4)$\ \left| \rho\left( X,Y \right) \right| \leq 1$</p><p>(5) $\ \rho\left( X,Y \right) = 1 \Leftrightarrow P\left( Y = aX + b \right) = 1$ ,其中$a > 0$</p><p>$\rho\left( X,Y \right) = - 1 \Leftrightarrow P\left( Y = aX + b \right) = 1$<br>,其中$a < 0$</p><p><strong>9.重要公式与结论</strong></p><p>(1)$\ D(X) = E(X^{2}) - E^{2}(X)$</p><p>(2)$\ Cov(X,Y) = E(XY) - E(X)E(Y)$</p><p>(3) $\left| \rho\left( X,Y \right) \right| \leq 1,$且 $\rho\left( X,Y \right) = 1 \Leftrightarrow P\left( Y = aX + b \right) = 1$,其中$a > 0$</p><p>$\rho\left( X,Y \right) = - 1 \Leftrightarrow P\left( Y = aX + b \right) = 1$,其中$a < 0$</p><p>(4) 下面5个条件互为充要条件:</p><p>$\rho(X,Y) = 0$ $\Leftrightarrow Cov(X,Y) = 0$ $\Leftrightarrow E(X,Y) = E(X)E(Y)$ $\Leftrightarrow D(X + Y) = D(X) + D(Y)$ $\Leftrightarrow D(X - Y) = D(X) + D(Y)$</p><p>注:$X$与$Y$独立为上述5个条件中任何一个成立的充分条件,但非必要条件。</p><h2 id="数理统计的基本概念"><a href="#数理统计的基本概念" class="headerlink" title="数理统计的基本概念"></a>数理统计的基本概念</h2><p><strong>1.基本概念</strong></p><p>总体:研究对象的全体,它是一个随机变量,用$X$表示。</p><p>个体:组成总体的每个基本元素。</p><p>简单随机样本:来自总体$X$的$n$个相互独立且与总体同分布的随机变量$X_{1},X_{2}\cdots,X_{n}$,称为容量为$n$的简单随机样本,简称样本。</p><p>统计量:设$X_{1},X_{2}\cdots,X_{n},$是来自总体$X$的一个样本,$g(X_{1},X_{2}\cdots,X_{n})$)是样本的连续函数,且$g()$中不含任何未知参数,则称$g(X_{1},X_{2}\cdots,X_{n})$为统计量。</p><p>样本均值:$\overline{X} = \frac{1}{n}\sum_{i = 1}^{n}X_{i}$</p><p>样本方差:$S^{2} = \frac{1}{n - 1}\sum_{i = 1}^{n}{(X_{i} - \overline{X})}^{2}$</p><p>样本矩:样本$k$阶原点矩:$A_{k} = \frac{1}{n}\sum_{i = 1}^{n}X_{i}^{k},k = 1,2,\cdots$</p><p>样本$k$阶中心矩:$B_k = \frac{1}{n}\sum_{i = 1}^n(X_{i} - \bar{X})^k,k = 1,2,\cdots$</p><p><strong>2.分布</strong></p><p>$\chi^{2}$分布:$\chi^{2} = X_{1}^{2} + X_{2}^{2} + \cdots + X_{n}^{2}\sim\chi^{2}(n)$,其中$X_{1},X_{2}\cdots,X_{n},$相互独立,且同服从$N(0,1)$</p><p>$t$分布:$T = \frac{X}{\sqrt{Y/n}}\sim t(n)$ ,其中$X\sim N\left( 0,1 \right),Y\sim\chi^{2}(n),$且$X$,$Y$ 相互独立。</p><p>$F$分布:$F = \frac{X/n_{1}}{Y/n_{2}}\sim F(n_{1},n_{2})$,其中$X\sim\chi^{2}\left( n_{1} \right),Y\sim\chi^{2}(n_{2}),$且$X$,$Y$相互独立。</p><p>分位数:若$P(X \leq x_{\alpha}) = \alpha,$则称$x_{\alpha}$为$X$的$\alpha$分位数</p><p><strong>3.正态总体的常用样本分布</strong></p><p>(1) 设$X_{1},X_{2}\cdots,X_{n}$为来自正态总体$N(\mu,\sigma^{2})$的样本,</p><p>$\overline{X} = \frac{1}{n}\sum_{i = 1}^{n}X_{i},S^{2} = \frac{1}{n - 1}\sum_{i = 1}^{n}{(X_{i} - \overline{X})^{2},}$则:</p><ol><li><p>$\overline{X}\sim N\left( \mu,\frac{\sigma^{2}}{n} \right){\ \ }$或者$\frac{\overline{X} - \mu}{\frac{\sigma}{\sqrt{n}}}\sim N(0,1)$</p></li><li><p>$\frac{(n - 1)S^{2}}{\sigma^{2}} = \frac{1}{\sigma^{2}}\sum_{i = 1}^{n}{(X_{i} - \overline{X})^{2}\sim\chi^{2}(n - 1)}$</p></li><li><p>$\frac{1}{\sigma^{2}}\sum_{i = 1}^{n}{(X_{i} - \mu)^{2}\sim\chi^{2}(n)}$</p></li></ol><p>4)${\ \ }\frac{\overline{X} - \mu}{S/\sqrt{n}}\sim t(n - 1)$</p><p><strong>4.重要公式与结论</strong></p><p>(1) 对于$\chi^{2}\sim\chi^{2}(n)$,有$E(\chi^{2}(n)) = n,D(\chi^{2}(n)) = 2n;$</p><p>(2) 对于$T\sim t(n)$,有$E(T) = 0,D(T) = \frac{n}{n - 2}(n > 2)$;</p><p>(3) 对于$F\tilde{\ }F(m,n)$,有 $\frac{1}{F}\sim F(n,m),F_{a/2}(m,n) = \frac{1}{F_{1 - a/2}(n,m)};$</p><p>(4) 对于任意总体$X$,有 $E(\overline{X}) = E(X),E(S^{2}) = D(X),D(\overline{X}) = \frac{D(X)}{n}$</p>]]></content>
<tags>
<tag>Math</tag>
<tag>ML</tag>
</tags>
</entry>
<entry>
<title>BP神经网络原理和简明理解</title>
<link href="/2019/09/14/BP%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%8E%9F%E7%90%86%E5%92%8C%E7%AE%80%E6%98%8E%E7%90%86%E8%A7%A3/"/>
<url>/2019/09/14/BP%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%8E%9F%E7%90%86%E5%92%8C%E7%AE%80%E6%98%8E%E7%90%86%E8%A7%A3/</url>
<content type="html"><![CDATA[<h1 id="单个神经元结构"><a href="#单个神经元结构" class="headerlink" title="单个神经元结构"></a>单个神经元结构</h1><p><img src="/2019/09/14/BP%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%8E%9F%E7%90%86%E5%92%8C%E7%AE%80%E6%98%8E%E7%90%86%E8%A7%A3/bp1.jpg" alt="bp1"></p><ul><li><p>输入:$x_1,x_2,…,x_n$</p></li><li><p>输出:$y$</p></li><li><p>输入和输出的关系(函数):$y = (x_1\ast w_1+x_2\ast w_2+…+x_n\ast w_n+)+b = \sum_{i=1}^n x_i\ast w_i+b$,其中$w_i$是权重</p></li><li><p>将输入用矩阵表示:$X = [x_1,x_2,…,x_n]^T,X为一个n行1列的矩阵$</p></li><li><p>将权重用矩阵表示:$W=[w_1,x_2,…,w_n]$</p></li><li><p>那么输出可以表示为:$y=[w_1.w_2,…,w_n] \cdot [x_1,_2,…,x_n]^T+b=WX+b$</p></li></ul><h1 id="激活函数"><a href="#激活函数" class="headerlink" title="激活函数"></a>激活函数</h1><p>激活函数是一类复杂的问题,为便于理解这里有几条重要的特性:</p><ul><li><strong>非线性</strong>:即导数不是常数,不然求导之后退化为直线。对于一些画一条直线仍然无法分开的问题,非线性可以把直线掰弯,自从变弯以后,就能包罗万象了。</li><li><strong>几乎处处可导</strong>:数学上,处处可导为后面的后向传播算法(BP算法)提供了核心条件。</li><li><strong>输出范围有限</strong>:一般是限定在[0,1],有限的输出范围使得神经元对于一些比较大的输入也会比较稳定。</li><li><strong>非饱和性</strong>:饱和就是指,当输入比较大的时候,输出几乎没变化了,那么会导致梯度消失!梯度消失带来的负面影响就是会限制了神经网络表达能力。<strong>sigmoid</strong>,<strong>tanh</strong>函数都是软饱和的,<strong>阶跃函数</strong>是硬饱和。<strong>软</strong>是指输入趋于无穷大的时候输出无限接近上线,<strong>硬</strong>是指像阶跃函数那样,输入非0输出就已经始终都是上限值。关于数学表示**<a href="https://www.cnblogs.com/rgvb178/p/6055213.html">传送门</a>** 里面有详细写到。如果激活函数是饱和的,带来的缺陷就是系统迭代更新变慢,系统收敛就慢,当然这是可以有办法弥补的,一种方法是使用交叉熵函数作为损失函数。<strong>ReLU</strong> 是非饱和的,效果挺不错。</li><li><strong>单调性</strong>:即导数符号不变。导出要么一直大于0,要么一直小于0,不要上蹿下跳。导数符号不变,让神经网络训练容易收敛。</li></ul><p>这里用到<strong>Sigmoid</strong>函数方便理解:</p><p>Sigmoid函数:$$y = \frac{1}{e^{(-x)}+1}$$</p><p><img src="/2019/09/14/BP%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%8E%9F%E7%90%86%E5%92%8C%E7%AE%80%E6%98%8E%E7%90%86%E8%A7%A3/bp2.jpg" alt="bp2"></p><p>S函数的导数:</p><p>$$<br>\begin{aligned}<br>&y’= (\frac{1}{e^{-x}+1})’ \\<br>&=(\frac{u}{v})’,这里u=1,v=e^{-x}+1 \\<br>&=\frac{u’v-uv’}{v^2} \\<br>&=\frac{1’\ast (e^{-x}+1)-1\ast (e^{-x}+1)’}{(e^{-x}+1)^2} \\<br>&=\frac{e^{-x}}{(e^{-x}+1)^2} \\<br>&=\frac{1}{1+e^{-x}}\ast \frac{1+e^{-x}-1}{1+e^{-x}} \\<br>&=\frac{1}{1+e^{-x}}\ast (1-\frac{1}{1+e^{-x}}), 令y=\frac{1}{e^{-x}+1} \\<br>&=y\ast (1-y)<br>\end{aligned}<br>$$</p><p>S函数的导数的图像:</p><p><img src="/2019/09/14/BP%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%8E%9F%E7%90%86%E5%92%8C%E7%AE%80%E6%98%8E%E7%90%86%E8%A7%A3/bp3.jpg" alt="bp3"></p><h1 id="传播过程"><a href="#传播过程" class="headerlink" title="传播过程"></a>传播过程</h1><p>下面是一个典型的三层神经网络结构,第一层是输入层,第二层是隐藏层,第三层是输出层。</p><p><img src="/2019/09/14/BP%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%8E%9F%E7%90%86%E5%92%8C%E7%AE%80%E6%98%8E%E7%90%86%E8%A7%A3/bp4.jpg" alt="bp4"></p><ul><li><strong>正向传播</strong>:输入$i_1,i_2$数据,然后一层一层传播下去,知道输出层输出结果。</li><li><strong>反向传播</strong>:输入、期望的输出为已知。在开始时,权重$w$,偏置$b$初始化为随机值,按网络计算后观察结果。根据结果的<strong>误差</strong>(也叫损失),调整权重$w$,偏置$b$,这时就完成了一次反向传播。</li><li>当完成了一次正反向传播,也就完成了一次神经网络的训练迭代,反复迭代,误差越来越小,直至训练完成。</li></ul><h1 id="BP算法推导和数值计算"><a href="#BP算法推导和数值计算" class="headerlink" title="BP算法推导和数值计算"></a>BP算法推导和数值计算</h1><h2 id="初始化参数"><a href="#初始化参数" class="headerlink" title="初始化参数"></a>初始化参数</h2><ul><li>输入:$i_1=0.1,i_2=0.2$</li><li>输出:$O_1=0.01,O_2=0.99,(训练时的输出期望值)$</li><li>权重:$ \begin{aligned} &w_1=0.1,w_2=0.2,w_3=0.3,w_4=0.4 \\ &w_5=0.5,w_6=0.6,w_7=0.7,w_8=0.8 \\ &(这些权重是随机初始化的,通过多次迭代训练调整直到训练完成)\end{aligned} $</li><li>偏置:$b_1=0.55,b_2=0.56,b_3=0.66,b_4=0.67 \\ (同随机初始化)$</li></ul><h2 id="正向传播"><a href="#正向传播" class="headerlink" title="正向传播"></a>正向传播</h2><ul><li>输入层–>隐藏层:<ul><li>计算<strong>隐藏层</strong>神经元$h_1$的输入加权和:$$\begin{aligned} IN_{h1}&=w_1\ast i_1+w_2\ast i_2+1\ast b_1 \\ &=0.1\ast 0.1+0.2\ast 0.2+1\ast 0.55 \\ &=0.6\end{aligned}$$</li><li>计算<strong>隐藏层</strong>神经元$h_1$的输出,要通过激活函数Sigmoid处理:$$OUT_{h1}=\frac{1}{e^{-IN_{h1}}+1} \ =\frac{1}{e^{-0.6}+1} \ =0.6456563062$$</li><li>同理计算出<strong>隐藏层</strong>神经元$h_2$的输出:$$OUT_{h2}=0.6592603884$$</li></ul></li><li>隐藏层–>输出层:<ul><li>计算<strong>输出层</strong>神经元$O_1$的<strong>输入</strong>加权和:$$\begin{aligned}IN_{O_1}&=w_5\ast OUT_{h_1}+w_6\ast OUT_{h_2}+1\ast b_3 \\ &=0.5\ast 0.6456563062+0.6\ast 0.6592603884+1\ast 0.66 \\ &=1.3783843861\end{aligned}$$</li><li>计算<strong>输出层</strong>神经元$O_1$的输出:$$OUT_{O_1}=\frac{1}{e^{-IN_{O_1}}+1} \ =\frac{1}{e^{-1.3783843861}}\ =0.7987314002 $$</li><li>同理计算出<strong>输出层</strong>神经元$O_2$的输出:$$OUT_{O_2}=0.8374488853$$</li></ul></li></ul><p>正向传播结束,可以看到输出层输出的结果:$[0.7987314002,0.8374488853]$,但是训练数据的期望输出是$[0.01,0.99]$,相差太大,这时就需要利用反向传播,更新权重$w$,然后重新计算输出。</p><h2 id="反向传播"><a href="#反向传播" class="headerlink" title="反向传播"></a>反向传播</h2><h3 id="计算输出误差:"><a href="#计算输出误差:" class="headerlink" title="计算输出误差:"></a>计算输出误差:</h3><ul><li><p>误差计算:$$\begin{aligned} E_{total}&=\sum_{i=1}^2E_{OUT_{O_i}} \\ &=E_{OUT_{O_1}} + E_{OUT_{O_2}} \\ &=\frac{1}{2}(expected_{OUT_{O_1}}-OUT_{O_1})^2+\frac{1}{2}(expected_{OUT_{O_2}}-OUT_{O_2})^2 \\ &=\frac{1}{2}\ast (O_1-OUT_{O_1})^2+\frac{1}{2}\ast (O_2-OUT_{O_2})^2 \\ &=\frac{1}{2}\ast (0.01-0.7987314002)^2+\frac{1}{2}\ast (0.99-0.8374488853)^2 \\ &=0.0116359213+0.3110486109 \\ &=0.3226845322 \\ &其中:E_{OUT_{O_1}}=0.0116359213,E_{OUT_{O_2}}= 0.3110486109 \end{aligned}$$</p></li><li><p>PS:这里使用这个简单的误差计算便于理解,实际上其效果有待提高。如果激活函数是饱和的,带来的缺陷就是系统迭代更新变慢,系统收敛就慢,当然这是可以有办法弥补的,一种方法是使用<strong>交叉熵函数</strong>作为损失函数。<a href="https://blog.csdn.net/lanchunhui/article/details/50086025">这里</a>有更详细的介绍。</p></li><li><p>交叉熵损失函数:$$E_{total}=\frac{1}{m}\sum_{i=1}^m(O_i\cdot log OUT_{O_i}+(1-O_i)\cdot log(1-OUT_{O_i}))$$</p></li><li><p>对输出求偏导:$$\frac{\partial E_{total}}{\partial OUT_{O_i}}=\frac{1}{m}\sum_{i=1}^m(\frac{O_i}{OUT_{O_i}}-\frac{1-O_i}{1-OUT_{O_i}})$$</p><h3 id="隐藏层–-gt-输出层的权重的更新:"><a href="#隐藏层–-gt-输出层的权重的更新:" class="headerlink" title="隐藏层–>输出层的权重的更新:"></a>隐藏层–>输出层的权重的更新:</h3></li><li><p>链式求导法则(详细可参考<a href="https://loopvoid.github.io/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/">这篇文章</a>:$$假设y是u的函数,而u是x的函数:y=f(u),u=g(x) \\ 那么对应的复合函数就是:y=f(g(x)) \\ 那么y对x的导数则有:\frac{dy}{dx}=\frac{dy}{du}\cdot \frac{du}{dx}$$</p></li><li><p>以权重$w_5$举例计算:权重$w$的大小能直接影响输出,$w$不合适会使输出有误差。要知道某个$w$对误差影响的程度,可以用<strong>误差对该$w$的变化率</strong>来表达。如果$w$的很少的变化,会导致误差增大很多,说明这个$w$对误差影响的程度就更大,也就是说,误差对该$w$的变化率越高。而误差对$w$的变化率就是误差对$w$的偏导。如图,总误差的大小首先受输出层神经元$O_1$的输出影响,继续反推,$O_1$的输出受它自己的输入的影响,而它自己的输入会受到$w_5$的影响。<img src="/2019/09/14/BP%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%8E%9F%E7%90%86%E5%92%8C%E7%AE%80%E6%98%8E%E7%90%86%E8%A7%A3/bp5.jpg" alt="bp5"></p></li><li><p>那么根据链式法则有:$$\begin{aligned} \frac{\partial E_{total}}{\partial w_5}&=\frac{\partial E_{total}}{\partial OUT_{O_1}}\frac{\partial OUT_{O_1}}{\partial IN_{O_1}}\frac{\partial IN_{O_1}}{\partial w_5}\end{aligned} $$</p></li><li><p>第一部分: $$ \begin{aligned} \because E_{total}&=\frac{1}{2}(O_1-OUT_{O_1})^2+\frac{1}{2}(O_2-OUT_{O_2})^2 \\ \therefore \frac{\partial E_{total}}{\partial OUT_{O_1}}&=\frac{\partial (\frac{1}{2}(O_1-OUT_{O_1})^2+\frac{1}{2}(O_2-OUT_{O_2})^2)}{\partial OUT_{O_1}} \\ & =2\ast \frac{1}{2}(O_1-OUT_{O_1})^{2-1}\ast (0-1)+0 \\ & =-(O_1-OUT_{O_1}) \\ & =-(0.01-0.7987314002) \\ & =0.7887314002 \end{aligned}$$</p></li><li><p>第二部分:$$\begin{aligned}\because OUT_{O_1}&=\frac{1}{e^{-IN_{O_1}}+1} \\ \therefore \frac{\partial OUT_{O_1}}{\partial IN_{O_1}}&=\frac{\partial (\frac{1}{e^{-IN_{O_1}}+1})}{\partial IN_{O_1}} \\ & =OUT_{O_1}(1-OUT_{O_1}) \\ &=0.7987314002*(1-0.7987314002) \\ &=0.1607595505 \end{aligned}$$</p></li><li><p>第三部分:$$\begin{aligned} \because IN_{O_1}&=w_5\ast OUT_{h_1}+w_6\ast OUT_{h_2}+1\ast b_3 \\ \therefore \frac{\partial IN_{O_1}}{\partial w_5}&=\frac{\partial (w_5\ast OUT_{h_1}+w_6\ast OUT_{H}+1\ast b_3)}{\partial w_5} \\ &=1\ast w_5^{(1-1)}\ast OUT_{h_1}+0+0 \\ &=OUT_{h_1} \\ &=0.6456563062\end{aligned}$$</p></li><li><p>所以:$$\begin{aligned}\frac{\partial E_{total}}{\partial w_5}&=\frac{\partial E_{total}}{\partial OUT_{O_1}}\frac{\partial OUT_{O_1}}{\partial IN_{O_1}}\frac{\partial IN_{O_1}}{\partial w_5} \\ &=0.7887314002\ast 0.1607595505\ast 0.6456563062\\ &=0.0818667051\end{aligned}$$</p></li><li><p>归纳如下:$$\begin{aligned}\frac{\partial E_{total}}{\partial w_5}&=\frac{\partial E_{total}}{\partial OUT_{O_1}}\frac{\partial OUT_{O_1}}{\partial IN_{O_1}}\frac{\partial IN_{O_1}}{\partial w_5} \\ &=-(O_1-OUT_{O_1})\cdot OUT_{O_1}\cdot (1-OUT_{O_1})\cdot OUT_{h_1}\\ &=\sigma_{O_1}\cdot OUT_{h_1} \\ & 其中,\sigma_{O_1}=-(O_1-OUT_{O_1})\cdot OUT_{O_1}\cdot (1-OUT_{O_1})\end{aligned}$$</p><h3 id="隐藏层–-gt-输出层的偏置的更新:"><a href="#隐藏层–-gt-输出层的偏置的更新:" class="headerlink" title="隐藏层–>输出层的偏置的更新:"></a>隐藏层–>输出层的偏置的更新:</h3></li><li><p>同理<strong>输出层</strong>偏置b更新如下:$$\begin{aligned} IN_{O_1}&=w_5\ast OUT_{h_1}+w_6\ast OUT_{h_2}+1\ast b_3 \\ \frac{\partial IN_{O_1}}{\partial b_3}&=\frac{w_5\ast OUT_{h_1}+w_6\ast OUT_{h_2}+1\ast b_3}{\partial b_3} \\ &=0+0+b_3^{(1-1)} \\&=1 \end{aligned}$$ </p></li><li><p>所以:$$\begin{aligned}\frac{\partial E_{total}}{\partial b_3}&=\frac{\partial E_{total}}{\partial OUT_{O_1}}\frac{\partial OUT_{O_1}}{\partial IN_{O_1}}\frac{\partial IN_{O_1}}{\partial b_3} \\ &=0.7887314002\ast 0.1607595505\ast 1\\ &=0.1267961053\end{aligned}$$</p></li><li><p>归纳如下: $$\begin{aligned} \frac{\partial E_{total}}{\partial b_3}&=\frac{\partial E_{total}}{\partial OUT_{O_1}}\frac{\partial OUT_{O_1}}{\partial IN_{O_1}}\frac{\partial IN_{O_1}}{\partial b_3}\\ &=-(O_1-OUT_{O_1})\cdot OUT_{O_1}\cdot (1-OUT_{O_1})\cdot 1\\ &=\sigma_{O_1}\\ &其中,\sigma_{O_1}=-(O_1-OUT_{O_1})\cdot OUT_{O_1}\cdot (1-OUT_{O_1}) \end{aligned}$$</p></li></ul><h3 id="更新-w-5-的值:"><a href="#更新-w-5-的值:" class="headerlink" title="更新$w_5$的值:"></a>更新$w_5$的值:</h3><ul><li>暂时设定学习率为<strong>0.5</strong>,学习率不宜过大也不宜过小,<a href="https://blog.csdn.net/qq_41204464/article/details/83660728">这篇文章</a>学习率有更为详细的介绍,更新$w_5$:$$\begin{aligned}w_5^+&=w_5-\alpha \cdot \frac{\partial E_{total}}{\partial w_5} \\ &=0.5-0.5\ast 0.0818667051\\ &=0.45906664745\end{aligned}$$</li><li>同理可以计算出其他$w_n$的值</li><li>归纳输出层$w$的更新公式:$$\begin{aligned}w_O^+&=w_o-\alpha \cdot (-OUT_O\cdot (1-OUT_O)\cdot (O-OUT_O)\cdot OUT_h)\\ &=w_O+\alpha \cdot (O-OUT_O)\cdot OUT_O\cdot (1-OUT_O)\cdot OUT_h\end{aligned}$$</li></ul><h3 id="更新-b-3-的值:"><a href="#更新-b-3-的值:" class="headerlink" title="更新$b_3$的值:"></a>更新$b_3$的值:</h3><ul><li>更新偏置b:$$\begin{aligned}b_3^+&=b_{O_3}-\alpha \cdot \frac{\partial E_{total}}{\partial b_{O_3}} \\ &=0.66-0.5\cdot 0.1267961053\\ &=0.596601947 \end{aligned}$$</li><li>归纳如下:$$\begin{aligned}b_O^+&=b_O-\alpha \cdot(-OUT_O\cdot(1-OUT_O)\cdot(O_OUT_O))\\ &=b_O+\alpha \cdot (O-OUT_O)\cdot OUT_O\cdot(1-OUT_O)\end{aligned}$$</li></ul><h3 id="输入层–-gt-隐藏层的权值更新:"><a href="#输入层–-gt-隐藏层的权值更新:" class="headerlink" title="输入层–>隐藏层的权值更新:"></a>输入层–>隐藏层的权值更新:</h3><ul><li>以权重$w_1$举例计算:在求$w_5$的更新,误差反向传递路径输出层–>隐层,即$OUT_{O_1}\rightarrow IN_{O_1}\rightarrow w_5$,总误差只有一条路径能传回来。但是求$w_1$时,误差反向传递路径是隐藏层–>输入层,但是隐藏层的神经元是有2条的,所以总误差沿着2个路径回来,也就是说,计算偏导时,要分开来算。<img src="/2019/09/14/BP%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%8E%9F%E7%90%86%E5%92%8C%E7%AE%80%E6%98%8E%E7%90%86%E8%A7%A3/bp6.jpg" alt="bp6"></li></ul><h3 id="计算总误差对-w-1-的偏导:"><a href="#计算总误差对-w-1-的偏导:" class="headerlink" title="计算总误差对$w_1$的偏导:"></a>计算总误差对$w_1$的偏导:</h3><p>$$<br>\begin{aligned}\frac{\partial E_{total}}{\partial w_1}&=\frac{\partial E_{total}}{\partial OUT_{h_1}}\cdot \frac{\partial OUT_{h_1}}{\partial IN_{h_1}}\cdot \frac{\partial IN_{h_1}}{\partial w_1} \\ &=(\frac{\partial E_{O_1}}{\partial OUT_{h_1}}+\frac{\partial E_{O_2}}{\partial OUT_{h_1}})\cdot \frac{\partial OUT_{h_1}}{\partial IN_{h_1}}\cdot \frac{\partial IN_{h_1}}{\partial w_1}\end{aligned}<br>$$</p><ul><li>计算$E_{O_1}对OUT_{h_1}$的偏导:$$\begin{aligned}\frac{\partial E_{total}}{\partial OUT_{h_1}} &= \frac{\partial E_{O_1}}{\partial OUT_{h_1}}+\frac{\partial E_{O_2}}{\partial OUT_{h_1}}\\ \frac{\partial E_{O_1}}{\partial OUT_{h_1}}&=\frac{\partial E_{O_1}}{\partial IN_{O_1}}\cdot\frac{\partial IN_{O_1}}{\partial OUT_{h_1}} \\ (左边)\frac{\partial E_{O_1}}{\partial IN_{O_1}}&=\frac{\partial E_{O_1}}{\partial OUT_{O_1}}\cdot\frac{\partial OUT_{O_1}}{\partial IN_{O_1}}\\ &=\frac{\frac{1}{2}(O_1-OUT_{O_1})^2}{\partial OUT_{O_1}}\cdot \frac{\partial OUT_{O_1}}{\partial IN_{O_1}}\\ &=-(O_1-OUT_{O_1})\cdot \frac{\partial OUT_{O_1}}{\partial IN_{O_1}}\\ &=0.7987314002\ast 0.1607595505\\ &=0.1284037009\\IN_{O_1}&=w_5\ast OUT_{h_1}+w_6\ast OUT_{h_2}+1\ast b_3\\ (右边)\frac{\partial IN_{O_1}}{\partial OUT_{h_1}}&=\frac{\partial (w_5\ast OUT_{h_1}+w_6\ast OUT_{h_2}+1\ast b_3)}{\partial OUT_{h_1}}\\ &=w_5\ast OUT_{h_1}^{(1-1)}+0+0\\ &=w_5=0.5 \\ \frac{\partial E_{O_1}}{\partial OUT_{h_1}} &=\frac{\partial E_{O_1}}{\partial IN_{O_1}}\cdot \frac{\partial IN_{O_1}}{\partial OUT_{h_1}}\\ &=0.1284037009\ast 0.5=0.06420185045\end{aligned}$$</li><li>j计算$E_{O_2}对OUT_{h_1}$的偏导:$$\begin{aligned}\frac{\partial E_{O_2}}{\partial OUT_{h_1}}&=\frac{\partial E_{O_2}}{\partial IN_{O_2}}\cdot\frac{\partial IN_{O_2}}{\partial OUT_{h_1}}\\ &=-(O_2-OUT_{O_2})\cdot \frac{\partial OUT_{O_2}}{\partial IN_{O_2}}\cdot \frac{\partial IN_{O_2}}{\partial OUT_{h_1}}\\ &=-(O_2-OUT_{O_2})\cdot OUT_{O_2}(1-OUT_{O_2})\cdot w_7\\ &=-(0.99-0.8374488853)\ast 0.8374488853\ast (1-0.8374488853)\ast 0.7=-0.0145365614\end{aligned}$$</li><li>则$E_{total}对OUT_{h_1}$的偏导为:$$\begin{aligned}\frac{\partial E_{total}}{\partial OUT_{h_1}} &= \frac{\partial E_{O_1}}{\partial OUT_{h_1}}+\frac{\partial E_{O_2}}{\partial OUT_{h_1}}\\ &=0.06420185045+(-0.0145365614)=0.04966528905\end{aligned}$$</li><li>计算$OUT_{h_1}$对$IN_{h_1}$的偏导:$$\begin{aligned}\because OUT_{h_1}&=\frac{1}{e^{-IN_{h_1}}+1} \\ \therefore \frac{\partial OUT_{h_1}}{\partial IN_{h_1}}&= \frac{\partial (\frac{1}{e^{-IN_{h_1}}+1})}{\partial IN_{h_1}} \\ &=OUT(1-OUT_{h_1})\\ &=0.6456563062\ast (1-0.6456563062)=0.2298942405 \end{aligned}$$</li><li>计算$IN_{h_1}对w_1$的偏导:$$\begin{aligned}\frac{\partial IN_{h_1}}{\partial w_1}&=\frac{\partial(w_1\ast i_1+w_2\ast i_2+1\ast b)}{\partial w_1}\\ &=w_1^{(1-1)}\ast i_1+0+0=i_1=0.1\end{aligned}$$</li><li>三者相乘计算$E_{total}$对$w_1$的偏导:$$\begin{aligned}\frac{\partial E_{total}}{\partial w_1}&=\frac{\partial E_{total}}{\partial OUT_{h_1}}\cdot \frac{\partial OUT_{h_1}}{\partial IN_{h_1}}\cdot \frac{\partial IN_{h_1}}{\partial w_1}\\ &=0.04966528905\ast 0.2298942405\ast 0.1=0.0011362635\end{aligned}$$</li><li>归纳:$$\begin{aligned}\frac{\partial E_{total}}{\partial w_1}&=\frac{\partial E_{total}}{\partial OUT_{h_1}}\cdot \frac{\partial OUT_{h_1}}{\partial IN_{h_1}}\cdot \frac{\partial IN_{h_1}}{\partial w_1}\\ &=(\frac{\partial E_{O_1}}{\partial OUT_{h_1}}+\frac{\partial E_{O_2}}{\partial OUT_{h_1}})\cdot \frac{\partial OUT_{h_1}}{\partial IN_{h_1}}\cdot \frac{\partial IN_{h_1}}{\partial w_1}\\ &=(\sum_{n=1}^2\frac{\partial E_{O_n}}{\partial OUT_{O_n}}\cdot\frac{\partial OUT_{O_n}}{\partial IN_{O_n}}\cdot \frac{\partial IN_{O_n}}{\partial OUT_{h_n}})\cdot \frac{\partial OUT_{h_1}}{\partial IN_{h_1}}\cdot \frac{\partial IN_{h_1}}{\partial w_1}\\ &=(\sum_{n=1}^2\sigma_{O_n}w_{O_n})\cdot OUT_{h_n}(1-OUT_{h_n})\cdot i_1\\ &=\sigma_{h_1}\cdot i_1\\&其中,\sigma_{h_1}=(\sum_{n=1}^2\sigma_{O_n}w_{O_n})\cdot OUT_{h_1}(1-OUT_{h_1})\\&\sigma_{O_i}看作输出层的误差,误差和w相乘,相当于通过w传播了过来;如果是深层网络,隐藏层数量>1,那么公式中的\sigma_{O_n}写为\sigma_{h_n},w_O写成w_h\end{aligned}$$</li><li>现在更新$w_1$的值:$$\begin{aligned}w_1^+&=w_1-\alpha\cdot \frac{\partial E_{total}}{\partial w_1}\\ &=0.1-0.1\ast 0.0011362635=0.0998863737\end{aligned}$$</li><li>归纳隐藏层$w$更新的公式:$$\begin{aligned}w_h^+&=w_h-\alpha\cdot \frac{\partial E_{total}}{\partial w}\\ &=w_h+\alpha\cdot (-\sum_{n=1}^2\sigma_{O_n}w_{O_n})\cdot OUT_{h_n}(1-OUT_{h_n})\cdot i_1\end{aligned}$$<h3 id="计算隐藏层偏置b的更新:"><a href="#计算隐藏层偏置b的更新:" class="headerlink" title="计算隐藏层偏置b的更新:"></a>计算隐藏层偏置b的更新:</h3></li></ul><p>$$<br>\begin{aligned}\frac{\partial E_{total}}{\partial b_h}&=(\sum_h\sigma_hw_h)\cdot OUT_h(1-OUT_h)\\ b_h^+&=b_h-\alpha\cdot \frac{\partial E_{total}}{\partial b_n}\\ &=w_h+\alpha\cdot (\sum_h\sigma_hw_h)\cdot OUT_h(1-OUT_h)\end{aligned}<br>$$</p><h1 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h1><figure class="highlight python"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br></pre></div></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment">#coding:utf-8</span><br><span class="hljs-keyword">import</span> h5py<br><span class="hljs-keyword">import</span> sklearn.datasets<br><span class="hljs-keyword">import</span> sklearn.linear_model<br><span class="hljs-keyword">import</span> matplotlib<br><span class="hljs-keyword">import</span> matplotlib.font_manager <span class="hljs-keyword">as</span> fm<br><span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt<br><span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np<br><br>np.random.seed(<span class="hljs-number">1</span>)<br><br>font = fm.FontProperties(fname=<span class="hljs-string">'/System/Library/Fonts/STHeiti Light.ttc'</span>)<br>matplotlib.rcParams[<span class="hljs-string">'figure.figsize'</span>] = (<span class="hljs-number">10.0</span>, <span class="hljs-number">8.0</span>)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">sigmoid</span>(<span class="hljs-params">input_sum</span>):<br><br> <span class="hljs-string">"""</span><br><span class="hljs-string"></span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 激活函数Sigmoid</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> input_sum: 输入,即神经元的加权和</span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"></span><br><span class="hljs-string"> output: 激活后的输出</span><br><span class="hljs-string"> input_sum: 把输入缓存起来返回</span><br><span class="hljs-string"> """</span><br><br> output = <span class="hljs-number">1.0</span>/(<span class="hljs-number">1</span>+np.exp(-input_sum))<br> <span class="hljs-keyword">return</span> output, input_sum<br><br><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">sigmoid_back_propagation</span>(<span class="hljs-params">derror_wrt_output, input_sum</span>):<br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 误差关于神经元输入的偏导: dE/dIn = dE/dOut * dOut/dIn 参照式(5.6)</span><br><span class="hljs-string"> 其中: dOut/dIn 就是激活函数的导数 dy=y(1 - y),见式(5.9)</span><br><span class="hljs-string"> dE/dOut 误差对神经元输出的偏导,见式(5.8)</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> derror_wrt_output:误差关于神经元输出的偏导: dE/dyⱼ = 1/2(d(expect_to_output - output)**2/doutput) = -(expect_to_output - output)</span><br><span class="hljs-string"> input_sum: 输入加权和</span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"> derror_wrt_dinputs: 误差关于输入的偏导,见式(5.13)</span><br><span class="hljs-string"> """</span><br> output = <span class="hljs-number">1.0</span>/(<span class="hljs-number">1</span> + np.exp(- input_sum))<br> doutput_wrt_dinput = output * (<span class="hljs-number">1</span> - output)<br> derror_wrt_dinput = derror_wrt_output * doutput_wrt_dinput<br> <span class="hljs-keyword">return</span> derror_wrt_dinput<br><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">relu</span>(<span class="hljs-params">input_sum</span>):<br><br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 激活函数ReLU</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> input_sum: 输入,即神经元的加权和</span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"> outputs: 激活后的输出</span><br><span class="hljs-string"> input_sum: 把输入缓存起来返回</span><br><span class="hljs-string"> """</span><br><br> output = np.maximum(<span class="hljs-number">0</span>, input_sum)<br> <span class="hljs-keyword">return</span> output, input_sum<br><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">relu_back_propagation</span>(<span class="hljs-params">derror_wrt_output, input_sum</span>):<br><br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 误差关于神经元输入的偏导: dE/dIn = dE/dOut * dOut/dIn</span><br><span class="hljs-string"> 其中: dOut/dIn 就是激活函数的导数</span><br><span class="hljs-string"> dE/dOut 误差对神经元输出的偏导</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> derror_wrt_output:误差关于神经元输出的偏导</span><br><span class="hljs-string"> input_sum: 输入加权和</span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"> derror_wrt_dinputs: 误差关于输入的偏导</span><br><span class="hljs-string"> """</span><br><br> derror_wrt_dinputs = np.array(derror_wrt_output, copy=<span class="hljs-literal">True</span>)<br> derror_wrt_dinputs[input_sum <= <span class="hljs-number">0</span>] = <span class="hljs-number">0</span><br><br> <span class="hljs-keyword">return</span> derror_wrt_dinputs<br><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">tanh</span>(<span class="hljs-params">input_sum</span>):<br><br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 激活函数 tanh</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> input_sum: 输入,即神经元的加权和</span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"> output: 激活后的输出</span><br><span class="hljs-string"> input_sum: 把输入缓存起来返回</span><br><span class="hljs-string"> """</span><br> output = np.tanh(input_sum)<br> <span class="hljs-keyword">return</span> output, input_sum<br><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">tanh_back_propagation</span>(<span class="hljs-params">derror_wrt_output, input_sum</span>):<br><br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 误差关于神经元输入的偏导: dE/dIn = dE/dOut * dOut/dIn</span><br><span class="hljs-string"> 其中: dOut/dIn 就是激活函数的导数 tanh'(x) = 1 - x²</span><br><span class="hljs-string"> dE/dOut 误差对神经元输出的偏导</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> derror_wrt_output:误差关于神经元输出的偏导: dE/dyⱼ = 1/2(d(expect_to_output - output)**2/doutput) = -(expect_to_output - output)</span><br><span class="hljs-string"> input_sum: 输入加权和</span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"> derror_wrt_dinputs: 误差关于输入的偏导</span><br><span class="hljs-string"> """</span><br><br> output = np.tanh(input_sum)<br> doutput_wrt_dinput = <span class="hljs-number">1</span> - np.power(output, <span class="hljs-number">2</span>)<br> derror_wrt_dinput = derror_wrt_output * doutput_wrt_dinput<br><br> <span class="hljs-keyword">return</span> derror_wrt_dinput<br><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">activated</span>(<span class="hljs-params">activation_choose, <span class="hljs-built_in">input</span></span>):<br><br> <span class="hljs-string">"""把正向激活包装一下"""</span><br> <span class="hljs-keyword">if</span> activation_choose == <span class="hljs-string">"sigmoid"</span>:<br> <span class="hljs-keyword">return</span> sigmoid(<span class="hljs-built_in">input</span>)<br> <span class="hljs-keyword">elif</span> activation_choose == <span class="hljs-string">"relu"</span>:<br> <span class="hljs-keyword">return</span> relu(<span class="hljs-built_in">input</span>)<br> <span class="hljs-keyword">elif</span> activation_choose == <span class="hljs-string">"tanh"</span>:<br> <span class="hljs-keyword">return</span> tanh(<span class="hljs-built_in">input</span>)<br><br> <span class="hljs-keyword">return</span> sigmoid(<span class="hljs-built_in">input</span>)<br><br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">activated_back_propagation</span>(<span class="hljs-params">activation_choose, derror_wrt_output, output</span>):<br> <span class="hljs-string">"""包装反向激活传播"""</span><br> <span class="hljs-keyword">if</span> activation_choose == <span class="hljs-string">"sigmoid"</span>:<br> <span class="hljs-keyword">return</span> sigmoid_back_propagation(derror_wrt_output, output)<br> <span class="hljs-keyword">elif</span> activation_choose == <span class="hljs-string">"relu"</span>:<br> <span class="hljs-keyword">return</span> relu_back_propagation(derror_wrt_output, output)<br> <span class="hljs-keyword">elif</span> activation_choose == <span class="hljs-string">"tanh"</span>:<br> <span class="hljs-keyword">return</span> tanh_back_propagation(derror_wrt_output, output)<br><br> <span class="hljs-keyword">return</span> sigmoid_back_propagation(derror_wrt_output, output)<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">NeuralNetwork</span>:<br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, layers_strcuture, print_cost = <span class="hljs-literal">False</span></span>):<br> self.layers_strcuture = layers_strcuture<br> self.layers_num = <span class="hljs-built_in">len</span>(layers_strcuture)<br><br> <span class="hljs-comment"># 除掉输入层的网络层数,因为其他层才是真正的神经元层</span><br> self.param_layers_num = self.layers_num - <span class="hljs-number">1</span><br><br> self.learning_rate = <span class="hljs-number">0.0618</span><br> self.num_iterations = <span class="hljs-number">2000</span><br> self.x = <span class="hljs-literal">None</span><br> self.y = <span class="hljs-literal">None</span><br> self.w = <span class="hljs-built_in">dict</span>()<br> self.b = <span class="hljs-built_in">dict</span>()<br> self.costs = []<br> self.print_cost = print_cost<br><br> self.init_w_and_b()<br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">set_learning_rate</span>(<span class="hljs-params">self, learning_rate</span>):<br> <span class="hljs-string">"""设置学习率"""</span><br> self.learning_rate = learning_rate<br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">set_num_iterations</span>(<span class="hljs-params">self, num_iterations</span>):<br> <span class="hljs-string">"""设置迭代次数"""</span><br> self.num_iterations = num_iterations<br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">set_xy</span>(<span class="hljs-params">self, <span class="hljs-built_in">input</span>, expected_output</span>):<br> <span class="hljs-string">"""设置神经网络的输入和期望的输出"""</span><br> self.x = <span class="hljs-built_in">input</span><br> self.y = expected_output<br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">init_w_and_b</span>(<span class="hljs-params">self</span>):<br><br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 初始化神经网络所有参数</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> layers_strcuture: 神经网络的结构,例如[2,4,3,1],4层结构:</span><br><span class="hljs-string"> 第0层输入层接收2个数据,第1层隐藏层4个神经元,第2层隐藏层3个神经元,第3层输出层1个神经元</span><br><span class="hljs-string"> 返回: 神经网络各层参数的索引表,用来定位权值 wᵢ 和偏置 bᵢ,i为网络层编号</span><br><span class="hljs-string"> """</span><br> np.random.seed(<span class="hljs-number">3</span>)<br><br> <span class="hljs-comment"># 当前神经元层的权值为 n_i x n_(i-1)的矩阵,i为网络层编号,n为下标i代表的网络层的节点个数</span><br> <span class="hljs-comment"># 例如[2,4,3,1],4层结构:第0层输入层为2,那么第1层隐藏层神经元个数为4</span><br> <span class="hljs-comment"># 那么第1层的权值w是一个 4x2 的矩阵,如:</span><br> <span class="hljs-comment"># w1 = array([ [-0.96927756, -0.59273074],</span><br> <span class="hljs-comment"># [ 0.58227367, 0.45993021],</span><br> <span class="hljs-comment"># [-0.02270222, 0.13577601],</span><br> <span class="hljs-comment"># [-0.07912066, -1.49802751] ])</span><br> <span class="hljs-comment"># 当前层的偏置一般给0就行,偏置是个1xnᵢ的矩阵,nᵢ为第i层的节点个数,例如第1层为4个节点,那么:</span><br> <span class="hljs-comment"># b1 = array([ 0., 0., 0., 0.])</span><br><br><br><br> <span class="hljs-keyword">for</span> l <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, self.layers_num):<br> self.w[<span class="hljs-string">"w"</span> + <span class="hljs-built_in">str</span>(l)] = np.random.randn(self.layers_strcuture[l], self.layers_strcuture[l-<span class="hljs-number">1</span>])/np.sqrt(self.layers_strcuture[l-<span class="hljs-number">1</span>])<br> self.b[<span class="hljs-string">"b"</span> + <span class="hljs-built_in">str</span>(l)] = np.zeros((self.layers_strcuture[l], <span class="hljs-number">1</span>))<br> <span class="hljs-keyword">return</span> self.w, self.b<br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">layer_activation_forward</span>(<span class="hljs-params">self, x, w, b, activation_choose</span>):<br><br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 网络层的正向传播</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> x: 当前网络层输入(即上一层的输出),一般是所有训练数据,即输入矩阵</span><br><span class="hljs-string"> w: 当前网络层的权值矩阵</span><br><span class="hljs-string"> b: 当前网络层的偏置矩阵</span><br><span class="hljs-string"> activation_choose: 选择激活函数 "sigmoid", "relu", "tanh"</span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"> output: 网络层的激活输出</span><br><span class="hljs-string"> cache: 缓存该网络层的信息,供后续使用: (x, w, b, input_sum) -> cache</span><br><span class="hljs-string"> """</span><br><br> <span class="hljs-comment"># 对输入求加权和,见式(5.1)</span><br> input_sum = np.dot(w, x) + b<br><br> <span class="hljs-comment"># 对输入加权和进行激活输出</span><br> output, _ = activated(activation_choose, input_sum)<br><br> <span class="hljs-keyword">return</span> output, (x, w, b, input_sum)<br><br><br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">forward_propagation</span>(<span class="hljs-params">self, x</span>):<br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 神经网络的正向传播</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"></span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"> output: 正向传播完成后的输出层的输出</span><br><span class="hljs-string"> caches: 正向传播过程中缓存每一个网络层的信息: (x, w, b, input_sum),... -> caches</span><br><span class="hljs-string"> """</span><br> caches = []<br><br> <span class="hljs-comment">#作为输入层,输出 = 输入</span><br> output_prev = x<br><br> <span class="hljs-comment">#第0层为输入层,只负责观察到输入的数据,并不需要处理,正向传播从第1层开始,一直到输出层输出为止</span><br><br> <span class="hljs-comment"># range(1, n) => [1, 2, ..., n-1]</span><br> L = self.param_layers_num<br> <span class="hljs-keyword">for</span> l <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">1</span>, L):<br><br> <span class="hljs-comment"># 当前网络层的输入来自前一层的输出</span><br> input_cur = output_prev<br> output_prev, cache = self.layer_activation_forward(input_cur, self.w[<span class="hljs-string">"w"</span>+ <span class="hljs-built_in">str</span>(l)], self.b[<span class="hljs-string">"b"</span> + <span class="hljs-built_in">str</span>(l)], <span class="hljs-string">"tanh"</span>)<br> caches.append(cache)<br><br><br><br> output, cache = self.layer_activation_forward(output_prev, self.w[<span class="hljs-string">"w"</span> + <span class="hljs-built_in">str</span>(L)], self.b[<span class="hljs-string">"b"</span> + <span class="hljs-built_in">str</span>(L)], <span class="hljs-string">"sigmoid"</span>)<br> caches.append(cache)<br><br> <span class="hljs-keyword">return</span> output, caches<br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">show_caches</span>(<span class="hljs-params">self, caches</span>):<br> <span class="hljs-string">"""显示网络层的缓存参数信息"""</span><br> i = <span class="hljs-number">1</span><br> <span class="hljs-keyword">for</span> cache <span class="hljs-keyword">in</span> caches:<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">"%dtd Layer"</span> % i)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">" input: %s"</span> % cache[<span class="hljs-number">0</span>])<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">" w: %s"</span> % cache[<span class="hljs-number">1</span>])<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">" b: %s"</span> % cache[<span class="hljs-number">2</span>])<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">" input_sum: %s"</span> % cache[<span class="hljs-number">3</span>])<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">"----------"</span>)<br> i += <span class="hljs-number">1</span><br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">compute_error</span>(<span class="hljs-params">self, output</span>):<br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 计算档次迭代的输出总误差</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"></span><br><span class="hljs-string"> """</span><br><br> m = self.y.shape[<span class="hljs-number">1</span>]<br><br> <span class="hljs-comment"># 计算误差,见式(5.5): E = Σ1/2(期望输出-实际输出)²</span><br> <span class="hljs-comment">#error = np.sum(0.5 * (self.y - output) ** 2) / m</span><br> <span class="hljs-comment"># 交叉熵作为误差函数</span><br><br> error = -np.<span class="hljs-built_in">sum</span>(np.multiply(np.log(output),self.y) + np.multiply(np.log(<span class="hljs-number">1</span> - output), <span class="hljs-number">1</span> - self.y)) / m<br> error = np.squeeze(error)<br><br> <span class="hljs-keyword">return</span> error<br><br><br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">layer_activation_backward</span>(<span class="hljs-params">self, derror_wrt_output, cache, activation_choose</span>):<br><br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 网络层的反向传播</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> derror_wrt_output: 误差关于输出的偏导</span><br><span class="hljs-string"> cache: 网络层的缓存信息 (x, w, b, input_sum)</span><br><span class="hljs-string"> activation_choose: 选择激活函数 "sigmoid", "relu", "tanh"</span><br><span class="hljs-string"> 返回: 梯度信息,即</span><br><span class="hljs-string"> derror_wrt_output_prev: 反向传播到上一层的误差关于输出的梯度</span><br><span class="hljs-string"> derror_wrt_dw: 误差关于权值的梯度</span><br><span class="hljs-string"> derror_wrt_db: 误差关于偏置的梯度</span><br><span class="hljs-string"> """</span><br><br> <span class="hljs-built_in">input</span>, w, b, input_sum = cache<br> output_prev = <span class="hljs-built_in">input</span> <span class="hljs-comment"># 上一层的输出 = 当前层的输入; 注意是'输入'不是输入的加权和(input_sum)</span><br> m = output_prev.shape[<span class="hljs-number">1</span>] <span class="hljs-comment"># m是输入的样本数量,我们要取均值,所以下面的求值要除以m</span><br><br><br> <span class="hljs-comment"># 实现式(5.13)-> 误差关于权值w的偏导数</span><br> derror_wrt_dinput = activated_back_propagation(activation_choose, derror_wrt_output, input_sum)<br> derror_wrt_dw = np.dot(derror_wrt_dinput, output_prev.T) / m<br><br> <span class="hljs-comment"># 实现式 (5.32)-> 误差关于偏置b的偏导数</span><br> derror_wrt_db = np.<span class="hljs-built_in">sum</span>(derror_wrt_dinput, axis=<span class="hljs-number">1</span>, keepdims=<span class="hljs-literal">True</span>)/m<br><br> <span class="hljs-comment"># 为反向传播到上一层提供误差传递,见式(5.28)的 (Σδ·w) 部分</span><br> derror_wrt_output_prev = np.dot(w.T, derror_wrt_dinput)<br><br> <span class="hljs-keyword">return</span> derror_wrt_output_prev, derror_wrt_dw, derror_wrt_db<br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">back_propagation</span>(<span class="hljs-params">self, output, caches</span>):<br><br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 神经网络的反向传播</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> output:神经网络输</span><br><span class="hljs-string"> caches:所有网络层(输入层不算)的缓存参数信息 [(x, w, b, input_sum), ...]</span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"> grads: 返回当前迭代的梯度信息</span><br><span class="hljs-string"> """</span><br><br> grads = {}<br> L = self.param_layers_num <span class="hljs-comment">#</span><br> output = output.reshape(output.shape) <span class="hljs-comment"># 把输出层输出输出重构成和期望输出一样的结构</span><br><br> expected_output = self.y<br><br> <span class="hljs-comment"># 见式(5.8)</span><br> <span class="hljs-comment">#derror_wrt_output = -(expected_output - output)</span><br><br> <span class="hljs-comment"># 交叉熵作为误差函数</span><br> derror_wrt_output = - (np.divide(expected_output, output) - np.divide(<span class="hljs-number">1</span> - expected_output, <span class="hljs-number">1</span> - output))<br><br> <span class="hljs-comment"># 反向传播:输出层 -> 隐藏层,得到梯度:见式(5.8), (5.13), (5.15)</span><br> current_cache = caches[L - <span class="hljs-number">1</span>] <span class="hljs-comment"># 取最后一层,即输出层的参数信息</span><br> grads[<span class="hljs-string">"derror_wrt_output"</span> + <span class="hljs-built_in">str</span>(L)], grads[<span class="hljs-string">"derror_wrt_dw"</span> + <span class="hljs-built_in">str</span>(L)], grads[<span class="hljs-string">"derror_wrt_db"</span> + <span class="hljs-built_in">str</span>(L)] = \<br> self.layer_activation_backward(derror_wrt_output, current_cache, <span class="hljs-string">"sigmoid"</span>)<br><br> <span class="hljs-comment"># 反向传播:隐藏层 -> 隐藏层,得到梯度:见式 (5.28)的(Σδ·w), (5.28), (5.32)</span><br> <span class="hljs-keyword">for</span> l <span class="hljs-keyword">in</span> <span class="hljs-built_in">reversed</span>(<span class="hljs-built_in">range</span>(L - <span class="hljs-number">1</span>)):<br> current_cache = caches[l]<br> derror_wrt_output_prev_temp, derror_wrt_dw_temp, derror_wrt_db_temp = \<br> self.layer_activation_backward(grads[<span class="hljs-string">"derror_wrt_output"</span> + <span class="hljs-built_in">str</span>(l + <span class="hljs-number">2</span>)], current_cache, <span class="hljs-string">"tanh"</span>)<br><br> grads[<span class="hljs-string">"derror_wrt_output"</span> + <span class="hljs-built_in">str</span>(l + <span class="hljs-number">1</span>)] = derror_wrt_output_prev_temp<br> grads[<span class="hljs-string">"derror_wrt_dw"</span> + <span class="hljs-built_in">str</span>(l + <span class="hljs-number">1</span>)] = derror_wrt_dw_temp<br> grads[<span class="hljs-string">"derror_wrt_db"</span> + <span class="hljs-built_in">str</span>(l + <span class="hljs-number">1</span>)] = derror_wrt_db_temp<br><br> <span class="hljs-keyword">return</span> grads<br><br><br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">update_w_and_b</span>(<span class="hljs-params">self, grads</span>):<br> <span class="hljs-string">"""</span><br><span class="hljs-string"> 函数:</span><br><span class="hljs-string"> 根据梯度信息更新w,b</span><br><span class="hljs-string"> 输入:</span><br><span class="hljs-string"> grads:当前迭代的梯度信息</span><br><span class="hljs-string"> 返回:</span><br><span class="hljs-string"></span><br><span class="hljs-string"> """</span><br><br> <span class="hljs-comment"># 权值w和偏置b的更新,见式:(5.16),(5.18)</span><br> <span class="hljs-keyword">for</span> l <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(self.param_layers_num):<br> self.w[<span class="hljs-string">"w"</span> + <span class="hljs-built_in">str</span>(l + <span class="hljs-number">1</span>)] = self.w[<span class="hljs-string">"w"</span> + <span class="hljs-built_in">str</span>(l + <span class="hljs-number">1</span>)] - self.learning_rate * grads[<span class="hljs-string">"derror_wrt_dw"</span> + <span class="hljs-built_in">str</span>(l + <span class="hljs-number">1</span>)]<br> self.b[<span class="hljs-string">"b"</span> + <span class="hljs-built_in">str</span>(l + <span class="hljs-number">1</span>)] = self.b[<span class="hljs-string">"b"</span> + <span class="hljs-built_in">str</span>(l + <span class="hljs-number">1</span>)] - self.learning_rate * grads[<span class="hljs-string">"derror_wrt_db"</span> + <span class="hljs-built_in">str</span>(l + <span class="hljs-number">1</span>)]<br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">training_modle</span>(<span class="hljs-params">self</span>):<br> <span class="hljs-string">"""训练神经网络模型"""</span><br><br> np.random.seed(<span class="hljs-number">5</span>)<br> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">0</span>, self.num_iterations):<br> <span class="hljs-comment"># 正向传播,得到网络输出,以及每一层的参数信息</span><br> output, caches = self.forward_propagation(self.x)<br><br> <span class="hljs-comment"># 计算网络输出误差</span><br> cost = self.compute_error(output)<br><br> <span class="hljs-comment"># 反向传播,得到梯度信息</span><br> grads = self.back_propagation(output, caches)<br><br> <span class="hljs-comment"># 根据梯度信息,更新权值w和偏置b</span><br> self.update_w_and_b(grads)<br><br> <span class="hljs-comment"># 当次迭代结束,打印误差信息</span><br> <span class="hljs-keyword">if</span> self.print_cost <span class="hljs-keyword">and</span> i % <span class="hljs-number">1000</span> == <span class="hljs-number">0</span>:<br> <span class="hljs-built_in">print</span> (<span class="hljs-string">"Cost after iteration %i: %f"</span> % (i, cost))<br> <span class="hljs-keyword">if</span> self.print_cost <span class="hljs-keyword">and</span> i % <span class="hljs-number">1000</span> == <span class="hljs-number">0</span>:<br> self.costs.append(cost)<br><br> <span class="hljs-comment"># 模型训练完后显示误差曲线</span><br> <span class="hljs-keyword">if</span> <span class="hljs-literal">False</span>:<br> plt.plot(np.squeeze(self.costs))<br> plt.ylabel(<span class="hljs-string">u'神经网络误差'</span>, fontproperties = font)<br> plt.xlabel(<span class="hljs-string">u'迭代次数 (*100)'</span>, fontproperties = font)<br> plt.title(<span class="hljs-string">u"学习率 ="</span> + <span class="hljs-built_in">str</span>(self.learning_rate), fontproperties = font)<br> plt.show()<br><br> <span class="hljs-keyword">return</span> self.w, self.b<br><br> <span class="hljs-keyword">def</span> <span class="hljs-title function_">predict_by_modle</span>(<span class="hljs-params">self, x</span>):<br> <span class="hljs-string">"""使用训练好的模型(即最后求得w,b参数)来决策输入的样本的结果"""</span><br> output, _ = self.forward_propagation(x.T)<br> output = output.T<br> result = output / np.<span class="hljs-built_in">sum</span>(output, axis=<span class="hljs-number">1</span>, keepdims=<span class="hljs-literal">True</span>)<br> <span class="hljs-keyword">return</span> np.argmax(result, axis=<span class="hljs-number">1</span>)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">plot_decision_boundary</span>(<span class="hljs-params">xy, colors, pred_func</span>):<br> <span class="hljs-comment"># xy是坐标点的集合,把集合的范围算出来</span><br> <span class="hljs-comment"># 加减0.5相当于扩大画布的范围,不然画出来的图坐标点会落在图的边缘,逼死强迫症患者</span><br> x_min, x_max = xy[:, <span class="hljs-number">0</span>].<span class="hljs-built_in">min</span>() - <span class="hljs-number">0.5</span>, xy[:, <span class="hljs-number">0</span>].<span class="hljs-built_in">max</span>() + <span class="hljs-number">0.5</span><br> y_min, y_max = xy[:, <span class="hljs-number">1</span>].<span class="hljs-built_in">min</span>() - <span class="hljs-number">0.5</span>, xy[:, <span class="hljs-number">1</span>].<span class="hljs-built_in">max</span>() + <span class="hljs-number">0.5</span><br><br> <span class="hljs-comment"># 以h为分辨率,生成采样点的网格,就像一张网覆盖所有颜色点</span><br> h = <span class="hljs-number">.01</span><br> xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))<br><br> <span class="hljs-comment"># 把网格点集合作为输入到模型,也就是预测这个采样点是什么颜色的点,从而得到一个决策面</span><br> Z = pred_func(np.c_[xx.ravel(), yy.ravel()])<br> Z = Z.reshape(xx.shape)<br><br> <span class="hljs-comment"># 利用等高线,把预测的结果画出来,效果上就是画出红蓝点的分界线</span><br> plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)<br><br> <span class="hljs-comment"># 训练用的红蓝点点也画出来</span><br> plt.scatter(xy[:, <span class="hljs-number">0</span>], xy[:, <span class="hljs-number">1</span>], c=colors, marker=<span class="hljs-string">'o'</span>, cmap=plt.cm.Spectral, edgecolors=<span class="hljs-string">'black'</span>)<br><br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:<br> plt.figure(figsize=(<span class="hljs-number">16</span>, <span class="hljs-number">32</span>))<br><br> <span class="hljs-comment"># 用sklearn的数据样本集,产生2种颜色的坐标点,noise是噪声系数,噪声越大,2种颜色的点分布越凌乱</span><br> xy, colors = sklearn.datasets.make_moons(<span class="hljs-number">60</span>, noise=<span class="hljs-number">1.0</span>)<br><br> <span class="hljs-comment"># 因为点的颜色是1bit,我们设计一个神经网络,输出层有2个神经元。</span><br> <span class="hljs-comment"># 标定输出[1,0]为红色点,输出[0,1]为蓝色点</span><br> expect_output = []<br> <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> colors:<br> <span class="hljs-keyword">if</span> c == <span class="hljs-number">1</span>:<br> expect_output.append([<span class="hljs-number">0</span>,<span class="hljs-number">1</span>])<br> <span class="hljs-keyword">else</span>:<br> expect_output.append([<span class="hljs-number">1</span>,<span class="hljs-number">0</span>])<br><br> expect_output = np.array(expect_output).T<br><br> <span class="hljs-comment"># 设计3层网络,改变隐藏层神经元的个数,观察神经网络分类红蓝点的效果</span><br> hidden_layer_neuron_num_list = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">4</span>,<span class="hljs-number">10</span>,<span class="hljs-number">20</span>,<span class="hljs-number">50</span>]<br><br> <span class="hljs-keyword">for</span> i, hidden_layer_neuron_num <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(hidden_layer_neuron_num_list):<br> plt.subplot(<span class="hljs-number">5</span>, <span class="hljs-number">2</span>, i + <span class="hljs-number">1</span>)<br> plt.title(<span class="hljs-string">u'隐藏层神经元数量: %d'</span> % hidden_layer_neuron_num, fontproperties = font)<br><br> nn = NeuralNetwork([<span class="hljs-number">2</span>, hidden_layer_neuron_num, <span class="hljs-number">2</span>], <span class="hljs-literal">True</span>)<br><br> <span class="hljs-comment"># 输出和输入层都是2个节点,所以输入和输出的数据集合都要是 nx2的矩阵</span><br> nn.set_xy(xy.T, expect_output)<br> nn.set_num_iterations(<span class="hljs-number">30000</span>)<br> nn.set_learning_rate(<span class="hljs-number">0.1</span>)<br> w, b = nn.training_modle()<br> plot_decision_boundary(xy, colors, nn.predict_by_modle)<br><br> plt.show()<br> <br></code></pre></td></tr></table></figure>]]></content>
</entry>
<entry>
<title>自动微分法</title>
<link href="/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/"/>
<url>/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/</url>
<content type="html"><![CDATA[<h1 id="几种微分求解的方法"><a href="#几种微分求解的方法" class="headerlink" title="几种微分求解的方法"></a>几种微分求解的方法</h1><ul><li>手动求解法(Manual Differentiation)</li><li>数值微分法(Numerical Differentiation)</li><li>符号微分法(Symbolic Differentiation)</li><li>自动微分法(Automatic Differentiation)</li></ul><h1 id="自动微分"><a href="#自动微分" class="headerlink" title="自动微分"></a>自动微分</h1><p>在数学和计算代数领域,automatic differentiation (AD)又称为 algorithmic differentiation 或者 computational differentiation。AD是一个可以对程序代码表示的数学函数进行自动微分的技术。AD利用链式法则来达到自动求解的目录,AD有两种主要的方法:</p><ul><li><strong>代码转换</strong>(source-code transformation)(R. Giering and T. Kaminski. 1998): <ul><li>利用一个代码转换编译器,这个编译器会分析源代码,然后产生一个和源代码对应的伴随模式(adjoint model)程序,编译时的代码生成(如用 flex-bison 做词法、语法分析);</li><li>优点是静态生成效率高(原始算法的3~4倍) ;</li><li>一次生成,多次使用,缺点是学习门槛较高(编译原理…);</li><li>很多比较好的工具非免费;</li><li>对现代编程语言特性的限制(如C++类、模板等);</li></ul></li><li><strong>运算符重载</strong>(operator overloading) <ul><li>应用比较广泛,很多编程语言特性可以很好的工作;</li><li>优点是简单直接,缺点是动态生成成本较高(代表性的工具效率是原始算法的10~35倍)。</li><li>较多免费开源 C++ 工具 (e.g. ADOL-C, CppAD, Sacado);</li></ul></li></ul><p>AD 这两种实现方式:<strong>运算符重载</strong>与<strong>代码生成</strong>,两种方式的原理都一样, <code>链式法则</code>。AD相关工具,请到这个<a href="http://www.autodiff.org/">http://www.autodiff.org/</a> 页面。自动微分(AD)是计算导数的最优方法,比符号计算、有限微分更快更精确,AD已经广泛应用在优化领域,包括人工神经网络的训练算法 back-propagation(BP)等。</p><h1 id="AD基本原理"><a href="#AD基本原理" class="headerlink" title="AD基本原理"></a>AD基本原理</h1><h2 id="链式法则"><a href="#链式法则" class="headerlink" title="链式法则"></a>链式法则</h2><p>AD基本原理是<code>链式法则</code>。链式法则又分为正向和反向。如下例子</p><p>$$y=f(g(h(x)))=f(g(h(w_0)))=f(g(w_1))=f(w_2)=w_3$$</p><p>那么链式法则就表示为:</p><p>$$\frac{dy}{dx}=\frac{dy}{dw_2}\frac{dw_2}{dw_1}\frac{dw_1}{dx}$$</p><p>正向链式法则:则从链里往外算(即,先算$\frac{dw_1}{dx}$,再算$\frac{dw_2}{dw_1}$,最后算$\frac{dy}{dw_2}$)。</p><p>反向链式法则:则从链外往外里(即,先算$\frac{dy}{dw_2}$,再算$\frac{dw_2}{dw_1}$,最后算$\frac{dw_1}{dx}$)。</p><p>简洁的表示为:</p><p>正向链式法则:计算递归式$$\frac{dw_i}{dx}=\frac{dw_i}{dw_{i-1}}\frac{dw_{i-1}}{dx},且w_3=y$$</p><p>反向链式法则:计算递归式$$\frac{dy}{dw_i}=\frac{dy}{dw_{i+1}}\frac{dw_{i+1}}{dw_i},且w_0=y$$</p><p> 通常,正向跟反向链式法则都是通过计算图来表示和计算,这样更加的方便。</p><h2 id="正向链式法则计算图"><a href="#正向链式法则计算图" class="headerlink" title="正向链式法则计算图"></a>正向链式法则计算图</h2><p>将函数转化为一个DAG(有向无环图),就能很容易的求解每一步的值。</p><h3 id="例1:"><a href="#例1:" class="headerlink" title="例1:"></a>例1:</h3><p>假设有计算式子:$y=x_0*x_1+x_1$ (1),我们要求解$\frac{\partial y}{\partial x_1}$。</p><p>首先,其计算图表示为:</p><p><img src="/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/%E8%AE%A1%E7%AE%97%E5%9B%BE1.png" alt="计算图1.png"></p><p>把(1)式展开为链式计算序列为:</p><figure class="highlight ini"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">c</span>=x0*x1<br><span class="hljs-attr">d</span>=c+x1<br><span class="hljs-attr">y</span>=d<br></code></pre></td></tr></table></figure><p>那么其对应的计算图则为:</p><p><img src="/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/%E8%AE%A1%E7%AE%97%E5%9B%BE2.png" alt="计算图2.png"></p><p>这里假设$x_0,x_1$的初值分别为:1, 2。 (1)式展开计算子序列为:</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">x0</span>=<span class="hljs-number">1</span><br><span class="hljs-attr">x1</span>=<span class="hljs-number">2</span><br><span class="hljs-attr">c</span>=x0*x1<br><span class="hljs-attr">d</span>=c+x1<br><span class="hljs-attr">y</span>=d<br></code></pre></td></tr></table></figure><p>那么其对应计算图为:</p><p><img src="/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/%E8%AE%A1%E7%AE%97%E5%9B%BE3.png" alt="计算图3.png"></p><p>然后对该计算图求解偏导数:</p><p><img src="/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/%E8%AE%A1%E7%AE%97%E5%9B%BE4.png" alt="计算图4.png"></p><p>由链式法则得到:$$\frac{\partial y}{\partial x_1}=\frac{\partial y}{\partial d}\frac{\partial d}{\partial c}\frac{\partial c}{\partial x_1}+\frac{\partial y}{\partial d}\frac{\partial d}{\partial x_1}$$</p><p>结合计算图中对应的值可知:$\frac{\partial y}{\partial x_1}=1\times1\times1+1\times1=2$</p><h3 id="例2:"><a href="#例2:" class="headerlink" title="例2:"></a>例2:</h3><p>对于下列函数:$$f(x_1,x_2)=ln(x_1)+x_1*x_2-sin(x_2)$$</p><p>转化为计算图:</p><p><img src="/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/%E8%AE%A1%E7%AE%97%E5%9B%BE5.png" alt="计算图5.png"></p><p>那么求每一步的导数值就可以表示为:</p><p><img src="/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/%E8%AE%A1%E7%AE%97%E5%9B%BE6.png" alt="计算图6.png"></p><p>上表,左半部分是从左往右计算图每个节点的求值结果,右半部分是每个节点对于$x_1$的求导结果,比如$\dot{v_1}=\frac{dv}{dx_1}$,注意到每一步的求导都会利用到上一步的求导结果。</p><p>对于自动微分的正向模式,如果函数输入输出为:$R \rightarrow R^m $</p><p>那么正向模式只需要计算一次上表右侧过程即可,非常高效。但是对于输入输出映射为:$R^n\rightarrow R^m$,这样一个有$n$个输入的函数,对于函数的梯度求解则需要处理$n$遍上述过程。而且再实际算法模型中,通常输入输出是极度不成比例的,也就是$n>>m$,那么利用正向模式进行自动微分的效率就太低了,因此有了反向模式的出现。</p><h2 id="反向链式法则计算图"><a href="#反向链式法则计算图" class="headerlink" title="反向链式法则计算图"></a>反向链式法则计算图</h2><p>自动微分的反向模式其实就是一种通用的<a href="https://en.wikipedia.org/wiki/Backpropagation">BackPropagation</a>(<a href="http://galaxy.agh.edu.pl/~vlsi/AI/backp_t_en/backprop.html">反向传播算法</a>),即backpropagation是自动微分反向模式的一种特殊形式。</p><p>反向模式从最终结果开始求导,利用最终输出对每一个节点进行求导,其过程如下计算图所示: </p><p><img src="/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/%E8%AE%A1%E7%AE%97%E5%9B%BE7.png" alt="计算图7.png"></p><p>其具体计算过程如下表所示: </p><p><img src="/2018/10/15/%E8%87%AA%E5%8A%A8%E5%BE%AE%E5%88%86%E6%B3%95/%E8%AE%A1%E7%AE%97%E5%9B%BE8.png" alt="计算图8.png"></p><p>上表左边和之前的正向模式一致,用于求解函数值,右边则是反向模式的计算过程,须从下向上看,也就是一开始先计算输出$y$对于节点$v_5$的导数,用$\bar{v_5}=\frac{dy}{dv_5}$,这样的记号可以强调我们对当前计算结果进行缓存,以便用于后续计算,而不必重复计算。再由链式法则我们可以计算输出节点对于图种每个节点的导数。 </p><p>比如对节点$v_3$:</p><p>$$\frac{y}{v_3}=\frac{dy}{dv_5}\frac{dv_5}{dv_3}$$</p><p>用$\bar{v_i}=\frac{dy}{dv_i}$记法,则有:</p><p>$$\frac{y}{v_3}=\bar{v_5}\frac{dv_5}{dv_3}$$</p><p>比如对于节点$v_0$:</p><p> $$\frac{y}{v_0}=\frac{dy}{dv_2}\frac{dv_2}{dv_0}+\frac{dy}{dv_3}\frac{dv_3}{dv_0}$$</p><p>用$\bar{v_i}=\frac{dy}{dv_i}$记法,则有:</p><p> $$\frac{y}{v_0}=\bar{v_2}\frac{dv_2}{dv_0}+\bar{v_3}\frac{dv_3}{dv_0}$$</p><p>和backpropagation算法一样,我们必须记住前向时当前节点发出的边,然后在反向传播时,可以搜集所有受到当前节点影响的节点。 </p><p>如上的计算过程,对于像神经网络这种模型,输入通常是上万到上百万维,而输出损失函数是1维的模型,则只需要一遍反向模式计算过程,便可以求出输出对于各个输入的导数,从而轻松求取梯度用于后续优化更新。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p>AD:</p><ul><li><a href="https://arxiv.org/pdf/1502.05767.pdf">Automatic differentiation in machine learning: a survey</a></li><li><a href="http://www.met.reading.ac.uk/~swrhgnrj/publications/adept.pdf">Fast Reverse-Mode Automatic Differentiation using Expression</a></li><li><a href="https://www.zhihu.com/question/48356514">https://www.zhihu.com/question/48356514</a></li><li><a href="https://blog.csdn.net/daniel_ustc/article/details/77133329">https://blog.csdn.net/daniel_ustc/article/details/77133329</a></li><li><a href="https://en.wikipedia.org/wiki/Automatic_differentiation">https://en.wikipedia.org/wiki/Automatic_differentiation</a></li><li><a href="https://github.com/autodiff/autodiff">https://github.com/autodiff/autodiff</a></li><li><a href="https://www.jianshu.com/p/4c2032c685dc">https://www.jianshu.com/p/4c2032c685dc</a></li><li><a href="http://www.autodiff.org/">http://www.autodiff.org/</a></li><li><a href="https://blog.csdn.net/u013527419/article/details/70184690">https://blog.csdn.net/u013527419/article/details/70184690</a></li><li><a href="https://github.com/zakheav/automatic-differentiation-framework">https://github.com/zakheav/automatic-differentiation-framework</a></li><li><a href="https://blog.csdn.net/daniel_ustc/article/details/77133329">https://blog.csdn.net/daniel_ustc/article/details/77133329</a></li><li><a href="https://blog.csdn.net/aws3217150/article/details/70214422">https://blog.csdn.net/aws3217150/article/details/70214422</a></li></ul><p>BP:</p><ul><li><a href="https://www.zhihu.com/question/27239198">https://www.zhihu.com/question/27239198</a></li><li><a href="http://galaxy.agh.edu.pl/~vlsi/AI/backp_t_en/backprop.html">http://galaxy.agh.edu.pl/~vlsi/AI/backp_t_en/backprop.html</a></li><li><a href="https://en.wikipedia.org/wiki/Backpropagation">https://en.wikipedia.org/wiki/Backpropagation</a></li></ul>]]></content>
<tags>
<tag>Math</tag>
</tags>
</entry>
<entry>
<title>Jacobian矩阵与Hessian矩阵与最小二乘</title>
<link href="/2018/04/28/Jacobian%E7%9F%A9%E9%98%B5%E4%B8%8EHessian%E7%9F%A9%E9%98%B5%E4%B8%8E%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98/"/>
<url>/2018/04/28/Jacobian%E7%9F%A9%E9%98%B5%E4%B8%8EHessian%E7%9F%A9%E9%98%B5%E4%B8%8E%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98/</url>
<content type="html"><![CDATA[<h1 id="雅可比矩阵-Jacobian-matrix"><a href="#雅可比矩阵-Jacobian-matrix" class="headerlink" title="雅可比矩阵 (Jacobian matrix)"></a>雅可比矩阵 (Jacobian matrix)</h1><p>在向量分析中, 雅可比矩阵是<strong>一阶偏导</strong>数以一定方式排列成的<strong>矩阵</strong>, 其行列式称为雅可比行列式. 还有, 在代数几何中, 代数曲线的雅可比量表示雅可比簇:伴随该曲线的一个代数群, 曲线可以嵌入其中. 它们全部都以数学家卡尔·雅可比(Carl Jacob, 1804年10月4日-1851年2月18日)命名;英文雅可比量”Jacobian”可以发音为[ja ˈko bi ən]或者[ʤə ˈko bi ən].</p><p>雅可比矩阵的重要性在于它体现了一个可微方程与给出点的<strong>最优线性逼近</strong>. 因此, 雅可比矩阵类似于<strong>多元函数的导数</strong>.</p><p>假设$F:R_n→R_m$是一个从欧式n维空间转换到欧式m维空间的函数。这个函数由m个实函数组成:$ y_1(x_1,…,x_n), …, y_m(x_1,…,x_n)$. 这些函数的偏导数(如果存在)可以组成一个m行n列的矩阵,这就是所谓的雅可比矩阵:<br>$$\begin{bmatrix} \frac{\partial y_1}{\partial x_1} & \cdots & \frac{\partial y_1}{\partial x_n}\ \vdots & \ddots & \vdots \ \frac{\partial y_n}{\partial x_1} & \cdots & \frac{\partial y_n}{\partial x_n} \end{bmatrix}$$</p><p>此矩阵表示为:$J_F(x_1,\cdots ,x_2)$,或者:$\frac{\partial (y_1,\cdots,y_n)}{\partial (x_1, \cdots ,x_n)}$</p><p>这个矩阵的第$i$行是由梯度函数的转置$y_i(i=1,…,m)$表示的。</p><p>如果$p$是$R_n$中的一点, $F$在$p$点可<strong>微分</strong>, 那么在$p$这一点的导数由$J_{F(p)}$给出(这是求该点导数最简便的方法). 在此情况下, 由$F_(p)$描述的<strong>线性算子</strong>即接近点$p$的$F$的<strong>最优线性逼近</strong>, $x$逼近于$p$:$F_(x) \approx F_{(p)} + J_{F(p)} \cdot (x-p)$</p><p>从数学意义上来解释雅可比矩阵,我们可以想象有6个函数,每个函数对应着有6个变量。那么针对每个输入变量$x_i$,就会能够得到对应的$y_i$.<br>$$y_1=f_1(x_1,x_2,x_3,x_4,x_5,x_6)$$<br>$$y_2=f_2(x_1,x_2,x_3,x_4,x_5,x_6)$$<br>$$y_3=f_3(x_1,x_2,x_3,x_4,x_5,x_6)$$<br>$$y_4=f_4(x_1,x_2,x_3,x_4,x_5,x_6)$$<br>$$y_5=f_5(x_1,x_2,x_3,x_4,x_5,x_6)$$<br>$$y_6=f_6(x_1,x_2,x_3,x_4,x_5,x_6)$$<br>因此$y_i$的导数可以被写成:<br>$${\mathrm{d} y_i} = \frac{\partial f_i}{\partial x_1}{\mathrm{d} x_1}+\frac{\partial f_i}{\partial x_2}{\mathrm{d} x_2}+\frac{\partial f_i}{\partial x_3}{\mathrm{d} x_3}+\frac{\partial f_i}{\partial x_4}{\mathrm{d} x_4}+\frac{\partial f_i}{\partial x_5}{\mathrm{d} x_5}+\frac{\partial f_i}{\partial x_6}{\mathrm{d} x_6}$$<br>因此结合上面的方程,我们可以将上面的方程写为向量的形式:<br>$${\mathrm{d} Y}=\frac{\partial F}{\partial X}{\mathrm{d} X}$$<br>函数F对于X的偏导数矩阵,就被称为雅可比矩阵(Jacobian)。</p><p>换句话说,雅可比矩阵就是X的速度到Y的速度的映射:$\dot{Y}=J(X)\dot{X}$<br>在某个时间步长,雅可比矩阵其实也就是针对于$x_i$的函数。在下一个时间步长的时候,$x$改变了,因此雅可比矩阵也进行了改变。</p><p>在某个给定点的雅可比行列式提供了<strong>在接近该点时的表现的重要信息</strong>. 例如, 如果连续可微函数$F$在$p$点的雅可比行列式不是零, 那么它在该点附近具有反函数. 这称为反函数定理. 更进一步, 如果pp点的雅可比行列式是正数, 则$F$在$p$点的取向不变;如果是负数, 则FF的取向相反. 而从雅可比行列式的绝对值, 就可以知道函数$F$在$p$点的缩放因子;这就是为什么它出现在换元积分法中.</p><p>对于取向问题可以这么理解, 例如一个物体在平面上匀速运动, 如果施加一个正方向的力$F$, 即取向相同, 则加速运动, 类比于速度的导数加速度为正;如果施加一个反方向的力$F$, 即取向相反, 则减速运动, 类比于速度的导数加速度为负.</p><p><a href="ttp://jacoxu.com/jacobian%E7%9F%A9%E9%98%B5%E5%92%8Chessian%E7%9F%A9%E9%98%B5/">Jacobian矩阵和Hessian矩阵</a><br><a href="https://blog.csdn.net/noahzuo/article/details/54314112">使用雅可比矩阵(Jacobian)来结算IK</a><br><a href="https://www.zhihu.com/question/22586361">如何理解雅克比矩阵?—知乎</a><br><a href="https://blog.csdn.net/u011494690/article/details/43274301">雅克比矩阵、海森矩阵与非线性最小二乘间的关系与在SFM和Pose Estimation中的应用</a><br><a href="ttp://jacoxu.com/jacobian%E7%9F%A9%E9%98%B5%E5%92%8Chessian%E7%9F%A9%E9%98%B5/">acobian矩阵和Hessian矩阵</a><br><a href="https://blog.csdn.net/noahzuo/article/details/54314112">动画研究 – 使用雅可比矩阵(Jacobian)来结算IK</a></p><h1 id="海森矩阵(Hessian-matrix)"><a href="#海森矩阵(Hessian-matrix)" class="headerlink" title="海森矩阵(Hessian matrix)"></a>海森矩阵(Hessian matrix)</h1><p><strong>海森矩阵</strong>是一个自变量为向量的实值函数的<strong>二阶偏导数</strong>组成的<strong>方块矩阵</strong>.</p><p>此函数如下:$$f(x_1,\cdots,x_n)$$</p><p>如果 <em>f</em> 所有的二阶导数都存在,那么 <em>f</em> 的海森矩阵即:$H(f)_{ij}(x)=D_iD_jf(x)$其中$x=(x_1,x_2,\cdots,x_n)$,即是:</p><p>$$H(f)=\begin{bmatrix} \frac{\partial^2 f}{\partial x_1^2} &\frac{\partial^2 f}{\partial x_1\partial x_2} &\cdots &\frac{\partial^2 f}{\partial x_1\partial x_n} \ \frac{\partial^2 f}{\partial x_2\partial x_1} &\frac{\partial^2 f}{\partial x_2^2} &\cdots &\frac{\partial^2 f}{\partial x_2\partial x_n} \ \vdots &\vdots &\ddots &\vdots \ \frac{\partial^2 f}{\partial x_n\partial x_1} &\frac{\partial^2 f}{\partial x_n\partial x_2} &\cdots &\frac{\partial^2 f}{\partial x_n^2} \end{bmatrix}$$</p><p>(也有人把海森定义为以上矩阵的行列式)海森矩阵被应用于牛顿法解决的大规模优化问题.</p><h2 id="海森矩阵在牛顿法中的应用"><a href="#海森矩阵在牛顿法中的应用" class="headerlink" title="海森矩阵在牛顿法中的应用"></a>海森矩阵在牛顿法中的应用</h2><p>一般来说, 牛顿法主要应用在两个方面:</p><ul><li>求方程的根</li><li>最优化</li></ul><h3 id="求根"><a href="#求根" class="headerlink" title="求根"></a>求根</h3><p>并不是所有的方程都有求根公式, 或者求根公式很复杂, 导致求解困难. 利用牛顿法, 可以迭代求解.</p><p>原理是利用泰勒公式, 在$x_0$处展开, 且展开到一阶, 即$f(x)=f(x_0)+(x-x_0)f’(x_0)$</p><p>求解方程$f(x)=0$,即求解$f(x_0)+(x-x_0)f’(x_0) = 0$</p><p>即是求解$$x=x_1=x_0-\frac{f(x_0)}{f’(x_0)}$$</p><p>因为这是利用泰勒公式的一阶展开,$f(x)$的值是近似相等,这里求得的$x_1$,只能让$f(x_1) \approx 0$。于是乎, 迭代求解的想法就很自然了, 可以进而推出:$x_{n+1}=x_n-\frac{f(x_n)}{f’(x_n)}$, 通过迭代, 这个式子必然在$f(x^*)=0$的时候收敛.过程如下图:</p><p><img src="/2018/04/28/Jacobian%E7%9F%A9%E9%98%B5%E4%B8%8EHessian%E7%9F%A9%E9%98%B5%E4%B8%8E%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98/hessian.jpg" alt="hessian"></p><h3 id="最优化"><a href="#最优化" class="headerlink" title="最优化"></a>最优化</h3><p>在最优化的问题中, 线性最优化至少可以使用单纯形法(或称**<a href="https://en.wikipedia.org/wiki/Fixed-point_arithmetic">不动点算法</a>**)求解, 但对于非线性优化问题, 牛顿法提供了一种求解的办法.假设任务是优化一个目标函数$f$,求函数$f$的极大极小问题可以转化为求解函数$f$的导数${f}’=0$的问题。, 这样求可以把优化问题看成方程(${f}’=0$)求解问题。剩下的问题就和前面提到的牛顿法求解很相似了。</p><p>这次为了求解${f}’=0$的根, 首先把$f(x)$在探索点$x_n$处泰勒展开, 展开到2阶形式进行近似:</p><p>$$f(x)=f(x_n)+f’(x_n)(x-x_n)+\frac{f’’(x_n)}{2}(x-x_n)^2$$</p><p>然后用$f(x)$的最小点做为新的探索点$f(x+1)$,据此,令:</p><p>$$f’(x)=f’(x_n)+f’’(x_n)(x-x_n)=0$$</p><p>求得出迭代公式:</p><p>$$x_{n+1}=x_n-\frac{f’(n)}{f’’(n)},n=0,1,…$$</p><p>一般认为牛顿法可以利用到曲线本身的信息, 比梯度下降法更容易收敛(迭代更少次数), 如下图是一个最小化一个目标方程的例子, 红色曲线是利用牛顿法迭代求解, 绿色曲线是利用梯度下降法求解.</p><p><img src="/2018/04/28/Jacobian%E7%9F%A9%E9%98%B5%E4%B8%8EHessian%E7%9F%A9%E9%98%B5%E4%B8%8E%E6%9C%80%E5%B0%8F%E4%BA%8C%E4%B9%98/optimize.jpg" alt="optimize"></p><p>在上面讨论的是2维情况, 高维情况的牛顿迭代公式是:</p><p>$$x_{n+1}=x_n-[\bigtriangledown ^2f(x_n)]^{-1}\bigtriangledown f(x_n),n\geqslant 0$$</p><p>其中$\bigtriangledown ^2$是<strong>Hessian矩阵</strong>,$\bigtriangledown$为$f$的<strong>梯度向量</strong>,$f$的矢量微分运算。</p><p>高维情况依然可以用牛顿迭代求解, 但是问题是Hessian矩阵引入的复杂性, 使得牛顿迭代求解的难度大大增加, 但是已经有了解决这个问题的办法就是Quasi-Newton method, 不再直接计算Hessian矩阵, 而是每一步的时候使用<strong>梯度向量</strong>更新Hessian矩阵的近似.</p><p><strong>Reference:</strong><br><a href="ttp://jacoxu.com/jacobian%E7%9F%A9%E9%98%B5%E5%92%8Chessian%E7%9F%A9%E9%98%B5/">Jacobian矩阵和Hessian矩阵</a><br><a href="https://blog.csdn.net/u011494690/article/details/43274301">雅克比矩阵、海森矩阵与非线性最小二乘间的关系与在SFM和Pose Estimation中的应用</a><br><a href="https://my.oschina.net/u/3579120/blog/1508433">最优化方法:牛顿迭代法和拟牛顿迭代法</a></p><h1 id="最小二乘"><a href="#最小二乘" class="headerlink" title="最小二乘"></a>最小二乘</h1><p>最小二乘法的本质是最小化系数矩阵所张成的向量空间到观测向量的欧式误差距离.</p><p>最小二乘法的一种常见的描述是残差满足正态分布的最大似然估计</p><p>现行的最小二乘法是勒让德( A. M. Legendre)于1805年在其著作《计算慧星轨道的新方法》中提出的。它的主要思想就是选择未知参数,使得<strong>理论值</strong>与<strong>观测值</strong>之差的平方和达到最小:$H = \sum_{0}^{m}(y-y_i)^2$</p><p>##线性最小二乘问题</p><p>采用线性模型去拟合数据,即$y(x)=Jx$,则目标函数为$f(x)=\frac{1}{2}||Jx-y||^2$,同时$\bigtriangledown f(x)=J^T(Jx-y)$,</p><p>$\bigtriangledown^2 f(x)=J^TJ$。</p><p>根据最优化定理,该问题的最优解满足如下条件:$\bigtriangledown f(x)=0$,即是$J^TJx=J^Ty$。</p><p>求解该方程,有如下方法:</p><ol><li>**<a href="https://en.wikipedia.org/wiki/Cholesky_decomposition">Cholesky分解</a>**:当m>>n时,并且J比较稀疏时比较实用,但是其精度和条件数的平方相关 </li><li>**<a href="https://en.wikipedia.org/wiki/QR_decomposition">QR分解</a>**:精度和条件数相关 </li><li>**<a href="https://en.wikipedia.org/wiki/Singular-value_decomposition">SVD分解</a>**:相对比较鲁棒,同时也可以添加正则项避免J是个病态矩阵。 </li><li><strong>数值迭代算法</strong>:对于大规模数据比较实用。</li></ol><h2 id="非线性最小二乘问题"><a href="#非线性最小二乘问题" class="headerlink" title="非线性最小二乘问题"></a>非线性最小二乘问题</h2><p>采用非线性模型拟合数据,模型本身可能矢量梯度或者Hessian矩阵不容易得到。</p><h3 id="高斯牛顿方法(Gauss-Newton方法)"><a href="#高斯牛顿方法(Gauss-Newton方法)" class="headerlink" title="高斯牛顿方法(Gauss-Newton方法)"></a>高斯牛顿方法(Gauss-Newton方法)</h3><p>根据标准牛顿方程,$\bigtriangledown f(x_k)p=-\bigtriangledown f(x_k) $可以得到该问题的需要满</p><p>足的条件,即$J^TJP^{GN}=-J^Tr_K$</p><p>此时Hessian矩阵用第一项代替,即$\bigtriangledown^2f(x) \approx J^TJ$</p><p>得到搜索方向后,可以使用线搜索的方法得到步长,重复进行可以得到最优解。</p><p><strong>Reference:</strong><br><a href="https://blog.csdn.net/fangqingan_java/article/details/48948487">最小二乘问题(Least-Squares)</a><br><a href="https://blog.csdn.net/bitcarmanlee/article/details/51589143">最小二乘法 来龙去脉</a><br><a href="http://lanbing510.info/2016/03/28/Least-Squares-Parameter-Estimation.html">最小二乘法的参数估计</a><br><a href="https://blog.csdn.net/qll125596718/article/details/8248249">一元线性回归模型与最小二乘法及其C++实现</a><br><a href="http://blog.sciencenet.cn/home.php?mod=space&uid=465130&do=blog&id=1001688">机器视觉中的非线性最小二乘法</a><br><a href="https://blog.csdn.net/ivysister/article/details/45438413">最小二乘法,牛顿法,梯度下降法以及比较</a><br><a href="https://www.cnblogs.com/iamccme/archive/2013/05/15/3080737.html">机器学习经典算法之—–最小二乘法</a></p>]]></content>
<categories>
<category>基础知识</category>
</categories>
<tags>
<tag>基础知识</tag>
<tag>最优化</tag>
</tags>
</entry>
<entry>
<title>C++并发学习笔记(三)(std::mutex 详解)</title>
<link href="/2018/03/17/C-%E5%B9%B6%E5%8F%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%B8%89-std-mutex-%E8%AF%A6%E8%A7%A3/"/>
<url>/2018/03/17/C-%E5%B9%B6%E5%8F%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%B8%89-std-mutex-%E8%AF%A6%E8%A7%A3/</url>
<content type="html"><![CDATA[<p><strong>Mutex</strong>又称为互斥量,C++ 11中与 Mutex 相关的类(包括锁类型)和函数都声明在<code> <mutex></code> 头文件中,所以如果你需要使用 std::mutex,就必须包含 <code><mutex> </code>头文件。</p><p>std::mutex 是C++11 中最基本的互斥量,std::mutex 对象提供了独占所有权的特性——即不支持递归地对 std::mutex 对象上锁,而 std::recursive_lock 则可以递归地对互斥量对象上锁。</p><h1 id="mutex头文件介绍"><a href="#mutex头文件介绍" class="headerlink" title="mutex头文件介绍"></a>mutex头文件介绍</h1><p><strong>互斥量基本作用:</strong> 互斥占有一个变量,一段时间内仅一个线程可以访问。即该类可以限制对某物的访问,只有先获得许可才可访问某物,否则一般可设为阻塞等待。能有效避免资源竞争问题。</p><h2 id="Mutexes类(四种)"><a href="#Mutexes类(四种)" class="headerlink" title="Mutexes类(四种)"></a>Mutexes类(四种)</h2><ul><li><code>std::mutex</code>,最基本的 Mutex 类。</li><li><code>std::recursive_mutex</code>,递归 Mutex 类。</li><li><code>std::time_mutex</code>,定时 Mutex 类。</li><li><code>std::recursive_timed_mutex</code>,定时递归 Mutex 类。</li></ul><p>##Lock类(两种)</p><ul><li><a href="http://www.cplusplus.com/reference/mutex/lock_guard/">std::lock_guard</a>,与 Mutex RAII 相关,方便线程对互斥量上锁。</li><li><a href="http://www.cplusplus.com/reference/mutex/unique_lock/">std::unique_lock</a>,与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。</li></ul><p>##其他类型</p><ul><li><a href="http://www.cplusplus.com/reference/mutex/once_flag/">std::once_flag</a></li><li><a href="http://www.cplusplus.com/reference/mutex/adopt_lock_t/">std::adopt_lock_t</a></li><li><a href="http://www.cplusplus.com/reference/mutex/defer_lock_t/">std::defer_lock_t</a></li><li><a href="http://www.cplusplus.com/reference/mutex/try_to_lock_t/">std::try_to_lock_t</a></li></ul><p>##函数</p><ul><li><a href="http://www.cplusplus.com/reference/mutex/try_lock/">std::try_lock</a>,尝试同时对多个互斥量上锁。</li><li><a href="http://www.cplusplus.com/reference/mutex/lock/">std::lock</a>,可以同时对多个互斥量上锁。</li><li><a href="http://www.cplusplus.com/reference/mutex/call_once/">std::call_once</a>,如果多个线程需要同时调用某个函数,call_once 可以保证多个线程对该函数只调用一次。</li></ul><h1 id="std-mutex-介绍"><a href="#std-mutex-介绍" class="headerlink" title="std::mutex 介绍"></a>std::mutex 介绍</h1><ul><li><p><strong>构造函数</strong>,std::mutex不允许拷贝构造,也不允许 move 拷贝,最初产生的 mutex 对象是处于 <strong>unlocked</strong> 状态的。</p></li><li><p>**lock()**,调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:</p><p>(1). 如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。</p><p>(2). 如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住。</p><p>(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。</p></li><li><p>**unlock()**, 解锁,释放对互斥量的所有权。</p></li><li><p>**try_lock()**,尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况,</p><p>(1). 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。</p><p>(2). 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。</p><p>(3). 如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。</p></li></ul><p>**mutex::lock **Example(<a href="http://www.cplusplus.com/reference/mutex/mutex/lock/">reference</a>):</p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// mutex::lock/unlock</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><mutex></span> <span class="hljs-comment">// std::mutex</span></span><br><br>std::mutex mtx; <span class="hljs-comment">// mutex for critical section</span><br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">print_thread_id</span> <span class="hljs-params">(<span class="hljs-type">int</span> id)</span> </span>{<br> <span class="hljs-comment">// critical section (exclusive access to std::cout signaled by locking mtx):</span><br> mtx.<span class="hljs-built_in">lock</span>();<br> std::cout << <span class="hljs-string">"thread #"</span> << id << <span class="hljs-string">'\n'</span>;<br> mtx.<span class="hljs-built_in">unlock</span>();<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span> <span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> std::thread threads[<span class="hljs-number">10</span>];<br> <span class="hljs-comment">// spawn 10 threads:</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>; i<<span class="hljs-number">10</span>; ++i)<br> threads[i] = std::<span class="hljs-built_in">thread</span>(print_thread_id,i+<span class="hljs-number">1</span>);<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span>& th : threads) th.<span class="hljs-built_in">join</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs shell">thread #1<br>thread #2<br>thread #3<br>thread #4<br>thread #5<br>thread #6<br>thread #7<br>thread #8<br>thread #9<br>thread #10<br></code></pre></td></tr></table></figure><p>**mutex::try_lock **Example(<a href="http://www.cplusplus.com/reference/mutex/mutex/try_lock/">reference</a>):</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// mutex::try_lock example</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><mutex></span> <span class="hljs-comment">// std::mutex</span></span><br><br><span class="hljs-function"><span class="hljs-keyword">volatile</span> <span class="hljs-type">int</span> <span class="hljs-title">counter</span> <span class="hljs-params">(<span class="hljs-number">0</span>)</span></span>; <span class="hljs-comment">// non-atomic counter</span><br>std::mutex mtx; <span class="hljs-comment">// locks access to counter</span><br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">attempt_10k_increases</span> <span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>; i<<span class="hljs-number">10000</span>; ++i) {<br> <span class="hljs-keyword">if</span> (mtx.<span class="hljs-built_in">try_lock</span>()) { <span class="hljs-comment">// only increase if currently not locked:</span><br> ++counter;<br> mtx.<span class="hljs-built_in">unlock</span>();<br> }<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span> <span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> std::thread threads[<span class="hljs-number">10</span>];<br> <span class="hljs-comment">// spawn 10 threads:</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>; i<<span class="hljs-number">10</span>; ++i)<br> threads[i] = std::<span class="hljs-built_in">thread</span>(attempt_10k_increases);<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span>& th : threads) th.<span class="hljs-built_in">join</span>();<br> std::cout << counter << <span class="hljs-string">" successful increases of the counter.\n"</span>;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Possible output (any count between 1 and 100000 possible):</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">17987 successful increases of the counter.<br></code></pre></td></tr></table></figure><p>#std::recursive_mutex 介绍</p><p><code>std::recursive_mutex</code> 与<code> std::mutex</code> 一样,也是一种可以被上锁的对象,但和 <code>std::mutex</code> 不同的是,<code>std::recursive_mutex</code> <strong>允许同一个线程对互斥量多次上锁(即递归上锁)</strong>,来获得对互斥量对象的<strong>多层所有权</strong>,<code>std::recursive_mutex</code> 释放互斥量时需要调用与该锁层次深度相同次数的 <code>unlock()</code>,可理解为 <code>lock()</code> 次数和<code>unlock()</code>次数相同,除此之外,<code>std::recursive_mutex</code> 的特性和 <code>std::mutex</code> 大致相同。</p><p>如果<strong>一个线程中可能在执行中需要再次获得锁</strong>的情况,按常规的做法会出现<strong>死锁</strong>。此时就需要使用递归式互斥量<code>std::recursive_mutex</code>来避免这个问题。<code>std::recursive_mutex</code>不会产生上述的死锁问题,只是是增加锁的计数,但必须确保你unlock和lock的次数相同,其他线程才可能锁这个mutex。</p><h1 id="std-time-mutex-介绍"><a href="#std-time-mutex-介绍" class="headerlink" title="std::time_mutex 介绍"></a>std::time_mutex 介绍</h1><p><code>std::time_mutex</code> 比 <code>std::mutex</code> 多了两个成员函数,<code>try_lock_for(),try_lock_until()</code>。</p><h2 id="try-lock-for"><a href="#try-lock-for" class="headerlink" title="try_lock_for()"></a>try_lock_for()</h2><p><code>try_lock_for</code> 函数接受一个时间范围,表示在这一段时间范围之内线程如果没有获得锁则被阻塞住(与 std::mutex 的 try_lock() 不同,try_lock 如果被调用时没有获得锁则直接返回 false),如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。</p><p>**try_lock_for() —**Example(<a href="http://www.cplusplus.com/reference/mutex/timed_mutex/try_lock_for/">reference</a>) </p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span> <span class="hljs-comment">// std::chrono::milliseconds</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><mutex></span> <span class="hljs-comment">// std::timed_mutex</span></span><br><br>std::timed_mutex mtx;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">fireworks</span> <span class="hljs-params">()</span> </span>{<br> <span class="hljs-comment">// waiting to get a lock: each thread prints "-" every 200ms:</span><br> <span class="hljs-keyword">while</span> (!mtx.<span class="hljs-built_in">try_lock_for</span>(std::chrono::<span class="hljs-built_in">milliseconds</span>(<span class="hljs-number">200</span>))) {<br> std::cout << <span class="hljs-string">"-"</span>;<br> }<br> <span class="hljs-comment">// got a lock! - wait for 1s, then this thread prints "*"</span><br> std::this_thread::<span class="hljs-built_in">sleep_for</span>(std::chrono::<span class="hljs-built_in">milliseconds</span>(<span class="hljs-number">1000</span>));<br> std::cout << <span class="hljs-string">"*\n"</span>;<br> mtx.<span class="hljs-built_in">unlock</span>();<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span> <span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> std::thread threads[<span class="hljs-number">5</span>];<br> <span class="hljs-comment">// spawn 5 threads:</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>; i<<span class="hljs-number">5</span>; ++i)<br> threads[i] = std::<span class="hljs-built_in">thread</span>(fireworks);<br> <br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span>& th : threads) th.<span class="hljs-built_in">join</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs shell">----------------*<br>---------------*<br>----------*<br>-----*<br>*<br></code></pre></td></tr></table></figure><h2 id="try-lock-until"><a href="#try-lock-until" class="headerlink" title="try_lock_until()"></a>try_lock_until()</h2><p><code>try_lock_until</code> 函数则接受一个时间点作为参数,在指定时间点未到来之前线程如果没有获得锁则被阻塞住,如果在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁,如果超时(即在指定时间内还是没有获得锁),则返回 false。</p><h1 id="std-lock-guard-介绍"><a href="#std-lock-guard-介绍" class="headerlink" title="std::lock_guard 介绍"></a>std::lock_guard 介绍</h1><p>与 Mutex RAII 相关,方便线程对互斥量上锁。</p><p>Example(<a href="http://www.cplusplus.com/reference/mutex/lock_guard/">reference</a>):</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><mutex></span> <span class="hljs-comment">// std::mutex, std::lock_guard</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdexcept></span> <span class="hljs-comment">// std::logic_error</span></span><br><br>std::mutex mtx;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">print_even</span> <span class="hljs-params">(<span class="hljs-type">int</span> x)</span> </span>{<br> <span class="hljs-keyword">if</span> (x%<span class="hljs-number">2</span>==<span class="hljs-number">0</span>) std::cout << x << <span class="hljs-string">" is even\n"</span>;<br> <span class="hljs-keyword">else</span> <span class="hljs-built_in">throw</span> (std::<span class="hljs-built_in">logic_error</span>(<span class="hljs-string">"not even"</span>));<br>}<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">print_thread_id</span> <span class="hljs-params">(<span class="hljs-type">int</span> id)</span> </span>{<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-comment">// using a local lock_guard to lock mtx guarantees unlocking on destruction / exception:</span><br> <span class="hljs-function">std::lock_guard<std::mutex> <span class="hljs-title">lck</span> <span class="hljs-params">(mtx)</span></span>;<br> <span class="hljs-built_in">print_even</span>(id);<br> }<br> <span class="hljs-built_in">catch</span> (std::logic_error&) {<br> std::cout << <span class="hljs-string">"[exception caught]\n"</span>;<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span> <span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> std::thread threads[<span class="hljs-number">10</span>];<br> <span class="hljs-comment">// spawn 10 threads:</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>; i<<span class="hljs-number">10</span>; ++i)<br> threads[i] = std::<span class="hljs-built_in">thread</span>(print_thread_id,i+<span class="hljs-number">1</span>);<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span>& th : threads) th.<span class="hljs-built_in">join</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs shell">[exception caught]<br>2 is even<br>[exception caught]<br>4 is even<br>[exception caught]<br>6 is even<br>[exception caught]<br>8 is even<br>[exception caught]<br>10 is even<br></code></pre></td></tr></table></figure><h1 id="std-unique-lock-介绍"><a href="#std-unique-lock-介绍" class="headerlink" title="std::unique_lock 介绍"></a>std::unique_lock 介绍</h1><p>与 Mutex RAII 相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制。</p><p>Example(<a href="http://www.cplusplus.com/reference/mutex/unique_lock/">reference</a>):</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><mutex></span> <span class="hljs-comment">// std::mutex, std::unique_lock</span></span><br><br>std::mutex mtx; <span class="hljs-comment">// mutex for critical section</span><br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">print_block</span> <span class="hljs-params">(<span class="hljs-type">int</span> n, <span class="hljs-type">char</span> c)</span> </span>{<br> <span class="hljs-comment">// critical section (exclusive access to std::cout signaled by lifetime of lck):</span><br> <span class="hljs-function">std::unique_lock<std::mutex> <span class="hljs-title">lck</span> <span class="hljs-params">(mtx)</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>; i<n; ++i) {<br> std::cout << c;<br> }<br> std::cout << <span class="hljs-string">'\n'</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span> <span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-function">std::thread <span class="hljs-title">th1</span> <span class="hljs-params">(print_block,<span class="hljs-number">50</span>,<span class="hljs-string">'*'</span>)</span></span>;<br> <span class="hljs-function">std::thread <span class="hljs-title">th2</span> <span class="hljs-params">(print_block,<span class="hljs-number">50</span>,<span class="hljs-string">'$'</span>)</span></span>;<br><br> th1.<span class="hljs-built_in">join</span>();<br> th2.<span class="hljs-built_in">join</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">**************************************************<br><span class="hljs-meta prompt_">$</span><span class="language-bash">$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$</span><br></code></pre></td></tr></table></figure><h1 id="std-lock-介绍"><a href="#std-lock-介绍" class="headerlink" title="std::lock 介绍"></a>std::lock 介绍</h1><p>Example(<a href="http://www.cplusplus.com/reference/mutex/lock/">reference</a>):</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// std::lock example</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><mutex></span> <span class="hljs-comment">// std::mutex, std::lock</span></span><br><br>std::mutex foo,bar;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">task_a</span> <span class="hljs-params">()</span> </span>{<br> <span class="hljs-comment">// foo.lock(); bar.lock(); // replaced by:</span><br> std::<span class="hljs-built_in">lock</span> (foo,bar);<br> std::cout << <span class="hljs-string">"task a\n"</span>;<br> foo.<span class="hljs-built_in">unlock</span>();<br> bar.<span class="hljs-built_in">unlock</span>();<br>}<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">task_b</span> <span class="hljs-params">()</span> </span>{<br> <span class="hljs-comment">// bar.lock(); foo.lock(); // replaced by:</span><br> std::<span class="hljs-built_in">lock</span> (bar,foo);<br> std::cout << <span class="hljs-string">"task b\n"</span>;<br> bar.<span class="hljs-built_in">unlock</span>();<br> foo.<span class="hljs-built_in">unlock</span>();<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span> <span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-function">std::thread <span class="hljs-title">th1</span> <span class="hljs-params">(task_a)</span></span>;<br> <span class="hljs-function">std::thread <span class="hljs-title">th2</span> <span class="hljs-params">(task_b)</span></span>;<br><br> th1.<span class="hljs-built_in">join</span>();<br> th2.<span class="hljs-built_in">join</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Possible output (order of lines may vary):</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">task a<br>task b<br></code></pre></td></tr></table></figure><h1 id="std-try-lock-介绍"><a href="#std-try-lock-介绍" class="headerlink" title="std::try_lock 介绍"></a>std::try_lock 介绍</h1><p>尝试lock,若线程不可被lock返回false,否则进行lock。</p><p>Example(<a href="http://www.cplusplus.com/reference/mutex/try_lock/">reference</a>):</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><mutex></span> <span class="hljs-comment">// std::mutex, std::try_lock</span></span><br><br>std::mutex foo,bar;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">task_a</span> <span class="hljs-params">()</span> </span>{<br> foo.<span class="hljs-built_in">lock</span>();<br> std::cout << <span class="hljs-string">"task a\n"</span>;<br> bar.<span class="hljs-built_in">lock</span>();<br> <span class="hljs-comment">// ...</span><br> foo.<span class="hljs-built_in">unlock</span>();<br> bar.<span class="hljs-built_in">unlock</span>();<br>}<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">task_b</span> <span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">int</span> x = <span class="hljs-built_in">try_lock</span>(bar,foo);<br> <span class="hljs-keyword">if</span> (x==<span class="hljs-number">-1</span>) {<br> std::cout << <span class="hljs-string">"task b\n"</span>;<br> <span class="hljs-comment">// ...</span><br> bar.<span class="hljs-built_in">unlock</span>();<br> foo.<span class="hljs-built_in">unlock</span>();<br> }<br> <span class="hljs-keyword">else</span> {<br> std::cout << <span class="hljs-string">"[task b failed: mutex "</span> << (x?<span class="hljs-string">"foo"</span>:<span class="hljs-string">"bar"</span>) << <span class="hljs-string">" locked]\n"</span>;<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span> <span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-function">std::thread <span class="hljs-title">th1</span> <span class="hljs-params">(task_a)</span></span>;<br> <span class="hljs-function">std::thread <span class="hljs-title">th2</span> <span class="hljs-params">(task_b)</span></span>;<br><br> th1.<span class="hljs-built_in">join</span>();<br> th2.<span class="hljs-built_in">join</span>();<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">task a<br>[task b failed: mutex foo locked]<br></code></pre></td></tr></table></figure><h1 id="std-call-once-介绍"><a href="#std-call-once-介绍" class="headerlink" title="std::call_once 介绍"></a>std::call_once 介绍</h1><p>call_once可以很好的满足,某些场景下,我们需要代码只被执行一次,比如单例类的初始化,考虑到多线程安全,需要进行加锁控制。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-keyword">template</span> <<span class="hljs-keyword">class</span> <span class="hljs-title class_">Fn</span>, <span class="hljs-keyword">class</span>... Args><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">call_once</span> <span class="hljs-params">(once_flag& flag, Fn&& fn, Args&&... args)</span></span>;<br></code></pre></td></tr></table></figure><ul><li><p>第一个参数是<code>std::once_flag</code>的对象(once_flag是不允许修改的,其拷贝构造函数和operator=函数都声明为delete),</p></li><li><p>第二个参数可调用实体,即要求只执行一次的代码,后面可变参数是其参数列表。</p></li></ul><p>call_once保证函数fn只被执行一次,如果有多个线程同时执行函数fn调用,则只有一个活动线程(active call)会执行函数,其他的线程在这个线程执行返回之前会处于”passive execution”(被动执行状态)——不会直接返回,直到活动线程对fn调用结束才返回。对于所有调用函数fn的并发线程,数据可见性都是同步的(一致的)。</p><p>如果活动线程在执行fn时抛出异常,则会从处于”passive execution”状态的线程中挑一个线程成为活动线程继续执行fn,依此类推。一旦活动线程返回,所有”passive execution”状态的线程也返回,不会成为活动线程。(实际上once_flag相当于一个锁,使用它的线程都会在上面等待,只有一个线程允许执行。如果该线程抛出异常,那么从等待中的线程中选择一个,重复上面的流程)。</p><p>还有一个要注意的地方是 once_flag的生命周期,它必须要比使用它的线程的生命周期要长。所以通常定义成全局变量比较好。</p><p>Example(<a href="http://www.cplusplus.com/reference/mutex/call_once/">reference</a>)</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// call_once example</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread, std::this_thread::sleep_for</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span> <span class="hljs-comment">// std::chrono::milliseconds</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><mutex></span> <span class="hljs-comment">// std::call_once, std::once_flag</span></span><br><br><span class="hljs-type">int</span> winner;<br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_winner</span> <span class="hljs-params">(<span class="hljs-type">int</span> x)</span> </span>{ winner = x; }<br>std::once_flag winner_flag;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">wait_1000ms</span> <span class="hljs-params">(<span class="hljs-type">int</span> id)</span> </span>{<br> <span class="hljs-comment">// count to 1000, waiting 1ms between increments:</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>; i<<span class="hljs-number">1000</span>; ++i)<br> std::this_thread::<span class="hljs-built_in">sleep_for</span>(std::chrono::<span class="hljs-built_in">milliseconds</span>(<span class="hljs-number">1</span>));<br> <span class="hljs-comment">// claim to be the winner (only the first such call is executed):</span><br> std::<span class="hljs-built_in">call_once</span> (winner_flag,set_winner,id);<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span> <span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> std::thread threads[<span class="hljs-number">10</span>];<br> <span class="hljs-comment">// spawn 10 threads:</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>; i<<span class="hljs-number">10</span>; ++i)<br> threads[i] = std::<span class="hljs-built_in">thread</span>(wait_1000ms,i+<span class="hljs-number">1</span>);<br><br> std::cout << <span class="hljs-string">"waiting for the first among 10 threads to count 1000 ms...\n"</span>;<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span>& th : threads) th.<span class="hljs-built_in">join</span>();<br> std::cout << <span class="hljs-string">"winner thread: "</span> << winner << <span class="hljs-string">'\n'</span>;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">waiting for the first among 10 threads to count 1000 ms...<br>winner thread: 2<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Cpp</category>
</categories>
<tags>
<tag>Cpp</tag>
<tag>Thread</tag>
</tags>
</entry>
<entry>
<title>C++并发学习笔记(二)(std::thread 详解)</title>
<link href="/2018/03/17/C-%E5%B9%B6%E5%8F%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%BA%8C-std-thread-%E8%AF%A6%E8%A7%A3/"/>
<url>/2018/03/17/C-%E5%B9%B6%E5%8F%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%BA%8C-std-thread-%E8%AF%A6%E8%A7%A3/</url>
<content type="html"><![CDATA[<h1 id="std-thread-构造"><a href="#std-thread-构造" class="headerlink" title="std::thread 构造"></a>std::thread 构造</h1><p>std::thread 在<code> <thread></code> 头文件中声明,因此使用 <code>std::thread </code>时需要包含<code><thread></code>头文件。</p><table><thead><tr><th align="left">default</th><th align="left">thread() noexcept;</th><th align="left">默认构造函数,创建一个空的 thread 执行对象</th></tr></thead><tbody><tr><td align="left">initialization</td><td align="left">template <class Fn, class… Args> explicit thread (Fn&& fn, Args&&… args);</td><td align="left">初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。</td></tr><tr><td align="left">copy [deleted]</td><td align="left">thread (const thread&) = delete;</td><td align="left">拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。</td></tr><tr><td align="left">move</td><td align="left">thread (thread&& x) noexcept;</td><td align="left">move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象</td></tr></tbody></table><p>注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached.</p><p>Example (<a href="http://en.cppreference.com/w/cpp/thread/thread/thread">reference</a>):</p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><utility></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><functional></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><atomic></span></span><br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">f1</span><span class="hljs-params">(<span class="hljs-type">int</span> n)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; ++i) {<br> std::cout << <span class="hljs-string">"Thread 1 executing\n"</span>;<br> ++n;<br> std::this_thread::<span class="hljs-built_in">sleep_for</span>(std::chrono::<span class="hljs-built_in">milliseconds</span>(<span class="hljs-number">10</span>));<br> }<br>}<br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">f2</span><span class="hljs-params">(<span class="hljs-type">int</span>& n)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; ++i) {<br> std::cout << <span class="hljs-string">"Thread 2 executing\n"</span>;<br> ++n;<br> std::this_thread::<span class="hljs-built_in">sleep_for</span>(std::chrono::<span class="hljs-built_in">milliseconds</span>(<span class="hljs-number">10</span>));<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">int</span> n = <span class="hljs-number">0</span>;<br> std::thread t1; <span class="hljs-comment">// t1 is not a thread</span><br> <span class="hljs-function">std::thread <span class="hljs-title">t2</span><span class="hljs-params">(f1, n + <span class="hljs-number">1</span>)</span></span>; <span class="hljs-comment">// pass by value</span><br> <span class="hljs-function">std::thread <span class="hljs-title">t3</span><span class="hljs-params">(f2, std::ref(n))</span></span>; <span class="hljs-comment">// pass by reference</span><br> <span class="hljs-function">std::thread <span class="hljs-title">t4</span><span class="hljs-params">(std::move(t3))</span></span>; <span class="hljs-comment">// t4 is now running f2(). t3 is no longer a thread</span><br> t2.<span class="hljs-built_in">join</span>();<br> t4.<span class="hljs-built_in">join</span>();<br> std::cout << <span class="hljs-string">"Final value of n is "</span> << n << <span class="hljs-string">'\n'</span>;<br>}<br></code></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs shell">Thread 1 executing<br>Thread 2 executing<br>Thread 1 executing<br>Thread 2 executing<br>Thread 1 executing<br>Thread 2 executing<br>Thread 1 executing<br>Thread 2 executing<br>Thread 1 executing<br>Thread 2 executing<br>Final value of n is 5<br></code></pre></td></tr></table></figure><h1 id="move-赋值操作"><a href="#move-赋值操作" class="headerlink" title="move 赋值操作"></a>move 赋值操作</h1><table><thead><tr><th>move</th><th><code>thread& operator= (thread&& rhs) noexcept;</code></th><th>move 赋值操作,如果当前对象不可 joinable,需要传递一个右值引用(rhs)给 move 赋值操作;如果当前对象可被 joinable,则 terminate() 报错。</th></tr></thead><tbody><tr><td>copy [deleted]</td><td><code>thread& operator= (const thread&) = delete;</code></td><td>拷贝赋值操作被禁用,thread 对象不可被拷贝。</td></tr></tbody></table><p>Example :</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span></span><br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">sub_task</span><span class="hljs-params">(<span class="hljs-type">int</span> n)</span></span><br><span class="hljs-function"></span>{<br>std::this_thread::<span class="hljs-built_in">sleep_for</span>(std::chrono::<span class="hljs-built_in">milliseconds</span>(n));<br>std::cout << <span class="hljs-string">"hello world "</span> <br><< std::this_thread::<span class="hljs-built_in">get_id</span>()<br><< <span class="hljs-string">" paused "</span> << n << <span class="hljs-string">" milliseconds"</span> << std::endl;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>** argv )</span></span><br><span class="hljs-function"></span>{<br>std::thread threads[<span class="hljs-number">5</span>];<br>std::cout << <span class="hljs-string">"Spawning 5 threads...\n"</span>;<br><span class="hljs-keyword">for</span> (<span class="hljs-type">size_t</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++)<br>threads[i] = std::<span class="hljs-built_in">thread</span>(sub_task, i);<br><br>std::cout << <span class="hljs-string">"Done spawning threads! Now wait for them to join\n"</span>;<br><br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> &t : threads)<br>t.<span class="hljs-built_in">join</span>();<br><br>std::cout << <span class="hljs-string">"All threads joined.\n"</span>;<br><br><br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><h1 id="其他成员函数"><a href="#其他成员函数" class="headerlink" title="其他成员函数"></a>其他成员函数</h1><h2 id="get-id"><a href="#get-id" class="headerlink" title="get_id"></a>get_id</h2><p><a href="http://www.cplusplus.com/reference/thread/thread/get_id/"><strong>get_id</strong></a>获取线程 ID<br>若该线程是joinable的,该函数返回该线程ID<br>若该线程不是joinable的,该函数返回成员默认类型<a href="http://www.cplusplus.com/thread::id"><strong>thread::id</strong></a></p><p>Example:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// thread::get_id / this_thread::get_id</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread, std::thread::id, std::this_thread::get_id</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span> <span class="hljs-comment">// std::chrono::seconds</span></span><br> <br>std::thread::id main_thread_id = std::this_thread::<span class="hljs-built_in">get_id</span>();<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">is_main_thread</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">if</span> ( main_thread_id == std::this_thread::<span class="hljs-built_in">get_id</span>() )<br> std::cout << <span class="hljs-string">"This is the main thread.\n"</span>;<br> <span class="hljs-keyword">else</span><br> std::cout << <span class="hljs-string">"This is not the main thread.\n"</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{<br> <span class="hljs-built_in">is_main_thread</span>();<br> <span class="hljs-function">std::thread <span class="hljs-title">th</span> <span class="hljs-params">(is_main_thread)</span></span>;<br> th.<span class="hljs-built_in">join</span>();<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">This is the main thread.<br>This is not the main thread.<br></code></pre></td></tr></table></figure><h2 id="joinable"><a href="#joinable" class="headerlink" title="joinable"></a>joinable</h2><p><a href="http://www.cplusplus.com/reference/thread/thread/joinable/"><strong>joinable</strong></a> 检查线程是否可被 join</p><p>线程可被join:当其为可执行线程时。</p><p>线程不可被join:</p><ul><li>1、线程是**<a href="http://www.cplusplus.com/thread::thread">default-constructed</a>**(默认构造)</li><li>2、当其被moved</li><li>3、当其成员<strong>join</strong>或<strong>detach</strong>被调用时</li></ul><p>当去销毁一个仍然可以“joinable”的C++线程对象会被认为是一种错误。为了销毁一个C++线程对象,约么join()函数需要被调用(并结束),要么detach()函数被调用。如果一个C++线程对象当销毁时仍然可以被join,异常会被抛出。</p><p>Example:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread</span></span><br> <br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">mythread</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{<br> <span class="hljs-comment">// do stuff...</span><br>}<br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{<br> std::thread foo;<br> <span class="hljs-function">std::thread <span class="hljs-title">bar</span><span class="hljs-params">(mythread)</span></span>;<br><br> std::cout << <span class="hljs-string">"Joinable after construction:\n"</span> << std::boolalpha;<br> std::cout << <span class="hljs-string">"foo: "</span> << foo.<span class="hljs-built_in">joinable</span>() << <span class="hljs-string">'\n'</span>;<br> std::cout << <span class="hljs-string">"bar: "</span> << bar.<span class="hljs-built_in">joinable</span>() << <span class="hljs-string">'\n'</span>;<br><br> <span class="hljs-keyword">if</span> (foo.<span class="hljs-built_in">joinable</span>()) foo.<span class="hljs-built_in">join</span>();<br> <span class="hljs-keyword">if</span> (bar.<span class="hljs-built_in">joinable</span>()) bar.<span class="hljs-built_in">join</span>();<br><br> std::cout << <span class="hljs-string">"Joinable after joining:\n"</span> << std::boolalpha;<br> std::cout << <span class="hljs-string">"foo: "</span> << foo.<span class="hljs-built_in">joinable</span>() << <span class="hljs-string">'\n'</span>;<br> std::cout << <span class="hljs-string">"bar: "</span> << bar.<span class="hljs-built_in">joinable</span>() << <span class="hljs-string">'\n'</span>;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output(after 3 seconds):</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs shell">Joinable after construction:<br>foo: false<br>bar: true<br>Joinable after joining:<br>foo: false<br>bar: false<br></code></pre></td></tr></table></figure><h2 id="join"><a href="#join" class="headerlink" title="join"></a>join</h2><p><a href="http://www.cplusplus.com/reference/thread/thread/join/"><strong>join</strong></a>Join线程</p><p>当thread::join()函数被调用后,调用它的线程会被block,直到线程的执行被完成。基本上,这是一种可以用来知道一个线程已结束的机制。当thread::join()返回时,OS的执行的线程已经完成,C++线程对象可以被销毁。</p><p>Exapmle:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread, std::this_thread::sleep_for</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span> <span class="hljs-comment">// std::chrono::seconds</span></span><br> <br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">pause_thread</span><span class="hljs-params">(<span class="hljs-type">int</span> n)</span> </span><br><span class="hljs-function"></span>{<br> std::this_thread::<span class="hljs-built_in">sleep_for</span> (std::chrono::<span class="hljs-built_in">seconds</span>(n));<br> std::cout << <span class="hljs-string">"pause of "</span> << n << <span class="hljs-string">" seconds ended\n"</span>;<br>}<br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{<br> std::cout << <span class="hljs-string">"Spawning 3 threads...\n"</span>;<br> <span class="hljs-function">std::thread <span class="hljs-title">t1</span> <span class="hljs-params">(pause_thread,<span class="hljs-number">1</span>)</span></span>;<br> <span class="hljs-function">std::thread <span class="hljs-title">t2</span> <span class="hljs-params">(pause_thread,<span class="hljs-number">2</span>)</span></span>;<br> <span class="hljs-function">std::thread <span class="hljs-title">t3</span> <span class="hljs-params">(pause_thread,<span class="hljs-number">3</span>)</span></span>;<br> std::cout << <span class="hljs-string">"Done spawning threads. Now waiting for them to join:\n"</span>;<br> t1.<span class="hljs-built_in">join</span>();<br> t2.<span class="hljs-built_in">join</span>();<br> t3.<span class="hljs-built_in">join</span>();<br> std::cout << <span class="hljs-string">"All threads joined!\n"</span>;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output (after 3 seconds):</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs shell">Spawning 3 threads...<br>Done spawning threads. Now waiting for them to join:<br>pause of 1 seconds ended<br>pause of 2 seconds ended<br>pause of 3 seconds ended<br>All threads joined!<br></code></pre></td></tr></table></figure><h2 id="detach"><a href="#detach" class="headerlink" title="detach"></a>detach</h2><p><a href="http://www.cplusplus.com/reference/thread/thread/detach/"><strong>detach</strong></a>Detach线程</p><p>detach允许线程独立运行,线程结束,资源会释放。</p><p>当thread::detach()函数被调用后,执行的线程从线程对象中被分离,已不再被一个线程对象所表达–这是两个独立的事情。C++线程对象可以被销毁,同时OS执行的线程可以继续。如果程序想要知道执行的线程何时结束,就需要一些其它的机制。join()函数在那个thread对象上不能再被调用,因为它已经不再和一个执行的线程相关联。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> <span class="hljs-comment">// std::cout</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> <span class="hljs-comment">// std::thread, std::this_thread::sleep_for</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span> <span class="hljs-comment">// std::chrono::seconds</span></span><br> <br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">pause_thread</span><span class="hljs-params">(<span class="hljs-type">int</span> n)</span> </span><br><span class="hljs-function"></span>{<br> std::this_thread::<span class="hljs-built_in">sleep_for</span> (std::chrono::<span class="hljs-built_in">seconds</span>(n));<br> std::cout << <span class="hljs-string">"pause of "</span> << n << <span class="hljs-string">" seconds ended\n"</span>;<br>}<br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{<br> std::cout << <span class="hljs-string">"Spawning and detaching 3 threads...\n"</span>;<br> std::<span class="hljs-built_in">thread</span> (pause_thread,<span class="hljs-number">1</span>).<span class="hljs-built_in">detach</span>();<br> std::<span class="hljs-built_in">thread</span> (pause_thread,<span class="hljs-number">2</span>).<span class="hljs-built_in">detach</span>();<br> std::<span class="hljs-built_in">thread</span> (pause_thread,<span class="hljs-number">3</span>).<span class="hljs-built_in">detach</span>();<br> std::cout << <span class="hljs-string">"Done spawning threads.\n"</span>;<br><br> std::cout << <span class="hljs-string">"(the main thread will now pause for 5 seconds)\n"</span>;<br> <span class="hljs-comment">// give the detached threads time to finish (but not guaranteed!):</span><br> <span class="hljs-built_in">pause_thread</span>(<span class="hljs-number">5</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs shell">Spawning and detaching 3 threads...<br>Done spawning threads.<br>(the main thread will now pause for 5 seconds)<br>pause of 1 seconds ended<br>pause of 2 seconds ended<br>pause of 3 seconds ended<br>pause of 5 seconds ended<br></code></pre></td></tr></table></figure><h2 id="swap"><a href="#swap" class="headerlink" title="swap"></a>swap</h2><p><a href="http://www.cplusplus.com/reference/thread/thread/swap/"><strong>swap</strong></a>Swap线程</p><p>互换两个 thread 对象的底层句柄</p><p>Example:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><chrono></span></span><br> <br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">foo</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> std::this_thread::<span class="hljs-built_in">sleep_for</span>(std::chrono::<span class="hljs-built_in">seconds</span>(<span class="hljs-number">1</span>));<br>}<br> <br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">bar</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> std::this_thread::<span class="hljs-built_in">sleep_for</span>(std::chrono::<span class="hljs-built_in">seconds</span>(<span class="hljs-number">1</span>));<br>}<br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-function">std::thread <span class="hljs-title">t1</span><span class="hljs-params">(foo)</span></span>;<br> <span class="hljs-function">std::thread <span class="hljs-title">t2</span><span class="hljs-params">(bar)</span></span>;<br> <br> std::cout << <span class="hljs-string">"thread 1 id: "</span> << t1.<span class="hljs-built_in">get_id</span>() << std::endl;<br> std::cout << <span class="hljs-string">"thread 2 id: "</span> << t2.<span class="hljs-built_in">get_id</span>() << std::endl;<br> <br> std::<span class="hljs-built_in">swap</span>(t1, t2);<br> <br> std::cout << <span class="hljs-string">"after std::swap(t1, t2):"</span> << std::endl;<br> std::cout << <span class="hljs-string">"thread 1 id: "</span> << t1.<span class="hljs-built_in">get_id</span>() << std::endl;<br> std::cout << <span class="hljs-string">"thread 2 id: "</span> << t2.<span class="hljs-built_in">get_id</span>() << std::endl;<br> <br> t1.<span class="hljs-built_in">swap</span>(t2);<br> <br> std::cout << <span class="hljs-string">"after t1.swap(t2):"</span> << std::endl;<br> std::cout << <span class="hljs-string">"thread 1 id: "</span> << t1.<span class="hljs-built_in">get_id</span>() << std::endl;<br> std::cout << <span class="hljs-string">"thread 2 id: "</span> << t2.<span class="hljs-built_in">get_id</span>() << std::endl;<br> <br> t1.<span class="hljs-built_in">join</span>();<br> t2.<span class="hljs-built_in">join</span>();<br> <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs shell">thread 1 id: 1892<br>thread 2 id: 2584<br>after std::swap(t1, t2):<br>thread 1 id: 2584<br>thread 2 id: 1892<br>after t1.swap(t2):<br>thread 1 id: 1892<br>thread 2 id: 2584<br></code></pre></td></tr></table></figure><h2 id="native-handle"><a href="#native-handle" class="headerlink" title="native_handle"></a>native_handle</h2><p><a href="http://www.cplusplus.com/reference/thread/thread/native_handle/"><strong>native_handle</strong></a>返回 native handle 表示线程的实现定义句柄类型</p><p>Example: 在 POSIX 系统上用 native_handle 启用 C++ 线程的实时调度</p><p>只有库函数支持该函数时该方法才会有效,用于获得与操作系统相关的原生线程句柄</p><p>Example:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><ctime></span> </span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">show</span><span class="hljs-params">(<span class="hljs-type">int</span> n)</span></span>{ <br> cout<<<span class="hljs-string">"n="</span><<n<<endl; <br>} <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-function">thread <span class="hljs-title">t</span><span class="hljs-params">(show,<span class="hljs-number">18</span>)</span></span>; <br> cout<<<span class="hljs-string">"t.get_id="</span><<t.<span class="hljs-built_in">get_id</span>()<<endl; <br> <span class="hljs-keyword">auto</span> tn=t.<span class="hljs-built_in">native_handle</span>(); <br> t.<span class="hljs-built_in">join</span>(); <br> cout<<<span class="hljs-string">"tn="</span><<tn<<endl; <br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; <br>} <br></code></pre></td></tr></table></figure><p>Output:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">t.get_id=n=2<br>18<br>tn=2<br></code></pre></td></tr></table></figure><h2 id="hardware-concurrency-static"><a href="#hardware-concurrency-static" class="headerlink" title="hardware_concurrency [static]"></a>hardware_concurrency [static]</h2><p><a href="http://www.cplusplus.com/reference/thread/thread/hardware_concurrency/"><strong>hardware_concurrency (static)</strong></a>检测硬件并发特性</p><p>公共静态成员函数、检测硬件的并发特性、返回硬件线程上下文的数量</p><p>返回值:支持的并发线程数。若值不可计算,则返回 0;</p><p>Example:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span></span><br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-type">unsigned</span> <span class="hljs-type">int</span> n = std::thread::<span class="hljs-built_in">hardware_concurrency</span>();<br> std::cout << n << <span class="hljs-string">" concurrent threads are supported.\n"</span>;<br>}<br></code></pre></td></tr></table></figure><p>Output:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">8 concurrent threads are supported.<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Cpp</category>
</categories>
<tags>
<tag>Cpp</tag>
<tag>Thread</tag>
</tags>
</entry>
<entry>
<title>C++并发学习笔记(一)</title>
<link href="/2018/03/17/C-%E5%B9%B6%E5%8F%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%B8%80/"/>
<url>/2018/03/17/C-%E5%B9%B6%E5%8F%91%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%B8%80/</url>
<content type="html"><![CDATA[<h1 id="资料汇总"><a href="#资料汇总" class="headerlink" title="资料汇总"></a>资料汇总</h1><p><a href="http://www.open-std.org/jtc1/sc22/wg21/">http://www.open-std.org/jtc1/sc22/wg21/</a></p><p>C++0x/C++11 Support in GCC:<a href="http://gcc.gnu.org/projects/cxx0x.html">http://gcc.gnu.org/projects/cxx0x.html</a></p><p>What is C++0x:<a href="https://www2.research.att.com/~bs/what-is-2009.pdf">https://www2.research.att.com/~bs/what-is-2009.pdf</a></p><p>Overview of the New C++:<a href="http://www.artima.com/shop/overview_of_the_new_cpp">http://www.artima.com/shop/overview_of_the_new_cpp</a></p><p>Overview of the New C++ (C++0x).pdf:<a href="http://ishare.iask.sina.com.cn/f/20120005.html?from=like">http://ishare.iask.sina.com.cn/f/20120005.html?from=like</a></p><p>A Brief Look at C++0x:<a href="http://www.artima.com/cppsource/cpp0x.html">http://www.artima.com/cppsource/cpp0x.html</a></p><p>Summary of C++11 Feature Availability in gcc and MSVC:<a href="http://www.aristeia.com/C++11/C++11FeatureAvailability.htm">http://www.aristeia.com/C++11/C++11FeatureAvailability.htm</a></p><p>C++ 11: Come Closer:<a href="http://www.codeproject.com/Articles/344282/Cplusplus-11-Come-Closer">http://www.codeproject.com/Articles/344282/Cplusplus-11-Come-Closer</a></p><p>C++11 threads, locks and condition variables: <a href="http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables">http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables</a></p><p>Move Semantics and Perfect Forwarding in C++11:<a href="http://www.codeproject.com/Articles/397492/Move-Semantics-and-Perfect-Forwarding-in-Cplusplus">http://www.codeproject.com/Articles/397492/Move-Semantics-and-Perfect-Forwarding-in-Cplusplus</a></p><p><a href="http://solarianprogrammer.com/categories/C++11/">http://solarianprogrammer.com/categories/C++11/</a></p><p>C++11 Concurrency:<a href="http://www.baptiste-wicht.com/2012/03/cpp11-concurrency-part1-start-threads/">http://www.baptiste-wicht.com/2012/03/cpp11-concurrency-part1-start-threads/</a></p><p><a href="http://www.hpl.hp.com/personal/Hans_Boehm/misc_slides/sfacm-cleaned.pdf">http://www.hpl.hp.com/personal/Hans_Boehm/misc_slides/sfacm-cleaned.pdf</a></p><p><a href="http://en.cppreference.com/w/cpp/thread">http://en.cppreference.com/w/cpp/thread</a></p><p><a href="http://isocpp.org/blog/2012/12/c11-a-cheat-sheet-alex-sinyakov">http://isocpp.org/blog/2012/12/c11-a-cheat-sheet-alex-sinyakov</a></p><p>The Biggest Changes in C++11:<a href="http://blog.smartbear.com/c-plus-plus/the-biggest-changes-in-c11-and-why-you-should-care/">http://blog.smartbear.com/c-plus-plus/the-biggest-changes-in-c11-and-why-you-should-care/</a></p><p>Ten C++11 Features Every C++ Developer Should Use:<a href="http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer">http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer</a></p><p> C++11 – A Glance [part 1 of n]:<a href="http://www.codeproject.com/Articles/312029/Cplusplus11-A-Glance-part-1-of-n">http://www.codeproject.com/Articles/312029/Cplusplus11-A-Glance-part-1-of-n</a></p><p> C++11 – A Glance [part 2 of n]:<a href="http://www.codeproject.com/Articles/314415/Cplusplus11-A-Glance-part-2-of-n">http://www.codeproject.com/Articles/314415/Cplusplus11-A-Glance-part-2-of-n</a></p><p>C++11(及现代C++风格)和快速迭代式开发:<a href="http://mindhacks.cn/2012/08/27/modern-cpp-practices/">http://mindhacks.cn/2012/08/27/modern-cpp-practices/</a></p><p>Lambda Functions in C++11 - the Definitive Guide:<a href="http://www.cprogramming.com/c++11/c++11-lambda-closures.html">http://www.cprogramming.com/c++11/c++11-lambda-closures.html</a></p><p>Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint:<a href="http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html">http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html</a></p><p>Rvalue-references-and-move-semantics-in-c++11:<a href="http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html">http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html</a></p><p><a href="http://www.gotw.ca/publications/index.htm">http://www.gotw.ca/publications/index.htm</a></p><p><a href="http://www.devx.com/SpecialReports/Door/38865">http://www.devx.com/SpecialReports/Door/38865</a></p><p>Multi-threading in C++0x:<a href="http://accu.org/index.php/journals/1584">http://accu.org/index.php/journals/1584</a></p><p>C++ 0X feature summary cheat sheat:<a href="http://www.iesensor.com/blog/2011/05/31/c-0x-feature-summary-cheat-sheat/">http://www.iesensor.com/blog/2011/05/31/c-0x-feature-summary-cheat-sheat/</a></p><p>Multithreading in C++0x part 1: Starting Threads:<a href="http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html">http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html</a></p><p><a href="http://en.cppreference.com/w/cpp/thread">http://en.cppreference.com/w/cpp/thread</a></p><p><a href="http://www.cplusplus.com/reference/multithreading/">http://www.cplusplus.com/reference/multithreading/</a></p><h1 id="与-C-11-多线程相关的头文件"><a href="#与-C-11-多线程相关的头文件" class="headerlink" title="与 C++11 多线程相关的头文件"></a>与 C++11 多线程相关的头文件</h1><p>C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是<code><atomic></code> ,<code><thread></code>,<code><mutex></code>,<code><condition_variable></code>和<code><future></code>。</p><ul><li><code><atomic></code>:该头文主要声明了两个类, <code>std::atomic</code> 和<code> std::atomic_flag</code>,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。</li><li><code><thread></code>:该头文件主要声明了 <code>std::thread</code> 类,另外 <code>std::this_thread</code> 命名空间也在该头文件中。</li><li><code><mutex></code>:该头文件主要声明了与**互斥量(mutex)**相关的类,包括 <code>std::mutex</code> 系列类,<code>std::lock_guard</code>, <code>std::unique_lock</code>, 以及其他的类型和函数。</li><li><code><condition_variable></code>:该头文件主要声明了与条件变量相关的类,包括 <code>std::condition_variable</code> 和 <code>std::condition_variable_any</code>。</li><li><code><future></code>:该头文件主要声明了 <code>std::promise</code>, <code>std::package_task</code> 两个 Provider 类,以及 <code>std::future</code> 和 <code>std::shared_future</code> 两个 Future 类,另外还有一些与之相关的类型和函数,<code>std::async()</code> 函数就声明在此头文件中。</li></ul><h1 id="多线程Hello-World"><a href="#多线程Hello-World" class="headerlink" title="多线程Hello World!"></a>多线程Hello World!</h1><p>Example 1:</p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span></span><br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">sub_task</span><span class="hljs-params">()</span></span>{std::cout << <span class="hljs-string">"hello world"</span> << std::endl;}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>** argv )</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-function">std::thread <span class="hljs-title">t</span><span class="hljs-params">(sub_task)</span></span>;<br>t.<span class="hljs-built_in">join</span>();<br><br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>Example 2:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><thread></span></span><br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">output</span><span class="hljs-params">(<span class="hljs-type">int</span> i,<span class="hljs-type">int</span> j)</span></span>{std::cout << i+j << std::endl;}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">for</span> (<span class="hljs-type">uint8_t</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">4</span>; i++)<br>{<br><span class="hljs-function">std::thread <span class="hljs-title">t</span><span class="hljs-params">(output, i, i)</span></span>;<br>t.<span class="hljs-built_in">detach</span>();<br>}<br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>参考:<a href="http://www.cnblogs.com/haippy/">256code</a></p>]]></content>
<categories>
<category>Cpp</category>
</categories>
<tags>
<tag>Cpp</tag>
<tag>Thread</tag>
</tags>
</entry>
<entry>
<title>Cpp回调函数的方式和作用</title>
<link href="/2018/03/17/Cpp%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E7%9A%84%E6%96%B9%E5%BC%8F%E5%92%8C%E4%BD%9C%E7%94%A8/"/>
<url>/2018/03/17/Cpp%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0%E7%9A%84%E6%96%B9%E5%BC%8F%E5%92%8C%E4%BD%9C%E7%94%A8/</url>
<content type="html"><![CDATA[<h1 id="指针函数和函数指针"><a href="#指针函数和函数指针" class="headerlink" title="指针函数和函数指针"></a>指针函数和函数指针</h1><p>谈到回调函数得先区分好<strong>指针函数</strong>和<strong>函数指针。</strong></p><p>##指针函数</p><p><strong>指针函数:</strong>是指带指针的函数,本质是一个<strong>函数</strong>,函数的返回类型是某一类型的指针。</p><p><strong>声明格式:</strong><code>类型标识符 *函数名(参数表)</code></p><p><strong>示例:</strong> <code>int *func(x,y)</code></p><p>首先它是一个函数,只不过这个函数的返回值是一个地址值。函数返回值必须用同类型的指针变量来接受,也就是说,指针函数一定有函数返回值,而且,在主调函数中,函数返回值必须赋给同类型的指针变量。由于返回的是一个地址,所以类型说明符一般都是int。</p><p>函数返回的是一个地址值,经常使用在返回数组的某一元素地址上。</p><p>下面是一个简单的例子:</p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-function"><span class="hljs-type">int</span> *<span class="hljs-title">get_letter</span><span class="hljs-params">(<span class="hljs-type">int</span> num)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">static</span> <span class="hljs-type">int</span> arr[]={<span class="hljs-string">'a'</span>,<span class="hljs-string">'b'</span>,<span class="hljs-string">'c'</span>,<span class="hljs-string">'d'</span>,<span class="hljs-string">'e'</span>};<br> <span class="hljs-keyword">return</span> &arr[num<span class="hljs-number">-1</span>];<br>}<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">int</span> num;<br> std::cin >> num;<br> std::cout << (<span class="hljs-type">char</span>) *<span class="hljs-built_in">get_letter</span>(num) << std::endl;<br><br> <span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>子函数<code>int *get_letter(int num)</code>返回的是数组某元素的地址。输出的是这个地址里的值。</p><p><code>(char) *get_letter(num) </code>其中(char)是将*get_letter(num)返回的数组某元素的地址里的值转换成char类型输出。</p><h2 id="函数指针"><a href="#函数指针" class="headerlink" title="函数指针"></a>函数指针</h2><p><strong>函数指针</strong>是指向函数的指针变量<strong>,****本质是一个指针变量。</strong></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-type">int</span> (*f_p)(<span class="hljs-type">int</span> x);<span class="hljs-comment">//声明一个函数指针</span><br>f_p = func;<span class="hljs-comment">//将func函数首地址赋值给指针f_P</span><br></code></pre></td></tr></table></figure><p>指向函数的指针包含了函数的地址的入口地址,可以通过它来调用函数。</p><p><strong>声明格式:</strong><code>类型说明符 (*函数名)(参数)</code></p><p>其实这里不能称为函数名,应该叫做指针的变量名。这个特殊的指针指向一个返回整型值的函数。</p><p>指针的声明必须和它指向函数的声明保持一致。</p><p>指针名和指针运算符外面的括号改变了默认的运算符优先级。如果没有圆括号,就变成了一个返回整型指针的函数原型的声明。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs c"><span class="hljs-type">void</span> (*f_p)();<span class="hljs-comment">//声明一个函数指针,参数列表为控</span><br><span class="hljs-comment">//把函数的地址赋值给函数指针,可以采用下面两种形式:</span><br>f_p = &Function;<br>f_p = Function;<br><br></code></pre></td></tr></table></figure><p>取地址运算符&不是必需的,因为单单一个函数标识符就标号表示了它的地址.<br>如果是函数调用,还必须包含一个圆括号括起来的参数表。</p><p>可以采用如下两种方式来通过指针调用函数:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs c">x = (*f_p)();<br>x = f_p;<br></code></pre></td></tr></table></figure><p>下面是一个简单的例子:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><br><span class="hljs-built_in">void</span>(*f_p)();<br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">FileFunc</span><span class="hljs-params">()</span></span>{std::cout << <span class="hljs-string">"FileFunc"</span> << std::endl;}<br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">EditFunc</span><span class="hljs-params">()</span></span>{std::cout << <span class="hljs-string">"EditFunc"</span> << std::endl;}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br>f_p = FileFunc;<br>(*f_p)();<span class="hljs-comment">//第一种调用方式</span><br>f_p =EditFunc;<br><span class="hljs-built_in">f_p</span>();<span class="hljs-comment">//第二种调用方式</span><br> <br> <span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs shell">FileFunc<br>EditFunc<br>请按任意键继续. . .<br></code></pre></td></tr></table></figure><h1 id="回调函数"><a href="#回调函数" class="headerlink" title="回调函数"></a>回调函数</h1><p>回调函数是通过<strong>函数指针</strong>调用的函数:把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,就称为回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。</p><p>引用知乎 <a href="https://www.zhihu.com/question/19801131/answer/13005983">常溪玲</a>一个形象的示例:</p><p>你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫<strong>回调函数</strong>,你把电话留给店员就叫<strong>登记回调函数</strong>,店里后来有货了叫做<strong>触发回调关联的事件</strong>,店员给你打电话叫做<strong>调用回调函数</strong>,你到店里去取货叫做<strong>响应回调事件</strong>。</p><p>下面是一个简单的例子:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br> <br><span class="hljs-function"><span class="hljs-keyword">typedef</span> <span class="hljs-title">void</span> <span class="hljs-params">(*Fun_p)</span><span class="hljs-params">(<span class="hljs-type">int</span>)</span></span>;<span class="hljs-comment">//定义一个函数指针类型 </span><br>Fun_p p = <span class="hljs-literal">NULL</span>;<span class="hljs-comment">//用Fun定义一个变量p,它指向一个返回值为空参数为int的函数 </span><br> <br><span class="hljs-comment">// 向外提供的回调函数注册接口,提供注册登记服务 </span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">registerFun</span><span class="hljs-params">(Fun_p pCallback)</span> </span><br><span class="hljs-function"></span>{ <br> p = pCallback; <br> <br>} <br> <span class="hljs-comment">//达成某一条件后,通过名片(函数指针p),传回结果 </span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">trigger</span><span class="hljs-params">(<span class="hljs-type">int</span> result)</span></span><br><span class="hljs-function"></span>{<br> (*p)(result);<span class="hljs-comment">//将result传入当前函数指针(*p)所指向的函数(callback函数)</span><br>}<br><span class="hljs-comment">//回调函数 </span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">callback</span><span class="hljs-params">(<span class="hljs-type">int</span> a)</span> </span><br><span class="hljs-function"></span>{ <br> std::cout << <span class="hljs-string">"callback result = "</span> << a << std::endl; <br>} <br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-built_in">registerFun</span>(callback);<span class="hljs-comment">//注册回调函数</span><br> <br> <span class="hljs-type">int</span> result = <span class="hljs-number">1</span>;<br> <span class="hljs-built_in">trigger</span>(result);<span class="hljs-comment">//触发回调函数关联事件</span><br> <br> <span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>); <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; <br>} <br></code></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">输出:callback result = 1<br></code></pre></td></tr></table></figure><p><code>typedef int (* Fun_p)(void);</code>定义了一个新类型 Fun</p><p><code>Fun_p p = NULL;</code>//用Fun_p定义一个变量p,p的类型是<code>void (*Fun_p)(int)</code>这样一个<strong>函数指针</strong>类型。</p>]]></content>
<categories>
<category>Cpp</category>
</categories>
<tags>
<tag>Cpp</tag>
</tags>
</entry>
<entry>
<title>Linux多线程编程入门笔记</title>
<link href="/2018/03/16/Linux%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B%E5%85%A5%E9%97%A8%E7%AC%94%E8%AE%B0/"/>
<url>/2018/03/16/Linux%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B%E5%85%A5%E9%97%A8%E7%AC%94%E8%AE%B0/</url>
<content type="html"><![CDATA[<h2 id="最基础,进程同时创建5个线程,各自调用同一个函数"><a href="#最基础,进程同时创建5个线程,各自调用同一个函数" class="headerlink" title="最基础,进程同时创建5个线程,各自调用同一个函数"></a>最基础,进程同时创建5个线程,各自调用同一个函数</h2><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><pthread.h></span> <span class="hljs-comment">//多线程相关操作头文件,可移植众多平台 </span></span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br> <br><span class="hljs-meta">#<span class="hljs-keyword">define</span> NUM_THREADS 5 <span class="hljs-comment">//线程数 </span></span><br> <br><span class="hljs-function"><span class="hljs-type">void</span>* <span class="hljs-title">say_hello</span><span class="hljs-params">( <span class="hljs-type">void</span>* args )</span> </span><br><span class="hljs-function"></span>{ <br> cout << <span class="hljs-string">"hello..."</span> << endl; <br> <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;<br>} <span class="hljs-comment">//函数返回的是函数指针,便于后面作为参数 </span><br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">pthread_t</span> tids[NUM_THREADS]; <span class="hljs-comment">//线程id </span><br> <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < NUM_THREADS; ++i ) <br> { <br> <span class="hljs-type">int</span> ret = <span class="hljs-built_in">pthread_create</span>( &tids[i], <span class="hljs-literal">NULL</span>, say_hello, <span class="hljs-literal">NULL</span> ); <span class="hljs-comment">//参数:创建的线程id,线程参数,线程运行函数的起始地址,运行函数的参数 </span><br> <span class="hljs-keyword">if</span>( ret != <span class="hljs-number">0</span> ) <span class="hljs-comment">//创建线程成功返回0 </span><br> { <br> cout << <span class="hljs-string">"pthread_create error:error_code="</span> << ret << endl; <br> } <br> } <br> <span class="hljs-built_in">pthread_exit</span>( <span class="hljs-literal">NULL</span> ); <span class="hljs-comment">//等待各个线程退出后,进程才结束,否则进程强制结束,线程处于未终止的状态 </span><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; <br>} <br></code></pre></td></tr></table></figure><p>输入命令:<code>g++ pthread_chap1.cpp -o pthread_chap1 -lpthread</code></p><p>注意:</p><blockquote><ul><li>此为c++程序,故用g++来编译生成可执行文件,并且要调用处理多线程操作相关的静态链接库文件pthread。</li><li>-lpthread 编译选项到位置可任意,如g++ -o pthread_chap1 pthread_chap1.cpp -lpthread</li><li></li></ul></blockquote><p>测试结果:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs bash">jack@jack:~/coding/muti_thread$ ./pthread_chap1 <br>hello...hello... <br>hello... <br>hello... <br> <br>hello... <br><br><br>jack@jack:~/coding/muti_thread$ ./pthread_chap1 <br>hello...hello...hello... <br>hello... <br> <br>hello... <br></code></pre></td></tr></table></figure><p>可知,两次运行的结果会有差别,这不是多线程的特点吧?这显然没有同步?还有待进一步探索…<br>多线程的运行是混乱的,混乱就是正常?</p><h2 id="线程调用到函数在一个类中,那必须将该函数声明为静态函数函数"><a href="#线程调用到函数在一个类中,那必须将该函数声明为静态函数函数" class="headerlink" title="线程调用到函数在一个类中,那必须将该函数声明为静态函数函数"></a>线程调用到函数在一个类中,那必须将该函数声明为静态函数函数</h2><p>因为静态成员函数属于静态全局区,线程可以共享这个区域,故可以各自调用。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><pthread.h></span> </span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br> <br><span class="hljs-meta">#<span class="hljs-keyword">define</span> NUM_THREADS 5 </span><br> <br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Hello</span> <br>{ <br><span class="hljs-keyword">public</span>: <br> <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span>* <span class="hljs-title">say_hello</span><span class="hljs-params">( <span class="hljs-type">void</span>* args )</span> </span><br><span class="hljs-function"> </span>{ <br> cout << <span class="hljs-string">"hello..."</span> << endl; <br> <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;<br> } <br>}; <br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">pthread_t</span> tids[NUM_THREADS]; <br> <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < NUM_THREADS; ++i ) <br> { <br> <span class="hljs-type">int</span> ret = <span class="hljs-built_in">pthread_create</span>( &tids[i], <span class="hljs-literal">NULL</span>, Hello::say_hello, <span class="hljs-literal">NULL</span> ); <br> <span class="hljs-keyword">if</span>( ret != <span class="hljs-number">0</span> ) <br> { <br> cout << <span class="hljs-string">"pthread_create error:error_code"</span> << ret << endl; <br> } <br> } <br> <span class="hljs-built_in">pthread_exit</span>( <span class="hljs-literal">NULL</span> ); <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>} <br></code></pre></td></tr></table></figure><p>测试结果:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs cpp">jack@jack:~/coding/muti_thread$ ./pthread_chap2<br>hello...hello...hello... <br>hello... <br>hello... <br><br>jack@jack:~/coding/muti_thread$ ./pthread_chap2<br>hello...<br>hello...<br>hello... <br>hello... <br>hello... <br></code></pre></td></tr></table></figure><h2 id="如何在线程调用函数时传入参数呢?"><a href="#如何在线程调用函数时传入参数呢?" class="headerlink" title="如何在线程调用函数时传入参数呢?"></a>如何在线程调用函数时传入参数呢?</h2><p>先看下面修改的代码,传入线程编号作为参数:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><pthread.h></span> <span class="hljs-comment">//多线程相关操作头文件,可移植众多平台 </span></span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br> <br><span class="hljs-meta">#<span class="hljs-keyword">define</span> NUM_THREADS 5 <span class="hljs-comment">//线程数 </span></span><br> <br><span class="hljs-function"><span class="hljs-type">void</span>* <span class="hljs-title">say_hello</span><span class="hljs-params">( <span class="hljs-type">void</span>* args )</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">int</span> i = *( (<span class="hljs-type">int</span>*)args ); <span class="hljs-comment">//对传入的参数进行强制类型转换,由无类型指针转变为整形指针,再用*读取其指向到内容 </span><br> cout << <span class="hljs-string">"hello in "</span> << i << endl; <br> <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>; <br>} <span class="hljs-comment">//函数返回的是函数指针,便于后面作为参数 </span><br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">pthread_t</span> tids[NUM_THREADS]; <span class="hljs-comment">//线程id </span><br> cout << <span class="hljs-string">"hello in main.."</span> << endl; <br> <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < NUM_THREADS; ++i ) <br> { <br> <span class="hljs-type">int</span> ret = <span class="hljs-built_in">pthread_create</span>( &tids[i], <span class="hljs-literal">NULL</span>, say_hello, (<span class="hljs-type">void</span>*)&i ); <span class="hljs-comment">//传入到参数必须强转为void*类型,即无类型指针,&i表示取i的地址,即指向i的指针 </span><br> cout << <span class="hljs-string">"Current pthread id = "</span> << tids[i] << endl; <span class="hljs-comment">//用tids数组打印创建的进程id信息 </span><br> <span class="hljs-keyword">if</span>( ret != <span class="hljs-number">0</span> ) <span class="hljs-comment">//创建线程成功返回0 </span><br> { <br> cout << <span class="hljs-string">"pthread_create error:error_code="</span> << ret << endl; <br> } <br> } <br> <span class="hljs-built_in">pthread_exit</span>( <span class="hljs-literal">NULL</span> ); <span class="hljs-comment">//等待各个线程退出后,进程才结束,否则进程强制结束,线程处于未终止的状态 </span><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; <br>}<br></code></pre></td></tr></table></figure><p>测试结果:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs bash">jack@jack:~/coding/muti_thread$ ./pthread_chap2 <br>hello <span class="hljs-keyword">in</span> main.. <br>Current pthread <span class="hljs-built_in">id</span> = 3078458224 <br>Current pthread <span class="hljs-built_in">id</span> = 3070065520 <br>hello <span class="hljs-keyword">in</span> hello <span class="hljs-keyword">in</span> 2 <br>1 <br>Current pthread <span class="hljs-built_in">id</span> = hello <span class="hljs-keyword">in</span> 2 <br>3061672816 <br>Current pthread <span class="hljs-built_in">id</span> = 3053280112 <br>hello <span class="hljs-keyword">in</span> 4 <br>Current pthread <span class="hljs-built_in">id</span> = hello <span class="hljs-keyword">in</span> 4 <br>3044887408 <br></code></pre></td></tr></table></figure><p>显然不是想要的结果,调用顺序很乱,这是为什么呢?<br>这是因为多线程到缘故,主进程还没开始对i赋值,线程已经开始跑了…?<br>修改代码如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><pthread.h></span> <span class="hljs-comment">//多线程相关操作头文件,可移植众多平台 </span></span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br> <br><span class="hljs-meta">#<span class="hljs-keyword">define</span> NUM_THREADS 5 <span class="hljs-comment">//线程数 </span></span><br> <br><span class="hljs-function"><span class="hljs-type">void</span>* <span class="hljs-title">say_hello</span><span class="hljs-params">( <span class="hljs-type">void</span>* args )</span> </span><br><span class="hljs-function"></span>{ <br> cout << <span class="hljs-string">"hello in thread "</span> << *( (<span class="hljs-type">int</span> *)args ) << endl; <br> <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;<br>} <span class="hljs-comment">//函数返回的是函数指针,便于后面作为参数 </span><br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">pthread_t</span> tids[NUM_THREADS]; <span class="hljs-comment">//线程id </span><br> <span class="hljs-type">int</span> indexes[NUM_THREADS]; <span class="hljs-comment">//用来保存i的值避免被修改 </span><br> <br> <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < NUM_THREADS; ++i ) <br> { <br> indexes[i] = i; <br> <span class="hljs-type">int</span> ret = <span class="hljs-built_in">pthread_create</span>( &tids[i], <span class="hljs-literal">NULL</span>, say_hello, (<span class="hljs-type">void</span>*)&(indexes[i]) ); <br> <span class="hljs-keyword">if</span>( ret != <span class="hljs-number">0</span> ) <span class="hljs-comment">//创建线程成功返回0 </span><br> { <br> cout << <span class="hljs-string">"pthread_create error:error_code="</span> << ret << endl; <br> } <br> } <br> <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < NUM_THREADS; ++i ) <br> <span class="hljs-built_in">pthread_join</span>( tids[i], <span class="hljs-literal">NULL</span> ); <span class="hljs-comment">//pthread_join用来等待一个线程的结束,是一个线程阻塞的函数 </span><br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; <br>}<br></code></pre></td></tr></table></figure><p>测试结果:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">jack@jack:~/coding/muti_thread$ ./pthread_chap3 <br>hello <span class="hljs-keyword">in</span> thread hello <span class="hljs-keyword">in</span> thread hello <span class="hljs-keyword">in</span> thread hello <span class="hljs-keyword">in</span> thread hello <span class="hljs-keyword">in</span> thread 30124 <br></code></pre></td></tr></table></figure><p>这是正常的吗?感觉还是有问题…待续</p><p>代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。</p><h2 id="线程创建时属性参数的设置pthread-attr-t及join功能的使用"><a href="#线程创建时属性参数的设置pthread-attr-t及join功能的使用" class="headerlink" title="线程创建时属性参数的设置pthread_attr_t及join功能的使用"></a>线程创建时属性参数的设置pthread_attr_t及join功能的使用</h2><p>线程的属性由结构体pthread_attr_t进行管理。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span><br>{<br> <span class="hljs-type">int</span> detachstate; 线程的分离状态<br> <span class="hljs-type">int</span> schedpolicy; 线程调度策略<br> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">sched_param</span> schedparam; 线程的调度参数<br> <span class="hljs-type">int</span> inheritsched; 线程的继承性 <br> <span class="hljs-type">int</span> scope; 线程的作用域 <br> <span class="hljs-type">size_t</span> guardsize; 线程栈末尾的警戒缓冲区大小 <br> <span class="hljs-type">int</span> stackaddr_set; <span class="hljs-type">void</span> * stackaddr; 线程栈的位置 <br> <span class="hljs-type">size_t</span> stacksize; 线程栈的大小<br>}<span class="hljs-type">pthread_attr_t</span>;<br></code></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><pthread.h></span> </span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br> <br><span class="hljs-meta">#<span class="hljs-keyword">define</span> NUM_THREADS 5 </span><br> <br><span class="hljs-function"><span class="hljs-type">void</span>* <span class="hljs-title">say_hello</span><span class="hljs-params">( <span class="hljs-type">void</span>* args )</span> </span><br><span class="hljs-function"></span>{ <br> cout << <span class="hljs-string">"hello in thread "</span> << *(( <span class="hljs-type">int</span> * )args) << endl; <br> <span class="hljs-type">int</span> status = <span class="hljs-number">10</span> + *(( <span class="hljs-type">int</span> * )args); <span class="hljs-comment">//线程退出时添加退出的信息,status供主程序提取该线程的结束信息 </span><br> <span class="hljs-built_in">pthread_exit</span>( ( <span class="hljs-type">void</span>* )status ); <br> <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>; <br>} <br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">pthread_t</span> tids[NUM_THREADS]; <br> <span class="hljs-type">int</span> indexes[NUM_THREADS]; <br> <br> <span class="hljs-type">pthread_attr_t</span> attr; <span class="hljs-comment">//线程属性结构体,创建线程时加入的参数 </span><br> <span class="hljs-built_in">pthread_attr_init</span>( &attr ); <span class="hljs-comment">//初始化 </span><br> <span class="hljs-built_in">pthread_attr_setdetachstate</span>( &attr, PTHREAD_CREATE_JOINABLE ); <span class="hljs-comment">//是设置你想要指定线程属性参数,这个参数表明这个线程是可以join连接的,join功能表示主程序可以等线程结束后再去做某事,实现了主程序和线程同步功能 </span><br> <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < NUM_THREADS; ++i ) <br> { <br> indexes[i] = i; <br> <span class="hljs-type">int</span> ret = <span class="hljs-built_in">pthread_create</span>( &tids[i], &attr, say_hello, ( <span class="hljs-type">void</span>* )&( indexes[i] ) ); <br> <span class="hljs-keyword">if</span>( ret != <span class="hljs-number">0</span> ) <br> { <br> cout << <span class="hljs-string">"pthread_create error:error_code="</span> << ret << endl; <br> } <br> } <br> <span class="hljs-built_in">pthread_attr_destroy</span>( &attr ); <span class="hljs-comment">//释放内存 </span><br> <span class="hljs-type">void</span> *status; <br> <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < NUM_THREADS; ++i ) <br> { <br> <span class="hljs-type">int</span> ret = <span class="hljs-built_in">pthread_join</span>( tids[i], &status ); <span class="hljs-comment">//主程序join每个线程后取得每个线程的退出信息status </span><br> <span class="hljs-keyword">if</span>( ret != <span class="hljs-number">0</span> ) <br> { <br> cout << <span class="hljs-string">"pthread_join error:error_code="</span> << ret << endl; <br> } <br> <span class="hljs-keyword">else</span> <br> { <br> cout << <span class="hljs-string">"pthread_join get status:"</span> << (<span class="hljs-type">long</span>)status << endl; <br> } <br> } <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>} <br></code></pre></td></tr></table></figure><p>测试结果:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs cpp">jack@jack:~/coding/muti_thread$ ./pthread_chap4 <br>hello in thread hello in thread hello in thread hello in thread <span class="hljs-number">0</span>hello in thread <span class="hljs-number">321</span> <br> <br><span class="hljs-number">4</span> <br>pthread_join get status:<span class="hljs-number">10</span> <br>pthread_join get status:<span class="hljs-number">11</span> <br>pthread_join get status:<span class="hljs-number">12</span> <br>pthread_join get status:<span class="hljs-number">13</span> <br>pthread_join get status:<span class="hljs-number">14</span> <br></code></pre></td></tr></table></figure><h2 id="互斥锁的实现"><a href="#互斥锁的实现" class="headerlink" title="互斥锁的实现"></a>互斥锁的实现</h2><p>互斥锁是实现线程同步的一种机制,只要在临界区前后对资源加锁就能阻塞其他进程的访问。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><pthread.h></span> </span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br> <br><span class="hljs-meta">#<span class="hljs-keyword">define</span> NUM_THREADS 5 </span><br> <br><span class="hljs-type">int</span> sum = <span class="hljs-number">0</span>; <span class="hljs-comment">//定义全局变量,让所有线程同时写,这样就需要锁机制 </span><br><span class="hljs-type">pthread_mutex_t</span> sum_mutex; <span class="hljs-comment">//互斥锁 </span><br> <br><span class="hljs-function"><span class="hljs-type">void</span>* <span class="hljs-title">say_hello</span><span class="hljs-params">( <span class="hljs-type">void</span>* args )</span> </span><br><span class="hljs-function"></span>{ <br> cout << <span class="hljs-string">"hello in thread "</span> << *(( <span class="hljs-type">int</span> * )args) << endl; <br> <span class="hljs-built_in">pthread_mutex_lock</span>( &sum_mutex ); <span class="hljs-comment">//先加锁,再修改sum的值,锁被占用就阻塞,直到拿到锁再修改sum; </span><br> cout << <span class="hljs-string">"before sum is "</span> << sum << <span class="hljs-string">" in thread "</span> << *( ( <span class="hljs-type">int</span>* )args ) << endl; <br> sum += *( ( <span class="hljs-type">int</span>* )args ); <br> cout << <span class="hljs-string">"after sum is "</span> << sum << <span class="hljs-string">" in thread "</span> << *( ( <span class="hljs-type">int</span>* )args ) << endl; <br> <span class="hljs-built_in">pthread_mutex_unlock</span>( &sum_mutex ); <span class="hljs-comment">//释放锁,供其他线程使用 </span><br> <span class="hljs-built_in">pthread_exit</span>( <span class="hljs-number">0</span> ); <br> <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>; <br>} <br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">pthread_t</span> tids[NUM_THREADS]; <br> <span class="hljs-type">int</span> indexes[NUM_THREADS]; <br> <br> <span class="hljs-type">pthread_attr_t</span> attr; <span class="hljs-comment">//线程属性结构体,创建线程时加入的参数 </span><br> <span class="hljs-built_in">pthread_attr_init</span>( &attr ); <span class="hljs-comment">//初始化 </span><br> <span class="hljs-built_in">pthread_attr_setdetachstate</span>( &attr, PTHREAD_CREATE_JOINABLE ); <span class="hljs-comment">//是设置你想要指定线程属性参数,这个参数表明这个线程是可以join连接的,join功能表示主程序可以等线程结束后再去做某事,实现了主程序和线程同步功能 </span><br> <span class="hljs-built_in">pthread_mutex_init</span>( &sum_mutex, <span class="hljs-literal">NULL</span> ); <span class="hljs-comment">//对锁进行初始化 </span><br> <br> <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < NUM_THREADS; ++i ) <br> { <br> indexes[i] = i; <br> <span class="hljs-type">int</span> ret = <span class="hljs-built_in">pthread_create</span>( &tids[i], &attr, say_hello, ( <span class="hljs-type">void</span>* )&( indexes[i] ) ); <span class="hljs-comment">//5个进程同时去修改sum </span><br> <span class="hljs-keyword">if</span>( ret != <span class="hljs-number">0</span> ) <br> { <br> cout << <span class="hljs-string">"pthread_create error:error_code="</span> << ret << endl; <br> } <br> } <br> <span class="hljs-built_in">pthread_attr_destroy</span>( &attr ); <span class="hljs-comment">//释放内存 </span><br> <span class="hljs-type">void</span> *status; <br> <span class="hljs-keyword">for</span>( <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < NUM_THREADS; ++i ) <br> { <br> <span class="hljs-type">int</span> ret = <span class="hljs-built_in">pthread_join</span>( tids[i], &status ); <span class="hljs-comment">//主程序join每个线程后取得每个线程的退出信息status </span><br> <span class="hljs-keyword">if</span>( ret != <span class="hljs-number">0</span> ) <br> { <br> cout << <span class="hljs-string">"pthread_join error:error_code="</span> << ret << endl; <br> } <br> } <br> cout << <span class="hljs-string">"finally sum is "</span> << sum << endl; <br> <span class="hljs-built_in">pthread_mutex_destroy</span>( &sum_mutex ); <span class="hljs-comment">//注销锁 </span><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>} <br></code></pre></td></tr></table></figure><p>测试结果:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs cpp">jack@jack:~/coding/muti_thread$ ./pthread_chap5 <br>hello in thread hello in thread hello in thread <span class="hljs-number">410</span> <br>before sum is hello in thread <span class="hljs-number">0</span> in thread <span class="hljs-number">4</span> <br>after sum is <span class="hljs-number">4</span> in thread <span class="hljs-number">4</span>hello in thread <br> <br> <br><span class="hljs-number">2</span> <br><span class="hljs-number">3</span> <br>before sum is <span class="hljs-number">4</span> in thread <span class="hljs-number">1</span> <br>after sum is <span class="hljs-number">5</span> in thread <span class="hljs-number">1</span> <br>before sum is <span class="hljs-number">5</span> in thread <span class="hljs-number">0</span> <br>after sum is <span class="hljs-number">5</span> in thread <span class="hljs-number">0</span> <br>before sum is <span class="hljs-number">5</span> in thread <span class="hljs-number">2</span> <br>after sum is <span class="hljs-number">7</span> in thread <span class="hljs-number">2</span> <br>before sum is <span class="hljs-number">7</span> in thread <span class="hljs-number">3</span> <br>after sum is <span class="hljs-number">10</span> in thread <span class="hljs-number">3</span> <br>finally sum is <span class="hljs-number">10</span> <br></code></pre></td></tr></table></figure><p>可知,sum的访问和修改顺序是正常的,这就达到了多线程的目的了,但是线程的运行顺序是混乱的,混乱就是正常?</p><h2 id="信号量的实现"><a href="#信号量的实现" class="headerlink" title="信号量的实现"></a>信号量的实现</h2><p>信号量是线程同步的另一种实现机制,信号量的操作有signal和wait,本例子采用条件信号变量pthread_cond_t tasks_cond;<br>信号量的实现也要给予锁机制。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><pthread.h></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdio.h></span> </span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br> <br><span class="hljs-meta">#<span class="hljs-keyword">define</span> BOUNDARY 5 </span><br> <br><span class="hljs-type">int</span> tasks = <span class="hljs-number">10</span>; <br><span class="hljs-type">pthread_mutex_t</span> tasks_mutex; <span class="hljs-comment">//互斥锁 </span><br><span class="hljs-type">pthread_cond_t</span> tasks_cond; <span class="hljs-comment">//条件信号变量,处理两个线程间的条件关系,当task>5,hello2处理,反之hello1处理,直到task减为0 </span><br> <br><span class="hljs-function"><span class="hljs-type">void</span>* <span class="hljs-title">say_hello2</span><span class="hljs-params">( <span class="hljs-type">void</span>* args )</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">pthread_t</span> pid = <span class="hljs-built_in">pthread_self</span>(); <span class="hljs-comment">//获取当前线程id </span><br> cout << <span class="hljs-string">"["</span> << pid << <span class="hljs-string">"] hello in thread "</span> << *( ( <span class="hljs-type">int</span>* )args ) << endl; <br> <br> <span class="hljs-type">bool</span> is_signaled = <span class="hljs-literal">false</span>; <span class="hljs-comment">//sign </span><br> <span class="hljs-keyword">while</span>(<span class="hljs-number">1</span>) <br> { <br> <span class="hljs-built_in">pthread_mutex_lock</span>( &tasks_mutex ); <span class="hljs-comment">//加锁 </span><br> <span class="hljs-keyword">if</span>( tasks > BOUNDARY ) <br> { <br> cout << <span class="hljs-string">"["</span> << pid << <span class="hljs-string">"] take task: "</span> << tasks << <span class="hljs-string">" in thread "</span> << *( (<span class="hljs-type">int</span>*)args ) << endl; <br> --tasks; <span class="hljs-comment">//modify </span><br> } <br> <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>( !is_signaled ) <br> { <br> cout << <span class="hljs-string">"["</span> << pid << <span class="hljs-string">"] pthread_cond_signal in thread "</span> << *( ( <span class="hljs-type">int</span>* )args ) << endl; <br> <span class="hljs-built_in">pthread_cond_signal</span>( &tasks_cond ); <span class="hljs-comment">//signal:向hello1发送信号,表明已经>5 </span><br> is_signaled = <span class="hljs-literal">true</span>; <span class="hljs-comment">//表明信号已发送,退出此线程 </span><br> } <br> <span class="hljs-built_in">pthread_mutex_unlock</span>( &tasks_mutex ); <span class="hljs-comment">//解锁 </span><br> <span class="hljs-keyword">if</span>( tasks == <span class="hljs-number">0</span> ) <br> <span class="hljs-keyword">break</span>; <br> } <br> <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;<br>} <br> <br><span class="hljs-function"><span class="hljs-type">void</span>* <span class="hljs-title">say_hello1</span><span class="hljs-params">( <span class="hljs-type">void</span>* args )</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">pthread_t</span> pid = <span class="hljs-built_in">pthread_self</span>(); <span class="hljs-comment">//获取当前线程id </span><br> cout << <span class="hljs-string">"["</span> << pid << <span class="hljs-string">"] hello in thread "</span> << *( ( <span class="hljs-type">int</span>* )args ) << endl; <br> <br> <span class="hljs-keyword">while</span>(<span class="hljs-number">1</span>) <br> { <br> <span class="hljs-built_in">pthread_mutex_lock</span>( &tasks_mutex ); <span class="hljs-comment">//加锁 </span><br> <span class="hljs-keyword">if</span>( tasks > BOUNDARY ) <br> { <br> cout << <span class="hljs-string">"["</span> << pid << <span class="hljs-string">"] pthread_cond_signal in thread "</span> << *( ( <span class="hljs-type">int</span>* )args ) << endl; <br> <span class="hljs-built_in">pthread_cond_wait</span>( &tasks_cond, &tasks_mutex ); <span class="hljs-comment">//wait:等待信号量生效,接收到信号,向hello2发出信号,跳出wait,执行后续 </span><br> } <br> <span class="hljs-keyword">else</span> <br> { <br> cout << <span class="hljs-string">"["</span> << pid << <span class="hljs-string">"] take task: "</span> << tasks << <span class="hljs-string">" in thread "</span> << *( (<span class="hljs-type">int</span>*)args ) << endl; <br> --tasks; <br> } <br> <span class="hljs-built_in">pthread_mutex_unlock</span>( &tasks_mutex ); <span class="hljs-comment">//解锁 </span><br> <span class="hljs-keyword">if</span>( tasks == <span class="hljs-number">0</span> ) <br> <span class="hljs-keyword">break</span>; <br> } <br> <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;<br>} <br> <br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">pthread_attr_t</span> attr; <span class="hljs-comment">//线程属性结构体,创建线程时加入的参数 </span><br> <span class="hljs-built_in">pthread_attr_init</span>( &attr ); <span class="hljs-comment">//初始化 </span><br> <span class="hljs-built_in">pthread_attr_setdetachstate</span>( &attr, PTHREAD_CREATE_JOINABLE ); <span class="hljs-comment">//是设置你想要指定线程属性参数,这个参数表明这个线程是可以join连接的,join功能表示主程序可以等线程结束后再去做某事,实现了主程序和线程同步功能 </span><br> <span class="hljs-built_in">pthread_cond_init</span>( &tasks_cond, <span class="hljs-literal">NULL</span> ); <span class="hljs-comment">//初始化条件信号量 </span><br> <span class="hljs-built_in">pthread_mutex_init</span>( &tasks_mutex, <span class="hljs-literal">NULL</span> ); <span class="hljs-comment">//初始化互斥量 </span><br> <span class="hljs-type">pthread_t</span> tid1, tid2; <span class="hljs-comment">//保存两个线程id </span><br> <span class="hljs-type">int</span> index1 = <span class="hljs-number">1</span>; <br> <span class="hljs-type">int</span> ret = <span class="hljs-built_in">pthread_create</span>( &tid1, &attr, say_hello1, ( <span class="hljs-type">void</span>* )&index1 ); <br> <span class="hljs-keyword">if</span>( ret != <span class="hljs-number">0</span> ) <br> { <br> cout << <span class="hljs-string">"pthread_create error:error_code="</span> << ret << endl; <br> } <br> <span class="hljs-type">int</span> index2 = <span class="hljs-number">2</span>; <br> ret = <span class="hljs-built_in">pthread_create</span>( &tid2, &attr, say_hello2, ( <span class="hljs-type">void</span>* )&index2 ); <br> <span class="hljs-keyword">if</span>( ret != <span class="hljs-number">0</span> ) <br> { <br> cout << <span class="hljs-string">"pthread_create error:error_code="</span> << ret << endl; <br> } <br> <span class="hljs-built_in">pthread_join</span>( tid1, <span class="hljs-literal">NULL</span> ); <span class="hljs-comment">//连接两个线程 </span><br> <span class="hljs-built_in">pthread_join</span>( tid2, <span class="hljs-literal">NULL</span> ); <br> <br> <span class="hljs-built_in">pthread_attr_destroy</span>( &attr ); <span class="hljs-comment">//释放内存 </span><br> <span class="hljs-built_in">pthread_mutex_destroy</span>( &tasks_mutex ); <span class="hljs-comment">//注销锁 </span><br> <span class="hljs-built_in">pthread_cond_destroy</span>( &tasks_cond ); <span class="hljs-comment">//正常退出 </span><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>} <br></code></pre></td></tr></table></figure><p>测试结果:<br>先在线程2中执行say_hello2,再跳转到线程1中执行say_hello1,直到tasks减到0为止。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs bash">jack@jack:~/coding/muti_thread$ ./pthread_chap6 <br>[3069823856] hello <span class="hljs-keyword">in</span> thread 2 <br>[3078216560] hello <span class="hljs-keyword">in</span> thread 1[3069823856] take task: 10 <span class="hljs-keyword">in</span> thread 2 <br> <br>[3069823856] take task: 9 <span class="hljs-keyword">in</span> thread 2 <br>[3069823856] take task: 8 <span class="hljs-keyword">in</span> thread 2 <br>[3069823856] take task: 7 <span class="hljs-keyword">in</span> thread 2 <br>[3069823856] take task: 6 <span class="hljs-keyword">in</span> thread 2 <br>[3069823856] pthread_cond_signal <span class="hljs-keyword">in</span> thread 2 <br>[3078216560] take task: 5 <span class="hljs-keyword">in</span> thread 1 <br>[3078216560] take task: 4 <span class="hljs-keyword">in</span> thread 1 <br>[3078216560] take task: 3 <span class="hljs-keyword">in</span> thread 1 <br>[3078216560] take task: 2 <span class="hljs-keyword">in</span> thread 1 <br>[3078216560] take task: 1 <span class="hljs-keyword">in</span> thread 1 <br></code></pre></td></tr></table></figure><p>到此,对多线程编程有了一个初步的了解,当然还有其他实现线程同步的机制,有待进一步探索。</p>]]></content>
<categories>
<category>Cpp</category>
</categories>
<tags>
<tag>Cpp</tag>
<tag>Pthread</tag>
</tags>
</entry>
<entry>
<title>Opencv基于特征点的图像对齐</title>
<link href="/2018/03/12/Opencv%E5%9F%BA%E4%BA%8E%E7%89%B9%E5%BE%81%E7%82%B9%E7%9A%84%E5%9B%BE%E5%83%8F%E5%AF%B9%E9%BD%90/"/>
<url>/2018/03/12/Opencv%E5%9F%BA%E4%BA%8E%E7%89%B9%E5%BE%81%E7%82%B9%E7%9A%84%E5%9B%BE%E5%83%8F%E5%AF%B9%E9%BD%90/</url>
<content type="html"><![CDATA[<h1 id="二维图像之间的单应变换"><a href="#二维图像之间的单应变换" class="headerlink" title="二维图像之间的单应变换"></a>二维图像之间的单应变换</h1><p>图像中的2D点(x,y)(x,y)可以被表示成3D向量的形式$(x_1,x_2,x_3)$,其中$x=\frac{x_1}{x_3}$,$y=\frac{x_2}{x_3}$。它被叫做点的齐次表达,位于投影平面$P^2$上。所谓单应就是发生在投影平面$P^2$上的点和线可逆的映射。其它叫法包括射影变换、投影变换和平面投影变换等。</p><p>单应变换矩阵是一个3*3的矩阵H。这个变换可以被任意乘上一个非零常数,而不改变变换本身。它虽然具有9个元素,但是具有8个自由度。这意味这它里面有8个未知参数待求。</p><p>典型地,可以通过图像之间的特征匹配来估计单应矩阵。</p><h2 id="单应和齐次坐标"><a href="#单应和齐次坐标" class="headerlink" title="单应和齐次坐标"></a>单应和齐次坐标</h2><p>一个单应矩阵是大小为3*3的矩阵$H = \begin{bmatrix} h_{11} & h_{12} & h_{13}\ h_{21} & h_{22} & h_{23}\ h_{31} & h_{32} & h_{33}\end{bmatrix}$,满足给定一个点 $P_1 = \left[ \begin{matrix} x_1 \ y_1 \ w_1 \end{matrix} \right]$ ,矩阵H把点$P_1$变换成一个新的点$P_2 = \left[ \begin{matrix} x_2 \ y_2 \ w_2 \end{matrix} \right] = \begin{bmatrix} h_{11} & h_{12} & h_{13}\ h_{21} & h_{22} & h_{23}\ h_{31} & h_{32} & h_{33}\end{bmatrix}\cdot \left[ \begin{matrix} x_1 \ y_1 \ w_1 \end{matrix} \right]$ 。由于他们都是齐次坐标,对应在图像上的两个点分别是$\left[ \begin{matrix} \frac{x_1}{w_1} \ \frac{y_1}{w_1}\end{matrix} \right]$,$\left[ \begin{matrix} \frac{x_2}{w_2} \ \frac{y_2}{w_2}\end{matrix} \right]$ 。</p><h2 id="单应的自由度"><a href="#单应的自由度" class="headerlink" title="单应的自由度"></a>单应的自由度</h2><p>如果给定一个单位$H={h_{ij}}$,给H的每个元素乘上a,得到的单应$aH$和$H$作用相同,因为新的单应无非把齐次点$P_1$变成了齐次点$aP_2$,$aP_2$和$P_2$在图像上对应的点是相同的。所以一个单应中只有8个自由度,一般令$h_{33}=1$来归一化。</p><h2 id="求解单应"><a href="#求解单应" class="headerlink" title="求解单应"></a>求解单应</h2><p>8个未知数需要8个方程来求解,之所以4对点能求解,因为他们一个点能提供两个方程。</p><p>假设图像上有两个点$(x_1,y_1)$,$(x_2,y_2)$,他们的齐次坐标为$\left[ \begin{matrix} x_1 \ y_1 \ 1 \end{matrix} \right]$,$\left[ \begin{matrix} x_2 \ y_2 \ 1 \end{matrix} \right]$。</p><p>带入上述的推导可以得到:</p><p>$$<br>x_2=x_1h_{11}+y_1h_{12}+h_{13}<br>$$</p><p>$$<br>y_2=x_1h_{21}+y_1h_{22}+h_{23}<br>$$</p><p>$$<br>1=x_1h_{31}+y_1h_{32}+h_{33}<br>$$</p><p>一般令$h_{33}=1$来归一化,可得到:</p><p>$$<br>x_2=\frac{x_1h_{11}+y_1h_{12}+h_{13}}{x_1h_{31}+y_1h_{32}+1}<br>$$</p><p>$$<br>y_2=\frac{x_1h_{21}+y_1h_{22}+h_{23}}{x_1h_{31}+y_1h_{32}+1}<br>$$</p><p>把这两个式子重新组织一下,得到等价的矩阵形式:</p><p>$$<br>Au=v<br>$$</p><p>$$<br>A = \left[ \begin{matrix} x_1 \ y_1 \ 1 \ 0 \ 0 \ 0 \ -x_1x_2 \ -x_2y_1 \ 0 \ 0 \ 0 \ x_1 \ y_1 \ 1 \ -x_1y_2 \ -y_1y_2 \end{matrix} \right]<br>$$</p><p>$$<br>u=\left[ \begin{matrix} h_{11} \ h_{12} \ h_{13} \ h_{21} \ h_{22} \ h_{23} \ h_{31} \ h_{32} \ h_{33} \end{matrix} \right]^T<br>$$</p><p>$$<br>v=\left[ \begin{matrix} x_2 \ y_2 \end{matrix} \right]^T<br>$$</p><p>如果有四对不共线匹配点对,这个方程组就能够垒到8行,存在唯一解。</p><p>如果多于四对点,比如有n对点,方程就垒到2n行,用最小二乘法或SVD分解就可以求解$H$。</p><p>由于点对中可能存在不少错误匹配,一般使用RANSAC算法剔除错误匹配点对。</p><h1 id="Opencv中单应性矩阵H的计算"><a href="#Opencv中单应性矩阵H的计算" class="headerlink" title="Opencv中单应性矩阵H的计算"></a>Opencv中单应性矩阵H的计算</h1><p>如果在两幅对应的图像中已知4个映射点的坐标,就可以使用 <a href="https://docs.opencv.org/3.4.1/d9/d0c/group__calib3d.html#gafd3ef89257e27d5235f4467cbb1b6a63">findHomography</a>函数如下:</p><p>**C++: ** <code>Mat cv::findHomography(InputArray srcPoints,InputArray dstPoints,OutputArray mask,int method = 0,double ransacReprojThreshold = 3)</code><br><strong>Python:</strong> <code>retval,mask=cv.findHomography(srcPoints,dstPoints[,method[,ransacReprojThreshold[,mask[,maxIters[,confidence]]]]])</code></p><h1 id="自动寻找对应点-corresponding-points"><a href="#自动寻找对应点-corresponding-points" class="headerlink" title="自动寻找对应点(corresponding points)"></a>自动寻找对应点(corresponding points)</h1><p>由上文知道知道两对对应点(4个映射点)的坐标即可求得单应性矩阵H。</p><p>我们可以使用在OpenCV中的几个关键点检测器(例如SIFT,SURF和ORB)。</p><p>本文将采用ORB关键点检测器,SIFT和SURF已经注册专利。</p><p>一个特征点检测器由两个部分组成:</p><p><strong>定位器(Locator):</strong>它可以识别图像上在平移(移位),缩放(缩小增大/缩小)和旋转等图像变换下稳定的点。 定位器查找这些点的x,y坐标。 ORB检测器使用的定位器叫做<a href="https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_fast/py_fast.html">FAST</a>。<br><strong>描述符(Descriptor):</strong>上述步骤中的定位器仅告诉我们特征点在哪里。 特征检测器的第二部分是对点的外观进行编码的描述符,以便我们可以从另幅图中指出同一个特征点。 在特征点处评定的描述符只是一个数字数组。 理想情况下,两幅图像中的相同物理点应具有相同的描述符。 ORB使用的是<a href="https://www.robots.ox.ac.uk/~vgg/rg/papers/brisk.pdf">BRISK</a>特征描述符的修改版本。</p><h1 id="总结:图像对齐思路"><a href="#总结:图像对齐思路" class="headerlink" title="总结:图像对齐思路"></a>总结:图像对齐思路</h1><p>1、读取图像</p><p>2、特征点检测</p><p>3、匹配特征点</p><p>4、计算单应矩阵参数</p><p>5、矫正图像</p><h1 id="C-Code"><a href="#C-Code" class="headerlink" title="C++ Code"></a>C++ Code</h1><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><opencv2/opencv.hpp></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/xfeatures2d.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/features2d.hpp"</span></span><br><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv;<br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv::xfeatures2d;<br><br><span class="hljs-type">const</span> <span class="hljs-type">int</span> MAX_FEATURES = <span class="hljs-number">500</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">float</span> GOOD_MATCH_PERCENT = <span class="hljs-number">0.15f</span>;<br><br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">alignImages</span><span class="hljs-params">(Mat &im1, Mat &im2, Mat &im1Reg, Mat &h)</span></span><br><span class="hljs-function"></span><br><span class="hljs-function"></span>{<br><br> <span class="hljs-comment">// Convert images to grayscale</span><br> Mat im1Gray, im2Gray;<br> <span class="hljs-built_in">cvtColor</span>(im1, im1Gray, CV_BGR2GRAY);<br> <span class="hljs-built_in">cvtColor</span>(im2, im2Gray, CV_BGR2GRAY);<br><br> <span class="hljs-comment">// Variables to store keypoints and descriptors</span><br> std::vector<KeyPoint> keypoints1, keypoints2;<br> Mat descriptors1, descriptors2;<br><br> <span class="hljs-comment">// Detect ORB features and compute descriptors.</span><br> Ptr<Feature2D> orb = ORB::<span class="hljs-built_in">create</span>(MAX_FEATURES);<br> orb-><span class="hljs-built_in">detectAndCompute</span>(im1Gray, <span class="hljs-built_in">Mat</span>(), keypoints1, descriptors1);<br> orb-><span class="hljs-built_in">detectAndCompute</span>(im2Gray, <span class="hljs-built_in">Mat</span>(), keypoints2, descriptors2);<br><br> <span class="hljs-comment">// Match features.</span><br> std::vector<DMatch> matches;<br> Ptr<DescriptorMatcher> matcher = DescriptorMatcher::<span class="hljs-built_in">create</span>(<span class="hljs-string">"BruteForce-Hamming"</span>);<br> matcher-><span class="hljs-built_in">match</span>(descriptors1, descriptors2, matches, <span class="hljs-built_in">Mat</span>());<br><br> <span class="hljs-comment">// Sort matches by score</span><br> std::<span class="hljs-built_in">sort</span>(matches.<span class="hljs-built_in">begin</span>(), matches.<span class="hljs-built_in">end</span>());<br><br> <span class="hljs-comment">// Remove not so good matches</span><br> <span class="hljs-type">const</span> <span class="hljs-type">int</span> numGoodMatches = matches.<span class="hljs-built_in">size</span>() * GOOD_MATCH_PERCENT;<br> matches.<span class="hljs-built_in">erase</span>(matches.<span class="hljs-built_in">begin</span>()+numGoodMatches, matches.<span class="hljs-built_in">end</span>());<br><br><br> <span class="hljs-comment">// Draw top matches</span><br> Mat imMatches;<br> <span class="hljs-built_in">drawMatches</span>(im1, keypoints1, im2, keypoints2, matches, imMatches);<br> <span class="hljs-built_in">imwrite</span>(<span class="hljs-string">"matches.jpg"</span>, imMatches);<br><br><br> <span class="hljs-comment">// Extract location of good matches</span><br> std::vector<Point2f> points1, points2;<br><br> <span class="hljs-keyword">for</span>( <span class="hljs-type">size_t</span> i = <span class="hljs-number">0</span>; i < matches.<span class="hljs-built_in">size</span>(); i++ )<br> {<br> points1.<span class="hljs-built_in">push_back</span>( keypoints1[ matches[i].queryIdx ].pt );<br> points2.<span class="hljs-built_in">push_back</span>( keypoints2[ matches[i].trainIdx ].pt );<br> }<br><br> <span class="hljs-comment">// Find homography</span><br> h = <span class="hljs-built_in">findHomography</span>( points1, points2, RANSAC );<br><br> <span class="hljs-comment">// Use homography to warp image</span><br> <span class="hljs-built_in">warpPerspective</span>(im1, im1Reg, h, im2.<span class="hljs-built_in">size</span>());<br><br>}<br><br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span> **argv)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-comment">// Read reference image</span><br> <span class="hljs-function">string <span class="hljs-title">refFilename</span><span class="hljs-params">(<span class="hljs-string">"form.jpg"</span>)</span></span>; <br> cout << <span class="hljs-string">"Reading reference image : "</span> << refFilename << endl; <br> Mat imReference = <span class="hljs-built_in">imread</span>(refFilename);<br><br><br> <span class="hljs-comment">// Read image to be aligned</span><br> <span class="hljs-function">string <span class="hljs-title">imFilename</span><span class="hljs-params">(<span class="hljs-string">"scanned-form.jpg"</span>)</span></span>;<br> cout << <span class="hljs-string">"Reading image to align : "</span> << imFilename << endl; <br> Mat im = <span class="hljs-built_in">imread</span>(imFilename);<br><br><br> <span class="hljs-comment">// Registered image will be resotred in imReg. </span><br> <span class="hljs-comment">// The estimated homography will be stored in h. </span><br> Mat imReg, h;<br><br> <span class="hljs-comment">// Align images</span><br> cout << <span class="hljs-string">"Aligning images ..."</span> << endl; <br> <span class="hljs-built_in">alignImages</span>(im, imReference, imReg, h);<br><br> <span class="hljs-comment">// Write aligned image to disk. </span><br> <span class="hljs-function">string <span class="hljs-title">outFilename</span><span class="hljs-params">(<span class="hljs-string">"aligned.jpg"</span>)</span></span>;<br> cout << <span class="hljs-string">"Saving aligned image : "</span> << outFilename << endl; <br> <span class="hljs-built_in">imwrite</span>(outFilename, imReg);<br><br> <span class="hljs-comment">// Print estimated homography</span><br> cout << <span class="hljs-string">"Estimated homography : \n"</span> << h << endl; <br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Opencv</category>
</categories>
<tags>
<tag>Opencv</tag>
</tags>
</entry>
<entry>
<title>查找算法</title>
<link href="/2018/03/09/%E6%9F%A5%E6%89%BE%E7%AE%97%E6%B3%95/"/>
<url>/2018/03/09/%E6%9F%A5%E6%89%BE%E7%AE%97%E6%B3%95/</url>
<content type="html"><![CDATA[<h1 id="顺序查找-Sequential-Search"><a href="#顺序查找-Sequential-Search" class="headerlink" title="顺序查找(Sequential Search)"></a>顺序查找(Sequential Search)</h1><p><strong>说明:顺序查找适合于存储结构为顺序存储或链接存储的线性表</strong></p><p><strong>复杂度:O(n)</strong></p><p><strong>基本思想:</strong>顺序查找也称线性查找(Liner Search),属于无序查找算法。</p><p>从数据结构线性表的一端开始,顺序扫描,依次比较扫描到的结点,若相等则表示查找成功;</p><p>若扫描结束仍没有找到目标,则查找失败;</p><p>查找失败则需要进行n+1次比较,则时间复杂度为 <strong>O(n)</strong></p><h2 id="C-实现"><a href="#C-实现" class="headerlink" title="C++实现"></a>C++实现</h2><ul><li>Code Example 1<figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"stdafx.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><br><span class="hljs-function"><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T,<span class="hljs-keyword">typename</span> V> <span class="hljs-type">int</span> <span class="hljs-title">sequential_search</span><span class="hljs-params">(<span class="hljs-type">const</span> T *arr, V key, <span class="hljs-type">size_t</span> len)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < len; i++)<br>{<br><span class="hljs-keyword">if</span> (key == arr[i])<br><span class="hljs-keyword">return</span> i;<br>}<br><span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>** argv)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-type">int</span> arr1[] = { <span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">5</span>,<span class="hljs-number">8</span>,<span class="hljs-number">13</span> };<br><span class="hljs-type">int</span> len1 = (<span class="hljs-type">int</span>)<span class="hljs-built_in">sizeof</span>(arr1) / <span class="hljs-built_in">sizeof</span>(*arr1);<br><span class="hljs-type">int</span> key1 = <span class="hljs-number">8</span>;<br>std::cout << <span class="hljs-string">"Index: "</span> << <span class="hljs-built_in">sequential_search</span>(arr1, key1, len1) << std::endl;<br><br><span class="hljs-type">char</span> arr2[] = { <span class="hljs-string">'a'</span>,<span class="hljs-string">'b'</span>,<span class="hljs-string">'c'</span>,<span class="hljs-string">'d'</span>,<span class="hljs-string">'e'</span>,<span class="hljs-string">'f'</span>,<span class="hljs-string">'g'</span> };<br><span class="hljs-type">int</span> len2 = (<span class="hljs-type">int</span>)<span class="hljs-built_in">sizeof</span>(arr2) / <span class="hljs-built_in">sizeof</span>(*arr2);<br><span class="hljs-type">char</span> key2 = <span class="hljs-string">'e'</span>;<br>std::cout << <span class="hljs-string">"Index: "</span> << <span class="hljs-built_in">sequential_search</span>(arr2, key2, len2) << std::endl;<br><br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></li><li>Code Example 2<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>** argv)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-type">int</span> arr[] = { <span class="hljs-number">1</span>,<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">5</span>,<span class="hljs-number">8</span>,<span class="hljs-number">13</span> };<br><span class="hljs-type">int</span> count = <span class="hljs-number">0</span>;<br><span class="hljs-type">int</span> key = <span class="hljs-number">13</span>;<br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> elem : arr)<br>{<br><span class="hljs-keyword">if</span> (key == elem)<br>{<br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br>count++;<br>}<br>std::cout << <span class="hljs-string">"not fund !"</span> << std::endl;<br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br><br>}<br></code></pre></td></tr></table></figure></li></ul><h1 id="二分查找-Binary-Search"><a href="#二分查找-Binary-Search" class="headerlink" title="二分查找(Binary Search)"></a>二分查找(Binary Search)</h1><p><strong>说明:元素必须是有序的,若无序则需先排序</strong></p><p><strong>复杂度:O(log2n)</strong></p><p><strong>基本思想:</strong>将n个元素分成大致相等的两部分,取a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;</p><p>如果x<a[n/2],则只要在数组a的左半部分继续搜索x</p><p>如果x>a[n/2],则只要在数组a的右半部搜索x.</p><p>时间复杂度无非就是while循环的次数,总共有n个元素,</p><p>渐渐跟下去就是n,n/2,n/4,….n/2^k(接下来操作元素的剩余个数),其中k就是循环的次数</p><p>由于你n/2^k取整后>=1,即令n/2^k=1</p><p>可得k=log2n,(是以2为底,n的对数)</p><p>所以时间复杂度可以表示O(h)=O(log2n)</p><h2 id="C-实现-1"><a href="#C-实现-1" class="headerlink" title="C++实现"></a>C++实现</h2><ul><li>随机数生成</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><cstdlib></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><ctime></span></span><br><br><span class="hljs-built_in">srand</span>((<span class="hljs-type">unsigned</span>)<span class="hljs-built_in">time</span>(<span class="hljs-literal">NULL</span>));<br>std::cout<<<span class="hljs-built_in">rand</span>()<<std::endl;<br><br>取得(<span class="hljs-number">0</span>,x)的随机整数:<span class="hljs-built_in">rand</span>()%x;<br>取得(a,b)的随机整数:<span class="hljs-built_in">rand</span>()%(b-a);<br>取得[a,b)的随机整数:<span class="hljs-built_in">rand</span>()%(b-a)+a;<br>取得[a,b]的随机整数:<span class="hljs-built_in">rand</span>()%(b-a+<span class="hljs-number">1</span>)+a;<br>取得(a,b]的随机整数:<span class="hljs-built_in">rand</span>()%(b-a)+a+<span class="hljs-number">1</span>;<br>取得<span class="hljs-number">0</span><span class="hljs-number">-1</span>之间的浮点数:<span class="hljs-built_in">rand</span>()/<span class="hljs-built_in">double</span>(RAND_MAX)<br></code></pre></td></tr></table></figure><ul><li>每次都从中间分,完成查找次数相对稳定</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">int</span> mid = (left + right) / <span class="hljs-number">2</span>;<br></code></pre></td></tr></table></figure><ul><li>每次的mid值由左右界限来生成随机值</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">int</span> mid = <span class="hljs-built_in">rand</span>() % (right - left + <span class="hljs-number">1</span>) + left;<br></code></pre></td></tr></table></figure><ul><li>Code</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><math.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><cstdlib></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><ctime></span></span><br><br><span class="hljs-function"><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T,<span class="hljs-keyword">typename</span> V> <span class="hljs-type">int</span> <span class="hljs-title">binary_search</span><span class="hljs-params">(V *arr,T key, <span class="hljs-type">size_t</span> len)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-type">int</span> left = <span class="hljs-number">0</span>;<br><span class="hljs-type">int</span> right = len - <span class="hljs-number">1</span>;<br><span class="hljs-type">int</span> count = <span class="hljs-number">0</span>;<br><span class="hljs-built_in">srand</span>((<span class="hljs-type">unsigned</span>)<span class="hljs-built_in">time</span>(<span class="hljs-literal">NULL</span>));<br><span class="hljs-keyword">while</span> (left <= right)<br>{<br><span class="hljs-comment">//int mid = (left + right) / 2;</span><br><span class="hljs-type">int</span> mid = <span class="hljs-built_in">rand</span>() % (right - left + <span class="hljs-number">1</span>) + left;<br><span class="hljs-keyword">if</span> (arr[mid] == key)<br>{<br>std::cout << <span class="hljs-string">"real times to target:"</span> << <span class="hljs-string">"\t\t"</span> << count << std::endl;<br><span class="hljs-keyword">return</span> mid;<br>}<br><span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(key > arr[mid])<br>left = mid + <span class="hljs-number">1</span>;<br><span class="hljs-keyword">else</span><br>right = mid - <span class="hljs-number">1</span>;<br><br>count++;<br>}<br><span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>** argv )</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-type">char</span> key = <span class="hljs-string">'a'</span>+ <span class="hljs-number">29</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">int</span> len = <span class="hljs-number">30</span>;<br><span class="hljs-type">char</span> arr[len];<br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < len; i++)<br>arr[i] = <span class="hljs-string">'a'</span> + i;<br><br>std::cout << <span class="hljs-string">"expected times to target:"</span> << <span class="hljs-string">"\t"</span> << <span class="hljs-built_in">log2</span>(len) << std::endl;<br>std::cout << <span class="hljs-built_in">binary_search</span>(arr, key, len) << std::endl;<br><br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><h1 id="插值查找-Interpolation-Search"><a href="#插值查找-Interpolation-Search" class="headerlink" title="插值查找(Interpolation Search)"></a>插值查找(Interpolation Search)</h1><p>在二分查找的基础上,二分查找是每次都分一办查找。但是我们查字典的时候并不会是一半一半的翻页。</p><p>比如查c开头的单词,我们会从字典的**(c-a)/(z-a)**部分打开。</p><p>这种折半的查找方式并不是每次折半<strong>1/2</strong>,因此我们将二分法的折<strong>1/2</strong>的方式优化为自适应的**(c-a)/(z-a)**的比例。</p><p>二分法:<em><em>mid=(left+right )/2, 即mid=left+1/2</em>(right -left);</em>*</p><p>插值法则是: <strong>mid = left+ (key - arr[left])/(arr[right] - key) * (right - left)</strong></p><p><strong>基本思想:基于二分法,改进查找点的自适应性,从而提高效率。插值查找也属于有序查找。</strong></p><p><strong>复杂度:查找成功/失败都是$O(log_2(log_2^n))$。</strong></p><p>注:<strong>对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。</strong></p><h2 id="C-实现-2"><a href="#C-实现-2" class="headerlink" title="C++实现"></a>C++实现</h2><ul><li>Code<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"stdafx.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><math.h></span></span><br><br><span class="hljs-function"><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T, <span class="hljs-keyword">typename</span> V> <span class="hljs-type">size_t</span> <span class="hljs-title">interpolation_search</span><span class="hljs-params">(V *arr, T key, <span class="hljs-type">size_t</span> len, <span class="hljs-type">size_t</span> &count)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-type">int</span> left = <span class="hljs-number">0</span>;<br><span class="hljs-type">int</span> right = len - <span class="hljs-number">1</span>;<br><span class="hljs-type">int</span> mid = <span class="hljs-number">0</span>;<br><br><span class="hljs-keyword">while</span> ((arr[left]!=arr[right]) && (key>arr[left]) && (key<arr[right]))<br>{<br>count++;<br>mid = left + (right - left) * (key - arr[left]) / (arr[right] - arr[left]);<br><span class="hljs-keyword">if</span> (key > arr[mid])<br>left = mid + <span class="hljs-number">1</span>;<br><span class="hljs-keyword">else</span><br>right = mid - <span class="hljs-number">1</span>;<br>}<br><span class="hljs-keyword">if</span> (arr[mid] == key)<br><span class="hljs-keyword">return</span> mid;<br><span class="hljs-keyword">else</span><br><span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>** argv)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-type">char</span> key = <span class="hljs-string">'a'</span> + <span class="hljs-number">10</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> len = <span class="hljs-number">29</span>;<br><span class="hljs-type">size_t</span> count = <span class="hljs-number">0</span>;<br><span class="hljs-type">char</span> arr[len];<br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < len; i++)<br>arr[i] = <span class="hljs-string">'a'</span> + i;<br><br>std::cout << <span class="hljs-string">"expected times to target:\t"</span> << <span class="hljs-built_in">log2</span>(<span class="hljs-built_in">log2</span>(len)) << std::endl;<br>std::cout << <span class="hljs-string">"position: \t"</span> << <span class="hljs-built_in">interpolation_search</span>(arr, key, len, count) << std::endl;<br>std::cout << <span class="hljs-string">"real times to target:\t"</span> << count << std::endl;<br><br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure></li></ul><p>**输出: **</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">expected <span class="hljs-built_in">times</span> to target: 2.28036<br>position: 10<br>real <span class="hljs-built_in">times</span> to target: 1<br></code></pre></td></tr></table></figure><h1 id="分块查找-Block-Search"><a href="#分块查找-Block-Search" class="headerlink" title="分块查找(Block Search)"></a>分块查找(Block Search)</h1><p><strong>分块有序:</strong>整个表中的元素未必有序,但若划分为若干块后,每一块中的所有元素均小于(或大于)其后面块中的所有元素。</p><p>分块查找又称<strong>索引顺序查找</strong>,它是顺序查找的一种改进方法。首先须要对数组进行分块,分块查找须要建立一个“索引表”。索引表分为m块,每块含有N/m个元素,块内是无序的,块间是有序的,比如块2中最大元素小于块3中最小元素。先用二分查找索引表。确定须要查找的keyword在哪一块,然后再在对应的块内用顺序查找。</p><p><strong>操作步骤:</strong></p><p> step1 先选取各块中的最大关键字构成一个索引表;</p><p> step2 查找分两个部分:先对索引表进行二分查找或顺序查找,以确定待查记录在哪一块中;然后,在已确定的块中用顺序法进行查找。</p><p><strong>时间复杂度:</strong>O(log(m)+N/m)</p><h2 id="C-实现-Block-search"><a href="#C-实现-Block-search" class="headerlink" title="C++实现(Block search)"></a>C++实现(Block search)</h2><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"stdafx.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><br><span class="hljs-comment">//索引表--结构体模板</span><br><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">Index_table</span><br>{<br>T key;<br><span class="hljs-type">int</span> link;<br>};<br><br><span class="hljs-comment">// index_able为索引表,x为原数组,N为数组大小,m为块大小, keyword为查找目标</span><br><span class="hljs-function"><span class="hljs-keyword">template</span><<span class="hljs-keyword">typename</span> T> <span class="hljs-type">int</span> <span class="hljs-title">index_order_search</span><span class="hljs-params">(Index_table<T> *index_able, T *x, <span class="hljs-type">int</span> N, <span class="hljs-type">int</span> m, T keyword)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-type">int</span> L = (N + m - <span class="hljs-number">1</span>) / m;<br><span class="hljs-type">int</span> i = <span class="hljs-number">0</span>;<br><span class="hljs-keyword">while</span> (i < L && index_able[i].key < keyword)<br>i++;<br><span class="hljs-keyword">if</span> (i == L)<br><span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br><span class="hljs-keyword">else</span><br>{<br><span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = index_able[i].link; j < index_able[i].link + m; j++)<br><span class="hljs-keyword">if</span> (x[j] == keyword)<br><span class="hljs-keyword">return</span> j;<br>}<br><span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>** argv)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-comment">//block1(22,0) |block2(48,6) |block3(86,12)</span><br><span class="hljs-comment">//22,12,13,8,9,20,|33,42,44,38,24,48,|60,58,74,49,86,53</span><br><span class="hljs-comment">//0 ,1 ,2 ,3,4,5 ,|6 ,7 ,8 ,9 ,10,11,|12,13,14,15,16,17</span><br><span class="hljs-type">int</span> arr[] = {<span class="hljs-number">22</span>,<span class="hljs-number">12</span>,<span class="hljs-number">13</span>,<span class="hljs-number">8</span>,<span class="hljs-number">9</span>,<span class="hljs-number">20</span>,<span class="hljs-number">33</span>,<span class="hljs-number">42</span>,<span class="hljs-number">44</span>,<span class="hljs-number">38</span>,<span class="hljs-number">24</span>,<span class="hljs-number">48</span>,<span class="hljs-number">60</span>,<span class="hljs-number">58</span>,<span class="hljs-number">74</span>,<span class="hljs-number">49</span>,<span class="hljs-number">86</span>,<span class="hljs-number">53</span>};<br><span class="hljs-type">int</span> len = (<span class="hljs-type">unsigned</span>)<span class="hljs-built_in">sizeof</span>(arr) / <span class="hljs-built_in">sizeof</span>(*arr);<br><span class="hljs-type">int</span> block_size = <span class="hljs-number">6</span>;<br><span class="hljs-type">int</span> target = <span class="hljs-number">33</span>;<br><br>Index_table<<span class="hljs-type">int</span>> index_table[<span class="hljs-number">3</span>];<br>index_table[<span class="hljs-number">0</span>].key = <span class="hljs-number">22</span>;<br>index_table[<span class="hljs-number">0</span>].link = <span class="hljs-number">0</span>;<br>index_table[<span class="hljs-number">1</span>].key = <span class="hljs-number">48</span>;<br>index_table[<span class="hljs-number">1</span>].link = <span class="hljs-number">6</span>; <br>index_table[<span class="hljs-number">2</span>].key = <span class="hljs-number">86</span>;<br>index_table[<span class="hljs-number">2</span>].link = <span class="hljs-number">12</span>;<br><br>std::cout << <span class="hljs-built_in">index_order_search</span>(index_table, arr, len, block_size, target) << std::endl;<br><br><span class="hljs-built_in">system</span>(<span class="hljs-string">"pause"</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Algorithm</category>
</categories>
<tags>
<tag>Cpp</tag>
<tag>Algorithm</tag>
</tags>
</entry>
<entry>
<title>Google C++ Style Guide</title>
<link href="/2018/03/06/Google-C-Style-Guide/"/>
<url>/2018/03/06/Google-C-Style-Guide/</url>
<content type="html"><</p><p><a href="http://blog.csdn.net/voidccc/article/details/37599203">Reference</a></p>]]></content>
<categories>
<category>Cpp</category>
</categories>
<tags>
<tag>Cpp</tag>
</tags>
</entry>
<entry>
<title>Mat和IplImage转换</title>
<link href="/2018/01/29/Mat%E5%92%8CIplImage%E8%BD%AC%E6%8D%A2/"/>
<url>/2018/01/29/Mat%E5%92%8CIplImage%E8%BD%AC%E6%8D%A2/</url>
<content type="html"><![CDATA[<p>Mat 是OpenCV和C++的接口矩阵类,ImlImage是OpenCV和C语言的接口的结构体,但是C++程序有时候时候还是要用到ImlImage。</p><p><strong>浅拷贝是不需要释放内存的。</strong></p><p><strong>深拷贝才需要释放内存。</strong></p><h1 id="IplImage-转-Mat"><a href="#IplImage-转-Mat" class="headerlink" title="IplImage 转 Mat"></a>IplImage 转 Mat</h1><h2 id="浅拷贝"><a href="#浅拷贝" class="headerlink" title="浅拷贝"></a>浅拷贝</h2><p>直接使用cvarrToMat函数:</p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp">IplImage* src = <span class="hljs-built_in">cvLoadImage</span>(<span class="hljs-string">"./res/tmp.jpg"</span>); <br>Mat img; <br>img = <span class="hljs-built_in">cvarrToMat</span>(src); <br></code></pre></td></tr></table></figure><h2 id="深拷贝"><a href="#深拷贝" class="headerlink" title="深拷贝"></a>深拷贝</h2><p>需要在Mat里创建一个新的Mat对象,然后进行数据的复制,再用上述的函数进行数据头的复制(即浅拷贝)。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs cpp">IplImage* src = <span class="hljs-built_in">cvLoadImage</span>(<span class="hljs-string">"c://tmp.jpg"</span>); <br>Mat img_tmp; <br>img_tmp = <span class="hljs-built_in">cvarrToMat</span>(src); <br>Mat img = img_tmp.<span class="hljs-built_in">clone</span>();<br></code></pre></td></tr></table></figure><h1 id="Mat-转-IplImage"><a href="#Mat-转-IplImage" class="headerlink" title="Mat 转 IplImage"></a>Mat 转 IplImage</h1><h2 id="浅拷贝-1"><a href="#浅拷贝-1" class="headerlink" title="浅拷贝"></a>浅拷贝</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cpp">Mat img = <span class="hljs-built_in">imread</span>(./res/tmp.jpg);<br>IplImage *src = &<span class="hljs-built_in">IplImage</span>(img);<br></code></pre></td></tr></table></figure><h2 id="深拷贝-即再复制一次数据"><a href="#深拷贝-即再复制一次数据" class="headerlink" title="深拷贝(即再复制一次数据)"></a>深拷贝(即再复制一次数据)</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs cpp">Mat img = <span class="hljs-built_in">imread</span>(./res/tmp.jpg);<br>IplImage *src_tmp = &<span class="hljs-built_in">IplImage</span>(img);<br>IplImage *src = <span class="hljs-built_in">cvCloneImage</span>(src_tmp);<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Opencv</category>
</categories>
<tags>
<tag>Opencv</tag>
</tags>
</entry>
<entry>
<title>Opencv截取屏幕(BitBlt API)</title>
<link href="/2018/01/29/Opencv%E6%88%AA%E5%8F%96%E5%B1%8F%E5%B9%95-BitBlt-API/"/>
<url>/2018/01/29/Opencv%E6%88%AA%E5%8F%96%E5%B1%8F%E5%B9%95-BitBlt-API/</url>
<content type="html"><![CDATA[<h1 id="截取整个屏幕"><a href="#截取整个屏幕" class="headerlink" title="截取整个屏幕"></a>截取整个屏幕</h1><p>直接获取整个屏幕当前显示画面,便于后期计算处理。</p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> _MSC_VER</span><br><span class="hljs-meta">#<span class="hljs-keyword">pragma</span> comment( linker, <span class="hljs-string">"/subsystem:\"windows\" /entry:\"mainCRTStartup\""</span> )</span><br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> _AFXDLL<span class="hljs-comment">//为了方便是用mfc类 </span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><afxwin.h></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><opencv2/opencv.hpp></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><opencv2/highgui.hpp></span></span><br><br>LPVOID screenCaptureData = <span class="hljs-literal">NULL</span>;<br>HBITMAP hBitmap;<br>HDC hDDC;<br>HDC hCDC;<br><span class="hljs-type">int</span> nWidth;<br><span class="hljs-type">int</span> nHeight;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">initGDI</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br>nWidth = <span class="hljs-built_in">GetSystemMetrics</span>(SM_CXSCREEN);<span class="hljs-comment">//得到屏幕的分辨率的x </span><br>nHeight = <span class="hljs-built_in">GetSystemMetrics</span>(SM_CYSCREEN);<span class="hljs-comment">//得到屏幕分辨率的y </span><br>screenCaptureData = <span class="hljs-keyword">new</span> <span class="hljs-type">char</span>[nWidth*nHeight * <span class="hljs-number">4</span>];<br><span class="hljs-built_in">memset</span>(screenCaptureData, <span class="hljs-number">0</span>, nWidth);<br><span class="hljs-comment">// Get desktop DC, create a compatible dc, create a comaptible bitmap and select into compatible dc. </span><br>hDDC = <span class="hljs-built_in">GetDC</span>(<span class="hljs-built_in">GetDesktopWindow</span>());<span class="hljs-comment">//得到屏幕的dc </span><br>hCDC = <span class="hljs-built_in">CreateCompatibleDC</span>(hDDC);<span class="hljs-comment">// </span><br>hBitmap = <span class="hljs-built_in">CreateCompatibleBitmap</span>(hDDC, nWidth, nHeight);<span class="hljs-comment">//得到位图 </span><br><span class="hljs-built_in">SelectObject</span>(hCDC, hBitmap); <span class="hljs-comment">//好像总得这么写。 </span><br>}<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">gdiScreenCapture</span><span class="hljs-params">(LPVOID screenCaptureData)</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-built_in">BitBlt</span>(hCDC, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, nWidth, nHeight, hDDC, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, SRCCOPY);<br><span class="hljs-built_in">GetBitmapBits</span>(hBitmap, nWidth*nHeight * <span class="hljs-number">4</span>, screenCaptureData);<span class="hljs-comment">//得到位图的数据,并存到screenCaptureData数组中。 </span><br>IplImage *img_tmp = <span class="hljs-built_in">cvCreateImage</span>(<span class="hljs-built_in">cvSize</span>(nWidth, nHeight), <span class="hljs-number">8</span>, <span class="hljs-number">4</span>);<span class="hljs-comment">//创建一个rgba格式的IplImage,内容为空 </span><br><span class="hljs-built_in">memcpy</span>(img_tmp->imageData, screenCaptureData, nWidth*nHeight * <span class="hljs-number">4</span>);<span class="hljs-comment">//这样比较浪费时间,但写的方便,这里必须得是*4。 </span><br>IplImage *img2_tmp = <span class="hljs-built_in">cvCreateImage</span>(<span class="hljs-built_in">cvSize</span>(nWidth, nHeight), <span class="hljs-number">8</span>, <span class="hljs-number">3</span>);<span class="hljs-comment">//创建一个bgr格式的IplImage,可以没有这个Img2这个变量。 </span><br><br>cv::Mat img = cv::<span class="hljs-built_in">cvarrToMat</span>(img_tmp);<br>cv::Mat img2 = cv::<span class="hljs-built_in">cvarrToMat</span>(img2_tmp);<br><br><span class="hljs-built_in">cvtColor</span>(img, img2, CV_BGRA2BGR);<br><span class="hljs-built_in">imwrite</span>(<span class="hljs-string">"rgba.jpg"</span>, img);<br>cv::<span class="hljs-built_in">waitKey</span>();<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br><span class="hljs-built_in">initGDI</span>();<br><span class="hljs-built_in">gdiScreenCapture</span>(screenCaptureData);<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Opencv</category>
</categories>
<tags>
<tag>Opencv</tag>
</tags>
</entry>
<entry>
<title>C++文件重定向</title>
<link href="/2018/01/02/C-%E6%96%87%E4%BB%B6%E9%87%8D%E5%AE%9A%E5%90%91/"/>
<url>/2018/01/02/C-%E6%96%87%E4%BB%B6%E9%87%8D%E5%AE%9A%E5%90%91/</url>
<content type="html"><![CDATA[<h1 id="方法一"><a href="#方法一" class="headerlink" title="方法一"></a>方法一</h1><p><strong>命令行使用 < 和 ></strong></p><figure class="highlight c++"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><pre><code class="hljs c++">func.exe < infile.txt > outfile.txt<br></code></pre></td></tr></table></figure><h1 id="方法二"><a href="#方法二" class="headerlink" title="方法二"></a>方法二</h1><p><strong>C++实现</strong></p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs arduino"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><ostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><fstream></span> </span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{ <br> <span class="hljs-function">ifstream <span class="hljs-title">fin</span><span class="hljs-params">(<span class="hljs-string">"input.txt"</span>)</span></span>; <span class="hljs-comment">// 已有输入文件</span><br> <span class="hljs-function">ofstream <span class="hljs-title">fout</span><span class="hljs-params">(<span class="hljs-string">"output.txt"</span>)</span></span>; <span class="hljs-comment">//输出文件</span><br><br> streambuf *cinbackup; <br> streambuf *coutbackup; <br><br> coutbackup= cout.<span class="hljs-built_in">rdbuf</span>(fout.<span class="hljs-built_in">rdbuf</span>()); <span class="hljs-comment">//用 rdbuf() 重新定向</span><br> cinbackup= cin.<span class="hljs-built_in">rdbuf</span>(fin.<span class="hljs-built_in">rdbuf</span>()); <span class="hljs-comment">//用 rdbuf() 重新定向</span><br> cout<<<span class="hljs-string">"Hello world"</span><<endl; <span class="hljs-comment">//去文件也</span><br><br> <span class="hljs-type">char</span> line[<span class="hljs-number">100</span>]; <br> cin>>line; <span class="hljs-comment">//从input.txt文件读入d</span><br> cout<<line<<endl; <span class="hljs-comment">//写入 output.txt</span><br> <span class="hljs-comment">//恢复标准流缓冲区 </span><br> cin.<span class="hljs-built_in">rdbuf</span>(cinbackup); <span class="hljs-comment">// 取消,恢复键盘输入</span><br> cout.<span class="hljs-built_in">rdbuf</span>(coutbackup); <span class="hljs-comment">//取消,恢复屏幕输出</span><br> <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>} <br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Cpp</category>
</categories>
<tags>
<tag>Cpp</tag>
</tags>
</entry>
<entry>
<title>G++编译模板类的问题</title>
<link href="/2017/11/29/G-%E7%BC%96%E8%AF%91%E6%A8%A1%E6%9D%BF%E7%B1%BB%E7%9A%84%E9%97%AE%E9%A2%98/"/>
<url>/2017/11/29/G-%E7%BC%96%E8%AF%91%E6%A8%A1%E6%9D%BF%E7%B1%BB%E7%9A%84%E9%97%AE%E9%A2%98/</url>
<content type="html"><![CDATA[<p><strong>g++目前不支持c++模板类 声明与实现分离,都要写到.h文件里面。</strong></p><p>以后记住了。</p>]]></content>
<categories>
<category>Cpp</category>
</categories>
<tags>
<tag>Cpp</tag>
</tags>
</entry>
<entry>
<title>G++简单的编译生成动态链接文件</title>
<link href="/2017/11/08/G-%E7%AE%80%E5%8D%95%E7%9A%84%E7%BC%96%E8%AF%91%E7%94%9F%E6%88%90%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E6%96%87%E4%BB%B6/"/>
<url>/2017/11/08/G-%E7%AE%80%E5%8D%95%E7%9A%84%E7%BC%96%E8%AF%91%E7%94%9F%E6%88%90%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%E6%96%87%E4%BB%B6/</url>
<content type="html"><![CDATA[<p>一个最简单的动态链接库程序,使用g++命令行编译。便于回忆,就把它记到Blog中。</p><h1 id="动态库建立"><a href="#动态库建立" class="headerlink" title="动态库建立"></a><em>动态库建立</em></h1><p>编译成obj文件:</p><p><em>g++ -c -o dll.obj dll.cpp</em></p><p>链接obj,生成dll: </p><p><em>g++ -shared -o dll.so dll.obj</em></p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">/* file: dll.h */</span> <br><span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> __dll_h__</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> __dll_h__</span><br><span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> __MY_DLL_LIB__</span><br> <span class="hljs-meta">#<span class="hljs-keyword">define</span> DLL_EXPORT extern <span class="hljs-string">"C"</span> __declspec(dllexport)</span><br><span class="hljs-meta">#<span class="hljs-keyword">else</span> </span><br> <span class="hljs-meta">#<span class="hljs-keyword">define</span> DLL_EXPORT extern <span class="hljs-string">"C"</span> __declspec(dllimport)</span><br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br><br><span class="hljs-function">DLL_EXPORT <span class="hljs-type">int</span> <span class="hljs-title">mymax</span><span class="hljs-params">(<span class="hljs-type">int</span> x, <span class="hljs-type">int</span> y)</span></span>;<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br></code></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">/* file: dll.cpp */</span> <br><span class="hljs-meta">#<span class="hljs-keyword">define</span> __MY_DLL_LIB__</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"dll.h"</span></span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">mymax</span><span class="hljs-params">(<span class="hljs-type">int</span> x, <span class="hljs-type">int</span> y)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-keyword">return</span> x > y ? x : y;<br>}<br></code></pre></td></tr></table></figure><h1 id="动态库调用"><a href="#动态库调用" class="headerlink" title="动态库调用"></a><em>动态库调用</em></h1><p>调用动态库:<br>直接编译成exe: </p><p><em>g++ main.cpp dll.so -o main.exe</em></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">/* file: main.cpp */</span> <br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"dll.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">int</span> a = <span class="hljs-number">2</span>;<br> <span class="hljs-type">int</span> b = <span class="hljs-number">4</span>;<br> cout << <span class="hljs-built_in">mymax</span>(a, b) << endl;<br> <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Cpp</category>
</categories>
<tags>
<tag>Cpp</tag>
</tags>
</entry>
<entry>
<title>Linux多线程编程</title>
<link href="/2017/07/05/Linux%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B/"/>
<url>/2017/07/05/Linux%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B/</url>
<content type="html"><![CDATA[<h1 id="创建线程"><a href="#创建线程" class="headerlink" title="创建线程"></a>创建线程</h1><p>使用__pthread_create__函数创建线程。</p><figure class="highlight c++"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></div></td><td class="code"><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><pthread.h></span></span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_create</span> <span class="hljs-params">(<span class="hljs-type">pthread_t</span> *__restrict __newthread,<span class="hljs-comment">//新创建的线程ID</span></span></span><br><span class="hljs-params"><span class="hljs-function"> __const <span class="hljs-type">pthread_attr_t</span> *__restrict __attr,<span class="hljs-comment">//线程属性</span></span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-type">void</span> *(*__start_routine) (<span class="hljs-type">void</span> *),<span class="hljs-comment">//新创建的线程从start_routine开始执行</span></span></span><br><span class="hljs-params"><span class="hljs-function"> <span class="hljs-type">void</span> *__restrict __arg)</span><span class="hljs-comment">//执行函数的参数</span></span><br><span class="hljs-function"></span><br><span class="hljs-function"><span class="hljs-comment">//返回值:成功-0,失败-返回错误编号</span></span><br><span class="hljs-function"><span class="hljs-comment">//用strerror(errno)函数得到错误信息。</span></span><br></code></pre></td></tr></table></figure><p>例程:</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-type">int</span> err; <br><span class="hljs-type">pthread_t</span> tid1;<br>err = <span class="hljs-built_in">pthread_create</span>(&tid1, <span class="hljs-literal">NULL</span>, thread_func, <span class="hljs-literal">NULL</span>);<span class="hljs-comment">//创建线程</span><br></code></pre></td></tr></table></figure><h1 id="终止线程"><a href="#终止线程" class="headerlink" title="终止线程"></a>终止线程</h1><p> 三种方式线程从执行函数返回,返回值是线程的退出码线程被同一进程的其他线程取消调用__pthread_exit()__函数退出。这里不是调用exit,因为线程调用exit函数,会导致线程所在的进程退出。</p><h1 id="例程一"><a href="#例程一" class="headerlink" title="例程一"></a>例程一</h1><p> 启动两个线程,一个线程对全局变量num执行加1操作,执行五百次,一个线程对全局变量执行减1操作,同样执行五次。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdio.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdlib.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><pthread.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><unistd.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><string.h></span></span><br><br><span class="hljs-type">int</span> num = <span class="hljs-number">0</span>;<br><span class="hljs-function"><span class="hljs-type">void</span> *<span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-type">void</span> *arg)</span></span><br><span class="hljs-function"></span>{<span class="hljs-comment">//线程执行函数,执行5次加法</span><br> <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>, tmp;<br> <span class="hljs-keyword">for</span> (; i < <span class="hljs-number">5</span>; i++)<br> {<br> tmp = num + <span class="hljs-number">1</span>;<br> num = tmp;<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"add+1,result is:%d\n"</span>, num);<br> }<br> <span class="hljs-keyword">return</span> ((<span class="hljs-type">void</span> *)<span class="hljs-number">0</span>);<br>}<br><span class="hljs-function"><span class="hljs-type">void</span> *<span class="hljs-title">sub</span><span class="hljs-params">(<span class="hljs-type">void</span> *arg)</span><span class="hljs-comment">//线程执行函数,执行5次减法</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>, tmp;<br> <span class="hljs-keyword">for</span> (; i < <span class="hljs-number">5</span>; i++)<br> {<br> tmp = num - <span class="hljs-number">1</span>;<br> num = tmp;<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"sub-1,result is:%d\n"</span>, num);<br> }<br> <span class="hljs-keyword">return</span> ((<span class="hljs-type">void</span> *)<span class="hljs-number">0</span>);<br>}<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>** argv)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">pthread_t</span> tid1, tid2;<br> <span class="hljs-type">int</span> err;<br> <span class="hljs-type">void</span> *tret;<br> err = <span class="hljs-built_in">pthread_create</span>(&tid1, <span class="hljs-literal">NULL</span>, add, <span class="hljs-literal">NULL</span>);<span class="hljs-comment">//创建线程</span><br> <span class="hljs-keyword">if</span> (err != <span class="hljs-number">0</span>)<br> {<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"pthread_create error:%s\n"</span>, <span class="hljs-built_in">strerror</span>(err));<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">-1</span>);<br> }<br> err = <span class="hljs-built_in">pthread_create</span>(&tid2, <span class="hljs-literal">NULL</span>, sub, <span class="hljs-literal">NULL</span>);<br> <span class="hljs-keyword">if</span> (err != <span class="hljs-number">0</span>)<br> {<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"pthread_create error:%s\n"</span>, <span class="hljs-built_in">strerror</span>(err));<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">-1</span>);<br> }<br> err = <span class="hljs-built_in">pthread_join</span>(tid1, &tret);<span class="hljs-comment">//阻塞等待线程id为tid1的线程,直到该线程退出</span><br> <span class="hljs-keyword">if</span> (err != <span class="hljs-number">0</span>)<br> {<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"can not join with thread1:%s\n"</span>, <span class="hljs-built_in">strerror</span>(err));<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">-1</span>);<br> }<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"thread 1 exit code %d\n"</span>, (<span class="hljs-type">int</span>)(<span class="hljs-type">intptr_t</span>)tret);<br> err = <span class="hljs-built_in">pthread_join</span>(tid2, &tret);<br> <span class="hljs-keyword">if</span> (err != <span class="hljs-number">0</span>)<br> {<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"can not join with thread1:%s\n"</span>, <span class="hljs-built_in">strerror</span>(err));<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">-1</span>);<br> }<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"thread 2 exit code %d\n"</span>, (<span class="hljs-type">int</span>)(<span class="hljs-type">intptr_t</span>)tret);<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p><strong>编译命令: g++ -o 1_example 1_example.cpp -lpthread</strong></p><p><strong>问题</strong>: 两个线程可以对同一变量进行修改。假如线程1执行tmp=4+1后,被系统中断,此时线程2对num=5执行了减一操作,当线程1恢复,在执行num=tmp=5。而正确结果应为4。所以当多个线程对共享区域进行修改时,应该采用<strong>同步</strong>的方式。</p><h1 id="线程同步-三种方式"><a href="#线程同步-三种方式" class="headerlink" title="线程同步(三种方式)"></a>线程同步(三种方式)</h1><h2 id="互斥量"><a href="#互斥量" class="headerlink" title="互斥量"></a>互斥量</h2><p>互斥量用pthread_mutex_t数据类型来表示。</p><blockquote><p>第一种:赋值为常量<strong>PTHREAD_MUTEX_INITIALIZER</strong>;<br>第二种,当互斥量为动态分配是,使用<strong>pthread_mutex_init</strong>函数进行初始化,使用<strong>pthread_mutex_destroy</strong>函数销毁。</p></blockquote><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><pthread.h></span></span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_mutex_init</span> <span class="hljs-params">(<span class="hljs-type">pthread_mutex_t</span> *__mutex,</span></span><br><span class="hljs-params"><span class="hljs-function"> __const <span class="hljs-type">pthread_mutexattr_t</span> *__mutexattr)</span></span>;<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_mutex_destroy</span> <span class="hljs-params">(<span class="hljs-type">pthread_mutex_t</span> *__mutex)</span></span>;<br><span class="hljs-comment">//返回值:成功-0,失败-错误编号 </span><br></code></pre></td></tr></table></figure><p>加解锁加锁调用<strong>pthread_mutex_lock</strong><br>解锁调用<strong>pthread_mutex_unlock</strong></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><pthread.h></span></span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_mutex_lock</span> <span class="hljs-params">(<span class="hljs-type">pthread_mutex_t</span> *__mutex)</span>;</span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_mutex_unlock</span> <span class="hljs-params">(<span class="hljs-type">pthread_mutex_t</span> *__mutex)</span>;</span><br></code></pre></td></tr></table></figure><p>使用互斥修改程序的<strong>add</strong>和<strong>sub</strong>两个函数:</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-type">pthread_mutex_t</span> mylock = PTHREAD_MUTEX_INITIALIZER;<br><span class="hljs-function"><span class="hljs-type">void</span> *<span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-type">void</span> *arg)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>, tmp;<br> <span class="hljs-keyword">for</span> (; i < <span class="hljs-number">500</span>; i++)<br> {<br> <span class="hljs-built_in">pthread_mutex_lock</span>(&mylock);<br> tmp = num + <span class="hljs-number">1</span>;<br> num = tmp;<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"+1,result is:%d\n"</span>, num);<br> <span class="hljs-built_in">pthread_mutex_unlock</span>(&mylock);<br> }<br> <span class="hljs-keyword">return</span> ((<span class="hljs-type">void</span> *)<span class="hljs-number">0</span>);<br>}<br><span class="hljs-function"><span class="hljs-type">void</span> *<span class="hljs-title">sub</span><span class="hljs-params">(<span class="hljs-type">void</span> *arg)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>, tmp;<br> <span class="hljs-keyword">for</span> (; i < <span class="hljs-number">500</span>; i++)<br> {<br> <span class="hljs-built_in">pthread_mutex_lock</span>(&mylock);<br> tmp = num - <span class="hljs-number">1</span>;<br> num = tmp;<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"-1,result is:%d\n"</span>, num);<br> <span class="hljs-built_in">pthread_mutex_unlock</span>(&mylock);<br> }<br> <span class="hljs-keyword">return</span> ((<span class="hljs-type">void</span> *)<span class="hljs-number">0</span>);<br>}<br></code></pre></td></tr></table></figure><h2 id="读写锁"><a href="#读写锁" class="headerlink" title="读写锁"></a>读写锁</h2><p>允许多个线程同时读,只能有一个线程同时写。适用于读的次数远大于写的情况。 </p><p>读写锁初始化: </p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><pthread.h></span></span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_rwlock_init</span> <span class="hljs-params">(<span class="hljs-type">pthread_rwlock_t</span> *__restrict __rwlock,</span></span><br><span class="hljs-params"><span class="hljs-function"> __const <span class="hljs-type">pthread_rwlockattr_t</span> *__restrict__attr)</span></span>;<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_rwlock_destroy</span> <span class="hljs-params">(<span class="hljs-type">pthread_rwlock_t</span> *__rwlock)</span></span>;<br><span class="hljs-comment">//返回值:成功--0,失败-错误编号</span><br></code></pre></td></tr></table></figure><p><strong>加锁</strong>,分为<strong>读加锁</strong>和<strong>写加锁</strong>。</p><p><strong>解锁</strong>使用同一个函数。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-comment">//读加锁:</span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_rwlock_rdlock</span> <span class="hljs-params">(<span class="hljs-type">pthread_rwlock_t</span> *__rwlock)</span></span><br><span class="hljs-function"><span class="hljs-comment">//写加锁:</span></span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_rwlock_wrlock</span> <span class="hljs-params">(<span class="hljs-type">pthread_rwlock_t</span> *__rwlock)</span></span><br><span class="hljs-function"><span class="hljs-comment">//解锁用同一个函数:</span></span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_rwlock_unlock</span> <span class="hljs-params">(<span class="hljs-type">pthread_rwlock_t</span> *__rwlock)</span></span><br></code></pre></td></tr></table></figure><h2 id="条件变量"><a href="#条件变量" class="headerlink" title="条件变量"></a>条件变量</h2><p>条件变量用pthread_cond_t数据类型表示。</p><p>条件变量本身由互斥量保护,所以在改变条件状态前必须锁住互斥量。</p><h3 id="条件变量初始化"><a href="#条件变量初始化" class="headerlink" title="条件变量初始化"></a>条件变量初始化</h3><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-comment">//第一种,赋值常量PTHREAD_COND_INITIALIZER;</span><br><span class="hljs-comment">//第二种,使用pthread_cond_init函数</span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_cond_init</span> <span class="hljs-params">(<span class="hljs-type">pthread_cond_t</span> *__restrict __cond,</span></span><br><span class="hljs-params"><span class="hljs-function"> __const <span class="hljs-type">pthread_condattr_t</span> *__restrict__cond_attr)</span></span>;<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_cond_destroy</span> <span class="hljs-params">(<span class="hljs-type">pthread_cond_t</span> *__cond)</span></span>;<br></code></pre></td></tr></table></figure><h3 id="条件等待"><a href="#条件等待" class="headerlink" title="条件等待"></a>条件等待</h3><p>使用pthread_cond_wait等待条件为真。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-built_in">pthread_cond_wait</span> (<span class="hljs-type">pthread_cond_t</span> *__restrict __cond,<span class="hljs-type">pthread_mutex_t</span> *__restrict __mutex)<br></code></pre></td></tr></table></figure><p>这里需要注意的是,调用pthread_cond_wait传递的互斥量已锁定,pthread_cond_wait将调用线程放入等待条件的线程列表,然后释放互斥量,在pthread_cond_wait返回时,再次锁定互斥量。</p><h3 id="唤醒线程"><a href="#唤醒线程" class="headerlink" title="唤醒线程"></a>唤醒线程</h3><p>pthread_cond_signal唤醒等待该条件的某个线程,pthread_cond_broadcast唤醒等待该条件的所有线程。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_cond_signal</span> <span class="hljs-params">(<span class="hljs-type">pthread_cond_t</span> *__cond)</span></span>;<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">pthread_cond_broadcast</span> <span class="hljs-params">(<span class="hljs-type">pthread_cond_t</span> *__cond)</span></span><br></code></pre></td></tr></table></figure><h1 id="例程二"><a href="#例程二" class="headerlink" title="例程二"></a>例程二</h1><p>主线程启动4个线程,每个线程有一个参数i(i=生成顺序),无论线程的启动顺序如何,执行顺序只能为,线程0、线程1、线程2、线程3。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdio.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdlib.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><pthread.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><unistd.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><string.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> DEBUG 1</span><br><br><span class="hljs-type">int</span> num = <span class="hljs-number">0</span>;<br><span class="hljs-type">pthread_mutex_t</span> mylock = PTHREAD_MUTEX_INITIALIZER;<span class="hljs-comment">//互斥量</span><br><span class="hljs-type">pthread_cond_t</span> qready = PTHREAD_COND_INITIALIZER;<span class="hljs-comment">//条件变量--本身由互斥量保护,所以在改变条件状态前必须锁住互斥量。</span><br><span class="hljs-function"><span class="hljs-type">void</span> * <span class="hljs-title">thread_func</span><span class="hljs-params">(<span class="hljs-type">void</span> *arg)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">int</span> i = (<span class="hljs-type">int</span>)arg;<br> <span class="hljs-type">int</span> ret;<br> <span class="hljs-built_in">sleep</span>(<span class="hljs-number">5</span> - i);<span class="hljs-comment">//线程睡眠,然最先生成的线程,最后苏醒</span><br> <span class="hljs-built_in">pthread_mutex_lock</span>(&mylock);<span class="hljs-comment">//调用pthread_cond_wait前,必须获得互斥锁</span><br> <span class="hljs-keyword">while</span> (i != num)<br> {<br><span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> DEBUG</span><br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"thread %d waiting\n"</span>, i);<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br> ret = <span class="hljs-built_in">pthread_cond_wait</span>(&qready, &mylock);<span class="hljs-comment">//该函数把线程放入等待条件的线程列表,然后对互斥锁进行解锁,这两部都是原子操作。并且在pthread_cond_wait返回时,互斥量再次锁住。</span><br> <span class="hljs-keyword">if</span> (ret == <span class="hljs-number">0</span>)<br> {<br><span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> DEBUG</span><br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"thread %d wait success\n"</span>, i);<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br> }<br> <span class="hljs-keyword">else</span><br> {<br><span class="hljs-meta">#<span class="hljs-keyword">ifdef</span> DEBUG</span><br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"thread %d wait failed:%s\n"</span>, i, <span class="hljs-built_in">strerror</span>(ret));<br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br> }<br> }<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"thread %d is running \n"</span>, i);<br> num++;<br> <span class="hljs-built_in">pthread_mutex_unlock</span>(&mylock);<span class="hljs-comment">//解锁</span><br> <span class="hljs-built_in">pthread_cond_broadcast</span>(&qready);<span class="hljs-comment">//唤醒等待该条件的所有线程</span><br> <span class="hljs-keyword">return</span> (<span class="hljs-type">void</span> *)<span class="hljs-number">0</span>;<br>}<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>** argv)</span> </span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">int</span> i = <span class="hljs-number">0</span>, err;<br> <span class="hljs-type">pthread_t</span> tid[<span class="hljs-number">4</span>];<br> <span class="hljs-type">void</span> *tret;<br> <span class="hljs-keyword">for</span> (; i < <span class="hljs-number">4</span>; i++)<br> {<br> err = <span class="hljs-built_in">pthread_create</span>(&tid[i], <span class="hljs-literal">NULL</span>, thread_func, (<span class="hljs-type">void</span> *)i);<span class="hljs-comment">//创建线程</span><br> <span class="hljs-keyword">if</span> (err != <span class="hljs-number">0</span>)<br> {<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"thread_create error:%s\n"</span>, <span class="hljs-built_in">strerror</span>(err));<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">-1</span>);<br> }<br> }<br> <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < <span class="hljs-number">4</span>; i++)<br> {<br> err = <span class="hljs-built_in">pthread_join</span>(tid[i], &tret);<span class="hljs-comment">//线程阻塞</span><br> <span class="hljs-keyword">if</span> (err != <span class="hljs-number">0</span>)<br> {<br> <span class="hljs-built_in">printf</span>(<span class="hljs-string">"can not join with thread %d:%s\n"</span>, i, <span class="hljs-built_in">strerror</span>(err));<br> <span class="hljs-built_in">exit</span>(<span class="hljs-number">-1</span>);<br> }<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>在非DEBUG模式,执行结果如下所示:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs shell">jack@desktop: g++ -o 1_example 1_example.cpp -lpthread<br>jack@desktop: ./1_example<br>jack@desktop: thread 0 is running<br>jack@desktop: thread 1 is running<br>jack@desktop: thread 2 is running<br>jack@desktop: thread 3 is running<br>jack@desktop: thread 4 is running<br></code></pre></td></tr></table></figure><p>在DEBUG模式,执行结果如下所示:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs shell">jack@desktop: g++ -o 1example 1example.cpp -lpthread<br>jack@desktop: ./1_example<br>jack@desktop: thread 3 waiting<br>jack@desktop: thread 2 waiting<br>jack@desktop: thread 1 waiting<br>jack@desktop: thread 0 is running<br>jack@desktop: thread 3 wait success<br>jack@desktop: thread 3 waiting<br>jack@desktop: thread 2 wait success<br>jack@desktop: thread 2 waiting<br>jack@desktop: thread 1 wait success<br>jack@desktop: thread 1 running<br>jack@desktop: thread 3 wait success<br>jack@desktop: thread 3 waiting<br>jack@desktop: thread 2 wait success<br>jack@desktop: thread 2 running<br>jack@desktop: thread 3 wait success<br>jack@desktop: thread 3 running<br></code></pre></td></tr></table></figure><p> 在DEBUG模式可以看出,线程3先被唤醒,然后执行pthread_cond_wait(输出thread 3 waiting),此时在pthread_cond_wait中先解锁互斥量,然后进入等待状态。这是thread 2加锁互斥量成功,进入pthread_cond_wait(输出thread 2 waiting) ,同样解锁互斥量,然后进入等待状态。直到线程0,全局变量与线程参数i一致,满足条件,不进入条件等待,输出thread 0 is running。全局变量num执行加1操作,解锁互斥量,然后唤醒所有等待该条件的线程。thread 3 被唤醒,输出thread 3 wait success。但是不满足条件,再次执行pthread_cond_wait。如此执行下去,满足条件的线程执行,不满足条件的线程等待。</p>]]></content>
<categories>
<category>Linux</category>
</categories>
<tags>
<tag>Cpp</tag>
<tag>Pthread</tag>
</tags>
</entry>
<entry>
<title>欧氏距离和余弦相似度</title>
<link href="/2017/04/22/%E6%AC%A7%E6%B0%8F%E8%B7%9D%E7%A6%BB%E5%92%8C%E4%BD%99%E5%BC%A6%E7%9B%B8%E4%BC%BC%E5%BA%A6/"/>
<url>/2017/04/22/%E6%AC%A7%E6%B0%8F%E8%B7%9D%E7%A6%BB%E5%92%8C%E4%BD%99%E5%BC%A6%E7%9B%B8%E4%BC%BC%E5%BA%A6/</url>
<content type="html"><![CDATA[<p>两者相同的地方,就是在机器学习中都可以用来计算相似度,但是两者的含义有很大差别,以我的理解就是:</p><ul><li>前者是看成坐标系中两个 <strong>点</strong> ,来计算两点之间的 距离 ;</li><li>后者是看成坐标系中两个 <strong>向量</strong> ,来计算两向量之间的 <strong>夹角</strong> 。</li><li>前者因为是 <strong>点</strong> ,所以一般指 <strong>位置</strong> 上的差别,即 <strong>距离</strong> ;</li><li>后者因为是 <strong>向量</strong> ,所以一般指 <strong>方向</strong> 上的差别,即所成 <strong>夹角</strong> 。</li></ul><p>如下图所示:<br><img src="/2017/04/22/%E6%AC%A7%E6%B0%8F%E8%B7%9D%E7%A6%BB%E5%92%8C%E4%BD%99%E5%BC%A6%E7%9B%B8%E4%BC%BC%E5%BA%A6/01.jpg" alt="01"></p><p>数据项A和B在坐标图中当做点时,两者相似度为距离dist(A,B),可通过欧氏距离(也叫欧几里得距离)公式计算:</p><p>$$dist(X,Y)=\sqrt{\sum_{i=1}^{n}(X_i-Y_i)^2}$$</p><p>当做向量时,两者相似度为cosθ,可通过余弦公式计算:</p><p>$$cos\theta = \frac{\sum_{i=1}^{n}(A_i \times B_i)}{\sqrt(\sum_{i=1}^{n}(A_i)^2) \times \sqrt(\sum_{i=1}^{n}(B_i)^2)}= \frac{A^T \cdot B}{||A||\times ||B||} $$</p><p>假设$||A||$、$||B||$表示向量A、B的L2范数,例如向量[1,2,3]的2范数为:</p><p>$$\sqrt{1^2+2^2=3^3} = \sqrt{14}$$<br>numpy中提供了范数的计算工具: linalg.norm()</p><p>所以计算$cosθ$起来非常方便(假定A、B均为列向量):</p><figure class="highlight python"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></div></td><td class="code"><pre><code class="hljs python">num = <span class="hljs-built_in">float</span>(A.T * B) <span class="hljs-comment">#若为行向量则 A * B.T</span><br>denom = linalg.norm(A) * linalg.norm(B)<br>cos = num / denom <span class="hljs-comment">#余弦值</span><br>sim = <span class="hljs-number">0.5</span> + <span class="hljs-number">0.5</span> * cos <span class="hljs-comment">#归一化</span><br></code></pre></td></tr></table></figure><p>因为有了linalg.norm(),欧氏距离公式实现起来更为方便:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python">dist = linalg.norm(A - B)<br>sim = <span class="hljs-number">1.0</span> / (<span class="hljs-number">1.0</span> + dist) <span class="hljs-comment">#归一化</span><br></code></pre></td></tr></table></figure><p>关于归一化:</p><p>因为余弦值的范围是 [-1,+1] ,相似度计算时一般需要把值归一化到 [0,1],一般通过如下方式:</p><p>$$sim = 0.5 + 0.5 * \cosθ $$<br>若在欧氏距离公式中,取值范围会很大,一般通过如下方式归一化:</p><p>$$sim = \frac{1}{1+dist(x,y)}$$</p><p>说完了原理,简单扯下实际意义,举个栗子吧:</p><p>例如某T恤从100块降到了50块(A(100,50)),某西装从1000块降到了500块(B(1000,500))</p><p>那么T恤和西装都是降价了50%,两者的价格变动趋势一致,余弦相似度为最大值,即两者有很高的 变化趋势相似度</p><p>但是从商品价格本身的角度来说,两者相差了好几百块的差距,欧氏距离较大,即两者有较低的 价格相似度。</p><p>参考:<a href="http://blog.csdn.net/linvo/article/details/9333019?utm_source=tuicool&utm_medium=referral">欧氏距离和余弦相似度</a></p>]]></content>
<categories>
<category>基础知识</category>
</categories>
<tags>
<tag>欧氏距离</tag>
<tag>余弦相似度</tag>
</tags>
</entry>
<entry>
<title>Opencv实现Kalman滤波器</title>
<link href="/2017/03/26/Opencv%E5%AE%9E%E7%8E%B0Kalman%E6%BB%A4%E6%B3%A2%E5%99%A8/"/>
<url>/2017/03/26/Opencv%E5%AE%9E%E7%8E%B0Kalman%E6%BB%A4%E6%B3%A2%E5%99%A8/</url>
<content type="html"><![CDATA[<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>卡尔曼滤波是一种高效率的<strong>递归滤波器(自回归滤波器)</strong>, 它能够从一系列的不完全及包含噪声的测量中,估计动态系统的状态。卡尔曼滤波的一个典型实例是从一组有限的,包含噪声的,对物体位置的观察序列(可能有偏差)预测出物体的位置的坐标及速度。<br>这种滤波方法以它的发明者<strong>鲁道夫.E.卡尔曼(Rudolph E. Kalman)</strong>命名,但是根据文献可知实际上<strong>Peter Swerling</strong>在更早之前就提出了一种类似的算法。<br><strong>斯坦利.施密特(Stanley Schmidt)<strong>首次实现了卡尔曼滤波器。卡尔曼在</strong>NASA埃姆斯研究中心</strong>访问时,发现他的方法对于解决阿波罗计划的轨道预测很有用,后来阿波罗飞船的导航电脑便使用了这种滤波器。 关于这种滤波器的论文由<strong>Swerling (1958)、Kalman (1960)与 Kalman and Bucy (1961)<strong>发表。<br>目前,卡尔曼滤波已经有很多不同的实现.卡尔曼最初提出的形式现在一般称为简单卡尔曼滤波器。除此以外,还有施密特扩展滤波器、信息滤波器以及很多</strong>Bierman</strong>, <strong>Thornton</strong> 开发的<strong>平方根滤波器</strong>的变种。也许最常见的卡尔曼滤波器是<strong>锁相环</strong>,它在收音机、计算机和几乎任何视频或通讯设备中广泛存在。</p><h1 id="模型"><a href="#模型" class="headerlink" title="模型"></a>模型</h1><h2 id="基本动态系统模型"><a href="#基本动态系统模型" class="headerlink" title="基本动态系统模型"></a>基本动态系统模型</h2><p>卡尔曼滤波建立在<strong>线性代数</strong>和<strong>隐马尔可夫模型</strong>(hidden Markov model)上。其基本动态系统可以用一个<strong>马尔可夫链</strong>表示,该马尔可夫链建立在一个被<strong>高斯噪声(即正态分布的噪声)<strong>干扰的线性算子上的。系统的状态可以用一个元素为实数的</strong>向量</strong>表示。 随着离散时间的每一个增加,这个线性算子就会作用在当前状态上,产生一个新的状态,并也会带入一些噪声,同时系统的一些已知的控制器的控制信息也会被加入。同时,另一个受<strong>噪声干扰</strong>的<strong>线性算子</strong>产生出这些<strong>隐含状态</strong>的可见输出。<br>模型图:<br><img src="/2017/03/26/Opencv%E5%AE%9E%E7%8E%B0Kalman%E6%BB%A4%E6%B3%A2%E5%99%A8/KalmanModelImg.png" alt="KalmanModelImg"><br>为了从一系列有噪声的观察数据中用卡尔曼滤波器估计出被观察过程的内部状态,我们必须把这个过程在<strong>卡尔曼滤波</strong>的框架下建立模型。也就是说对于每一步<strong>k</strong>,定义矩阵。<br>例如:<strong>KalmanFilter KF(stateNum, measureNum, 0);</strong></p><p><strong>Fk : KF.transitionMatrix ,Hk : KF.measurementMatrix</strong><br><strong>Qk : KF.processNoiseCov , Rk : KF.measurementNoiseCov</strong><br><strong>Pk : KF.errorCovPost , Bk : KF.ontrolMatrix</strong></p><p>尔曼滤波模型假设k时刻的真实状态是从**(k − 1)**时刻的状态演化而来,符合下式<br>$$ X_k = F_k * X_{k-1} + B_k * u_k + w_k $$</p><p>$F_k$是作用在$X_{k−1}$上的状态变换模型(矩阵,矢量)<br>$B_k$是作用在控制器向量uk上的输入–控制模型(矩阵,矢量)<br>$w_k$是过程噪声,并假定其符合均值为零,<strong>协方差矩阵</strong>为$Q_k$的<strong>多元正态分布</strong>。</p><p>$$ w_{k} - N(0,Q_k) $$<br>时刻<strong>k</strong>,对真实状态 $X_k$的一个测量$Z_k$满足下式<br>$$ Z_k = H_k * X_k + v_k $$<br>其中<strong>Hk</strong> 是<strong>观测模型</strong>,它把<strong>真实状态空间</strong>映射成<strong>观测空间</strong>,<strong>vk</strong> 是<strong>观测噪声</strong>,其均值为零,<strong>协方差矩阵</strong>为<strong>Rk</strong>,且服从<strong>正态分布</strong>。</p><p>$$ w_k - N(0,Q_k) $$<br>初始状态以及每一时刻的噪声${x_0, w_1, …, w_k, v_1 …v_k}$都认为是互相独立的.</p><h2 id="卡尔曼滤波器"><a href="#卡尔曼滤波器" class="headerlink" title="卡尔曼滤波器"></a>卡尔曼滤波器</h2><p>卡尔曼滤波是一种<strong>递归</strong>的估计,即只要获知<strong>上一时刻状态</strong>的<strong>估计值</strong>以及<strong>当前状态</strong>的<strong>观测值</strong>就可以计算出<strong>当前状态的估计值</strong>,因此不需要记录观测或者估计的历史信息。卡尔曼滤波器与大多数滤波器不同之处,在于它是一种纯粹的<strong>时域滤波器</strong>,它不需要像低通滤波器等频域滤波器那样,需要在频域设计再转换到时域实现。<br>卡尔曼滤波器的状态由以下两个变量表示:<br>$X_k|k$:在时刻k 的状态的估计<br>$P_k|k$:误差相关矩阵,度量估计值的精确程度。</p><p>卡尔曼滤波器的操作包括两个阶段:<strong>预测</strong>与<strong>更新</strong>。<br>在预测阶段,滤波器使用上一状态的估计,做出对当前状态的估计。<br>在更新阶段,滤波器利用对当前状态的观测值优化在预测阶段获得的预测值,以获得一个更精确的新估计值。<br><strong>预测 predict:</strong><br><strong>Mat prediction = KF.predict();</strong></p><p>$\hat{X}<em>{k|k-1} = F_k\hat{X}</em>{k-1|k-1}+B_ku_k$(预测状态)<br>$P_{k|k-1} = F_kP_{k-1|k-1}F_k^T+B_ku_k $(预测估计协方差矩阵)<br>可参考:<a href="http://www.cs.unc.edu/~welch/media/pdf/kalman_intro.pdf">http://www.cs.unc.edu/~welch/media/pdf/kalman_intro.pdf</a></p><p><strong>更新 updata:</strong><br><strong>KF.correct(measurement);</strong><br><strong>测量余量,measurement residual:</strong><br>$$Y_{k} = z_{k}-H_{k}\hat{x}<em>{k|k-1}$$<br><strong>测量余量协方差:</strong><br>$$S</em>{k} = H_{k}P_{k-1|k-1}H_{k}^{T}+R_{k}$$<br><strong>最优卡尔曼增益:</strong><br>$$K_{k} = P_{k|k-1}H_{k}^{T}+S_{k}^{-1}$$<br><strong>更新的状态估计:</strong><br>$$\hat{X}<em>{k|k} = \hat{X}</em>{k|k-1}+K_{k}Y_{k}$$<br><strong>更新的协方差估计:</strong><br>$$P_{k|k} = (I-K_{k}H_{k})P_{k|k-1}$$</p><p>使用上述公式计算$P_k|k$仅在最优卡尔曼增益的时候有效。使用其他增益的话,公式要复杂一些,请参见推导。</p><h2 id="不变量-Invariant"><a href="#不变量-Invariant" class="headerlink" title="不变量(Invariant)"></a>不变量(Invariant)</h2><p>如果模型准确,而且$X_0|0$与$P_0|0$的值准确的反映了最初状态的分布,那么以下不变量就保持不变:所有估计的误差均值为零<br>$$E[X_{k}-\hat{x}<em>{k|k}] = E[X</em>{k}-\hat{x}<em>{k|k-1}]$$<br>$$E[Y</em>{k}] = 0$$<br>且 <strong>协方差矩阵</strong> 准确的反映了估计的协方差:<br>$$P_{k|k} = cov(X_{k}-\hat{X}<em>{k|k})$$<br>$$P</em>{k|k-1} = cov(X_{k}-\hat{X}<em>{k|k-1})$$<br>$$S</em>{k} = cov(Y_{k})$$</p><p>其中,<strong>E[a]<strong>表示a的期望值,</strong>$$cov(a)=E[aa^T]$$</strong></p><h1 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h1><h2 id="class-Kalman"><a href="#class-Kalman" class="headerlink" title="class Kalman"></a>class Kalman</h2><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">class</span> <span class="hljs-title class_">CV_EXPORTS_W</span> KalmanFilter <br>{ <br><span class="hljs-keyword">public</span>: <br> <span class="hljs-comment">//! the default constructor </span><br> <span class="hljs-function">CV_WRAP <span class="hljs-title">KalmanFilter</span><span class="hljs-params">()</span></span>; <br> <span class="hljs-comment">//! the full constructor taking the dimensionality of the state, of the measurement and of the control vector </span><br> <span class="hljs-function">CV_WRAP <span class="hljs-title">KalmanFilter</span><span class="hljs-params">(<span class="hljs-type">int</span> dynamParams, <span class="hljs-type">int</span> measureParams, <span class="hljs-type">int</span> controlParams=<span class="hljs-number">0</span>, <span class="hljs-type">int</span> type=CV_32F)</span></span>; <br> <span class="hljs-comment">//! re-initializes Kalman filter. The previous content is destroyed. </span><br> <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">init</span><span class="hljs-params">(<span class="hljs-type">int</span> dynamParams, <span class="hljs-type">int</span> measureParams, <span class="hljs-type">int</span> controlParams=<span class="hljs-number">0</span>, <span class="hljs-type">int</span> type=CV_32F)</span></span>; <br> <br> <span class="hljs-comment">//! computes predicted state </span><br> <span class="hljs-function">CV_WRAP <span class="hljs-type">const</span> Mat& <span class="hljs-title">predict</span><span class="hljs-params">(<span class="hljs-type">const</span> Mat& control=Mat())</span></span>; <br> <span class="hljs-comment">//! updates the predicted state from the measurement </span><br> <span class="hljs-function">CV_WRAP <span class="hljs-type">const</span> Mat& <span class="hljs-title">correct</span><span class="hljs-params">(<span class="hljs-type">const</span> Mat& measurement)</span></span>; <br> <br> Mat statePre; <span class="hljs-comment">//!< predicted state (x'(k)): x(k)=A*x(k-1)+B*u(k) </span><br> Mat statePost; <span class="hljs-comment">//!< corrected state (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) </span><br> Mat transitionMatrix; <span class="hljs-comment">//!< state transition matrix (A) </span><br> Mat controlMatrix; <span class="hljs-comment">//!< control matrix (B) (not used if there is no control) </span><br> Mat measurementMatrix; <span class="hljs-comment">//!< measurement matrix (H) </span><br> Mat processNoiseCov; <span class="hljs-comment">//!< process noise covariance matrix (Q) </span><br> Mat measurementNoiseCov;<span class="hljs-comment">//!< measurement noise covariance matrix (R) </span><br> Mat errorCovPre; <span class="hljs-comment">//!< priori error estimate covariance matrix (P'(k)): P'(k)=A*P(k-1)*At + Q)*/ </span><br> Mat gain; <span class="hljs-comment">//!< Kalman gain matrix (K(k)): K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R) </span><br> Mat errorCovPost; <span class="hljs-comment">//!< posteriori error estimate covariance matrix (P(k)): P(k)=(I-K(k)*H)*P'(k) </span><br> <br> <span class="hljs-comment">// temporary matrices </span><br> Mat temp1; <br> Mat temp2; <br> Mat temp3; <br> Mat temp4; <br> Mat temp5; <br>}; <br></code></pre></td></tr></table></figure><h2 id="sample-kalman-cpp"><a href="#sample-kalman-cpp" class="headerlink" title="sample\kalman.cpp"></a>sample\kalman.cpp</h2><p>1个1维点的运动跟踪,虽然这个点是在2维平面中运动,但由于它是在一个圆弧上运动,只有一个自由度,角度,所以还是1维的。还是一个匀速运动,建立匀速运动模型,设定状态变量x = [ x1, x2 ] = [ 角度,角速度 ]</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/video/tracking.hpp"</span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/highgui/highgui.hpp"</span> </span><br> <br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdio.h></span> </span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv; <br> <br><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-keyword">inline</span> Point <span class="hljs-title">calcPoint</span><span class="hljs-params">(Point2f center, <span class="hljs-type">double</span> R, <span class="hljs-type">double</span> angle)</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-keyword">return</span> center + <span class="hljs-built_in">Point2f</span>((<span class="hljs-type">float</span>)<span class="hljs-built_in">cos</span>(angle), (<span class="hljs-type">float</span>)-<span class="hljs-built_in">sin</span>(angle))*(<span class="hljs-type">float</span>)R; <br>} <br> <br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">help</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-built_in">printf</span>( <span class="hljs-string">" Tracking of rotating point.\n"</span> <br><span class="hljs-string">" Rotation speed is constant.\n"</span> <br><span class="hljs-string">" Both state and measurements vectors are 1D (a point angle),\n"</span> <br><span class="hljs-string">" Measurement is the real point angle + gaussian noise.\n"</span> <br><span class="hljs-string">" The real and the estimated points are connected with yellow line segment,\n"</span> <br><span class="hljs-string">" the real and the measured points are connected with red line segment.\n"</span> <br><span class="hljs-string">" (if Kalman filter works correctly,\n"</span> <br><span class="hljs-string">" the yellow segment should be shorter than the red one).\n"</span> <br> <span class="hljs-string">"\n"</span> <br><span class="hljs-string">" Pressing any key (except ESC) will reset the tracking with a different speed.\n"</span> <br><span class="hljs-string">" Pressing ESC will stop the program.\n"</span> <br> ); <br>} <br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span>, <span class="hljs-type">char</span>**)</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-built_in">help</span>(); <br> <span class="hljs-function">Mat <span class="hljs-title">img</span><span class="hljs-params">(<span class="hljs-number">500</span>, <span class="hljs-number">500</span>, CV_8UC3)</span></span>; <br> <span class="hljs-function">KalmanFilter <span class="hljs-title">KF</span><span class="hljs-params">(<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>)</span></span>; <br> <span class="hljs-function">Mat <span class="hljs-title">state</span><span class="hljs-params">(<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, CV_32F)</span></span>; <span class="hljs-comment">/* (phi, delta_phi) */</span> <br> <span class="hljs-function">Mat <span class="hljs-title">processNoise</span><span class="hljs-params">(<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, CV_32F)</span></span>; <br> Mat measurement = Mat::<span class="hljs-built_in">zeros</span>(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>, CV_32F); <br> <span class="hljs-type">char</span> code = (<span class="hljs-type">char</span>)<span class="hljs-number">-1</span>; <br> <br> <span class="hljs-keyword">for</span>(;;) <br> { <br> <span class="hljs-built_in">randn</span>( state, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0</span>), Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0.1</span>) );<span class="hljs-comment">//随机生成一个矩阵,期望是0,标准差为0.1; </span><br> KF.transitionMatrix = *(<span class="hljs-built_in">Mat_</span><<span class="hljs-type">float</span>>(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>) << <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>);<span class="hljs-comment">//元素导入矩阵,按行; </span><br> <br> <span class="hljs-comment">//setIdentity: 缩放的单位对角矩阵; </span><br> <span class="hljs-comment">//!< measurement matrix (H) 观测模型 </span><br> <span class="hljs-built_in">setIdentity</span>(KF.measurementMatrix); <br> <span class="hljs-comment">//!< process noise covariance matrix (Q) </span><br> <span class="hljs-comment">// wk 是过程噪声,并假定其符合均值为零,协方差矩阵为Qk(Q)的多元正态分布; </span><br> <span class="hljs-built_in">setIdentity</span>(KF.processNoiseCov, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">1e-5</span>)); <br> <span class="hljs-comment">//!< measurement noise covariance matrix (R) </span><br> <span class="hljs-comment">//vk 是观测噪声,其均值为零,协方差矩阵为Rk,且服从正态分布; </span><br> <span class="hljs-built_in">setIdentity</span>(KF.measurementNoiseCov, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">1e-1</span>)); <br> <span class="hljs-comment">//!< priori error estimate covariance matrix (P'(k)): P'(k)=A*P(k-1)*At + Q)*/ A代表F: transitionMatrix </span><br> <span class="hljs-comment">//预测估计协方差矩阵; </span><br> <span class="hljs-built_in">setIdentity</span>(KF.errorCovPost, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">1</span>)); <br> <span class="hljs-comment">//!< corrected state (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) </span><br> <span class="hljs-built_in">randn</span>(KF.statePost, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0</span>), Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0.1</span>)); <br> <br> <span class="hljs-keyword">for</span>(;;) <br> { <br> <span class="hljs-function">Point2f <span class="hljs-title">center</span><span class="hljs-params">(img.cols*<span class="hljs-number">0.5f</span>, img.rows*<span class="hljs-number">0.5f</span>)</span></span>; <br> <span class="hljs-type">float</span> R = img.cols/<span class="hljs-number">3.f</span>; <br> <span class="hljs-type">double</span> stateAngle = state.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">0</span>); <br> Point statePt = <span class="hljs-built_in">calcPoint</span>(center, R, stateAngle); <br> <br> Mat prediction = KF.<span class="hljs-built_in">predict</span>(); <br> <span class="hljs-type">double</span> predictAngle = prediction.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">0</span>); <br> Point predictPt = <span class="hljs-built_in">calcPoint</span>(center, R, predictAngle); <br> <br> <span class="hljs-built_in">randn</span>( measurement, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0</span>), Scalar::<span class="hljs-built_in">all</span>(KF.measurementNoiseCov.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">0</span>))); <br> <br> <span class="hljs-comment">// generate measurement </span><br> measurement += KF.measurementMatrix*state; <br> <br> <span class="hljs-type">double</span> measAngle = measurement.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">0</span>); <br> Point measPt = <span class="hljs-built_in">calcPoint</span>(center, R, measAngle); <br> <br> <span class="hljs-comment">// plot points </span><br> <span class="hljs-meta">#<span class="hljs-keyword">define</span> drawCross( center, color, d ) \ </span><br> <span class="hljs-built_in">line</span>( img, <span class="hljs-built_in">Point</span>( center.x - d, center.y - d ), \ <br> <span class="hljs-built_in">Point</span>( center.x + d, center.y + d ), color, <span class="hljs-number">1</span>, CV_AA, <span class="hljs-number">0</span>); \ <br> <span class="hljs-built_in">line</span>( img, <span class="hljs-built_in">Point</span>( center.x + d, center.y - d ), \ <br> <span class="hljs-built_in">Point</span>( center.x - d, center.y + d ), color, <span class="hljs-number">1</span>, CV_AA, <span class="hljs-number">0</span> ) <br> <br> img = Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0</span>); <br> <span class="hljs-built_in">drawCross</span>( statePt, <span class="hljs-built_in">Scalar</span>(<span class="hljs-number">255</span>,<span class="hljs-number">255</span>,<span class="hljs-number">255</span>), <span class="hljs-number">3</span> ); <br> <span class="hljs-built_in">drawCross</span>( measPt, <span class="hljs-built_in">Scalar</span>(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">255</span>), <span class="hljs-number">3</span> ); <br> <span class="hljs-built_in">drawCross</span>( predictPt, <span class="hljs-built_in">Scalar</span>(<span class="hljs-number">0</span>,<span class="hljs-number">255</span>,<span class="hljs-number">0</span>), <span class="hljs-number">3</span> ); <br> <span class="hljs-comment">//line( img, statePt, measPt, Scalar(0,0,255), 3, CV_AA, 0 ); </span><br> <span class="hljs-comment">//line( img, statePt, predictPt, Scalar(0,255,255), 3, CV_AA, 0 ); </span><br> <br> KF.<span class="hljs-built_in">correct</span>(measurement); <br> <br> <span class="hljs-built_in">randn</span>( processNoise, <span class="hljs-built_in">Scalar</span>(<span class="hljs-number">0</span>), Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-built_in">sqrt</span>(KF.processNoiseCov.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>)))); <br> state = KF.transitionMatrix*state + processNoise; <br> <br> <span class="hljs-built_in">imshow</span>( <span class="hljs-string">"Kalman"</span>, img ); <br> code = (<span class="hljs-type">char</span>)<span class="hljs-built_in">waitKey</span>(<span class="hljs-number">100</span>); <br> <br> <span class="hljs-keyword">if</span>( code > <span class="hljs-number">0</span> ) <br> <span class="hljs-keyword">break</span>; <br> } <br> <span class="hljs-keyword">if</span>( code == <span class="hljs-number">27</span> || code == <span class="hljs-string">'q'</span> || code == <span class="hljs-string">'Q'</span> ) <br> <span class="hljs-keyword">break</span>; <br> } <br> <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; <br>} <br></code></pre></td></tr></table></figure><p><img src="/2017/03/26/Opencv%E5%AE%9E%E7%8E%B0Kalman%E6%BB%A4%E6%B3%A2%E5%99%A8/out1.JPG" alt="output1"></p><h1 id="鼠标跟踪(详解)"><a href="#鼠标跟踪(详解)" class="headerlink" title="鼠标跟踪(详解)"></a>鼠标跟踪(详解)</h1><h2 id="初始化状态"><a href="#初始化状态" class="headerlink" title="初始化状态"></a>初始化状态</h2><p><code>const int stateNum=4;</code>//状态数,包括(x,y,dx,dy)坐标及速度(每次移动的距离)<br><code>const int measureNum=2;</code>//观测量,能看到的是坐标值,当然也可以自己计算速度,但没必要<br>转移矩阵或者说增益矩阵的值好像有点莫名其妙</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">float</span> A[stateNum][stateNum] ={<span class="hljs-comment">//transition matrix </span><br> <span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span> <br> }; <br></code></pre></td></tr></table></figure><p>即是矩阵:<br>$$<br>\begin{bmatrix}<br>x’\<br>y’\<br>dx’\<br>dy’<br>\end{bmatrix}<br>=<br>\begin{bmatrix}<br>1 & 0 & 1 & 0\<br>0 & 1 & 0 & 1\<br>0 & 0 & 1 & 0\<br>0 & 0 & 1 & 1\<br>\end{bmatrix}<br>\begin{bmatrix}<br>x\<br>y\<br>dx\<br>dy<br>\end{bmatrix}<br>$$</p><p>$X_1=X+dx$,依次类推<br>所以这个矩阵还是很容易却确定的,可以根据自己的实际情况定制转移矩阵<br>同样的方法,三维坐标的转移矩阵可以如下</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">float</span> A[stateNum][stateNum] ={<span class="hljs-comment">//transition matrix </span><br> <span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span> <br> }; <br></code></pre></td></tr></table></figure><p>当然并不一定得是1和0</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-function">KalmanFilter <span class="hljs-title">KF</span><span class="hljs-params">(stateNum, measureNum, <span class="hljs-number">0</span>)</span></span>;<br><span class="hljs-comment">//KalmanFilter::KalmanFilter(intdynamParams, intmeasureParams, int controlParams=0, inttype=CV_32F)</span><br>Parameters: <br> * dynamParams – Dimensionality of the state.<br> * measureParams – Dimensionality of the measurement.<br> * controlParams – Dimensionality of the control vector.<br> * type – Type of the created matrices that should beCV_32F orCV_64F.<br> <br><span class="hljs-function">Mat <span class="hljs-title">state</span> <span class="hljs-params">(stateNum, <span class="hljs-number">1</span>, CV_32FC1)</span></span>; <span class="hljs-comment">// state(x,y,detaX,detaY)</span><br><span class="hljs-function">Mat <span class="hljs-title">processNoise</span><span class="hljs-params">(stateNum, <span class="hljs-number">1</span>, CV_32F)</span></span>; <span class="hljs-comment">// processNoise(x,y,detaX,detaY)</span><br>Mat measurement = Mat::<span class="hljs-built_in">zeros</span>(measureNum, <span class="hljs-number">1</span>, CV_32F); <span class="hljs-comment">//measurement(x,y)</span><br></code></pre></td></tr></table></figure><p>需定义的参数矩阵:<br><code>Fk : KF.transitionMatrix</code><br><code>KF.transitionMatrix = *(Mat_<float>(2, 2) << 1, 0, 1, 0......); </code><br><code>Hk : KF.measurementMatrix</code><br><code>setIdentity(KF.measurementMatrix); </code><br><code>Qk : KF.processNoiseCov</code><br><code>setIdentity(KF.processNoiseCov, Scalar::all(1e-5)); </code><br><code>Rk : KF.measurementNoiseCov </code><br><code>setIdentity(KF.measurementNoiseCov, Scalar::all(1e-1)); </code><br><code>Pk : KF.errorCovPost </code><br><code>setIdentity(KF.errorCovPost, Scalar::all(1)); </code><br>注意:其中: KF.transitionMatrix :<br> (1,0,1,0,<br> 0,1,0,1,<br> 0,0,1,0,<br> 0,0,0,1 );</p><h2 id="预测predict,读出预测的位置"><a href="#预测predict,读出预测的位置" class="headerlink" title="预测predict,读出预测的位置"></a>预测predict,读出预测的位置</h2><h2 id="更新updata"><a href="#更新updata" class="headerlink" title="更新updata"></a>更新updata</h2><h3 id="更新观测矩阵:updata-x2F-generate-measurement"><a href="#更新观测矩阵:updata-x2F-generate-measurement" class="headerlink" title="更新观测矩阵:updata/generate measurement"></a>更新观测矩阵:updata/generate measurement</h3><ul><li>对于鼠标跟踪:直接使用鼠标的实际位置更新,真实位置即为观测位置</li><li>对于自动更新:<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-built_in">randn</span>( measurement, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0</span>), Scalar::<span class="hljs-built_in">all</span>(KF.measurementNoiseCov.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">0</span>))); <br> <br><span class="hljs-comment">// generate measurement </span><br>measurement += KF.measurementMatrix*state; <br></code></pre></td></tr></table></figure></li></ul><h3 id="更新KF:KF-correct-measurement"><a href="#更新KF:KF-correct-measurement" class="headerlink" title="更新KF:KF.correct(measurement);"></a>更新KF:KF.correct(measurement);</h3><p>分别显示前一状态的位置:Point statePt = Point( (int)KF.statePost.at<float>(0), (int)KF.statePost.at<float>(1));;<br>预测位置:Point predictPt = Point( (int)prediction.at<float>(0), (int)prediction.at<float>(1));<br>观测位置(真实位置):mousePosition(由setMouseCallback(“Kalman”, mouseEvent);得到,递归方式通过measurement计算得到)</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><opencv/cv.h></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><opencv/highgui.h></span> </span><br> <br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv; <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br> <br><span class="hljs-type">const</span> <span class="hljs-type">int</span> winWidth = <span class="hljs-number">800</span>; <br><span class="hljs-type">const</span> <span class="hljs-type">int</span> winHeight = <span class="hljs-number">600</span>; <br> <br>Point mousePosition = <span class="hljs-built_in">Point</span>(winWidth>><span class="hljs-number">1</span>, winHeight>><span class="hljs-number">1</span>); <br> <br><span class="hljs-comment">//mouse call back </span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">mouseEvent</span><span class="hljs-params">(<span class="hljs-type">int</span> event, <span class="hljs-type">int</span> x, <span class="hljs-type">int</span> y, <span class="hljs-type">int</span> flags, <span class="hljs-type">void</span> *param)</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-keyword">if</span>(event==CV_EVENT_MOUSEMOVE) <br> { <br> mousePosition=<span class="hljs-built_in">Point</span>(x,y); <br> } <br>} <br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-comment">//1.kalman filter setup </span><br> <span class="hljs-type">const</span> <span class="hljs-type">int</span> stateNum=<span class="hljs-number">4</span>; <br> <span class="hljs-type">const</span> <span class="hljs-type">int</span> measureNum=<span class="hljs-number">2</span>; <br> <br> <span class="hljs-function">KalmanFilter <span class="hljs-title">KF</span><span class="hljs-params">(stateNum, measureNum, <span class="hljs-number">0</span>)</span></span>; <br> <span class="hljs-function">Mat <span class="hljs-title">state</span> <span class="hljs-params">(stateNum, <span class="hljs-number">1</span>, CV_32FC1)</span></span>; <span class="hljs-comment">//state(x,y,detaX,detaY) </span><br> <span class="hljs-function">Mat <span class="hljs-title">processNoise</span><span class="hljs-params">(stateNum, <span class="hljs-number">1</span>, CV_32F)</span></span>; <br> Mat measurement = Mat::<span class="hljs-built_in">zeros</span>(measureNum, <span class="hljs-number">1</span>, CV_32F); <span class="hljs-comment">//measurement(x,y) </span><br> <br> <br> <span class="hljs-built_in">randn</span>( state, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0</span>), Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0.1</span>) ); <span class="hljs-comment">//随机生成一个矩阵,期望是0,标准差为0.1; </span><br> KF.transitionMatrix = *(<span class="hljs-built_in">Mat_</span><<span class="hljs-type">float</span>>(<span class="hljs-number">4</span>, <span class="hljs-number">4</span>) << <br> <span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>, <br> <span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span> );<span class="hljs-comment">//元素导入矩阵,按行; </span><br> <br> <span class="hljs-comment">//setIdentity: 缩放的单位对角矩阵; </span><br> <span class="hljs-comment">//!< measurement matrix (H) 观测模型 </span><br> <span class="hljs-built_in">setIdentity</span>(KF.measurementMatrix); <br> <br> <span class="hljs-comment">//!< process noise covariance matrix (Q) </span><br> <span class="hljs-comment">// wk 是过程噪声,并假定其符合均值为零,协方差矩阵为Qk(Q)的多元正态分布; </span><br> <span class="hljs-built_in">setIdentity</span>(KF.processNoiseCov, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">1e-5</span>)); <br> <br> <span class="hljs-comment">//!< measurement noise covariance matrix (R) </span><br> <span class="hljs-comment">//vk 是观测噪声,其均值为零,协方差矩阵为Rk,且服从正态分布; </span><br> <span class="hljs-built_in">setIdentity</span>(KF.measurementNoiseCov, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">1e-1</span>)); <br> <br> <span class="hljs-comment">//!< priori error estimate covariance matrix (P'(k)): P'(k)=A*P(k-1)*At + Q)*/ A代表F: transitionMatrix </span><br> <span class="hljs-comment">//预测估计协方差矩阵; </span><br> <span class="hljs-built_in">setIdentity</span>(KF.errorCovPost, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">1</span>)); <br> <br> <br> <span class="hljs-comment">//!< corrected state (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) </span><br> <span class="hljs-comment">//initialize post state of kalman filter at random </span><br> <span class="hljs-built_in">randn</span>(KF.statePost, Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0</span>), Scalar::<span class="hljs-built_in">all</span>(<span class="hljs-number">0.1</span>)); <br> <span class="hljs-function">Mat <span class="hljs-title">showImg</span><span class="hljs-params">(winWidth, winHeight,CV_8UC3)</span></span>; <br> <br> <span class="hljs-keyword">for</span>(;;) <br> { <br> <span class="hljs-built_in">setMouseCallback</span>(<span class="hljs-string">"Kalman"</span>, mouseEvent); <br> showImg.<span class="hljs-built_in">setTo</span>(<span class="hljs-number">0</span>); <br> <br> Point statePt = <span class="hljs-built_in">Point</span>( (<span class="hljs-type">int</span>)KF.statePost.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">0</span>), (<span class="hljs-type">int</span>)KF.statePost.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">1</span>)); <br> <br> <span class="hljs-comment">//2.kalman prediction </span><br> Mat prediction = KF.<span class="hljs-built_in">predict</span>(); <br> Point predictPt = <span class="hljs-built_in">Point</span>( (<span class="hljs-type">int</span>)prediction.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">0</span>), (<span class="hljs-type">int</span>)prediction.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">1</span>)); <br> <br> <span class="hljs-comment">//3.update measurement </span><br> measurement.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">0</span>)= (<span class="hljs-type">float</span>)mousePosition.x; <br> measurement.<span class="hljs-built_in">at</span><<span class="hljs-type">float</span>>(<span class="hljs-number">1</span>) = (<span class="hljs-type">float</span>)mousePosition.y; <br> <br> <span class="hljs-comment">//4.update </span><br> KF.<span class="hljs-built_in">correct</span>(measurement); <br> <br> <span class="hljs-comment">//randn( processNoise, Scalar(0), Scalar::all(sqrt(KF.processNoiseCov.at<float>(0, 0)))); </span><br> <span class="hljs-comment">//state = KF.transitionMatrix*state + processNoise; </span><br> <span class="hljs-comment">//draw </span><br> <span class="hljs-built_in">circle</span>(showImg, statePt, <span class="hljs-number">5</span>, <span class="hljs-built_in">CV_RGB</span>(<span class="hljs-number">255</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>),<span class="hljs-number">1</span>);<span class="hljs-comment">//former point </span><br> <span class="hljs-built_in">circle</span>(showImg, predictPt, <span class="hljs-number">5</span>, <span class="hljs-built_in">CV_RGB</span>(<span class="hljs-number">0</span>,<span class="hljs-number">255</span>,<span class="hljs-number">0</span>),<span class="hljs-number">1</span>);<span class="hljs-comment">//predict point </span><br> <span class="hljs-built_in">circle</span>(showImg, mousePosition, <span class="hljs-number">5</span>, <span class="hljs-built_in">CV_RGB</span>(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">255</span>),<span class="hljs-number">1</span>);<span class="hljs-comment">//ture point </span><br> <br> <br><span class="hljs-comment">// CvFont font;//字体 </span><br><span class="hljs-comment">// cvInitFont(&font, CV_FONT_HERSHEY_SCRIPT_COMPLEX, 0.5f, 0.5f, 0, 1, 8); </span><br> <span class="hljs-built_in">putText</span>(showImg, <span class="hljs-string">"Red: Former Point"</span>, <span class="hljs-built_in">cvPoint</span>(<span class="hljs-number">10</span>,<span class="hljs-number">30</span>), FONT_HERSHEY_SIMPLEX, <span class="hljs-number">1</span> ,Scalar :: <span class="hljs-built_in">all</span>(<span class="hljs-number">255</span>)); <br> <span class="hljs-built_in">putText</span>(showImg, <span class="hljs-string">"Green: Predict Point"</span>, <span class="hljs-built_in">cvPoint</span>(<span class="hljs-number">10</span>,<span class="hljs-number">60</span>), FONT_HERSHEY_SIMPLEX, <span class="hljs-number">1</span> ,Scalar :: <span class="hljs-built_in">all</span>(<span class="hljs-number">255</span>)); <br> <span class="hljs-built_in">putText</span>(showImg, <span class="hljs-string">"Blue: Ture Point"</span>, <span class="hljs-built_in">cvPoint</span>(<span class="hljs-number">10</span>,<span class="hljs-number">90</span>), FONT_HERSHEY_SIMPLEX, <span class="hljs-number">1</span> ,Scalar :: <span class="hljs-built_in">all</span>(<span class="hljs-number">255</span>)); <br> <br> <span class="hljs-built_in">imshow</span>( <span class="hljs-string">"Kalman"</span>, showImg ); <br> <span class="hljs-type">int</span> key = <span class="hljs-built_in">waitKey</span>(<span class="hljs-number">3</span>); <br> <span class="hljs-keyword">if</span> (key == <span class="hljs-number">27</span>) <br> { <br> <span class="hljs-keyword">break</span>; <br> } <br> } <br>} <br></code></pre></td></tr></table></figure><p><img src="/2017/03/26/Opencv%E5%AE%9E%E7%8E%B0Kalman%E6%BB%A4%E6%B3%A2%E5%99%A8/out2.JPG" alt="output2"></p><h1 id="主要参考"><a href="#主要参考" class="headerlink" title="主要参考"></a>主要参考</h1><p>1.<a href="http://en.wikipedia.org/wiki/Kalman_filter">维基百科(英文)</a><a href="http://zh.wikipedia.org/wiki/%E5%8D%A1%E5%B0%94%E6%9B%BC%E6%BB%A4%E6%B3%A2#.E5.8D.A1.E5.B0.94.E6.9B.BC.E6.BB.A4.E6.B3.A2.E5.99.A8">维基百科(中文)</a><br>2.<a href="http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=kalman#kalmanfilter">OpenCV文档</a><br>3.<a href="http://www.cnblogs.com/feisky/archive/2009/12/04/1617287.html">博客园kalman详解blog</a><br>4.<a href="http://blog.csdn.net/yang_xian521/article/details/7050398">CSDN kalman.cpp讲解yangxian</a><br>5.<a href="http://blog.csdn.net/onezeros/article/details/6318944">CSDN 鼠标跟踪</a><br>6.<a href="http://www.cs.unc.edu/~welch/media/pdf/kalman_intro.pdf">模型论文</a><br>7.<a href="http://blog.csdn.net/yangtrees/article/details/8075911">Kalman-Opencv</a><br>8.<a href="http://robotsforroboticists.com/kalman-filtering/">公式彩色着色,含pyhton源码</a><br>9.<a href="http://alumni.media.mit.edu/~wad/mas864/psrc/kalman.c.txt">Kalman滤波的C代码</a><br>10.<a href="http://www.cs.unc.edu/~welch/kalman/">比较全的Kalman链接</a><br>11.<a href="http://blog.sina.com.cn/s/blog_7445c2940102whjq.html">卡尔曼滤波器(Kalman Filtering)入门</a><br>12.<a href="http://blog.csdn.net/xiahouzuoxin/article/details/39582483">Kalman滤波器从原理到实现</a><br>13.<a href="http://bbs.21ic.com/icview-292853-1-1.html">授之以渔: 卡尔曼滤波器 ….大泻蜜</a><br>14.<a href="http://blog.csdn.net/heyijia0327/article/details/17487467">卡尔曼滤波 – 从推导到应用(一)</a><br>15.<a href="http://blog.csdn.net/heyijia0327/article/details/17667341">卡尔曼滤波 – 从推导到应用(二)</a></p>]]></content>
<categories>
<category>Opencv</category>
</categories>
<tags>
<tag>Opencv</tag>
<tag>Kalman</tag>
</tags>
</entry>
<entry>
<title>RoboticsToolbox使用基本教程</title>
<link href="/2017/03/22/RoboticsToolbox%E4%BD%BF%E7%94%A8%E5%9F%BA%E6%9C%AC%E6%95%99%E7%A8%8B/"/>
<url>/2017/03/22/RoboticsToolbox%E4%BD%BF%E7%94%A8%E5%9F%BA%E6%9C%AC%E6%95%99%E7%A8%8B/</url>
<content type="html"><![CDATA[<h2 id="安装Robotics-Toolbox-for-MATLAB"><a href="#安装Robotics-Toolbox-for-MATLAB" class="headerlink" title="安装Robotics Toolbox for MATLAB"></a>安装Robotics Toolbox for MATLAB</h2><p>1、下载该工具箱<br>2、将压缩包解压到一个文件夹下面<br>3、打开MATLAB,在File菜单下选择Set Path,打开如下对话框<br>4、单击“Add With SubFolder”,选择上面的工具箱<br>5、点击“Save”,然后点击“Close”。这样就把工具箱的路径添加到MATLAB的路径中了,也就是工具箱安装了。</p><h2 id="PUMA560的MATLAB仿真"><a href="#PUMA560的MATLAB仿真" class="headerlink" title="PUMA560的MATLAB仿真"></a>PUMA560的MATLAB仿真</h2><p>要建立PUMA560的机器人对象,首先我们要了解PUMA560的D-H参数,之后我们可以利用Robotics Toolbox工具箱中的link和robot函数来建立PUMA560的机器人对象。<br>其中link函数的调用格式:</p><blockquote><ul><li>L = LINK([alpha A theta D])</li><li>L =LINK([alpha A theta D sigma])</li><li>L =LINK([alpha A theta D sigma offset])</li><li>L =LINK([alpha A theta D], CONVENTION)</li><li>L =LINK([alpha A theta D sigma], CONVENTION)</li><li>L =LINK([alpha A theta D sigma offset], CONVENTION)</li></ul></blockquote><p>参数CONVENTION可以取‘standard’和‘modified’,其中‘standard’代表采用标准的D-H参数,‘modified’代表采用改进的D-H参数。参数‘alpha’代表扭转角 ,参数‘A’代表杆件长度,参数‘theta’代表关节角,参数‘D’代表横距,参数‘sigma’代表关节类型:0代表旋转关节,非0代表移动关节。另外LINK还有一些数据域:</p><blockquote><ul><li><div class="code-wrapper"><pre><code class="hljs">LINK.alpha %返回扭转角</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.A %返回杆件长度</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.theta %返回关节角</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.D %返回横距</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.sigma %返回关节类型</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.RP %返回‘R’(旋转)或‘P’(移动)</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.mdh %若为标准D-H参数返回0,否则返回1</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.offset %返回关节变量偏移</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.qlim %返回关节变量的上下限 [min max]</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.islimit(q) %如果关节变量超限,返回 -1, 0, +1</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.I %返回一个3×3 对称惯性矩阵</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.m %返回关节质量</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.r %返回3×1的关节齿轮向量</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.G %返回齿轮的传动比</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.Jm %返回电机惯性</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.B %返回粘性摩擦</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.Tc %返回库仑摩擦</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.dh return legacy DH row</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">LINK.dyn return legacy DYN row</code></pre></div></li></ul></blockquote><p>其中robot函数的调用格式:</p><blockquote><ul><li><div class="code-wrapper"><pre><code class="hljs">ROBOT %创建一个空的机器人对象</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">ROBOT(robot) %创建robot的一个副本</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">ROBOT(robot, LINK) %用LINK来创建新机器人对象来代替robot</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">ROBOT(LINK, ...) %用LINK来创建一个机器人对象</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">ROBOT(DH, ...) %用D-H矩阵来创建一个机器人对象</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">ROBOT(DYN, ...) %用DYN矩阵来创建一个机器人对象</code></pre></div></li></ul></blockquote><p>利用MATLAB中Robotics Toolbox工具箱中的transl、rotx、roty和rotz可以实现用齐次变换矩阵表示平移变换和旋转变换。下面举例来说明:<br>A 机器人在x轴方向平移了0.5米,那么我们可以用下面的方法来求取平移变换后的齐次矩阵:</p><figure class="highlight matlab"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></div></td><td class="code"><pre><code class="hljs matlab">>> transl(<span class="hljs-number">0.5</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>)<br><span class="hljs-built_in">ans</span> =<br> <span class="hljs-number">1.0000</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0.5000</span><br> <span class="hljs-number">0</span> <span class="hljs-number">1.0000</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span><br> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1.0000</span> <span class="hljs-number">0</span><br> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1.0000</span><br></code></pre></td></tr></table></figure><p>B 机器人绕x轴旋转45度,那么可以用rotx来求取旋转后的齐次矩阵:</p><figure class="highlight matlab"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs matlab">>> rotx(<span class="hljs-built_in">pi</span>/<span class="hljs-number">4</span>)<br><span class="hljs-built_in">ans</span> =<br> <span class="hljs-number">1.0000</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span><br> <span class="hljs-number">0</span> <span class="hljs-number">0.7071</span> <span class="hljs-number">-0.7071</span> <span class="hljs-number">0</span><br> <span class="hljs-number">0</span> <span class="hljs-number">0.7071</span> <span class="hljs-number">0.7071</span> <span class="hljs-number">0</span><br> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1.0000</span><br></code></pre></td></tr></table></figure><p>C 机器人绕y轴旋转90度,那么可以用roty来求取旋转后的齐次矩阵:</p><figure class="highlight matlab"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs matlab">>> roty(<span class="hljs-built_in">pi</span>/<span class="hljs-number">2</span>)<br><span class="hljs-built_in">ans</span> =<br> <span class="hljs-number">0.0000</span> <span class="hljs-number">0</span> <span class="hljs-number">1.0000</span> <span class="hljs-number">0</span><br> <span class="hljs-number">0</span> <span class="hljs-number">1.0000</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span><br> <span class="hljs-number">-1.0000</span> <span class="hljs-number">0</span> <span class="hljs-number">0.0000</span> <span class="hljs-number">0</span><br> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1.0000</span><br></code></pre></td></tr></table></figure><p>D 机器人绕z轴旋转-90度,那么可以用rotz来求取旋转后的齐次矩阵:</p><figure class="highlight matlab"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs matlab">>> rotz(-<span class="hljs-built_in">pi</span>/<span class="hljs-number">2</span>)<br><span class="hljs-built_in">ans</span> =<br> <span class="hljs-number">0.0000</span> <span class="hljs-number">1.0000</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span><br> <span class="hljs-number">-1.0000</span> <span class="hljs-number">0.0000</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span><br> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1.0000</span> <span class="hljs-number">0</span><br> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1.0000</span><br></code></pre></td></tr></table></figure><p>当然,如果有多次旋转和平移变换,我们只需要多次调用函数在组合就可以了。另外,可以和我们学习的平移矩阵和旋转矩阵做个对比,相信是一致的。</p><h2 id="轨迹规划"><a href="#轨迹规划" class="headerlink" title="轨迹规划"></a>轨迹规划</h2><p>利用Robotics Toolbox提供的ctraj、jtraj和trinterp函数可以实现笛卡尔规划、关节空间规划和变换插值。<br>其中ctraj函数的调用格式:</p><blockquote><ul><li><div class="code-wrapper"><pre><code class="hljs"> TC = CTRAJ(T0, T1, N)</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs"> TC = CTRAJ(T0, T1, R)</code></pre></div></li></ul></blockquote><p>参数TC为从T0到T1的笛卡尔规划轨迹,N为点的数量,R为给定路径距离向量,R的每个值必须在0到1之间。<br>其中jtraj函数的调用格式:</p><blockquote><ul><li><div class="code-wrapper"><pre><code class="hljs"> [Q QD QDD] = JTRAJ(Q0, Q1, N)</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs"> [Q QD QDD] = JTRAJ(Q0, Q1, N, QD0, QD1)</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs"> [Q QD QDD] = JTRAJ(Q0, Q1, T)</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs"> [Q QD QDD] = JTRAJ(Q0, Q1, T, QD0, QD1)</code></pre></div></li></ul></blockquote><p>参数Q为从状态Q0到Q1的关节空间规划轨迹,N为规划的点数,T为给定的时间向量的长度,速度非零边界可以用QD0和QD1来指定。QD和QDD为返回的规划轨迹的速度和加速度。<br>其中trinterp函数的调用格式:<br><code>TR = TRINTERP(T0, T1, R)</code><br>参数TR为在T0和T1之间的坐标变化插值,R需在0和1之间。<br>要实现轨迹规划,首先我们要创建一个时间向量,假设在两秒内完成某个动作,采样间隔是56ms,那么可以用如下的命令来实现多项式轨迹规划:<code>t=0:0.056:2; [q,qd,qdd]=jtraj(qz,qr,t);</code><br>其中t为时间向量,qz为机器人的初始位姿,qr为机器人的最终位姿,q为经过的路径点,qd为运动的速度,qdd为运动的加速度。其中q、qd、qdd都是六列的矩阵,每列代表每个关节的位置、速度和加速度。如q(:,3)代表关节3的位置,qd(:,3)代表关节3的速度,qdd(:,3)代表关节3的加速度。</p><h2 id="运动学的正问题"><a href="#运动学的正问题" class="headerlink" title="运动学的正问题"></a>运动学的正问题</h2><p>利用Robotics Toolbox中的fkine函数可以实现机器人运动学正问题的求解。<br>其中fkine函数的调用格式:<br><code>TR = FKINE(ROBOT, Q)</code><br>参数ROBOT为一个机器人对象,TR为由Q定义的每个前向运动学的正解。<br>以PUMA560为例,定义关节坐标系的零点qz=[0 0 0 0 0 0],那么fkine(p560,qz)将返回最后一个关节的平移的齐次变换矩阵。如果有了关节的轨迹规划之后,我们也可以用fkine来进行运动学的正解。比如:<br><code>t=0:0.056:2; q=jtraj(qz,qr,t); T=fkine(p560,q);</code><br>返回的矩阵T是一个三维的矩阵,前两维是4×4的矩阵代表坐标变化,第三维是时间。</p><h2 id="运动学的逆问题"><a href="#运动学的逆问题" class="headerlink" title="运动学的逆问题"></a>运动学的逆问题</h2><p>利用Robotics Toolbox中的ikine函数可以实现机器人运动学逆问题的求解。<br>其中ikine函数的调用格式:</p><blockquote><ul><li><div class="code-wrapper"><pre><code class="hljs">Q = IKINE(ROBOT, T)</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">Q = IKINE(ROBOT, T, Q)</code></pre></div></li><li><div class="code-wrapper"><pre><code class="hljs">Q = IKINE(ROBOT, T, Q, M)</code></pre></div></li></ul></blockquote><p>参数ROBOT为一个机器人对象,Q为初始猜测点(默认为0),T为要反解的变换矩阵。当反解的机器人对象的自由度少于6时,要用M进行忽略某个关节自由度。<br>有了关节的轨迹规划之后,我们也可以用ikine函数来进行运动学逆问题的求解。比如:<br><code>t=0:0.056:2; T1=transl(0.6,-0.5,0); T2=transl(0.4,0.5,0.2); T=ctraj(T1,T2,length(t)); q=ikine(p560,T);</code><br>我们也可以尝试先进行正解,再进行逆解,看看能否还原。<br><code>Q=[0 –pi/4 –pi/4 0 pi/8 0]; T=fkine(p560,q); qi=ikine(p560,T);</code></p><h2 id="动画演示"><a href="#动画演示" class="headerlink" title="动画演示"></a>动画演示</h2><p>有了机器人的轨迹规划之后,我们就可以利用Robotics Toolbox中的plot函数来实现对规划路径的仿真。<br><code>puma560;T=0:0.056:2; q=jtraj(qz,qr,T); plot(p560,q);</code><br>当然,我们也可以来调节PUMA560的六个旋转角,来实现动画演示。<br><code>drivebot(p560)</code></p>]]></content>
<categories>
<category>机械臂</category>
</categories>
<tags>
<tag>机械臂</tag>
</tags>
</entry>
<entry>
<title>Cpp基础补全</title>
<link href="/2017/03/20/Cpp%E5%9F%BA%E7%A1%80%E8%A1%A5%E5%85%A8/"/>
<url>/2017/03/20/Cpp%E5%9F%BA%E7%A1%80%E8%A1%A5%E5%85%A8/</url>
<content type="html"><![CDATA[<h1 id="getline"><a href="#getline" class="headerlink" title="getline"></a>getline</h1><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp">std::string line;<br><span class="hljs-built_in">getline</span>(cin,line);<br></code></pre></td></tr></table></figure><h1 id="scope"><a href="#scope" class="headerlink" title="scope"></a>scope</h1><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">using</span> std::string<br>uisng std::cout<br><span class="hljs-keyword">using</span> std::cin<br></code></pre></td></tr></table></figure><h1 id="struct"><a href="#struct" class="headerlink" title="struct"></a>struct</h1><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Sales_data</span><br>{<br> <span class="hljs-comment">/**/</span><br>}accum, *trans;<br></code></pre></td></tr></table></figure><h1 id="auto-decltype"><a href="#auto-decltype" class="headerlink" title="auto decltype"></a>auto decltype</h1><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">auto</span> a = <span class="hljs-number">0</span>; <span class="hljs-comment">//(a -- int型 )</span><br><span class="hljs-keyword">decltype</span>(a) b;<span class="hljs-comment">//(未初始化的int型b)</span><br><span class="hljs-keyword">decltype</span>((a)) c = <span class="hljs-number">0</span>;<span class="hljs-comment">//(c为初始化为0的int&, 引用, 必须初始化)</span><br></code></pre></td></tr></table></figure><h1 id="typedef-using"><a href="#typedef-using" class="headerlink" title="typedef using"></a>typedef using</h1><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-keyword">typedef</span> <span class="hljs-type">double</span> wages <span class="hljs-comment">//wages是double的同义词</span><br><span class="hljs-keyword">typedef</span> wages base, *p <span class="hljs-comment">//wages base等于double base,wages *p为 double *p的同义词</span><br><br><span class="hljs-keyword">using</span> SI = Sales_data;<br>SI item; <span class="hljs-comment">//等价于 Sales_data item;</span><br></code></pre></td></tr></table></figure><h1 id="string"><a href="#string" class="headerlink" title="string"></a>string</h1><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs cpp">std::string::size_type len = line.<span class="hljs-built_in">size</span>();<br><br>std::string s1 = std::<span class="hljs-built_in">string</span>(<span class="hljs-number">10</span>,<span class="hljs-string">"c"</span>);<br><br>std::string s2 = s1 + <span class="hljs-string">"World"</span>; <span class="hljs-comment">//必须确保每个加法运算符两侧的运算对象至少有一个是string</span><br><br><span class="hljs-function">std::string <span class="hljs-title">str</span><span class="hljs-params">(<span class="hljs-string">"some string"</span>)</span></span>;<br><span class="hljs-keyword">for</span>(<span class="hljs-keyword">auto</span> c : str)<br> std::cout << c << std::endl;<br></code></pre></td></tr></table></figure><table><thead><tr><th>函数名称</th><th>返回值</th></tr></thead><tbody><tr><td>isalnum()</td><td>如果参数是字母数字,即字母或数字,该函数返回true</td></tr><tr><td>isalpha()</td><td>如果参数是字母,该函数返回真</td></tr><tr><td>isblank()</td><td>如果参数是空格或水平制表符,该函数返回true</td></tr><tr><td>iscntrl()</td><td>如果参数是控制字符,该函数返回true</td></tr><tr><td>isdigit()</td><td>如果参数是数字(0~9),该函数返回true</td></tr><tr><td>isgraph()</td><td>如果参数是除空格之外的打印字符,该函数返回true</td></tr><tr><td>islower()</td><td>如果参数是小写字母,该函数返回true</td></tr><tr><td>isprint()</td><td>如果参数是打印字符(包括空格),该函数返回true</td></tr><tr><td>ispunct()</td><td>如果参数是标点符号,该函数返回true</td></tr><tr><td>isspace()</td><td>如果参数是标准空白字符,如空格、进纸、换行符、回车、水平制表符或者垂直制表符,该函数返回true</td></tr><tr><td>isupper()</td><td>如果参数是大写字母,该函数返回true</td></tr><tr><td>isxdigit()</td><td>如果参数是十六进制的数字,即0~9、a<del>f、A</del>F,该函数返回true</td></tr><tr><td>tolower()</td><td>如果参数是大写字符,则返回其小写,否则返回该参数</td></tr><tr><td>toupper()</td><td>如果参数是小写字母,则返回其大写,否则返回该参数</td></tr></tbody></table>]]></content>
<categories>
<category>Cpp</category>
</categories>
<tags>
<tag>Cpp</tag>
</tags>
</entry>
<entry>
<title>Ubuntu16-04安装L2TP</title>
<link href="/2017/03/11/Ubuntu16-04%E5%AE%89%E8%A3%85L2TP/"/>
<url>/2017/03/11/Ubuntu16-04%E5%AE%89%E8%A3%85L2TP/</url>
<content type="html"><![CDATA[<p><a href="http://blog.z-proj.com/enabling-l2tp-over-ipsec-on-ubuntu-16-04/">Enabling L2TP over IPSec on Ubuntu 16.04</a></p><h1 id="安装依赖"><a href="#安装依赖" class="headerlink" title="安装依赖"></a>安装依赖</h1><figure class="highlight bash"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><pre><code class="hljs bash">sudo apt install intltool libtool network-manager-dev libnm-util-dev libnm-glib-dev libnm-glib-vpn-dev libnm-gtk-dev libnm-dev libnma-dev ppp-dev libdbus-glib-1-dev libsecret-1-dev libgtk-3-dev libglib2.0-dev xl2tpd strongswan <br></code></pre></td></tr></table></figure><h1 id="Build网络管理工具"><a href="#Build网络管理工具" class="headerlink" title="Build网络管理工具"></a>Build网络管理工具</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> https://github.com/nm-l2tp/network-manager-l2tp.git <br><span class="hljs-built_in">cd</span> network-manager-l2tp <br>autoreconf -<span class="hljs-keyword">fi</span> <br>intltoolize <br></code></pre></td></tr></table></figure><p>确保没有错误发生,如果发生错误,Google解决之。</p><h1 id="配置Build"><a href="#配置Build" class="headerlink" title="配置Build"></a>配置Build</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash">./configure \<br> --disable-static --prefix=/usr \<br> --sysconfdir=/etc --libdir=/usr/lib/x86_64-linux-gnu \<br> --libexecdir=/usr/lib/NetworkManager \<br> --localstatedir=/var \<br> --with-pppd-plugin-dir=/usr/lib/pppd/2.4.7<br></code></pre></td></tr></table></figure><p>确保没有错误发生,如果发生错误,Google解决之。</p><h1 id="Make"><a href="#Make" class="headerlink" title="Make"></a>Make</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">make <br>sudo make install<br></code></pre></td></tr></table></figure><h1 id="IPSec移除AppArmor设置"><a href="#IPSec移除AppArmor设置" class="headerlink" title="IPSec移除AppArmor设置"></a>IPSec移除AppArmor设置</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">sudo apparmor_parser -R /etc/apparmor.d/usr.lib.ipsec.charon <br>sudo apparmor_parser -R /etc/apparmor.d/usr.lib.ipsec.stroke <br></code></pre></td></tr></table></figure><h1 id="用libpcap替换x2ltpd"><a href="#用libpcap替换x2ltpd" class="headerlink" title="用libpcap替换x2ltpd"></a>用libpcap替换x2ltpd</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash">sudo apt remove xl2tpd <br>sudo apt install libpcap0.8-dev<br><br>wget https://github.com/xelerance/xl2tpd/archive/v1.3.6/xl2tpd-1.3.6.tar.gz <br>tar xvzf xl2tpd-1.3.6.tar.gz <br><span class="hljs-built_in">cd</span> xl2tpd-1.3.6 <br>make <br>sudo make install <br></code></pre></td></tr></table></figure><h1 id="重启系统"><a href="#重启系统" class="headerlink" title="重启系统"></a>重启系统</h1><p>重启后网络管理里面就会有L2TP选项了。<br><img src="http://blog.z-proj.com/content/images/2016/08/Screenshot-from-2016-08-22-20-54-20.png" alt="Add Network Connection"></p>]]></content>
<categories>
<category>Linux</category>
</categories>
<tags>
<tag>ubuntu</tag>
<tag>L2TP</tag>
</tags>
</entry>
<entry>
<title>结巴Jieba分词</title>
<link href="/2017/03/11/%E7%BB%93%E5%B7%B4Jieba%E5%88%86%E8%AF%8D/"/>
<url>/2017/03/11/%E7%BB%93%E5%B7%B4Jieba%E5%88%86%E8%AF%8D/</url>
<content type="html"><![CDATA[<p><a href="https://github.com/fxsjy/jieba">Jieba-Github</a></p><h1 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h1><ul><li><p>支持三种分词模式:</p><blockquote><ul><li>精确模式,试图将句子最精确地切开,适合文本分析; </li><li>全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义; </li><li>搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。</li></ul></blockquote></li><li><p>支持繁体分词</p></li><li><p>支持自定义词典</p></li></ul><h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><h2 id="Python-2-x-下的安装"><a href="#Python-2-x-下的安装" class="headerlink" title="Python 2.x 下的安装"></a>Python 2.x 下的安装</h2><p>全自动安装 :easy_install jieba 或者 pip install jieba<br>半自动安装 :先下载<a href="http://pypi.python.org/pypi/jieba/">http://pypi.python.org/pypi/jieba/</a> ,解压后运行python setup.py install<br>手动安装 :将jieba目录放置于当前目录或者site-packages目录<br>通过<strong>import jieba</strong>来引用</p><h2 id="Python-3-x-下的安装"><a href="#Python-3-x-下的安装" class="headerlink" title="Python 3.x 下的安装"></a>Python 3.x 下的安装</h2><p>目前master分支是只支持Python2.x 的<br><a href="https://github.com/fxsjy/jieba/tree/jieba3k">Python3.x</a>版本的分支也已经基本可用.</p><figure class="highlight bash"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></div></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> https://github.com/fxsjy/jieba.git<br>git checkout jieba3k<br>python setup.py install<br>````<br><span class="hljs-comment"># 算法实现:</span><br>基于Trie树结构实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图(DAG) <br>采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合 <br>对于未登录词,采用了基于汉字成词能力的HMM模型,使用了Viterbi算法<br><br><span class="hljs-comment"># 功能</span><br>jieba具有四大功能,**分词**,**添加自定义词典**,**关键词提取**,**词性标注**,**并行分词**。<br><span class="hljs-comment">## 分词</span><br>jieba.cut方法接受两个输入参数:<br>> * 第一个参数为需要分词的字符串 <br>> * cut_all参数用来控制是否采用全模式 <br>jieba.cut_for_search方法接受一个参数:需要分词的字符串,该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细 <br>注意:待分词的字符串可以是gbk字符串、utf-8字符串或者unicode <br>jieba.cut以及jieba.cut_for_search返回的结构都是一个可迭代的generator,可以使用<span class="hljs-keyword">for</span>循环来获得分词后得到的每一个词语(unicode),也可以用list(jieba.cut(...))转化为list <br><br>代码示例( 分词 )<br>```python<br><span class="hljs-comment">#encoding=utf-8</span><br>import jieba<br>seg_list = jieba.cut(<span class="hljs-string">"我来到北京清华大学"</span>, cut_all=True)<br><span class="hljs-built_in">print</span> <span class="hljs-string">"Full Mode:"</span>, <span class="hljs-string">"/ "</span>.<span class="hljs-built_in">join</span>(seg_list) <span class="hljs-comment"># 全模式</span><br>seg_list = jieba.cut(<span class="hljs-string">"我来到北京清华大学"</span>, cut_all=False)<br><span class="hljs-built_in">print</span> <span class="hljs-string">"Default Mode:"</span>, <span class="hljs-string">"/ "</span>.<span class="hljs-built_in">join</span>(seg_list) <span class="hljs-comment"># 精确模式</span><br>seg_list = jieba.cut(<span class="hljs-string">"他来到了网易杭研大厦"</span>) <span class="hljs-comment"># 默认是精确模式</span><br><span class="hljs-built_in">print</span> <span class="hljs-string">", "</span>.<span class="hljs-built_in">join</span>(seg_list)<br>seg_list = jieba.cut_for_search(<span class="hljs-string">"小明硕士毕业于中国科学院计算所,后在日本京都大学深造"</span>) <span class="hljs-comment"># 搜索引擎模式</span><br><span class="hljs-built_in">print</span> <span class="hljs-string">", "</span>.<span class="hljs-built_in">join</span>(seg_list)<br></code></pre></td></tr></table></figure><p>Output: </p><ul><li>全模式: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学 </li><li>精确模式: 我/ 来到/ 北京/ 清华大学 </li><li>新词识别:他, 来到, 了, 网易, 杭研, 大厦 (此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了) </li><li>搜索引擎模式: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造</li></ul><h2 id="添加自定义词典"><a href="#添加自定义词典" class="headerlink" title="添加自定义词典"></a>添加自定义词典</h2><p>开发者可以指定自己自定义的词典,以便包含jieba词库里没有的词。虽然jieba有新词识别能力,但是自行添加新词可以保证更高的正确率 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">jieba.load_userdict(file_name) <span class="hljs-comment"># file_name为自定义词典的路径</span><br></code></pre></td></tr></table></figure><p>词典格式和dict.txt一样,一个词占一行;每一行分三部分,一部分为词语,另一部分为词频,最后为词性(可省略),用空格隔开<br><strong>范例</strong>:<br>自定义词典:</p><blockquote><p>云计算 5<br>李小福 2 nr<br>创新办 3 i<br>easy_install 3 eng<br>好用 300<br>韩玉赏鉴 3 nz</p></blockquote><p><strong>用法示例</strong>:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment">#encoding=utf-8</span><br><span class="hljs-keyword">import</span> sys<br>sys.path.append(<span class="hljs-string">"../"</span>)<br><span class="hljs-keyword">import</span> jieba<br>jieba.load_userdict(<span class="hljs-string">"userdict.txt"</span>)<br><span class="hljs-keyword">import</span> jieba.posseg <span class="hljs-keyword">as</span> pseg<br><br>test_sent = <span class="hljs-string">"李小福是创新办主任也是云计算方面的专家;"</span><br>test_sent += <span class="hljs-string">"例如我输入一个带“韩玉赏鉴”的标题,在自定义词库中也增加了此词为N类型"</span><br>words = jieba.cut(test_sent)<br><span class="hljs-keyword">for</span> w <span class="hljs-keyword">in</span> words:<br><span class="hljs-built_in">print</span> w<br><br>result = pseg.cut(test_sent)<br><br><span class="hljs-keyword">for</span> w <span class="hljs-keyword">in</span> result:<br><span class="hljs-built_in">print</span> w.word, <span class="hljs-string">"/"</span>, w.flag, <span class="hljs-string">", "</span>,<br><br><span class="hljs-built_in">print</span> <span class="hljs-string">"\n========"</span><br><br>terms = jieba.cut(<span class="hljs-string">'easy_install is great'</span>)<br><span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> terms:<br> <span class="hljs-built_in">print</span> t<br><span class="hljs-built_in">print</span> <span class="hljs-string">'-------------------------'</span><br>terms = jieba.cut(<span class="hljs-string">'python 的正则表达式是好用的'</span>)<br><span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> terms:<br> <span class="hljs-built_in">print</span> t<br></code></pre></td></tr></table></figure><p>之前: 李小福 / 是 / 创新 / 办 / 主任 / 也 / 是 / 云 / 计算 / 方面 / 的 / 专家 /<br>加载自定义词库后: 李小福 / 是 / 创新办 / 主任 / 也 / 是 / 云计算 / 方面 / 的 / 专家 /<br><strong><a href="https://github.com/fxsjy/jieba/issues/14">通过用户自定义词典来增强歧义纠错能力</a></strong></p><h2 id="关键词提取"><a href="#关键词提取" class="headerlink" title="关键词提取"></a>关键词提取</h2><p>jieba.analyse.extract_tags(sentence,topK) #需要先import jieba.analyse<br>说明</p><p>setence为待提取的文本</p><p>topK为返回几个TF/IDF权重最大的关键词,默认值为20<br>代码示例 (关键词提取)</p><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs vim">import sys<br>sys.path.<span class="hljs-keyword">append</span>(<span class="hljs-string">'../'</span>)<br><br>import jieba<br>import jieba.analyse<br>from optparse import OptionParser<br><br>USAGE = <span class="hljs-string">"usage: python extract_tags.py [file name] -k [top k]"</span><br><br>parser = OptionParser(USAGE)<br>parser.add_option(<span class="hljs-string">"-k"</span>, dest=<span class="hljs-string">"topK"</span>)<br><span class="hljs-keyword">opt</span>, <span class="hljs-keyword">args</span> = parser.parse_args()<br><br><br><span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(<span class="hljs-keyword">args</span>) < <span class="hljs-number">1</span>:<br> <span class="hljs-keyword">print</span> USAGE<br> sys.<span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>)<br><br>file_name = <span class="hljs-keyword">args</span>[<span class="hljs-number">0</span>]<br><br><span class="hljs-keyword">if</span> <span class="hljs-keyword">opt</span>.topK <span class="hljs-keyword">is</span> None:<br> topK = <span class="hljs-number">10</span><br><span class="hljs-keyword">else</span>:<br> topK = <span class="hljs-keyword">int</span>(<span class="hljs-keyword">opt</span>.topK)<br><br>content = <span class="hljs-keyword">open</span>(file_name, <span class="hljs-string">'rb'</span>).<span class="hljs-keyword">read</span>()<br><br><span class="hljs-keyword">tags</span> = jieba.analyse.extract_tags(content, topK=topK)<br><br><span class="hljs-keyword">print</span> <span class="hljs-string">","</span>.<span class="hljs-keyword">join</span>(<span class="hljs-keyword">tags</span>)<br></code></pre></td></tr></table></figure><h2 id="词性标注"><a href="#词性标注" class="headerlink" title="词性标注"></a>词性标注</h2><p>标注句子分词后每个词的词性,采用和ictclas兼容的标记法<br>用法示例</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-meta">>>> </span><span class="hljs-keyword">import</span> jieba.posseg <span class="hljs-keyword">as</span> pseg<br><span class="hljs-meta">>>> </span>words = pseg.cut(<span class="hljs-string">"我爱北京天安门"</span>)<br><span class="hljs-meta">>>> </span><span class="hljs-keyword">for</span> w <span class="hljs-keyword">in</span> words:<br><span class="hljs-meta">... </span> <span class="hljs-built_in">print</span> w.word, w.flag<br>...<br>我 r<br>爱 v<br>北京 ns<br>天安门 ns<br></code></pre></td></tr></table></figure><h2 id="并行分词"><a href="#并行分词" class="headerlink" title="并行分词"></a>并行分词</h2><p>原理:将目标文本按行分隔后,把各行文本分配到多个python进程并行分词,然后归并结果,从而获得分词速度的可观提升<br>基于python自带的multiprocessing模块,目前暂不支持windows<br>用法:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python">jieba.enable_parallel(<span class="hljs-number">4</span>) <span class="hljs-comment"># 开启并行分词模式,参数为并行进程数</span><br>jieba.disable_parallel() <span class="hljs-comment"># 关闭并行分词模式</span><br></code></pre></td></tr></table></figure><p>例子:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> urllib2<br><span class="hljs-keyword">import</span> sys,time<br><span class="hljs-keyword">import</span> sys<br>sys.path.append(<span class="hljs-string">"../../"</span>)<br><span class="hljs-keyword">import</span> jieba<br>jieba.enable_parallel(<span class="hljs-number">4</span>)<br><br>url = sys.argv[<span class="hljs-number">1</span>]<br>content = <span class="hljs-built_in">open</span>(url,<span class="hljs-string">"rb"</span>).read()<br>t1 = time.time()<br>words = <span class="hljs-built_in">list</span>(jieba.cut(content))<br><br>t2 = time.time()<br>tm_cost = t2-t1<br><br>log_f = <span class="hljs-built_in">open</span>(<span class="hljs-string">"1.log"</span>,<span class="hljs-string">"wb"</span>)<br><span class="hljs-keyword">for</span> w <span class="hljs-keyword">in</span> words:<br><span class="hljs-built_in">print</span> >> log_f, w.encode(<span class="hljs-string">"utf-8"</span>), <span class="hljs-string">"/"</span> ,<br><span class="hljs-built_in">print</span> <span class="hljs-string">'speed'</span> , <span class="hljs-built_in">len</span>(content)/tm_cost, <span class="hljs-string">" bytes/second"</span><br></code></pre></td></tr></table></figure><p>实验结果:在4核3.4GHz Linux机器上,对金庸全集进行精确分词,获得了1MB/s的速度,是单进程版的3.3倍。<br>其他词典</p><p>占用内存较小的词典文件 <a href="https://github.com/fxsjy/jieba/raw/master/extra_dict/dict.txt.small">https://github.com/fxsjy/jieba/raw/master/extra_dict/dict.txt.small</a><br>支持繁体分词更好的词典文件 <a href="https://github.com/fxsjy/jieba/raw/master/extra_dict/dict.txt.big">https://github.com/fxsjy/jieba/raw/master/extra_dict/dict.txt.big</a><br>下载你所需要的词典,然后覆盖jieba/dict.txt 即可或者用jieba.set_dictionary(‘data/dict.txt.big’)</p><h1 id="模块初始化机制的改变-lazy-load-(从0-28版本开始)"><a href="#模块初始化机制的改变-lazy-load-(从0-28版本开始)" class="headerlink" title="模块初始化机制的改变:lazy load (从0.28版本开始)"></a>模块初始化机制的改变:lazy load (从0.28版本开始)</h1><p>jieba采用延迟加载,”import jieba”不会立即触发词典的加载,一旦有必要才开始加载词典构建trie。如果你想手工初始jieba,也可以手动初始化。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> jieba<br>jieba.initialize() <span class="hljs-comment"># 手动初始化(可选)在0.28之前的版本是不能指定主词典的路径的,有了延迟加载机制后,你可以改变主词典的路径: </span><br>jieba.set_dictionary(<span class="hljs-string">'data/dict.txt.big'</span>)<br></code></pre></td></tr></table></figure><p>例子: </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment">#encoding=utf-8</span><br><span class="hljs-keyword">import</span> sys<br>sys.path.append(<span class="hljs-string">"../"</span>)<br><span class="hljs-keyword">import</span> jieba<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">cuttest</span>(<span class="hljs-params">test_sent</span>):<br>result = jieba.cut(test_sent)<br><span class="hljs-built_in">print</span> <span class="hljs-string">" "</span>.join(result)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">testcase</span>():<br>cuttest(<span class="hljs-string">"这是一个伸手不见五指的黑夜。我叫孙悟空,我爱北京,我爱Python和C++。"</span>)<br>cuttest(<span class="hljs-string">"我不喜欢日本和服。"</span>)<br>cuttest(<span class="hljs-string">"雷猴回归人间。"</span>)<br>cuttest(<span class="hljs-string">"工信处女干事每月经过下属科室都要亲口交代24口交换机等技术性器件的安装工作"</span>)<br>cuttest(<span class="hljs-string">"我需要廉租房"</span>)<br>cuttest(<span class="hljs-string">"永和服装饰品有限公司"</span>)<br>cuttest(<span class="hljs-string">"我爱北京天安门"</span>)<br>cuttest(<span class="hljs-string">"abc"</span>)<br>cuttest(<span class="hljs-string">"隐马尔可夫"</span>)<br>cuttest(<span class="hljs-string">"雷猴是个好网站"</span>)<br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:<br>testcase()<br>jieba.set_dictionary(<span class="hljs-string">"foobar.txt"</span>)<br><span class="hljs-built_in">print</span> <span class="hljs-string">"================================"</span><br>testcase()<br><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>自然语言处理</category>
</categories>
<tags>
<tag>分词</tag>
<tag>TTS</tag>
</tags>
</entry>
<entry>
<title>逻辑回归模型和ML入门</title>
<link href="/2017/03/10/%E9%80%BB%E8%BE%91%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B%E5%92%8CML%E5%85%A5%E9%97%A8/"/>
<url>/2017/03/10/%E9%80%BB%E8%BE%91%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B%E5%92%8CML%E5%85%A5%E9%97%A8/</url>
<content type="html"><![CDATA[<h1 id="简介-Introduction"><a href="#简介-Introduction" class="headerlink" title="简介(Introduction)"></a>简介(Introduction)</h1><p><strong>问题:</strong>肿瘤医院提供了数据,并希望我们确定患者是否有致命的恶性肿瘤或良性肿瘤。 这被称为分类问题。 为了帮助分类每个患者,我们给出他们的年龄和肿瘤的大小。 直观地,可以想象年轻的患者和患有小的良性肿瘤的患者不太可能患有恶性癌症。 数据集模拟此应用程序,其中每个观察结果是表示为点(在下面的图中)的患者,其中红色表示恶性,蓝色表示良性。 注意:这是一个学习的玩具示例,在现实生活中有大量来自不同测试(检查)源的特征和医生的经验,这些特征对患者的诊断(治疗)决定起作用。<br><img src="/2017/03/10/%E9%80%BB%E8%BE%91%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B%E5%92%8CML%E5%85%A5%E9%97%A8/cancer_data_plot.jpg" alt="image1"><br><strong>目标:</strong>我们的目标是训练一个分类器,可以根据两个特征(年龄和肿瘤大小)自动将任何患者标记为良性或恶性类别。 本次,我们将创建一个线性分类器,它是深度网络中的一块基石。<br><img src="/2017/03/10/%E9%80%BB%E8%BE%91%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B%E5%92%8CML%E5%85%A5%E9%97%A8/cancer_classify_plot.jpg" alt="image2"><br>在上图中,绿线表示从数据中学习的模型,并将蓝点与红点分离。<strong>注意:</strong>这个分类器确实犯错误,其中蓝色点在绿色线的错误一侧。 但是,有办法解决这个问题,既是用非线性分类来解决。<br><strong>方法:</strong>任何学习算法通常具有五个阶段。<strong>数据读取</strong>,<strong>数据预处理</strong>,<strong>模型创建</strong>,<strong>模型参数学习</strong>和<strong>模型评估</strong>(模型测试或模型预测)。</p><blockquote><ul><li>1.**数据读取(Data reading)**:我们生成的模拟数据集,每个样品具有指示年龄和肿瘤大小的两个特征(绘制如下)。</li><li>2.**数据预处理(Data Preprocessing)**:通常需要对尺寸或年龄等个别特征进行缩放。 通常,可以在0和1之间缩放数据。为了简单起见,本次不进行任何缩放(有关详细信息,请参阅:特征缩放)。</li><li>3.**模型创建(Model creation)**:本次我们创建一个基本的线性模型。</li><li>4.<strong>模型学习(Learing the model)<strong>:这也被称为训练。虽然拟合线性模型可以以各种方式(线性回归),在CNTK中,我们使用</strong>随机梯度下降</strong><a href="https://en.wikipedia.org/wiki/Stochastic_gradient_descent">SGD</a>。</li><li>5.**评估(Evaluation)**:这也被称为测试,其中采用具有未曾用于训练的已知标签(也称ground-truth)的数据集。 这使我们能够评估模型在现实世界(以前看不到的)观察中的表现。</li></ul></blockquote><h1 id="逻辑回归-Logistic-Regression"><a href="#逻辑回归-Logistic-Regression" class="headerlink" title="逻辑回归(Logistic Regression)"></a>逻辑回归(Logistic Regression)</h1><p><a href="https://en.wikipedia.org/wiki/Logistic_regression">逻辑回归(Logistic Regression)</a>是使用特征的线性加权组合并且预测不同类的概率的基本机器学习技术。 在我们的情况下,分类器将在[0,1]中生成概率,然后与阈值(例如0.5)进行比较以产生二进制标签(0或1)。显然,所示的方法可以容易地扩展到多个类。<br><img src="/2017/03/10/%E9%80%BB%E8%BE%91%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B%E5%92%8CML%E5%85%A5%E9%97%A8/logistic_neuron.jpg" alt="image3"><br>在上图中,来自不同输入特征的贡献被线性加权并且聚合。 所得的总和通过sigmoid函数映射到0-1范围。 对于具有多于两个输出标签的分类器,可以使用[softmax][]函数。</p><figure class="highlight python"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></div></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># Import 相关组件</span><br><span class="hljs-keyword">from</span> __future__ <span class="hljs-keyword">import</span> print_function<br><span class="hljs-keyword">import</span> numpy <span class="hljs-keyword">as</span> np<br><span class="hljs-keyword">import</span> sys<br><span class="hljs-keyword">import</span> os<br><span class="hljs-keyword">from</span> cntk <span class="hljs-keyword">import</span> Trainer, learning_rate_schedule, UnitType<br><span class="hljs-keyword">from</span> cntk.learner <span class="hljs-keyword">import</span> sgd<br><span class="hljs-keyword">from</span> cntk.ops <span class="hljs-keyword">import</span> *<br><span class="hljs-comment"># 在测计算机时选择正确的目标设备:</span><br><span class="hljs-keyword">if</span> <span class="hljs-string">'TEST_DEVICE'</span> <span class="hljs-keyword">in</span> os.environ:<br> <span class="hljs-keyword">import</span> cntk<br> <span class="hljs-keyword">if</span> os.environ[<span class="hljs-string">'TEST_DEVICE'</span>] == <span class="hljs-string">'cpu'</span>:<br> cntk.device.set_default_device(cntk.device.cpu())<br> <span class="hljs-keyword">else</span>:<br> cntk.device.set_default_device(cntk.device.gpu(<span class="hljs-number">0</span>))<br></code></pre></td></tr></table></figure><h2 id="数据生成-Data-Generation"><a href="#数据生成-Data-Generation" class="headerlink" title="数据生成(Data Generation)"></a>数据生成(Data Generation)</h2><p>将使用numpy库生成一些模拟肿瘤示例的合成数据。我们有两个特征(以二维表示),每个特征都是两个类别中的一个(<strong>蓝色点:良性</strong>、<strong>红色点:恶性</strong>)。<br>训练数据中的每个表观(observation)具有对应于每个表观(observation)(特征 - 年龄和大小的集合)的标签(蓝色或红色)。 在这个例子中,我们有两个类由标签0或1表示,因此这是个二进制分类任务。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 定义该网络</span><br>input_dim = <span class="hljs-number">2</span><br>num_output_classes = <span class="hljs-number">2</span><br></code></pre></td></tr></table></figure><h2 id="输入-Input-和标签-Labels"><a href="#输入-Input-和标签-Labels" class="headerlink" title="输入(Input)和标签(Labels)"></a>输入(Input)和标签(Labels)</h2><p>在本示例中,我们使用numpy库来生成合成数据。在现实世界的问题中,将使用读取器,其将读取对应于每个观察(患者)的特征值(特征:年龄和肿瘤大小)。模拟的年龄变量按比例缩小使其具有与其他变量类似的范围。这是数据预处理的一个关键方面。**注意:**每个表观(observation)可以处在更高维度的空间中(当更多特征可用时),并且将在CNTK中表示为张量(tensor)。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 确保我们总是获得相同的随机性</span><br>np.random.seed(<span class="hljs-number">0</span>)<br><br><span class="hljs-comment"># 辅助函数生成随机数据样本</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">generate_random_data_sample</span>(<span class="hljs-params">sample_size, feature_dim, num_classes</span>):<br> <span class="hljs-comment"># 使用NumPy创建合成数据</span><br> Y = np.random.randint(size=(sample_size, <span class="hljs-number">1</span>), low=<span class="hljs-number">0</span>, high=num_classes)<br><br> <span class="hljs-comment"># 确保数据是可分离的 </span><br> X = (np.random.randn(sample_size, feature_dim)+<span class="hljs-number">3</span>) * (Y+<span class="hljs-number">1</span>)<br> <br> <span class="hljs-comment"># 指定数据类型以匹配后面使用的输入变量 </span><br> <span class="hljs-comment"># (默认double类型)</span><br> X = X.astype(np.float32) <br> <br> <span class="hljs-comment"># 将类 0 转换为向量 '1 0 0'</span><br> <span class="hljs-comment"># 类 1 转换为向量 '0 1 0', ...</span><br> class_ind = [Y==class_number <span class="hljs-keyword">for</span> class_number <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(num_classes)]<br> Y = np.asarray(np.hstack(class_ind), dtype=np.float32)<br> <span class="hljs-keyword">return</span> X, Y <br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 创建表征要素和标签数据的输入变量。 </span><br><span class="hljs-comment">#注意:input_variable不需要关于观察值(样本)的附加信息,因为CNTK首先仅创建网络拓扑</span><br>mysamplesize = <span class="hljs-number">32</span><br>features, labels = generate_random_data_sample(mysamplesize, input_dim, num_output_classes)<br></code></pre></td></tr></table></figure><p>接下来可视化数据处理。<br><strong>注意:</strong>如果matplotlib.pyplot的导入失败,请运行conda install matplotlib,这将修复pyplot版本依赖性。 如果你在一个不同于Anaconda的python环境中,那么使用pip install。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 绘制数据</span><br><span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt<br><span class="hljs-comment"># 给定一个2分类</span><br>colors = [<span class="hljs-string">'r'</span> <span class="hljs-keyword">if</span> l == <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'b'</span> <span class="hljs-keyword">for</span> l <span class="hljs-keyword">in</span> labels[:,<span class="hljs-number">0</span>]]<br><br>plt.scatter(features[:,<span class="hljs-number">0</span>], features[:,<span class="hljs-number">1</span>], c=colors)<br>plt.xlabel(<span class="hljs-string">"Scaled age (in yrs)"</span>)<br>plt.ylabel(<span class="hljs-string">"Tumor size (in cm)"</span>)<br>plt.show()<br></code></pre></td></tr></table></figure><p><img src="/2017/03/10/%E9%80%BB%E8%BE%91%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B%E5%92%8CML%E5%85%A5%E9%97%A8/image4.png" alt="image4"></p><h2 id="模型创建-Model-Creation"><a href="#模型创建-Model-Creation" class="headerlink" title="模型创建(Model Creation)"></a>模型创建(Model Creation)</h2><p>逻辑回归网络(又名LR)是最简单的构建块,但x在过去十年中已经为许多ML应用程序提供了动力。 LR是一个简单的线性模型,它以一个数字向量作为输入,描述我们所分类的属性(也称为特征向量X,下图中的蓝色节点),并发出Evidence(Z)(从绿色节点发出,也称为激活)。输入层中的每个特征通过相应的权重w(由不同厚度的黑线表示)与输出节点连接。<br><img src="/2017/03/10/%E9%80%BB%E8%BE%91%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B%E5%92%8CML%E5%85%A5%E9%97%A8/logistic_neuron2.jpg" alt="image5"></p><p>第一步是计算观察的证据(evidence)。<br>$$z = \sum_{i=1}^n w_i \times x_i + b = \textbf{w} \cdot \textbf{x} + b$$<br>$\bf{w}$ 是长度的权重向量. $b$ 是偏差项.<br><strong>Note</strong>: 使用<strong>粗体</strong>表示法来表示向量。<br>使用<a href="https://en.wikipedia.org/wiki/Sigmoid_function">sigmoid</a>(当结果可以取两个值之一时)或softmax函数(当结果可以采用多于2个类别值中的一个时),将所计算的证据映射到0-1标度。<br>**输入变量(a key CNTK概念)**:输入变量是面向用户代码的容器,其中用户提供的代码在模型学习(aka training)期间填充不同的观察(数据点或样本,在本示例中等同于蓝色/红色点)作为模型函数的输入, 模型评价(测试)。 因此,input_variable的形状必须匹配将提供的数据的形状。 例如,当数据是高度10像素和宽度5像素的图像时,输入特征尺寸将是2(表示图像高度和宽度)。类似地,在我们的示例中,维度是年龄和肿瘤大小,因此input_dim=2.更多关于数据及其维度,以显示在单独的教程中。</p><p><code>input = input_variable(input_dim, np.float32)</code></p><h2 id="网络设置-Network-Setup"><a href="#网络设置-Network-Setup" class="headerlink" title="网络设置(Network Setup)"></a>网络设置(Network Setup)</h2><p>linear_layer函数是上述方程的直接实现。 我们执行两个操作:</p><blockquote><ul><li>1.使用CNTK乘法运算符将权重($\bf{w}$)与特征($\bf{x}$)相乘,并且添加个体特征的贡献(contribution)</li><li>2.添加偏差项b</li></ul></blockquote><p>这些CNTK操作被优化并在可用硬件上执行,并且隐藏了,实现减少用户使用的复杂性。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 定义字典以存储模型参数</span><br>mydict = {<span class="hljs-string">"w"</span>:<span class="hljs-literal">None</span>,<span class="hljs-string">"b"</span>:<span class="hljs-literal">None</span>} <br><span class="hljs-keyword">def</span> <span class="hljs-title function_">linear_layer</span>(<span class="hljs-params">input_var, output_dim</span>):<br> input_dim = input_var.shape[<span class="hljs-number">0</span>]<br> weight_param = parameter(shape=(input_dim, output_dim))<br> bias_param = parameter(shape=(output_dim))<br> mydict[<span class="hljs-string">'w'</span>], mydict[<span class="hljs-string">'b'</span>] = weight_param, bias_param<br> <span class="hljs-keyword">return</span> times(input_var, weight_param) + bias_param <br><br><span class="hljs-comment"># z代表网络的输出</span><br><br>output_dim = num_output_classes<br>z = linear_layer(<span class="hljs-built_in">input</span>, output_dim)<br></code></pre></td></tr></table></figure><h2 id="训练模型参数-Learning-model-parameters"><a href="#训练模型参数-Learning-model-parameters" class="headerlink" title="训练模型参数(Learning model parameters)"></a>训练模型参数(Learning model parameters)</h2><p>现在网络已经建立,我们想学习我们的简单线性层的参数$\bf{w}$和b。 为此,我们使用softmax函数将计算的证据(evidence)z转换为一组预测概率($\bf{P}$)。</p><p>$$ \textbf{p} = \mathrm{softmax}(z)$$ </p><p>softmax是将累积的证据(evidence)映射到类别上的概率分布(<a href="https://www.cntk.ai/pythondocs/cntk.ops.html#cntk.ops.softmax">softmax</a>)的激活函数。 激活功能的其他选择可以在<a href="https://github.com/Microsoft/CNTK/wiki/Activation-Functions">这里</a>找到。</p><h3 id="训练-Training"><a href="#训练-Training" class="headerlink" title="训练(Training)"></a>训练(Training)</h3><p>softmax的输出是属于各个类别的观察(observation)的概率。 为了训练分类器,我们需要确定模型需要模拟的行为。 换句话说,我们希望生成的概率尽可能接近观察到的标签。 此函数称为成本或损失函数,并显示学习模型与由训练集生成的模型之间的差异。<br>交叉熵(<a href="https://cntk.ai/pythondocs/cntk.ops.html#cntk.ops.cross_entropy_with_softmax">Cross-entropy</a>)是衡量损失的常用函数。 它定义为:<br>$$ H(p) = - \sum_{j=1}^C y_j \log (p_j) $$<br>其中$p$是我们从<code>softmax</code>函数的预测概率,$y$表示标签。 提供用于训练的数据的该标签也称为**地面真值(ground-truth)**标签。 在两类示例中,<code>标签</code>变量具有两个维度(等于<code>num_output_classes</code>或$C$)。 一般来说,如果手中的任务需要分类到$C$不同的类,则标签变量将具有除了由数据点表示的类别1之外的所有地方的$C$元素。强烈推荐到<a href="http://colah.github.io/posts/2015-09-Visual-Information/">Blog</a>理解这个交叉熵函数的细节。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python">label = input_variable((num_output_classes), np.float32)<br>loss = cross_entropy_with_softmax(z, label)<br></code></pre></td></tr></table></figure><h3 id="测试-Evaluation"><a href="#测试-Evaluation" class="headerlink" title="测试(Evaluation)"></a>测试(Evaluation)</h3><p>为了评估分类,可以比较网络的输出,其对于每个观察发射(emits)具有等于类的数量的维度的证据的向量(可以使用softmax函数转换成概率)。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs python">eval_error = classification_error(z, label)<br></code></pre></td></tr></table></figure><h2 id="配置训练-Configer-Training"><a href="#配置训练-Configer-Training" class="headerlink" title="配置训练(Configer Training)"></a>配置训练(Configer Training)</h2><p>训练者努力通过不同的优化方法减少损失函数,随机梯度下降(<a href="https://en.wikipedia.org/wiki/Stochastic_gradient_descent">sgd</a>)是最流行的一种。通常,可以从模型参数的随机初始化开始。sgd优化器将计算预测标签与相应的地面真值标签之间的损失或误差,并使用<a href="http://www.statisticsviews.com/details/feature/5722691/Getting-to-the-Bottom-of-Regression-with-Gradient-Descent.html">gradient-decent</a>在单次迭代中生成新的集合模型参数。一次使用单个观察的上述模型参数更新是有吸引力的,因为其不需要将整个数据集(所有观察)加载到存储器中,并且还需要在较少数据点上的梯度计算,从而允许对大数据集进行训练。然而,使用单个观察样本一次产生的更新在迭代之间可以非常不同。中间点是加载一小组观测值,并使用来自该组的损失或误差的平均值来更新模型参数。这个子集称为minibatch。使用minibatches我们经常从较大的训练数据集采样观察。我们重复模型参数更新的过程,使用不同的训练样本组合,并在一段时间内使损失(和误差)最小化。当增量误差率不再显着变化或在预设数量的最大微型间隔训练之后,我们声称我们的模型是训练的。优化的关键参数之一称为learning_rate。现在,我们可以把它看作是一个缩放因子,它可以调节我们在任何迭代中改变参数的程度。我们将在后面的教程中介绍更多的细节。有了这些信息,我们随时准备创建我们的训练器。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># 实例化训练器对象以驱动模型训练</span><br>learning_rate = <span class="hljs-number">0.5</span><br>lr_schedule = learning_rate_schedule(learning_rate, UnitType.minibatch) <br>learner = sgd(z.parameters, lr_schedule)<br>trainer = Trainer(z, (loss, eval_error), [learner])<br></code></pre></td></tr></table></figure><p>首先让我们创建一些帮助函数,可视化与训练相关的不同功能。 注意这些令人信服的功能是为了理解其内部机理。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> cntk.utils <span class="hljs-keyword">import</span> get_train_eval_criterion, get_train_loss<br><br><span class="hljs-comment"># 定义一个效用函数来计算移动平均和。</span><br><span class="hljs-comment"># 使用np.cumsum()函数可以实现更高效的实现</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">moving_average</span>(<span class="hljs-params">a, w=<span class="hljs-number">10</span></span>):<br> <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(a) < w: <br> <span class="hljs-keyword">return</span> a[:] <br> <span class="hljs-keyword">return</span> [val <span class="hljs-keyword">if</span> idx < w <span class="hljs-keyword">else</span> <span class="hljs-built_in">sum</span>(a[(idx-w):idx])/w <span class="hljs-keyword">for</span> idx, val <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(a)]<br><span class="hljs-comment"># 定义打印训练进度的函数</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">print_training_progress</span>(<span class="hljs-params">trainer, mb, frequency, verbose=<span class="hljs-number">1</span></span>):<br> training_loss, eval_error = <span class="hljs-string">"NA"</span>, <span class="hljs-string">"NA"</span><br><br> <span class="hljs-keyword">if</span> mb % frequency == <span class="hljs-number">0</span>:<br> training_loss = get_train_loss(trainer)<br> eval_error = get_train_eval_criterion(trainer)<br> <span class="hljs-keyword">if</span> verbose: <br> <span class="hljs-built_in">print</span> (<span class="hljs-string">"Minibatch: {0}, Loss: {1:.4f}, Error: {2:.2f}"</span>.<span class="hljs-built_in">format</span>(mb, training_loss, eval_error))<br> <br> <span class="hljs-keyword">return</span> mb, training_loss, eval_error<br></code></pre></td></tr></table></figure><h2 id="运行训练器-Run-the-trainer"><a href="#运行训练器-Run-the-trainer" class="headerlink" title="运行训练器(Run the trainer)"></a>运行训练器(Run the trainer)</h2><p>我们现在准备训练我们的逻辑回归模型。 我们想决定我们需要将什么数据输入到训练引擎中。<br>在此示例中,优化程序的每次迭代将工作在25个样本(25点w.r.t.上面的图)a.k.a. minibatch_size。 我们想训练说20000个观察。 如果数据中的样本数量只有10000,训练器将使2次通过数据。 这由num_minibatches_to_train表示。<strong>注意:</strong>在现实世界的情况下,我们将被给予一定量的标记数据(在本示例的上下文中,观察(年龄,大小)及其意义(良性/恶性)。 我们将使用大量的训练观察值表示70%,并留出余下的值用于评估训练模型。<br>利用这些参数,我们可以继续训练我们的简单前馈网络。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># Initialize the parameters for the trainer</span><br>minibatch_size = <span class="hljs-number">25</span><br>num_samples_to_train = <span class="hljs-number">20000</span><br>num_minibatches_to_train = <span class="hljs-built_in">int</span>(num_samples_to_train / minibatch_size)<br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># Run the trainer and perform model training</span><br>training_progress_output_freq = <span class="hljs-number">50</span><br><br>plotdata = {<span class="hljs-string">"batchsize"</span>:[], <span class="hljs-string">"loss"</span>:[], <span class="hljs-string">"error"</span>:[]}<br><br><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">0</span>, num_minibatches_to_train):<br> features, labels = generate_random_data_sample(minibatch_size, input_dim, num_output_classes)<br> <br> <span class="hljs-comment"># Specify input variables mapping in the model to actual minibatch data to be trained with</span><br> trainer.train_minibatch({<span class="hljs-built_in">input</span> : features, label : labels})<br> batchsize, loss, error = print_training_progress(trainer, i, <br> training_progress_output_freq, verbose=<span class="hljs-number">1</span>)<br> <br> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> (loss == <span class="hljs-string">"NA"</span> <span class="hljs-keyword">or</span> error ==<span class="hljs-string">"NA"</span>):<br> plotdata[<span class="hljs-string">"batchsize"</span>].append(batchsize)<br> plotdata[<span class="hljs-string">"loss"</span>].append(loss)<br> plotdata[<span class="hljs-string">"error"</span>].append(error)<br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs python">Minibatch: <span class="hljs-number">0</span>, Loss: <span class="hljs-number">0.6931</span>, Error: <span class="hljs-number">0.32</span><br>Minibatch: <span class="hljs-number">50</span>, Loss: <span class="hljs-number">2.4317</span>, Error: <span class="hljs-number">0.36</span><br>Minibatch: <span class="hljs-number">100</span>, Loss: <span class="hljs-number">0.4701</span>, Error: <span class="hljs-number">0.20</span><br>Minibatch: <span class="hljs-number">150</span>, Loss: <span class="hljs-number">0.7732</span>, Error: <span class="hljs-number">0.40</span><br>Minibatch: <span class="hljs-number">200</span>, Loss: <span class="hljs-number">0.1273</span>, Error: <span class="hljs-number">0.04</span><br>Minibatch: <span class="hljs-number">250</span>, Loss: <span class="hljs-number">0.1316</span>, Error: <span class="hljs-number">0.08</span><br>Minibatch: <span class="hljs-number">300</span>, Loss: <span class="hljs-number">0.1012</span>, Error: <span class="hljs-number">0.04</span><br>Minibatch: <span class="hljs-number">350</span>, Loss: <span class="hljs-number">0.1071</span>, Error: <span class="hljs-number">0.04</span><br>Minibatch: <span class="hljs-number">400</span>, Loss: <span class="hljs-number">0.3085</span>, Error: <span class="hljs-number">0.08</span><br>Minibatch: <span class="hljs-number">450</span>, Loss: <span class="hljs-number">0.3214</span>, Error: <span class="hljs-number">0.12</span><br>Minibatch: <span class="hljs-number">500</span>, Loss: <span class="hljs-number">0.4114</span>, Error: <span class="hljs-number">0.20</span><br>Minibatch: <span class="hljs-number">550</span>, Loss: <span class="hljs-number">0.6799</span>, Error: <span class="hljs-number">0.20</span><br>Minibatch: <span class="hljs-number">600</span>, Loss: <span class="hljs-number">0.2982</span>, Error: <span class="hljs-number">0.12</span><br>Minibatch: <span class="hljs-number">650</span>, Loss: <span class="hljs-number">0.1675</span>, Error: <span class="hljs-number">0.12</span><br>Minibatch: <span class="hljs-number">700</span>, Loss: <span class="hljs-number">0.2770</span>, Error: <span class="hljs-number">0.12</span><br>Minibatch: <span class="hljs-number">750</span>, Loss: <span class="hljs-number">0.2308</span>, Error: <span class="hljs-number">0.04</span><br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># Compute the moving average loss to smooth out the noise in SGD</span><br>plotdata[<span class="hljs-string">"avgloss"</span>] = moving_average(plotdata[<span class="hljs-string">"loss"</span>])<br>plotdata[<span class="hljs-string">"avgerror"</span>] = moving_average(plotdata[<span class="hljs-string">"error"</span>])<br><br><span class="hljs-comment"># Plot the training loss and the training error</span><br><span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt<br><br>plt.figure(<span class="hljs-number">1</span>)<br>plt.subplot(<span class="hljs-number">211</span>)<br>plt.plot(plotdata[<span class="hljs-string">"batchsize"</span>], plotdata[<span class="hljs-string">"avgloss"</span>], <span class="hljs-string">'b--'</span>)<br>plt.xlabel(<span class="hljs-string">'Minibatch number'</span>)<br>plt.ylabel(<span class="hljs-string">'Loss'</span>)<br>plt.title(<span class="hljs-string">'Minibatch run vs. Training loss'</span>)<br><br>plt.show()<br><br>plt.subplot(<span class="hljs-number">212</span>)<br>plt.plot(plotdata[<span class="hljs-string">"batchsize"</span>], plotdata[<span class="hljs-string">"avgerror"</span>], <span class="hljs-string">'r--'</span>)<br>plt.xlabel(<span class="hljs-string">'Minibatch number'</span>)<br>plt.ylabel(<span class="hljs-string">'Label Prediction Error'</span>)<br>plt.title(<span class="hljs-string">'Minibatch run vs. Label Prediction Error'</span>)<br>plt.show()<br></code></pre></td></tr></table></figure><p><img src="/2017/03/10/%E9%80%BB%E8%BE%91%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B%E5%92%8CML%E5%85%A5%E9%97%A8/image6.png" alt="image6"></p><h1 id="评估测试-Evaluation-x2F-Testing"><a href="#评估测试-Evaluation-x2F-Testing" class="headerlink" title="评估测试(Evaluation/Testing)"></a>评估测试(Evaluation/Testing)</h1><p>现在我们已经训练了网络。 让我们评估训练网络对未用于训练的数据。 这称为测试。 让我们创建一些新的数据,并评估这个集合的平均错误和损失。 这是使用trainer.test_minibatch完成的。 注意,这个以前看不见的数据的错误与训练错误相当。 这是一个关键的检查。 如果误差大于训练误差大的幅度,则表明训练的模型在训练期间没有见到的数据上不能很好地执行。 这被称为<a href="https://en.wikipedia.org/wiki/Overfitting">过度拟合</a>。 有几种方法来解决超额配合,这超出了本教程的范围,但认知工具包提供了必要的组件来解决过度拟合。<br>注意:为了说明的目的,我们正在测试一个小型批次。 在实践中,运行几个测试数据的小型批次并报告平均值。<br>问题:为什么这是建议? 尝试使用用于训练的绘图函数在多组生成的数据样本和绘图上绘制测试错误。 你看到一个模式吗?</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># Run the trained model on newly generated dataset</span><br>test_minibatch_size = <span class="hljs-number">25</span><br>features, labels = generate_random_data_sample(test_minibatch_size, input_dim, num_output_classes)<br><br>trainer.test_minibatch({<span class="hljs-built_in">input</span> : features, label : labels}) <br></code></pre></td></tr></table></figure><p>输出:0.12</p><h2 id="检查预测-x2F-评估-Checking-prediction-x2F-evaluation"><a href="#检查预测-x2F-评估-Checking-prediction-x2F-evaluation" class="headerlink" title="检查预测/评估(Checking prediction/evaluation)"></a>检查预测/评估(Checking prediction/evaluation)</h2><p>为了评估,我们将网络的输出在0-1之间映射,并将它们转换为两个类的概率。 这表明每次观察的机会是恶性和良性的。 我们使用softmax函数来获得每个类的概率。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python">out = softmax(z)<br>result = out.<span class="hljs-built_in">eval</span>({<span class="hljs-built_in">input</span> : features})<br></code></pre></td></tr></table></figure><p>让我们将地面真实标签与预测进行比较。 他们应该符合。</p><p>Question:</p><ul><li>有多少预测被错误标记? 您可以更改下面的代码,以确定哪些观察被错误分类?<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-built_in">print</span>(<span class="hljs-string">"Label :"</span>, [np.argmax(label) <span class="hljs-keyword">for</span> label <span class="hljs-keyword">in</span> labels])<br><span class="hljs-built_in">print</span>(<span class="hljs-string">"Predicted:"</span>, [np.argmax(result[i,:,:]) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(result.shape[<span class="hljs-number">0</span>])])<br></code></pre></td></tr></table></figure>Label : [1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1]<br>Predicted: [1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1]</li></ul><h2 id="可视化-Visualization"><a href="#可视化-Visualization" class="headerlink" title="可视化(Visualization)"></a>可视化(Visualization)</h2><p>期望可视化结果。 在该示例中,数据方便地在二维中并且可以被绘制。 对于具有较高尺寸的数据,可视化可能具有挑战性。 存在允许这种<a href="https://en.wikipedia.org/wiki/T-distributed_stochastic_neighbor_embedding">可视化的先进的降维技术</a>。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># Model parameters</span><br><span class="hljs-built_in">print</span>(mydict[<span class="hljs-string">'b'</span>].value)<br><br>bias_vector = mydict[<span class="hljs-string">'b'</span>].value<br>weight_matrix = mydict[<span class="hljs-string">'w'</span>].value<br><br><span class="hljs-comment"># Plot the data </span><br><span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt<br><br><span class="hljs-comment"># given this is a 2 class </span><br>colors = [<span class="hljs-string">'r'</span> <span class="hljs-keyword">if</span> l == <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'b'</span> <span class="hljs-keyword">for</span> l <span class="hljs-keyword">in</span> labels[:,<span class="hljs-number">0</span>]]<br>plt.scatter(features[:,<span class="hljs-number">0</span>], features[:,<span class="hljs-number">1</span>], c=colors)<br>plt.plot([<span class="hljs-number">0</span>, bias_vector[<span class="hljs-number">0</span>]/weight_matrix[<span class="hljs-number">0</span>][<span class="hljs-number">1</span>]], <br> [ bias_vector[<span class="hljs-number">1</span>]/weight_matrix[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>], <span class="hljs-number">0</span>], c = <span class="hljs-string">'g'</span>, lw = <span class="hljs-number">3</span>)<br>plt.xlabel(<span class="hljs-string">"Scaled age (in yrs)"</span>)<br>plt.ylabel(<span class="hljs-string">"Tumor size (in cm)"</span>)<br>plt.show()<br></code></pre></td></tr></table></figure><p>输出结果:[ 7.98766518 -7.988904 ]</p><p><img src="/2017/03/10/%E9%80%BB%E8%BE%91%E5%9B%9E%E5%BD%92%E6%A8%A1%E5%9E%8B%E5%92%8CML%E5%85%A5%E9%97%A8/image7.png" alt="image7"></p><h1 id="探索建议-Exploration-Suggestions"><a href="#探索建议-Exploration-Suggestions" class="headerlink" title="探索建议(Exploration Suggestions)"></a>探索建议(Exploration Suggestions)</h1><ul><li>尝试探索分类器如何与不同的数据分布行为,建议更改minibatch_size参数从25到说64.为什么错误增加?</li><li>尝试探索不同的激活功能</li><li>尝试探索不同的学习模型</li><li>可以探索训练<a href="https://en.wikipedia.org/wiki/Multinomial_logistic_regression">多类逻辑回归</a>分类器。</li></ul>]]></content>
<categories>
<category>Machine Learning</category>
</categories>
<tags>
<tag>ML</tag>
<tag>逻辑回归模型</tag>
</tags>
</entry>
<entry>
<title>一维信号的高斯滤波</title>
<link href="/2017/03/04/%E4%B8%80%E7%BB%B4%E4%BF%A1%E5%8F%B7%E7%9A%84%E9%AB%98%E6%96%AF%E6%BB%A4%E6%B3%A2/"/>
<url>/2017/03/04/%E4%B8%80%E7%BB%B4%E4%BF%A1%E5%8F%B7%E7%9A%84%E9%AB%98%E6%96%AF%E6%BB%A4%E6%B3%A2/</url>
<content type="html"><![CDATA[<h1 id="正态分布"><a href="#正态分布" class="headerlink" title="正态分布"></a>正态分布</h1><p>高斯滤波使用的是高斯函数,即我们熟悉的正态分布的概率密度函数:<br>$$ f(x)=\frac{1}{σ\sqrt{2π}} exp (\frac{(x-μ)^2}{2σ^2}) $$</p><p>我们生成的高斯模板就是从这个公式来的。例如要生成一个大小为3,标准差为1的模板,则只需要代公式计算(此处均值μ为0,不为0将其平移即可):$f(−1) 、f(0)、f(1)$就可以得到模板的值了。</p><h1 id="高斯滤波是如何实现的?"><a href="#高斯滤波是如何实现的?" class="headerlink" title="高斯滤波是如何实现的?"></a>高斯滤波是如何实现的?</h1><p>其实思想很简单,高斯分布的特点是在均值μ两边的概率都很大,离之越远的概率越小,所以高斯函数用在滤波上体现的思想就是:离某个点越近的点对其产生的影响越大,所以让其权重大,越远的产生的影响越小,让其权重越小。</p><p>举个例子,有如下一个序列,对其中的6进行操作,模板为[1,2,1]:</p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">6</span> <span class="hljs-number">3</span> <span class="hljs-number">1</span> <span class="hljs-number">7</span> <span class="hljs-number">5</span> <span class="hljs-number">3</span> <span class="hljs-number">8</span> <br> |<br> <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure><p>那么结果为:(5x1+6x2+3x1)/(1+2+1)=5 ,对每个数据都进行这样的操作,就是所谓的高斯滤波了。<br>有一个问题,如果是开头和结尾怎么办?<br>一种做法是补0:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">6</span> <span class="hljs-number">3</span> <span class="hljs-number">1</span> <span class="hljs-number">7</span> <span class="hljs-number">5</span> <span class="hljs-number">3</span> <span class="hljs-number">8</span> <br> |<br><span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure><p>另一种做法就是不让模板超出信号的范围,此处采用后一种做法。</p><h1 id="matlab代码"><a href="#matlab代码" class="headerlink" title="matlab代码"></a>matlab代码</h1><p>高斯滤波函数<code>Gaussianfilter</code>:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs cpp">% 功能:对一维信号的高斯滤波,头尾r/<span class="hljs-number">2</span>的信号不进行滤波<br>% r :高斯模板的大小推荐奇数<br>% sigma :标准差<br>% y :需要进行高斯滤波的序列<br>function y_filted = <span class="hljs-built_in">Gaussianfilter</span>(r, sigma, y)<br><br>% 生成一维高斯滤波模板<br>GaussTemp = <span class="hljs-built_in">ones</span>(<span class="hljs-number">1</span>,r*<span class="hljs-number">2</span><span class="hljs-number">-1</span>);<br><span class="hljs-keyword">for</span> i=<span class="hljs-number">1</span> : r*<span class="hljs-number">2</span><span class="hljs-number">-1</span><br> <span class="hljs-built_in">GaussTemp</span>(i) = <span class="hljs-built_in">exp</span>(-(i-r)^<span class="hljs-number">2</span>/(<span class="hljs-number">2</span>*sigma^<span class="hljs-number">2</span>))/(sigma*<span class="hljs-built_in">sqrt</span>(<span class="hljs-number">2</span>*pi));<br>end<br><br>% 高斯滤波<br>y_filted = y;<br><span class="hljs-keyword">for</span> i = r : <span class="hljs-built_in">length</span>(y)-r<br> <span class="hljs-built_in">y_filted</span>(i) = <span class="hljs-built_in">y</span>(i-r+<span class="hljs-number">1</span> : i+r<span class="hljs-number">-1</span>)*GaussTemp<span class="hljs-number">'</span>;<br>end<br></code></pre></td></tr></table></figure><p>测试代码:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs cpp">% 测试数据<br>x = <span class="hljs-number">1</span>:<span class="hljs-number">50</span>;<br>y = x + <span class="hljs-built_in">rand</span>(<span class="hljs-number">1</span>,<span class="hljs-number">50</span>)*<span class="hljs-number">10</span>;<br><br>% 设置高斯模板大小和标准差<br>r = <span class="hljs-number">3</span>;<br>sigma = <span class="hljs-number">1</span>;<br>y_filted = <span class="hljs-built_in">Gaussianfilter</span>(r, sigma, y);<br><br>% 作图对比<br><span class="hljs-built_in">plot</span>(x, y, x, y_filted);<br><span class="hljs-built_in">title</span>(<span class="hljs-string">'高斯滤波'</span>);<br><span class="hljs-built_in">legend</span>(<span class="hljs-string">'滤波前'</span>,<span class="hljs-string">'滤波后'</span>,<span class="hljs-string">'Location'</span>,<span class="hljs-string">'northwest'</span>)<br></code></pre></td></tr></table></figure><p>如何使用?<br>新建2个m文件,一个命名为<code>Gaussianfilter</code>,把第一段代码复制进去;另一个命名为<code>testgauss</code>,把第二段代码复制进去,保存。在testgauss中点击<code>运行</code>按钮,即可看到结果。<br>结果:</p><p><img src="/2017/03/04/%E4%B8%80%E7%BB%B4%E4%BF%A1%E5%8F%B7%E7%9A%84%E9%AB%98%E6%96%AF%E6%BB%A4%E6%B3%A2/result.jpg" alt="Gausse"></p>]]></content>
<categories>
<category>滤波</category>
</categories>
<tags>
<tag>一维信号</tag>
<tag>高斯滤波</tag>
</tags>
</entry>
<entry>
<title>一维信号的滤波</title>
<link href="/2017/03/04/%E4%B8%80%E7%BB%B4%E4%BF%A1%E5%8F%B7%E7%9A%84%E6%BB%A4%E6%B3%A2/"/>
<url>/2017/03/04/%E4%B8%80%E7%BB%B4%E4%BF%A1%E5%8F%B7%E7%9A%84%E6%BB%A4%E6%B3%A2/</url>
<content type="html"><![CDATA[<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>数字滤波方法有很多种,每种方法有其不同的特点和使用范围。从大的范围可分为3类。</p><ul><li><p><strong>克服大脉冲干扰的数字滤波法</strong></p><blockquote><ul><li>限幅滤波法</li><li>中值滤波法</li></ul></blockquote></li><li><p><strong>抑制小幅度高频噪声的平均滤波法</strong></p><blockquote><ul><li>算数平均</li><li>滑动平均</li><li>加权滑动平均</li><li>一阶滞后滤波法</li></ul></blockquote></li><li><p><strong>复合滤波法</strong></p></li></ul><p>下面介绍常用滤波方法</p><h1 id="克服大脉冲干扰的数字滤波法"><a href="#克服大脉冲干扰的数字滤波法" class="headerlink" title="克服大脉冲干扰的数字滤波法"></a>克服大脉冲干扰的数字滤波法</h1><p>克服由仪器外部环境偶然因素引起的突变性扰动或仪器内部不稳定引起误码等造成的尖脉冲干扰,是仪器数据处理的第一步。通常采用简单的非线性滤波法。</p><h2 id="限幅滤波法(又称程序判断滤波法)"><a href="#限幅滤波法(又称程序判断滤波法)" class="headerlink" title="限幅滤波法(又称程序判断滤波法)"></a>限幅滤波法(又称程序判断滤波法)</h2><p>限幅滤波是通过程序判断被测信号的变化幅度,从而消除缓变信号中的尖脉冲干扰。</p><ul><li>方法: 根据经验判断,确定两次采样允许的最大偏差值(设为A)每次检测到新值时判断:如果本次值与上次值之差A,则本次值无效,放弃本次值,用上次值代替本次值.</li><li>优点:能有效克服因偶然因素引起的脉冲干扰</li><li>缺点:无法抑制那种周期性的干扰平滑度差</li><li>适用范围: 变化比较缓慢的被测量值</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">define</span> A 10 </span><br><span class="hljs-type">char</span> value; <br><span class="hljs-function"><span class="hljs-type">char</span> <span class="hljs-title">filter</span><span class="hljs-params">(<span class="hljs-type">int</span> value)</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">char</span> new_value; <br> new_value = <span class="hljs-built_in">get_ad</span>(); <br> <span class="hljs-keyword">if</span>(( new_value - value > δ)||( value - new_value >δ) <br> <span class="hljs-keyword">return</span> value; <br> <span class="hljs-keyword">return</span> new_value; <br>} <br></code></pre></td></tr></table></figure><h2 id="中位值滤波法"><a href="#中位值滤波法" class="headerlink" title="中位值滤波法"></a>中位值滤波法</h2><p>中位值滤波是一种典型的非线性滤波器,它运算简单,在滤除脉冲噪声的同时可以很好地保护信号的细节信息。</p><p>方法:连续采样N次(N取奇数)把N次采样值按大小排列(多采用冒泡法)取中间值为本次有效值<br>优点:能有效克服因偶然因素引起的波动(脉冲)干扰<br>缺点:对流量、速度等快速变化的参数不宜<br>适用范围:对温度、液位的变化缓慢的被测参数有良好的滤波效果</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">define</span> N 11 </span><br> <br><span class="hljs-function"><span class="hljs-type">char</span> <span class="hljs-title">filter</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">char</span> value_buf[N]; <br> <span class="hljs-type">char</span> count,i,j,temp; <br> <span class="hljs-keyword">for</span>(count=<span class="hljs-number">0</span>;count<N;count++) <br> { <br> value_buf[count] = <span class="hljs-built_in">get_ad</span>(); <br> <span class="hljs-built_in">delay</span>(); <br> } <br> <span class="hljs-keyword">for</span>(j=<span class="hljs-number">0</span>;j<N<span class="hljs-number">-1</span>;j++) <br> { <br> <span class="hljs-keyword">for</span>(i=<span class="hljs-number">0</span>;i<N-j;i++) <br> { <br> <span class="hljs-keyword">if</span>( value_buf>value_buf[i+<span class="hljs-number">1</span>] ) <br> { <br> temp = value_buf; <br> value_buf = value_buf[i+<span class="hljs-number">1</span>]; <br> value_buf[i+<span class="hljs-number">1</span>] = temp; <br> } <br> } <br> } <br> <span class="hljs-keyword">return</span> value_buf[(N<span class="hljs-number">-1</span>)/<span class="hljs-number">2</span>]; <br>} <br></code></pre></td></tr></table></figure><h1 id="抑制小幅度高频噪声的平均滤波法"><a href="#抑制小幅度高频噪声的平均滤波法" class="headerlink" title="抑制小幅度高频噪声的平均滤波法"></a>抑制小幅度高频噪声的平均滤波法</h1><p>小幅度高频电子噪声:电子器件热噪声、A/D量化噪声等。通常采用具有低通特性的线性滤波器:<code>算数平均滤波法</code>、<code>加权平均滤波法</code>、<code>滑动加权平均滤波法</code>、<code>一阶滞后滤波法</code>等。</p><h2 id="算术平均滤波法"><a href="#算术平均滤波法" class="headerlink" title="算术平均滤波法"></a>算术平均滤波法</h2><p>算术平均滤波法是对N个连续采样值相加,然后取其算术平均值作为本次测量的滤波值。</p><ul><li>方法:连续取N个采样值进行算术平均运算N值较大时:信号平滑度较高,但灵敏度较低N值较小时:信号平滑度较低,但灵敏度较高N值的选取:一般流量,N=12;压力:N=4</li><li>优点:对滤除混杂在被测信号上的随机干扰信号非常有效。被测信号的特点是有一个平均值,信号在某一数值范围附近上下波动</li><li>缺点:不易消除脉冲干扰引起的误差。对于采样速度较慢或要求数据更新率较高的实时系统,算术平均滤法无法使用的。比较浪费RAM</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">define</span> N 12 </span><br><span class="hljs-function"><span class="hljs-type">char</span> <span class="hljs-title">filter</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">int</span> sum = <span class="hljs-number">0</span>; <br> <span class="hljs-keyword">for</span>( count=<span class="hljs-number">0</span>;count<N;count++) <br> { <br> sum + = <span class="hljs-built_in">get_ad</span>(); <br> <span class="hljs-built_in">delay</span>(); <br> } <br><span class="hljs-keyword">return</span> (<span class="hljs-type">char</span>)(sum/N); <br>} <br></code></pre></td></tr></table></figure><h2 id="递推平均滤波法(又称滑动平均滤波法)"><a href="#递推平均滤波法(又称滑动平均滤波法)" class="headerlink" title="递推平均滤波法(又称滑动平均滤波法)"></a>递推平均滤波法(又称滑动平均滤波法)</h2><p>对于采样速度较慢或要求数据更新率较高的实时系统,算术平均滤法无法使用的。滑动平均滤波法把N个测量数据看成一个队列,队列的长度固定为N,每进行一次新的采样,把测量结果放入队尾,而去掉原来队首的一个数据,这样在队列中始终有N个“最新”的数据。</p><ul><li>方法:把连续取N个采样值看成一个队列,队列的长度固定为N,每次采样到一个新数据放入队尾,并扔掉原来队首的一次数据.(先进先出原则)把队列中的N个数据进行算术平均运算,就可获得新的滤波结果N值的选取:流量,N=12;压力:N=4;液面,N=4<del>12;温度,N=1</del>4</li><li>对周期性干扰有良好的抑制作用,平滑度高适用于高频振荡的系统</li><li>灵敏度低对偶然出现的脉冲性干扰的抑制作用较差不易消除由于脉冲干扰所引起的采样值偏差不适用于脉冲干扰比较严重的场合比较浪费RAM</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">define</span> N 12 </span><br><span class="hljs-type">char</span> value_buf[N]; <br><span class="hljs-type">char</span> i=<span class="hljs-number">0</span>; <br><span class="hljs-function"><span class="hljs-type">char</span> <span class="hljs-title">filter</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">char</span> count; <br> <span class="hljs-type">int</span> sum=<span class="hljs-number">0</span>; <br> value_buf[i++] = <span class="hljs-built_in">get_ad</span>(); <br> <span class="hljs-keyword">if</span>( i == N ) i = <span class="hljs-number">0</span>; <br> <span class="hljs-keyword">for</span>( count=<span class="hljs-number">0</span>;count<N,count++) <br> sum += value_buf[count]; <br> <span class="hljs-keyword">return</span> (<span class="hljs-type">char</span>)(sum/N); <br>} <br></code></pre></td></tr></table></figure><h2 id="加权递推平均滤波法"><a href="#加权递推平均滤波法" class="headerlink" title="加权递推平均滤波法"></a>加权递推平均滤波法</h2><ul><li>方法:是对递推平均滤波法的改进,即不同时刻的数据加以不同的权通常是,越接近现时刻的数据,权取得越大。给予新采样值的权系数越大,则灵敏度越高,但信号平滑度低</li><li>优点:适用于有较大纯滞后时间常数的对象和采样周期较短的系统</li><li>缺点:对于纯滞后时间常数较小,采样周期较长,变化缓慢的信号不能迅速反应系统当前所受干扰的严重程度,滤波效果差</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">define</span> N 12 </span><br><span class="hljs-type">char</span> code coe[N] = {<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">7</span>,<span class="hljs-number">8</span>,<span class="hljs-number">9</span>,<span class="hljs-number">10</span>,<span class="hljs-number">11</span>,<span class="hljs-number">12</span>}; <br><span class="hljs-type">char</span> code sum_coe = <span class="hljs-number">1</span>+<span class="hljs-number">2</span>+<span class="hljs-number">3</span>+<span class="hljs-number">4</span>+<span class="hljs-number">5</span>+<span class="hljs-number">6</span>+<span class="hljs-number">7</span>+<span class="hljs-number">8</span>+<span class="hljs-number">9</span>+<span class="hljs-number">10</span>+<span class="hljs-number">11</span>+<span class="hljs-number">12</span>; <br><span class="hljs-function"><span class="hljs-type">char</span> <span class="hljs-title">filter</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">char</span> count; <br> <span class="hljs-type">char</span> value_buf[N]; <br> <span class="hljs-type">int</span> sum=<span class="hljs-number">0</span>; <br> <span class="hljs-keyword">for</span> (count=<span class="hljs-number">0</span>,count<N;count++) <br> { <br> value_buf[count] = <span class="hljs-built_in">get_ad</span>(); <br> <span class="hljs-built_in">delay</span>(); <br> } <br> <span class="hljs-keyword">for</span>(count=<span class="hljs-number">0</span>,count<N;count++) <br> sum += value_buf[count] * coe[count]; <br> <span class="hljs-keyword">return</span> (<span class="hljs-type">char</span>)(sum/sum_coe); <br>} <br></code></pre></td></tr></table></figure><h2 id="一阶滞后滤波法"><a href="#一阶滞后滤波法" class="headerlink" title="一阶滞后滤波法"></a>一阶滞后滤波法</h2><p>一阶低通数字滤波器是用软件的方法实现硬件的RC滤波,以抑制干扰信号。在模拟量输入通道中,常用一阶滞后RC模拟滤波器来抑制干扰。<br>用此种方法来实现对低频干扰时,首先遇到的问题是要求滤波器有大的时间常数(时间常数=RC)和高精度的RC网络。时间常数越大,要求RC值越大,其漏电流也必然增大,从而使RC网络精度下降。采用一阶滞后的数字滤波方法,能很好的克服这种模拟量滤波器的缺点,在滤波常数要求较大的场合,此法更适合。</p><ul><li>方法:a=Tf/(Tf+T)Tf为滤波时间常数。T为采样周期本次滤波结果=(1-a)<em>本次采样值+a</em>上次滤波结果</li><li>优点:对周期性干扰具有良好的抑制作用适用于波动频率较高的场合</li><li>缺点:相位滞后,灵敏度低滞后程度取决于a值大小不能消除滤波频率高于采样频率的1/2的干扰信号</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">define</span> a 50 </span><br><span class="hljs-type">char</span> value; <br><span class="hljs-function"><span class="hljs-type">char</span> <span class="hljs-title">filter</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-type">char</span> new_value; <br> new_value = <span class="hljs-built_in">get_ad</span>(); <br> <span class="hljs-keyword">return</span> (<span class="hljs-number">1</span>-a)*value + a*new_value; <span class="hljs-comment">//a为滤波系数, 其值<<1 </span><br>} <br></code></pre></td></tr></table></figure><h1 id="复合滤波法"><a href="#复合滤波法" class="headerlink" title="复合滤波法"></a>复合滤波法</h1><p>在实际应用中,有时既要消除大幅度的脉冲干扰,有要做到数据平滑。因此常把前面介绍的两种以上的方法结合起来使用,形成复合滤波。去极值平均滤波算法:先用中值滤波算法滤除采样值中的脉冲性干扰,然后把剩余的各采样值进行平均滤波。连续采样N次,剔除其最大值和最小值,再求余下N-2个采样的平均值。显然,这种方法既能抑制随机干扰,又能滤除明显的脉冲干扰。</p><h2 id="中位值平均滤波法(又称防脉冲干扰平均滤法)"><a href="#中位值平均滤波法(又称防脉冲干扰平均滤法)" class="headerlink" title="中位值平均滤波法(又称防脉冲干扰平均滤法)"></a>中位值平均滤波法(又称防脉冲干扰平均滤法)</h2><p>中位值平均滤波法相当于“中位值滤波法”+“算术平均滤波法”。</p><ul><li>方法:连续采样N个数据,去掉一个最大值和一个最小值然后计算N-2个数据的算术平均值N值的选取:3~14</li><li>优点:融合了两种滤波法的优点这种方法既能抑制随机干扰,又能滤除明显的脉冲干扰。</li><li>缺点:测量速度较慢,和算术平均滤波法一样比较浪费RAM</li></ul><h2 id="限幅平均滤波法"><a href="#限幅平均滤波法" class="headerlink" title="限幅平均滤波法"></a>限幅平均滤波法</h2><p>在脉冲干扰较严重的场合,如采用一般的平均值法,则干扰会平均到结果中去。限幅平均滤波法相当于“限幅滤波法”+“递推平均滤波法”</p><ul><li>方法:每次采样到的新数据先进行限幅处理,再送入队列进行递推平均滤波处理</li><li>优点:融合了两种滤波法的优点,对于偶然出现的脉冲性干扰,可消除由于脉冲干扰所引起的采样值偏差</li><li>缺点:比较浪费RAM</li><li>适用范围:缓变信号</li></ul>]]></content>
<categories>
<category>滤波</category>
</categories>
</entry>
<entry>
<title>Vibe算法</title>
<link href="/2017/03/02/Vibe%E7%AE%97%E6%B3%95/"/>
<url>/2017/03/02/Vibe%E7%AE%97%E6%B3%95/</url>
<content type="html"><![CDATA[<p><a href="http://blog.csdn.net/tiandijun/article/details/50499708">原文链接</a></p><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p><strong>ViBe - a powerful technique for background detection and subtraction in video sequences</strong><br><a href="http://www2.ulg.ac.be/telecom/research/vibe/">ViBe</a>是一种像素级视频背景建模或前景检测的算法,效果优于所熟知的几种算法,对硬件内存占用也少。ViBe是一种像素级的背景建模、前景检测算法,该算法主要不同之处是背景模型的更新策略,随机选择需要替换的像素的样本,随机选择邻域像素进行更新。在无法确定像素变化的模型时,随机的更新策略,在一定程度上可以模拟像素变化的不确定性。</p><p>背景差方法实现运动物体检测面临的挑战主要有:</p><blockquote><ul><li>可直接应用在产品中,软硬件兼容性好;</li><li>必须适应环境的变化(比如光照的变化造成图像色度的变化); </li><li>相机抖动引起画面的抖动(比如手持相机拍照时候的移动);</li><li>图像中密集出现的物体(比如树叶或树干等密集出现的物体,要正确的检测出来);</li><li>须能够正确的检测出背景物体的改变(比如新停下的车必须及时的归为背景物体,而有静止开始移动的物体也需要及时的检测出来)。</li><li>物体检测中往往会出现Ghost区域,Ghost区域也就是指当一个原本静止的物体开始运动,背景差检测算法可能会将原来该物体所覆盖的区域错误的检测为运动的,这块区域就成为Ghost,当然原来运动的物体变为静止的也会引入Ghost区域,Ghost区域在检测中必须被尽快的消除。一个Ghost区域的实例如<strong>图1</strong>,在图中可以发现相比于原图,检测的结果中错误的多出现了两个人的形状,这就是Ghost。</li></ul></blockquote><p><img src="/2017/03/02/Vibe%E7%AE%97%E6%B3%95/vibe1.jpg" alt="Vibe1"> </p><h2 id="ViBe检测方法"><a href="#ViBe检测方法" class="headerlink" title="ViBe检测方法"></a>ViBe检测方法</h2><p>ViBe是本篇论文中所提出的一个检测方法,相比于其他方法它有很多的不同和优点。具体的思想就是<strong>为每个像素点存储了一个样本集,样本集中采样值就是该像素点过去的像素值和其邻居点的像素值,然后将每一个新的像素值和样本集进行比较来判断是否属于背景点。</strong></p><p>该模型主要包括三个方面:</p><blockquote><ul><li>模型的工作原理;</li><li>模型的初始化方法;</li><li>模型的更新策略。</li></ul></blockquote><h3 id="模型的工作原理"><a href="#模型的工作原理" class="headerlink" title="模型的工作原理"></a>模型的工作原理</h3><p><strong>背景物体就是指静止的或是非常缓慢的移动的物体</strong>,而<strong>前景物体就对应移动的物体</strong>。所以我们可以把物体检测看成一个分类问题,也就是来确定一个像素点是否属于背景点。在ViBe模型中,背景模型为每个背景点存储了一个样本集,然后将每一个新的像素值和样本集进行比较来判断是否属于背景点。可以知道如果一个新的观察值属于背景点那么它应该和样本集中的采样值比较接近。<br>具体的讲,我们记V(x):x点处的像素值;<strong>M(x)={$$V_1$$,$$V_2$$,…$$V_N$$}<strong>为x处的背景样本集(样本集大小为N);</strong>SR(v(x))<strong>:以x为中心R为半径的区域,如果</strong>M(x) [{SR(v(x))∩ {$$v_1$$,$$v_2$$, . . . , $$v_N$$}}]<strong>大于一个给定的阈值</strong>min</strong>,那么就认为x点属于背景点。</p><h3 id="背景模型的初始化"><a href="#背景模型的初始化" class="headerlink" title="背景模型的初始化"></a>背景模型的初始化</h3><p>初始化是建立背景模型的过程,一般的检测算法需要一定长度的视频序列学习完成,影响了检测的实时性,而且当视频画面突然变化时,重新学习背景模型需要较长时间。<br>ViBe算法主要是利用单帧视频序列初始化背景模型,对于一个像素点,结合相邻像素点拥有相近像素值的空间分布特性,随机的选择它的邻域点的像素值作为它的模型样本值。ViBe的初始化仅仅通过一帧图像即可完成。ViBe初始化就是填充像素的样本集的过程但是由于在一帧图像中不可能包含像素点的时空分布信息,我们利用了相近像素点拥有相近的时空分布特性,具体来讲就是:对于一个像素点,随机的选择它的邻居点的像素值作为它的模型样本值。**$$M_0(x) = {v_0(y | y ∈NG(x))},t=0$$<strong>初始时刻,</strong>NG(x)**即为邻居点 。这种初始化方法优点是对于噪声的反应比较灵敏,计算量小速度快,可以很快的进行运动物体的检测,缺点是容易引入Ghost区域。</p><blockquote><ul><li>优点:不仅减少了背景模型建立的过程,还可以处理背景突然变化的情况,当检测到背景突然变化明显时,只需要舍弃原始的模型,重新利用变化后的首帧图像建立背景模型。</li><li>缺点:由于可能采用了运动物体的像素初始化样本集,容易引入拖影(Ghost)区域。</li></ul></blockquote><h3 id="前景检测过程"><a href="#前景检测过程" class="headerlink" title="前景检测过程"></a>前景检测过程</h3><p>1.背景模型为每个背景点存储一个样本集,然后每个新的像素值和样本集比较判断是否属于背景。<br>2.计算新像素值和样本集中每个样本值的距离,若距离小于阈值,则近似样本点数目增加。<br>3.如果近似样本点数目大于阈值,则认为新的像素点为背景。<br>4.检测过程主要由三个参数决定:<strong>样本集数目N</strong>,<strong>阈值min</strong>和<strong>距离相近判定的阈值R</strong>,一般具体实现,参数设置为<strong>N=20,min=2,R=20</strong>。</p><h3 id="背景模型的更新策略"><a href="#背景模型的更新策略" class="headerlink" title="背景模型的更新策略"></a>背景模型的更新策略</h3><ul><li>背景模型的更新就是使得背景模型能够适应背景的不断变化,比如光照的变化,背景物体的变更等等。</li><li>保守的更新策略:前景点永远不会被用来填充背景模型,会引起死锁,比如初始化的时候如果一块静止的区域被错误的检测为运动的,那么在这种策略下它永远会被当做运动的物体来对待;</li><li>Blind策略:对死锁不敏感,前景背景都可以来更新背景模型,缺点是缓慢移动的物体会融入背景中无法被检测出来。在本方法中采用的更新策略是保守的更新策略+前景点计数方法。</li><li>前景点计数:对像素点进行统计,如果某个像素点连续N次被检测为前景,则将其更新为背景点。</li><li>随机的子采样:在每一个新的视频帧中都去更新背景模型中的每一个像素点的样本值是没有必要的,当一个像素点被分类为背景点时,它有1/ φ的概率去更新背景模型。<br> 具体的更新方法:每一个背景点有<strong>1/ φ</strong>的概率去更新自己的模型样本值,同时也有<strong>1/ φ</strong>的概率去更新它的邻居点的模型样本值。更新邻居的样本值利用了像素值的空间传播特性,背景模型逐渐向外扩 散,这也有利于Ghost区域的更快的识别。同时当前景点计数达到临界值时将其变为背景,并有<strong>1/ φ</strong>的概率去更新自己的模型样本值。<br>在选择要替换的样本集中的样本值时候,我们是随机选取一个样本值进行更新,这样可以保证样本值的平滑的生命周期由于是随机的更新,这样一个样本值在时刻t不被更新的概率是 <strong>(N-1)/N</strong>,假设时间是连续的,那么在dt的时间过去后,样本值仍然保留的概率是<br>$$ P(t,t+dt) = (\frac{N-1}{N})^{(t+dt)-t} $$<br>也可以写作:<br>$$ P(t,t+dt) = e^{-ln(\frac{N}{N-1})dt} $$<br>这就表明一个样本值在模型中是否被替换与时间t无关 ,随机策略是合适的。</li></ul><p>1).<strong>无记忆更新策略</strong><br>每次确定需要更新像素点的背景模型时,以新的像素值随机取代该像素点样本集的一个样本值。<br>2).<strong>时间取样更新策略</strong><br>并不是每处理一帧数据,都需要更新处理,而是按一定的更新率更新背景模型。当一个像素点被判定为背景时,它有1/rate的概率更新背景模型。rate是时间采样因子,一般取值为16。<br>3).<strong>空间邻域更新策略</strong><br>针对需要更新像素点,随机的选择一个该像素点邻域的背景模型,以新的像素点更新被选中的背景模型。</p><h2 id="ViBe的改进"><a href="#ViBe的改进" class="headerlink" title="ViBe的改进"></a>ViBe的改进</h2><blockquote><ul><li>不同的距离函数和二值化标准</li><li>对更新掩膜和输出掩膜的分割进行适当的滤波操作</li><li>抑制邻域更新</li><li>检测闪烁的像素点</li><li>增加更新因子</li></ul></blockquote><h3 id="距离计算方法"><a href="#距离计算方法" class="headerlink" title="距离计算方法"></a>距离计算方法</h3><p>以圆椎模型代替原来的几何距离计算方法<br><img src="/2017/03/02/Vibe%E7%AE%97%E6%B3%95/vibe2.png" alt="vibe2"><br><strong>以自适应阈值代替原来固定的距离判定阈值,阈值大小与样本集的方差成正比,样本集方差越大,说明背景越复杂,判定阈值应该越大.</strong><br>$$ 0.5 \times σ_{m} \in [20,40]$$</p><h3 id="分离UpdatingMask和SegmentationMask"><a href="#分离UpdatingMask和SegmentationMask" class="headerlink" title="分离UpdatingMask和SegmentationMask"></a>分离UpdatingMask和SegmentationMask</h3><p>引入目标整体的概念,弥补基于像素级前景检测的不足。针对updating mask和segmentation mask采用不同尺寸的形态学处理方法,提高检测准确率。</p><h3 id="抑制邻域更新"><a href="#抑制邻域更新" class="headerlink" title="抑制邻域更新"></a>抑制邻域更新</h3><p>在updating mask里,计算像素点的梯度,根据梯度大小,确定是否需要更新邻域。梯度值越大,说明像素值变化越大,说明该像素值可能为前景,不应该更新。</p><h3 id="检测闪烁像素点"><a href="#检测闪烁像素点" class="headerlink" title="检测闪烁像素点"></a>检测闪烁像素点</h3><p>引入闪烁程度的概念,当一个像素点的updating label与前一帧的updating label不一样时,blinking level增加15,否则,减少1,然后根据blinking level的大小判断该像素点是否为闪烁点。闪烁像素主要出现在背景复杂的场景,如树叶、水纹等,这些场景会出现像素背景和前景的频繁变化,因而针对这些闪烁应该单独处理,可以作为全部作为背景。</p><h3 id="增加更新因子"><a href="#增加更新因子" class="headerlink" title="增加更新因子"></a>增加更新因子</h3><p>ViBe算法中,默认的更新因子是16,当背景变化很快时,背景模型无法快速的更新,将会导致前景检测的较多的错误。因而,需要根据背景变化快慢程度,调整更新因子的大小,可将更新因子分多个等级,如rate = 16,rate = 5,rate = 1。</p><h2 id="ViBe实验结果"><a href="#ViBe实验结果" class="headerlink" title="ViBe实验结果"></a>ViBe实验结果</h2><p> 在实验中,我们和其他的一些检测算法在检测准确率和算法的计算量方面都进行了比较,实验表明我们的方法检测效果明显要好很多,对于光照的变化和相机抖动等的效果都十分稳定,而且计算量非常小,内存占用较少,这就使得该方法能够用于嵌入手持照相机中。一些具体的实验效果和数据如下:</p><p> <img src="/2017/03/02/Vibe%E7%AE%97%E6%B3%95/vibe3.png" alt="vibe3"><br> <img src="/2017/03/02/Vibe%E7%AE%97%E6%B3%95/vibe4.png" alt="vibe4"><br> <img src="/2017/03/02/Vibe%E7%AE%97%E6%B3%95/vibe5.png" alt="vibe5"><br> <img src="/2017/03/02/Vibe%E7%AE%97%E6%B3%95/vibe6.png" alt="vibe6"> <img src="/2017/03/02/Vibe%E7%AE%97%E6%B3%95/vibe7.png" alt="vibe7"> </p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在这片文章中,我们提出了一个新的背静差算法-ViBe,和以前相比它具有三个不同点。首先,我们提出了一个新的分类模型。其次,我们介绍了ViBe如何初始化,它只需要一帧图像即可完成初始化,而其他的算法通常需要等待数秒去完成初始化,这对于嵌入照相机中的要求实时性比较高的和一些比较短的视频序列很有帮助。 最后,我们提出了自己的更新策略,相比于其他算法将样本值在模型中保存一个固定的时间,我们采用随机的替换更新样本值,经过证明这样可以保证样本值的一个指数衰减的平滑生命周期,并且可以使得背景模型很好的适应视频场景的变化,从而达到更好的检测效果。<br>通过一系列实验表明ViBe方法相比于其他的一些检测算法具有计算量小、内存占用少、处理速度快、检测效果好、有更快的Ghost区域消融速度和应对噪声稳定可靠的特点,并且非常适合嵌入照相机等要求计算量小和内存占用少的情境中。<br>效果图:<br> <img src="/2017/03/02/Vibe%E7%AE%97%E6%B3%95/vibe8.png" alt="vibe8"> </p><h2 id="算法的主要优势"><a href="#算法的主要优势" class="headerlink" title="算法的主要优势"></a>算法的主要优势</h2><blockquote><ul><li>内存占用少,一个像素需要作一次比较,占用一个字节的内存;</li><li>无参数法;</li><li>性能优于混合高斯,参数化方法,SACON等;</li><li>像素级算法,视频处理中的预处理关键步骤;</li><li>背景模型及时初始化;</li><li>具有较好的抗噪能力。</li></ul></blockquote><h2 id="Code"><a href="#Code" class="headerlink" title="Code"></a>Code</h2><p>算法执行效率测试程序,windows和Linux操作系统下的程序和c/c++文件都可以在作者官网下载,如下:</p><ul><li><strong>Windows</strong> and <strong>Linux</strong> users: a benchmarking program to evaluate the time needed by ViBe on your platform and on your own sequences! Download an archive zip archive <a href="http://hdl.handle.net/2268/145853">15 MB</a> to evaluate the time needed by <strong>ViBe</strong> on your platform (Windows or Linux <a href="http://www.winehq.org/">Wine</a>), and on your own sequences.</li><li>A program for <strong>Windows</strong> and <strong>Linux</strong>. Download an archive zip archive <a href="http://hdl.handle.net/2268/145853">16 MB</a> to use ViBe on Windows (or under Wine in Linux).The program allows you to: (1) save the result for your own images, (2) change the few parameters of ViBe to experiment with, and (3) reproduce our results.</li><li>Linux: link a C/C++ object file to your own code. We provide the object (compiled) code of ViBe for non-commercial applications. Under Linux, download the <a href="http://orbi.ulg.ac.be/handle/2268/145853">32 bits zip</a> file, or the <a href="http://orbi.ulg.ac.be/handle/2268/145853">64 bits zip</a> file. <a href="http://www2.ulg.ac.be/telecom/research/vibe/download.html">Details on this page</a>.</li></ul><p>当然这里也借鉴<a href="http://blog.csdn.net/zouxy09/article/details/9622285">zouxy09</a>大神给出的Mat格式的代码(在VS2010+OpenCV2.4.2中测试通过):</p><p>** ViBe.h **</p><figure class="highlight hpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></div></td><td class="code"><pre><code class="hljs hpp"><span class="hljs-meta">#<span class="hljs-keyword">pragma</span> once </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/opencv.hpp"</span> </span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv; <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br> <br><span class="hljs-meta">#<span class="hljs-keyword">define</span> NUM_SAMPLES 20 <span class="hljs-comment">//每个像素点的样本个数 </span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> MIN_MATCHES 2 <span class="hljs-comment">//#min指数 </span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> RADIUS 20 <span class="hljs-comment">//Sqthere半径 </span></span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> SUBSAMPLE_FACTOR 16 <span class="hljs-comment">//子采样概率 </span></span><br> <br> <br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ViBe_BGS</span> <br>{ <br><span class="hljs-keyword">public</span>: <br> <span class="hljs-built_in">ViBe_BGS</span>(<span class="hljs-type">void</span>); <br> ~<span class="hljs-built_in">ViBe_BGS</span>(<span class="hljs-type">void</span>); <br> <br> <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">init</span><span class="hljs-params">(<span class="hljs-type">const</span> Mat _image)</span></span>; <span class="hljs-comment">//初始化 </span><br> <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">processFirstFrame</span><span class="hljs-params">(<span class="hljs-type">const</span> Mat _image)</span></span>; <br> <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">testAndUpdate</span><span class="hljs-params">(<span class="hljs-type">const</span> Mat _image)</span></span>; <span class="hljs-comment">//更新 </span><br> <span class="hljs-function">Mat <span class="hljs-title">getMask</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span></span>{<span class="hljs-keyword">return</span> m_mask;}; <br> <br><span class="hljs-keyword">private</span>: <br> Mat m_samples[NUM_SAMPLES]; <br> Mat m_foregroundMatchCount; <br> Mat m_mask; <br>}; <br></code></pre></td></tr></table></figure><p>** ViBe.cpp **</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><opencv2/opencv.hpp></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"ViBe.h"</span> </span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv; <br> <br><span class="hljs-type">int</span> c_xoff[<span class="hljs-number">9</span>] = {<span class="hljs-number">-1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>}; <span class="hljs-comment">//x的邻居点 </span><br><span class="hljs-type">int</span> c_yoff[<span class="hljs-number">9</span>] = {<span class="hljs-number">-1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">1</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">0</span>}; <span class="hljs-comment">//y的邻居点 </span><br> <br>ViBe_BGS::<span class="hljs-built_in">ViBe_BGS</span>(<span class="hljs-type">void</span>) <br>{ <br> <br>} <br>ViBe_BGS::~<span class="hljs-built_in">ViBe_BGS</span>(<span class="hljs-type">void</span>) <br>{ <br> <br>} <br> <br><span class="hljs-comment">/**************** Assign space and init ***************************/</span> <br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">ViBe_BGS::init</span><span class="hljs-params">(<span class="hljs-type">const</span> Mat _image)</span> </span><br><span class="hljs-function"></span>{ <br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < NUM_SAMPLES; i++) <br> { <br> m_samples[i] = Mat::<span class="hljs-built_in">zeros</span>(_image.<span class="hljs-built_in">size</span>(), CV_8UC1); <br> } <br> m_mask = Mat::<span class="hljs-built_in">zeros</span>(_image.<span class="hljs-built_in">size</span>(),CV_8UC1); <br> m_foregroundMatchCount = Mat::<span class="hljs-built_in">zeros</span>(_image.<span class="hljs-built_in">size</span>(),CV_8UC1); <br>} <br> <br><span class="hljs-comment">/**************** Init model from first frame ********************/</span> <br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">ViBe_BGS::processFirstFrame</span><span class="hljs-params">(<span class="hljs-type">const</span> Mat _image)</span> </span><br><span class="hljs-function"></span>{ <br> RNG rng; <br> <span class="hljs-type">int</span> row, col; <br> <br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < _image.rows; i++) <br> { <br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> j = <span class="hljs-number">0</span>; j < _image.cols; j++) <br> { <br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> k = <span class="hljs-number">0</span> ; k < NUM_SAMPLES; k++) <br> { <br> <span class="hljs-comment">// Random pick up NUM_SAMPLES pixel in neighbourhood to construct the model </span><br> <span class="hljs-type">int</span> random = rng.<span class="hljs-built_in">uniform</span>(<span class="hljs-number">0</span>, <span class="hljs-number">9</span>); <br> <br> row = i + c_yoff[random]; <br> <span class="hljs-keyword">if</span> (row < <span class="hljs-number">0</span>) <br> row = <span class="hljs-number">0</span>; <br> <span class="hljs-keyword">if</span> (row >= _image.rows) <br> row = _image.rows - <span class="hljs-number">1</span>; <br> <br> col = j + c_xoff[random]; <br> <span class="hljs-keyword">if</span> (col < <span class="hljs-number">0</span>) <br> col = <span class="hljs-number">0</span>; <br> <span class="hljs-keyword">if</span> (col >= _image.cols) <br> col = _image.cols - <span class="hljs-number">1</span>; <br> <br> m_samples[k].<span class="hljs-built_in">at</span><uchar>(i, j) = _image.<span class="hljs-built_in">at</span><uchar>(row, col); <br> } <br> } <br> } <br>} <br> <br><span class="hljs-comment">/**************** Test a new frame and update model ********************/</span> <br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">ViBe_BGS::testAndUpdate</span><span class="hljs-params">(<span class="hljs-type">const</span> Mat _image)</span> </span><br><span class="hljs-function"></span>{ <br> RNG rng; <br> <br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < _image.rows; i++) <br> { <br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> j = <span class="hljs-number">0</span>; j < _image.cols; j++) <br> { <br> <span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">matches</span><span class="hljs-params">(<span class="hljs-number">0</span>)</span>, <span class="hljs-title">count</span><span class="hljs-params">(<span class="hljs-number">0</span>)</span></span>; <br> <span class="hljs-type">float</span> dist; <br> <br> <span class="hljs-keyword">while</span>(matches < MIN_MATCHES && count < NUM_SAMPLES) <br> { <br> dist = <span class="hljs-built_in">abs</span>(m_samples[count].<span class="hljs-built_in">at</span><uchar>(i, j) - _image.<span class="hljs-built_in">at</span><uchar>(i, j)); <br> <span class="hljs-keyword">if</span> (dist < RADIUS) <br> matches++; <br> count++; <br> } <br> <br> <span class="hljs-keyword">if</span> (matches >= MIN_MATCHES) <br> { <br> <span class="hljs-comment">// It is a background pixel </span><br> m_foregroundMatchCount.<span class="hljs-built_in">at</span><uchar>(i, j) = <span class="hljs-number">0</span>; <br> <br> <span class="hljs-comment">// Set background pixel to 0 </span><br> m_mask.<span class="hljs-built_in">at</span><uchar>(i, j) = <span class="hljs-number">0</span>; <br> <br> <span class="hljs-comment">// 如果一个像素是背景点,那么它有 1 / defaultSubsamplingFactor 的概率去更新自己的模型样本值 </span><br> <span class="hljs-type">int</span> random = rng.<span class="hljs-built_in">uniform</span>(<span class="hljs-number">0</span>, SUBSAMPLE_FACTOR); <br> <span class="hljs-keyword">if</span> (random == <span class="hljs-number">0</span>) <br> { <br> random = rng.<span class="hljs-built_in">uniform</span>(<span class="hljs-number">0</span>, NUM_SAMPLES); <br> m_samples[random].<span class="hljs-built_in">at</span><uchar>(i, j) = _image.<span class="hljs-built_in">at</span><uchar>(i, j); <br> } <br> <br> <span class="hljs-comment">// 同时也有 1 / defaultSubsamplingFactor 的概率去更新它的邻居点的模型样本值 </span><br> random = rng.<span class="hljs-built_in">uniform</span>(<span class="hljs-number">0</span>, SUBSAMPLE_FACTOR); <br> <span class="hljs-keyword">if</span> (random == <span class="hljs-number">0</span>) <br> { <br> <span class="hljs-type">int</span> row, col; <br> random = rng.<span class="hljs-built_in">uniform</span>(<span class="hljs-number">0</span>, <span class="hljs-number">9</span>); <br> row = i + c_yoff[random]; <br> <span class="hljs-keyword">if</span> (row < <span class="hljs-number">0</span>) <br> row = <span class="hljs-number">0</span>; <br> <span class="hljs-keyword">if</span> (row >= _image.rows) <br> row = _image.rows - <span class="hljs-number">1</span>; <br> <br> random = rng.<span class="hljs-built_in">uniform</span>(<span class="hljs-number">0</span>, <span class="hljs-number">9</span>); <br> col = j + c_xoff[random]; <br> <span class="hljs-keyword">if</span> (col < <span class="hljs-number">0</span>) <br> col = <span class="hljs-number">0</span>; <br> <span class="hljs-keyword">if</span> (col >= _image.cols) <br> col = _image.cols - <span class="hljs-number">1</span>; <br> <br> random = rng.<span class="hljs-built_in">uniform</span>(<span class="hljs-number">0</span>, NUM_SAMPLES); <br> m_samples[random].<span class="hljs-built_in">at</span><uchar>(row, col) = _image.<span class="hljs-built_in">at</span><uchar>(i, j); <br> } <br> } <br> <span class="hljs-keyword">else</span> <br> { <br> <span class="hljs-comment">// It is a foreground pixel </span><br> m_foregroundMatchCount.<span class="hljs-built_in">at</span><uchar>(i, j)++; <br> <br> <span class="hljs-comment">// Set background pixel to 255 </span><br> m_mask.<span class="hljs-built_in">at</span><uchar>(i, j) = <span class="hljs-number">255</span>; <br> <br> <span class="hljs-comment">//如果某个像素点连续N次被检测为前景,则认为一块静止区域被误判为运动,将其更新为背景点 </span><br> <span class="hljs-keyword">if</span> (m_foregroundMatchCount.<span class="hljs-built_in">at</span><uchar>(i, j) > <span class="hljs-number">50</span>) <br> { <br> <span class="hljs-type">int</span> random = rng.<span class="hljs-built_in">uniform</span>(<span class="hljs-number">0</span>, SUBSAMPLE_FACTOR); <br> <span class="hljs-keyword">if</span> (random == <span class="hljs-number">0</span>) <br> { <br> random = rng.<span class="hljs-built_in">uniform</span>(<span class="hljs-number">0</span>, NUM_SAMPLES); <br> m_samples[random].<span class="hljs-built_in">at</span><uchar>(i, j) = _image.<span class="hljs-built_in">at</span><uchar>(i, j); <br> } <br> } <br> } <br> } <br> } <br>} <br></code></pre></td></tr></table></figure><p>** Main.cpp **</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/opencv.hpp"</span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"ViBe.h"</span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span> </span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><cstdio></span> </span><br> <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv; <br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std; <br> <br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">int</span> argc, <span class="hljs-type">char</span>* argv[])</span> </span><br><span class="hljs-function"></span>{ <br> Mat frame, gray, mask; <br> VideoCapture capture; <br> capture.<span class="hljs-built_in">open</span>(<span class="hljs-string">"video.avi"</span>); <br> <br> <span class="hljs-keyword">if</span> (!capture.<span class="hljs-built_in">isOpened</span>()) <br> { <br> cout<<<span class="hljs-string">"No camera or video input!\n"</span><<endl; <br> <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>; <br> } <br> <br> ViBe_BGS Vibe_Bgs; <br> <span class="hljs-type">int</span> count = <span class="hljs-number">0</span>; <br> <br> <span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>) <br> { <br> count++; <br> capture >> frame; <br> <span class="hljs-keyword">if</span> (frame.<span class="hljs-built_in">empty</span>()) <br> <span class="hljs-keyword">break</span>; <br> <span class="hljs-built_in">cvtColor</span>(frame, gray, CV_RGB2GRAY); <br> <br> <span class="hljs-keyword">if</span> (count == <span class="hljs-number">1</span>) <br> { <br> Vibe_Bgs.<span class="hljs-built_in">init</span>(gray); <br> Vibe_Bgs.<span class="hljs-built_in">processFirstFrame</span>(gray); <br> cout<<<span class="hljs-string">" Training GMM complete!"</span><<endl; <br> } <br> <span class="hljs-keyword">else</span> <br> { <br> Vibe_Bgs.<span class="hljs-built_in">testAndUpdate</span>(gray); <br> mask = Vibe_Bgs.<span class="hljs-built_in">getMask</span>(); <br> <span class="hljs-built_in">morphologyEx</span>(mask, mask, MORPH_OPEN, <span class="hljs-built_in">Mat</span>()); <br> <span class="hljs-built_in">imshow</span>(<span class="hljs-string">"mask"</span>, mask); <br> } <br> <br> <span class="hljs-built_in">imshow</span>(<span class="hljs-string">"input"</span>, frame); <br> <br> <span class="hljs-keyword">if</span> ( <span class="hljs-built_in">cvWaitKey</span>(<span class="hljs-number">10</span>) == <span class="hljs-string">'q'</span> ) <br> <span class="hljs-keyword">break</span>; <br> } <br> <br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; <br>} <br></code></pre></td></tr></table></figure><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><ul><li><a href="http://www.telecom.ulg.ac.be/publi/Author/BARNICH-O.html">O. Barnich</a> and <a href="http://www2.ulg.ac.be/telecom/publi/Author/VAN-DROOGENBROECK-M.html">M. Van Droogenbroeck</a>. <a href="http://ieeexplore.ieee.org/search/freesrchabstract.jsp?tp=&arnumber=5672785">ViBe: A universal background subtraction algorithm for video sequences</a>. In IEEE Transactions on Image Processing, 20(6):1709-1724, June 2011. Also available on <a href="http://hdl.handle.net/2268/81248">the University site</a> in <a href="http://orbi.ulg.ac.be/bitstream/2268/145853/1/Barnich2011ViBe.pdf">PDF format</a>, and <a href="http://www.telecom.ulg.ac.be/publi/publications/barnich/Barnich2011ViBe/index.html">HTML format</a>. This paper contains a very <a href="http://www.telecom.ulg.ac.be/research/vibe/Barnich2010ViBe.bib">detailed pseudo-code description of the complete algorithm</a>. </li><li><a href="http://www2.ulg.ac.be/telecom/publi/Author/VAN-DROOGENBROECK-M.html">M. Van Droogenbroeck</a> and O. Paquot. Background Subtraction: Experiments and Improvements for ViBe. In Change Detection Workshop (CDW), Providence, Rhode Island, June 2012. Available on the University site in <a href="http://orbi.ulg.ac.be/bitstream/2268/117561/1/VanDroogenbroeck2012Background.pdf">PDF format</a> and <a href="http://www.telecom.ulg.ac.be/publi/publications/mvd/VanDroogenbroeck2012Background/index.html">HTML format</a>. </li><li><a href="http://www2.ulg.ac.be/telecom/publi/Author/BARNICH-O.html">O. Barnich</a> and <a href="http://www2.ulg.ac.be/telecom/publi/Author/VAN-DROOGENBROECK-M.html">M. Van Droogenbroeck</a>. ViBe: a powerful random technique to estimate the background in video sequences. In International Conference on Acoustics, Speech, and Signal Processing (ICASSP 2009), pages 945-948, April 2009. Available as a <a href="http://ieeexplore.ieee.org//xpls/abs_all.jsp?arnumber=4959741">IEEE publication</a> or on <a href="http://hdl.handle.net/2268/12087">the University site</a>.</li><li><a href="http://www.freepatentsonline.com/EP2015252.html">Patent description at the “freepatentsonline” web site</a>.</li></ul><h2 id="Performance"><a href="#Performance" class="headerlink" title="Performance"></a>Performance</h2><ul><li>An independant evaluation is available in S. Brutzer, B. Hoferlin, and G. Heidemann. <a href="http://ieeexplore.ieee.org/search/freesrchabstract.jsp?arnumber=5995508">Evaluation of background subtraction techniques for video surveillance</a>. In IEEE International Conference on Computer Vision and Pattern Recognition (CVPR), pages 1937-1944, Colorado Spring, USA, June 2011. </li><li>In their conclusions, they claim: “Considering these aspects, Barnich is a <strong>strong favorite</strong>, since it is simple and almost parameterless.”</li></ul>]]></content>
<categories>
<category>Opencv</category>
</categories>
<tags>
<tag>背景建模</tag>
<tag>前景检测</tag>
</tags>
</entry>
<entry>
<title>双线性插值</title>
<link href="/2017/03/01/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC/"/>
<url>/2017/03/01/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC/</url>
<content type="html"><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。<br>假设源图像大小为$m\times n$,目标图像为$a \times b$。那么两幅图像的边长比分别为:$m/a$和$n/b$。注意,通常这个比例不是整数,编程存储的时候要用浮点型。目标图像的第(i,j)个像素点(i行j列)可以通过边长比对应回源图像。其对应坐标为`(i * m/a,j * n/b)。显然,这个对应坐标一般来说不是整数,而非整数的坐标是无法在图像这种离散数据上使用的。双线性插值通过寻找距离这个对应坐标最近的四个像素点,来计算该点的值(灰度值或者RGB值)。</p><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><p>若图像为灰度图像,那么(i,j)点的灰度值的数学计算模型是:<br>$$ f(x,y)=b_{1}+b_{2}x+b_{3}y+b_{4}xy $$<br>其中$b_{1},b_{2},b_{3},b_{4}$是相关的系数。关于其的计算过程如下如下:<br> 如图,已知$Q_{12},Q_{22},Q_{11},Q_{21}$,但是要插值的点为P点,这就要用双线性插值了,首先在x轴方向上,对$R_1$和$R_2$两个点进行插值,这个很简单,然后根据$R_{1}$和$R_{2}$对$P$点进行插值,这就是所谓的双线性插值。<br><img src="/2017/03/01/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC/img1.png" alt="Bilinear interpolation"></p><p> 附:维基百科–双线性插值:<br><strong>双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。</strong><br>假如我们想得到未知函数f在点P=(x,y)的值,假设我们已知函数f在$Q_{11}=(x_{1},y_{1}),Q_{12}=(x_{1},y_{2}),Q_{21}=(x_{2},y_{1}),Q_{22}=(x_{2},y_{2})$四个点的值。<br>首先在 x 方向进行线性插值,得到<br>$$ f(R_{1})\approx \frac{x_{2}-x}{x_{2}-x_{1}} f(Q_{11}) + \frac{x-x_{1}}{x_{2}-x_{1}} f(Q_{21}) \leftarrow R_{1}=(x,y_{1}) $$<br>$$ f(R_{2})\approx \frac{x_{2}-x}{x_{2}-x_{1}} f(Q_{12}) + \frac{x-x_{1}}{x_{2}-x_{1}} f(Q_{22}) \leftarrow R_{1}=(x,y_{2}) $$<br>然后在 y 方向进行线性插值,得到<br>$$ f(P)\approx \frac{y_{2}-y}{y_{2}-y_{1}} f(R_{1}) + \frac{y-y_{1}}{y_{2}-y_{1}} f(R_{2}) $$<br>这样就得到所要的结果f(x,y),<br>$$ f(x,y) \approx \frac{Q_{11}}{(x_{2}-x_{1})(y_{2}-y_{1})} (x_{2}-x)(y_{2}-y) + \frac{Q_{21}}{(x_{2}-x_{1})(y_{2}-y_{1})} (x-x_{1})(y-y_{1}) $$<br>如果选择一个坐标系统使得 f 的四个已知点坐标分别为 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那么插值公式就可以化简为:<br>$$ f(x,y) \approx f(0,0)(1-x)(1-y)+f(1,0)x(1-y)+f(0,1)(1-x)y+f(1,1)xy $$<br>或者用矩阵运算表示为:<br>$$<br>f(x,y) \approx [1-x \ x]<br>\begin{bmatrix}<br> f(0,0) & f(0,1) \<br> f(1,0) & f(1,1)<br>\end{bmatrix}<br>\begin{bmatrix}<br> 1-y\<br> y<br>\end{bmatrix}<br>$$<br>这种插值方法的结果通常不是线性的,线性插值的结果与插值的顺序无关。首先进行 y 方向的插值,然后进行 x 方向的插值,所得到的结果是一样的。</p><h2 id="源码实现"><a href="#源码实现" class="headerlink" title="源码实现"></a>源码实现</h2><p>那么根据原理借助Opencv的Mat数据结构通过<code>c++</code>实现源码如下:</p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/objdetect/objdetect.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/highgui/highgui.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/imgproc/imgproc.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/opencv.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string"><stdio.h></span></span><br><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv;<br><br><span class="hljs-comment">//srcX = dstX * (srcWidth /dstWidth)</span><br><span class="hljs-comment">//srcY = dstY * (srcHeight/dstHeight)</span><br><span class="hljs-comment">//f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1);</span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-type">double</span> scale = <span class="hljs-number">0.5</span>;<br> <span class="hljs-type">double</span> srcX = <span class="hljs-number">0</span>;<br> <span class="hljs-type">double</span> srcY = <span class="hljs-number">0</span>;<br> <span class="hljs-type">int</span> Int_tmp_x = <span class="hljs-number">0</span>;<br> <span class="hljs-type">double</span> Dot_tmp_x = <span class="hljs-number">0</span>;<br> <span class="hljs-type">int</span> Int_tmp_y = <span class="hljs-number">0</span>;<br> <span class="hljs-type">double</span> Dot_tmp_y = <span class="hljs-number">0</span>;<br> Mat src = <span class="hljs-built_in">imread</span>(<span class="hljs-string">"./res/lena.jpg"</span>, <span class="hljs-number">0</span>);<br> <span class="hljs-function">Mat <span class="hljs-title">dst</span><span class="hljs-params">(scale * src.cols, scale * src.rows, src.type())</span></span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">double</span> x = <span class="hljs-number">0</span>; x < src.cols * scale - (<span class="hljs-type">int</span>)scale; x++)<br> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">double</span> y = <span class="hljs-number">0</span>; y < src.rows * scale - (<span class="hljs-type">int</span>)scale; y++)<br> {<br> <span class="hljs-keyword">if</span> (x == <span class="hljs-number">0</span> && y == <span class="hljs-number">0</span>)<br> dst.<span class="hljs-built_in">at</span><<span class="hljs-type">char</span>>(x, y) = src.<span class="hljs-built_in">at</span><<span class="hljs-type">char</span>>(x, y);<br> <span class="hljs-keyword">else</span><br> {<br> srcX = x / scale;<br> Int_tmp_x = (<span class="hljs-type">int</span>)srcX;<br> Dot_tmp_x = srcX - Int_tmp_x;<br><br> srcY = y / scale;<br> Int_tmp_y = (<span class="hljs-type">int</span>)srcY;<br> Dot_tmp_y = srcY - Int_tmp_y;<br><br> dst.<span class="hljs-built_in">at</span><uchar>(x, y) =<br> (<span class="hljs-number">1</span> - Dot_tmp_x)*(<span class="hljs-number">1</span> - Dot_tmp_y)*src.<span class="hljs-built_in">at</span><uchar>(Int_tmp_x, Int_tmp_y) +<br> (<span class="hljs-number">1</span> - Dot_tmp_x)*Dot_tmp_y*src.<span class="hljs-built_in">at</span><uchar>(Int_tmp_x, Int_tmp_y + <span class="hljs-number">1</span>) +<br> Dot_tmp_x*(<span class="hljs-number">1</span> - Dot_tmp_y)*src.<span class="hljs-built_in">at</span><uchar>(Int_tmp_x + <span class="hljs-number">1</span>, Int_tmp_y) +<br> Dot_tmp_x*Dot_tmp_y*src.<span class="hljs-built_in">at</span><uchar>(Int_tmp_x + <span class="hljs-number">1</span>, Int_tmp_y + <span class="hljs-number">1</span>);<br> }<br> }<br> }<br> cout << dst.<span class="hljs-built_in">size</span>() << endl;<br> <span class="hljs-comment">//imwrite("./res/lena_large.jpg", dst);</span><br> <span class="hljs-built_in">imshow</span>(<span class="hljs-string">"Dst"</span>,dst);<br> <span class="hljs-built_in">waitKey</span>();<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Opencv</category>
</categories>
<tags>
<tag>Opencv</tag>
<tag>双线性插值</tag>
</tags>
</entry>
<entry>
<title>极线约束</title>
<link href="/2017/02/27/%E6%9E%81%E7%BA%BF%E7%BA%A6%E6%9D%9F/"/>
<url>/2017/02/27/%E6%9E%81%E7%BA%BF%E7%BA%A6%E6%9D%9F/</url>
<content type="html"><![CDATA[<p>三维空间中一点P,投影到两个不同的平面$I_{1}、I_{2}$,投影点分别为$P_{1},P_{2}$。<br>$P、P_{1}、P_{2}$在三维空间内构成一个平面S。<br><strong>S与面$I_{1}$的交线$L_{1}$过$P_{1}$点,称之为对应于$P_{2}$的极线。同理S与$I_{2}$的交线称之为对应于$P_{1}$的极线(对应于左边图像点的极线在右边图像上,右边与之相同).</strong></p><p>如图:<br><img src="/2017/02/27/%E6%9E%81%E7%BA%BF%E7%BA%A6%E6%9D%9F/epipolar.jpg" alt="epipolar"></p><p>所谓极线约束就是说同一个点在两幅图像上的映射,已知左图映射点$P_{1}$,那么右图映射点$P_{2}$一定在相对于$P_1$的极线上,这样可以减少待匹配的点数量。</p><p>对于极线约束方程可以由以下来表示,三维向量x和x’存放相关点,F为一个3*3且秩为2的基础矩阵,那么:<br>$$ x’^T Fx = 0 $$<br>且左右两个平面的两条极线的方程为(注意 ’):<br>$$ Ie = Fx $$<br>$$ Ie’ = F^T x’ $$<br>对于两条直线,以连续点的方式存储:I和I’分别在左右两幅图像上,若他们俩有对应关系,那么认为他们两条直线之间的点依次的存在对应关系。<br>对于左侧图像中直线I上的一点x,那么对应于右侧图像中直线I’中的点x’可以按照下面方式求得:<br>对应于x的极线为I’e,I’e与直线I’的交点为x对应的点x’因此:<br>$$ x’=I’\times Ie’=I’\times (Fx) $$</p>]]></content>
<categories>
<category>基础知识</category>
</categories>
<tags>
<tag>多视图集合</tag>
<tag>极线约束</tag>
</tags>
</entry>
<entry>
<title>几何距离总汇</title>
<link href="/2017/02/26/%E5%87%A0%E4%BD%95%E8%B7%9D%E7%A6%BB%E6%80%BB%E6%B1%87/"/>
<url>/2017/02/26/%E5%87%A0%E4%BD%95%E8%B7%9D%E7%A6%BB%E6%80%BB%E6%B1%87/</url>
<content type="html"><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>在做分类时常常需要估算不同样本之间的相似性度量(SimilarityMeasurement),这时通常采用的方法就是计算样本间的**“距离”(Distance)**。采用什么样的方法计算距离是很讲究,甚至关系到分类的正确与否。<br>本文的目的就是对常用的相似性度量作一个汇总。</p><blockquote><ul><li><strong>欧氏距离</strong></li><li><strong>曼哈顿距离</strong></li><li><strong>切比雪夫距离</strong></li><li><strong>闵可夫斯基距离</strong></li><li><strong>标准化欧氏距离</strong></li><li><strong>马氏距离</strong></li><li><strong>夹角余弦</strong></li><li><strong>汉明距离</strong></li><li><strong>杰卡德距离&杰卡德相似系数</strong></li><li><strong>相关系数&相关距离</strong></li><li><strong>信息熵</strong></li></ul></blockquote><h2 id="欧氏距离-EuclideanDistance"><a href="#欧氏距离-EuclideanDistance" class="headerlink" title="欧氏距离(EuclideanDistance)"></a>欧氏距离(EuclideanDistance)</h2><p>欧氏距离是最易于理解的一种距离计算方法,源自欧氏空间中两点间的距离公式。</p><ul><li><p>二维平面上两点$a(x_1,y_1)$与$b(x_2,y_2)$间的欧氏距离:<br> $$ d_{12}=\sqrt{(x_{1}-x_{2})^2+(y_{1}-y_{2})^2} $$</p></li><li><p>三维空间两点$a(x_1,y_1,z_1)$与$b(x_2,y_2,z_2)$间的欧氏距离:<br> $$ d_{12}=\sqrt{(x_{1}-x_{2})^2+(y_{1}-y_{2})^2+(z_{1}-z_{2})^2} $$</p></li><li><p>两个n维向量$a(x_{11},x_{12},…,x_{1n)}$与 $b(x_{21},x_{22},…,x_{2n})$间的欧氏距离:<br> $$ d_{12}=\sqrt{\sum <em>{k=1} ^n (x</em>{1k}-x_{2k})^2} $$</p></li><li><p>表示成向量运算的形式:<br> $$ d_{12}=\sqrt{(a-b)(a-b)^\tau} $$</p></li></ul><h2 id="曼哈顿距离-ManhattanDistance"><a href="#曼哈顿距离-ManhattanDistance" class="headerlink" title="曼哈顿距离(ManhattanDistance)"></a>曼哈顿距离(ManhattanDistance)</h2><p>出租车几何或曼哈顿距离(Manhattan Distance)是由十九世纪的赫尔曼·闵可夫斯基所创词汇 ,是种使用在几何度量空间的几何学用语,用以标明两个点在标准坐标系上的绝对轴距总和。图中红线代表曼哈顿距离,绿色代表欧氏距离,也就是直线距离,而蓝色和黄色代表等价的曼哈顿距离。曼哈顿距离——两点在南北方向上的距离加上在东西方向上的距离,即$d(i,j)=|x_{i}-x_{j}|+|y_{i}-y_{j}|$。对于一个具有正南正北、正东正西方向规则布局的城镇街道,从一点到达另一点的距离正是在南北方向上旅行的距离加上在东西方向上旅行的距离,因此,曼哈顿距离又称为出租车距离。曼哈顿距离不是距离不变量,当坐标轴变动时,点间的距离就会不同。<br> <img src="/2017/02/26/%E5%87%A0%E4%BD%95%E8%B7%9D%E7%A6%BB%E6%80%BB%E6%B1%87/Manhattan.jpg" alt="Manhattan"></p><ul><li><p>二维平面两点$a(x_1,y_1)$与$b(x_2,y_2)$间的曼哈顿距离<br> $$ d_{12}=|x_{1}-x_{2}|+|x_{1}-x_{2}| $$</p></li><li><p>两个n维向量$a(x_{11},x_{12},…,x_{1n})$与$b(x_{21},x_{22},…,x_{2n})$间的曼哈顿距离<br> $$ d_{12}=\sum ^n <em>{k=1}|x</em>{1k}-x_{2k}| $$</p></li></ul><h2 id="切比雪夫距离-Chebyshev-Distance"><a href="#切比雪夫距离-Chebyshev-Distance" class="headerlink" title="切比雪夫距离 ( Chebyshev Distance )"></a>切比雪夫距离 ( Chebyshev Distance )</h2><p>在数学中,切比雪夫距离或是L∞度量是向量空间中的一种度量,二个点之间的距离定义是其各坐标数值差绝对值的最大值。在国际象棋中,国王走一步能够移动到相邻的8个方格中的任意一个。那么国王从格子$(x_1,y_1)$走到格子$(x_2,y_2)$最少需要多少步?你会发现最少步数总是$max(| x_2-x_1 | , | y_2-y_1 | )$ 步。有一种类似的一种距离度量方法叫切比雪夫距离。</p><ul><li><p>二维平面两点$a(x_1,y_1)$与$b(x_2,y_2)$间的切比雪夫距离<br> $$ d_{12} = max(|x_{1}-x_{2}|,|y_{1}-y_{2}|) $$</p></li><li><p>两个n维向量$a(x_{11},x_{12},…,x_{1n})$与$b(x_{21},x_{22},…,x_{2n})$间的切比雪夫距离<br> $$ d_{12} = max_{i}(|x_{1i}-x_{2i}|) $$</p></li><li><p>公式的另一种等价形式:<br> $$ d_{12} = lin_{k\rightarrow\infty}({\sum <em>{i=1} ^n}|x</em>{1i}-x_{2i}|^2)^{\frac{1}{k}} $$</p></li></ul><h2 id="闵可夫斯基距离-MinkowskiDistance"><a href="#闵可夫斯基距离-MinkowskiDistance" class="headerlink" title="闵可夫斯基距离( MinkowskiDistance )"></a>闵可夫斯基距离( MinkowskiDistance )</h2><p>闵氏距离不是一种距离,而是一组距离的定义。</p><ul><li><p>闵氏距离的定义<br> 两个n维变量$a(x_{11},x_{12},…,x_{1n})$与$b(x_{21},x_{22},…,x_{2n})$间的闵可夫斯基距离定义为:<br> $$ d_{12} = \sqrt[p]{\sum <em>{i=1} ^n|x</em>{1k}-x_{2k}|^p} $$<br> 其中p是一个变参数。<br> 当p=1时,就是曼哈顿距离<br> 当p=2时,就是欧氏距离<br> 当p→∞时,就是切比雪夫距离<br> 根据变参数的不同,闵氏距离可以表示一类的距离。</p></li><li><p>闵氏距离的缺点<br> 闵氏距离,包括曼哈顿距离、欧氏距离和切比雪夫距离都存在明显的缺点。<br> 举个例子:二维样本(身高,体重),其中身高范围是150<del>190,体重范围是50</del>60,有三个样本:a(180,50),b(190,50),c(180,60)。那么a与b之间的闵氏距离(无论是曼哈顿距离、欧氏距离或切比雪夫距离)等于a与c之间的闵氏距离,但是身高的10cm真的等价于体重的10kg么?因此用闵氏距离来衡量这些样本间的相似度很有问题。简单说来,闵氏距离的缺点主要有两个:</p><blockquote><ul><li>(1)将各个分量的量纲(scale)也就是“单位”当作相同的看待了。</li><li>(2)没有考虑各个分量的分布(期望,方差等)可能是不同的。</li></ul></blockquote></li></ul><h2 id="标准化欧氏距离-Standardized-Euclidean-distance"><a href="#标准化欧氏距离-Standardized-Euclidean-distance" class="headerlink" title="标准化欧氏距离(Standardized Euclidean distance)"></a>标准化欧氏距离(Standardized Euclidean distance)</h2><ul><li>标准欧氏距离的定义<br> 标准化欧氏距离是针对简单欧氏距离的缺点而作的一种改进方案。标准欧氏距离的思路:既然数据各维分量的分布不一样,好吧!那我先将各个分量都“标准化”到均值、方差相等吧。均值和方差标准化到多少呢?这里先复习点统计学知识吧,假设样本集X的均值(mean)为m,标准差(standarddeviation)为s,那么X的“标准化变量”表示为:<br> $$ X^* = \frac{x-m}{s} $$<br> 而且标准化变量的数学期望为0,方差为1。因此样本集的标准化过程(standardization)用公式描述就是:<br> <code>标准化后的值 = ( 标准化前的值 - 分量的均值 ) /分量的标准差</code><br> 经过简单的推导就可以得到两个n维向量a(x<sub>11</sub>,x<sub>12</sub>,…,x<sub>1n</sub>)与b(x<sub>21</sub>,x<sub>22</sub>,…,x<sub>2n</sub>)间的标准化欧氏距离的公式:<br> $$ d_{12} = \sqrt{\sum <em>{k=1} ^n (\frac{x</em>{1k}-x_{2k}}{s_{k}})^p} $$<br> 如果将方差的倒数看成是一个权重,这个公式可以看成是一种加权欧氏距离(WeightedEuclidean distance)。</li></ul><h2 id="马氏距离-MahalanobisDistance"><a href="#马氏距离-MahalanobisDistance" class="headerlink" title="马氏距离(MahalanobisDistance)"></a>马氏距离(MahalanobisDistance)</h2><ul><li>马氏距离定义</li></ul><p> 有M个样本向量X<sub>1</sub><~X<sub>m</sub>,协方差矩阵记为S,均值记为向量μ,则其中样本向量X到u的马氏距离表示为:<br>$$ D(x)=\sqrt{(X-\mu)^T S^{-1}(X-\mu)} $$</p><p> 而其中向量X<sub>i</sub>与X<sub>j</sub>之间的马氏距离定义为:<br>$$ D(X_{i},X_{j}) = \sqrt{(X_{i}-X_{j})^T S^{-1}(X_{i}-X_{j})} $$</p><p> 若协方差矩阵是单位矩阵(各个样本向量之间独立同分布),则公式就成了:(也就是欧式距离了)<br>$$ D(X_{i},X_{j}) = \sqrt{(X_{i}-X_{j})^T (X_{i}-X_{j})} $$</p><ul><li>马氏距离的优点:量纲无关,排除变量之间的相关性的干扰。</li></ul><h2 id="夹角余弦-Cosine"><a href="#夹角余弦-Cosine" class="headerlink" title="夹角余弦(Cosine)"></a>夹角余弦(Cosine)</h2><p>几何中夹角余弦可用来衡量两个向量方向的差异,机器学习中借用这一概念来衡量样本向量之间的差异。</p><ul><li><p>在二维空间中向量A(x1,y1)与向量B(x2,y2)的夹角余弦公式:<br> $$ cosθ = \frac{x_{1}x_{2}+y_{1}y_{2}}{\sqrt{x_{1}^2 + y_{1}^2}\sqrt{x_{2}^2 + y_{2}^2}} $$</p></li><li><p>两个n维样本点a(x<sub>11</sub>,x<sub>12</sub>,…,x<sub>1n</sub>)和b(x<sub>21</sub>,x<sub>22</sub>,…,x<sub>2n</sub>)的夹角余弦<br> 类似的,对于两个n维样本点a(x<sub>11</sub>,x<sub>12</sub>,…,x<sub>1n</sub>)和b(x<sub>21</sub>,x<sub>22</sub>,…,x<sub>2n</sub>),可以使用类似于夹角余弦的概念来衡量它们间的相似程度。<br> $$ cosθ = \frac{a\cdot b}{|a||b|} $$<br> 即:<br> $$ cosθ = \frac{\sum <em>{k=1} ^n x</em>{1k}x_{2k}}{\sqrt{\sum <em>{k=1} ^n x</em>{1k}^2}\sqrt{\sum <em>{k=1} ^n x</em>{2k}^2}} $$<br> 夹角余弦取值范围为[-1,1]。夹角余弦越大表示两个向量的夹角越小,夹角余弦越小表示两向量的夹角越大。当两个向量的方向重合时夹角余弦取最大值1,当两个向量的方向完全相反夹角余弦取最小值-1。</p></li></ul><h2 id="汉明距离-Hammingdistance"><a href="#汉明距离-Hammingdistance" class="headerlink" title="汉明距离(Hammingdistance)"></a>汉明距离(Hammingdistance)</h2><ul><li>汉明距离的定义<br> 两个等长字符串s1与s2之间的汉明距离定义为将其中一个变为另外一个所需要作的最小替换次数。例如字符串“1111”与“1001”之间的汉明距离为2。</li><li>应用:信息编码(为了增强容错性,应使得编码间的最小汉明距离尽可能大)。</li></ul><h2 id="杰卡德相似系数-Jaccardsimilarity-coefficient"><a href="#杰卡德相似系数-Jaccardsimilarity-coefficient" class="headerlink" title="杰卡德相似系数(Jaccardsimilarity coefficient)"></a>杰卡德相似系数(Jaccardsimilarity coefficient)</h2><ul><li>杰卡德相似系数<br> 两个集合A和B的交集元素在A,B的并集中所占的比例,称为两个集合的杰卡德相似系数,用符号J(A,B)表示。<br> $$ J(A,B) = \frac{A\cap B}{A\cup B} $$<br> 杰卡德相似系数是衡量两个集合的相似度一种指标。</li><li>杰卡德距离<br> 与杰卡德相似系数相反的概念是杰卡德距离(Jaccarddistance)。杰卡德距离可用如下公式表示:<br> $$ J_{\delta}=1-J(A,B)=\frac{|A\cup B|-|A\cap B|}{|A\cup B|} $$<br> 杰卡德距离用两个集合中不同元素占所有元素的比例来衡量两个集合的区分度。</li><li>杰卡德相似系数与杰卡德距离的应用<br> 可将杰卡德相似系数用在衡量样本的相似度上。样本A与样本B是两个n维向量,而且所有维度的取值都是0或1。例如:A(0111)和B(1011)。我们将样本看成是一个集合,1表示集合包含该元素,0表示集合不包含该元素。<blockquote><ul><li>p:样本A与B都是1的维度的个数<blockquote><ul><li>q:样本A是1,样本B是0的维度的个数</li><li>r:样本A是0,样本B是1的维度的个数</li><li>s:样本A与B都是0的维度的个数<br> 那么样本A与B的杰卡德相似系数可以表示为:<br> 这里p+q+r可理解为A与B的并集的元素个数,而p是A与B的交集的元素个数。而样本A与B的杰卡德距离表示为:<br> $$ J = \frac{p}{p+q+r} $$</li></ul></blockquote></li></ul></blockquote></li></ul><h2 id="相关系数-Correlation-coefficient-与相关距离-Correlation-distance"><a href="#相关系数-Correlation-coefficient-与相关距离-Correlation-distance" class="headerlink" title="相关系数( Correlation coefficient )与相关距离(Correlation distance)"></a>相关系数( Correlation coefficient )与相关距离(Correlation distance)</h2><ul><li>相关系数的定义<br> $$ \rho_{XY} = \frac{Cov(X,Y)}{\sqrt{D(X)}\sqrt{D(Y)}} = \frac{E((X-EX)(Y-EY))}{\sqrt{D(X)}\sqrt{D(Y)}}$$<br> 相关系数是衡量随机变量X与Y相关程度的一种方法,相关系数的取值范围是[-1,1]。相关系数的绝对值越大,则表明X与Y相关度越高。当X与Y线性相关时,相关系数取值为1(正线性相关)或-1(负线性相关)。</li><li>相关距离的定义<br> $$ D_{xy}=1-\rho_{XY} $$</li></ul><h2 id="信息熵-Information-Entropy"><a href="#信息熵-Information-Entropy" class="headerlink" title="信息熵(Information Entropy)"></a>信息熵(Information Entropy)</h2><p>信息熵并不属于一种相似性度量。那为什么放在这篇文章中啊?这个。。。我也不知道。 (╯▽╰)信息熵是衡量分布的混乱程度或分散程度的一种度量。分布越分散(或者说分布越平均),信息熵就越大。分布越有序(或者说分布越集中),信息熵就越小。<br>计算给定的样本集X的信息熵的公式:<br>$$ Entropy(X)=\sum <em>{i=1} ^n -p</em>{i} log_{2} p_{i} $$<br>参数的含义:</p><blockquote><ul><li>n:样本集X的分类数</li><li>$p_i$:X中第i类元素出现的概率<br>信息熵越大表明样本集S分类越分散,信息熵越小则表明样本集X分类越集中。。当S中n个分类出现的概率一样大时(都是1/n),信息熵取最大值$log2(n)$。当X只有一个分类时,信息熵取最小值0</li></ul></blockquote><p>参考资料:<br>[1]<a href="http://www.google.com.hk/ggblog/googlechinablog/2006/07/12_4010.html">吴军. 数学之美 系列 12 -余弦定理和新闻的分类.</a><br>[2]<a href="http://en.wikipedia.org/wiki/Jaccard_index">Wikipedia. Jaccard index.</a><br>[3]<a href="http://en.wikipedia.org/wiki/Hamming_distance">Wikipedia. Hamming distance</a><br>[4]<a href="http://junjun0595.blog.163.com/blog/static/969561420100633351210/">求马氏距离(Mahalanobisdistance )matlab版</a><br>[5]<a href="http://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient">Pearson product-momentcorrelation coefficient</a></p>]]></content>
<categories>
<category>基础知识</category>
</categories>
<tags>
<tag>几何距离</tag>
</tags>
</entry>
<entry>
<title>非参数估计</title>
<link href="/2017/02/26/%E9%9D%9E%E5%8F%82%E6%95%B0%E4%BC%B0%E8%AE%A1/"/>
<url>/2017/02/26/%E9%9D%9E%E5%8F%82%E6%95%B0%E4%BC%B0%E8%AE%A1/</url>
<content type="html"><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p><strong>非参数估计</strong>和<strong>参数估计</strong>(即,<strong>监督参数估计</strong>和<strong>非监督参数估计</strong>)共同构成了概率密度估计方法。非参数估计是一种对先验知识要求最少,完全依靠训练数据进行估计,而且可以用于任意形状密度估计的方法。常见的非参数估计方法有以下几种:</p><h2 id="直方图"><a href="#直方图" class="headerlink" title="直方图"></a>直方图</h2><p>把数据的值域分成若干相等的区间,数据按照区间分为若干组,每组形成一个矩形区域,矩形的高和该组数据的数量成正比,其底为所属区间,将这些矩形依次排列组成的图行就是直方图。它提供数据一个直观的形象,但只适合低纬数据,当维度较高时,直方图所需的空间将随维度的增加呈指数级增加。</p><h2 id="核密度估计"><a href="#核密度估计" class="headerlink" title="核密度估计"></a>核密度估计</h2><p>原理和直方图类似,是一种平滑的无参数密度估计方法。对于=于一组数据,把数据的值域分为若干相等的区间,每个区间称为一个<code>bin</code>,数据就按区间分成若干组,每组数据的个数比上总参数个数的比率就是每个<strong>bin</strong>的概率值。相对于直方图,它多了一个用于平滑数据的核函数。并且密度估计方法适用于中小规模的数据集,可以很快产生一个渐进无偏的密度估计,有良好概率统计性质。具体来说,如果数据为$X_{1},X_{2},… …X_{3}$,在任意点的X的一种核密度估计为:<br>$$ f(x) = \frac{1}{nh}\mathbf{\sum^n_{i=1}}K(\frac{x-\mathbf{x_i}}{h}) $$<br>这里K(x)称为核函数(Kernel function),它通常满足对称性以及<br>$$ \int K(x){\rm d}x=1 $$<br>可以看出,核函数是一种权函数,该个估计利用数据点x_{i}到x的距离来决定x_{i}在估计点x的密度时所起的作用,距离x越近的样本所起的作用越大,其权值越大。式子中的h表示带宽,h越大,估计密度函数就越平滑,但偏差可能比较大。如果h选择的比较小,那么估计的密度曲线和样本拟合的比较好,但是可能很不光滑,一般以均方误差最小为选择原则。需要说明的是,核密度估计也可以扩展到多维空间,当x为d维空间向量时,多核密度估计可以表示为:<br>$$ f(x)=\frac{1}{nh^d}\mathbf{\sum ^n_{i=1}}K(\frac{x-\mathbf{x_{i}}}{h}) $$<br>常用的核函数有:</p><blockquote><ul><li>高斯</li><li>余弦</li><li>均匀</li><li>三角等式</li></ul></blockquote><h2 id="K临近估计"><a href="#K临近估计" class="headerlink" title="K临近估计"></a>K临近估计</h2><p>核密度估计的加权时以数据点到x的欧式距离为基准来进行的,而K临近估计是无论欧氏距离是多少,只要是离x点的最近的k个点其中之一就可以加权。即K临近密度估计可以表示为下列形式:<br>$$ f(x)=\frac{k-1}{2n\mathbf{d_{k}}(x)} $$<br>其中$d_{k}(x)$表示点x到所有样本点的欧式距离,而且$d_{1}(x)<=d_{2}(x)<=……<=d_{k}(x)$,显然k的取值决定了估计密度曲线的光滑程度,<strong>k越大越光滑</strong></p>]]></content>
<categories>
<category>基础知识</category>
</categories>
<tags>
<tag>基础知识</tag>
<tag>数理统计</tag>
</tags>
</entry>
<entry>
<title>飞盘运动模型的物理学</title>
<link href="/2017/02/24/%E9%A3%9E%E7%9B%98%E8%BF%90%E5%8A%A8%E6%A8%A1%E5%9E%8B%E7%9A%84%E7%89%A9%E7%90%86%E5%AD%A6/"/>
<url>/2017/02/24/%E9%A3%9E%E7%9B%98%E8%BF%90%E5%8A%A8%E6%A8%A1%E5%9E%8B%E7%9A%84%E7%89%A9%E7%90%86%E5%AD%A6/</url>
<content type="html"><![CDATA[<h2 id="飞盘的物理学"><a href="#飞盘的物理学" class="headerlink" title="飞盘的物理学"></a>飞盘的物理学</h2><blockquote><ul><li>V. R. Morrison Physics Department, Mount Allison University, Sackville, NB Canada E4L 1E6 (<a href="mailto:vrmrrsn@mta.ca">vrmrrsn@mta.ca</a>)</li><li><a href="http://web.mit.edu/womens-ult/www/smite/frisbee_physics.pdf?origin=publication_detail">参考原文</a></li></ul></blockquote><h2 id="摘要"><a href="#摘要" class="headerlink" title="摘要"></a>摘要</h2><p>飞盘是常见的娱乐方式和运动,虽然这些飞盘背后的物理常常被视为是理所当然。飞盘的运转主要基于两个主要物理概念,<strong>空气动力学升降</strong>和**陀螺稳定性(陀螺仪惯性)**。当飞盘在空中飞行时,可以被看作是一个翼,通过伯努利原理来控制提升力的大小,使飞盘悬浮在空中。 施加的各种力不在盘的中心,因此需要防止飞盘在高角动量上翻转。该角动量抑制由各种力引起的扭矩。 编写了使用欧拉方法的计算机程序来模拟飞盘飞行的轨迹。用不同的攻角和不同的距离和高度观察到飞盘达到不同位置的试验。</p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>几十年来,飞盘已经成为所有年龄段人们广泛使用的娱乐源。他们创造了许多新的运动(终极飞盘,高尔夫球和其他),飞盘每年的销售量比棒球,篮球和足球加起来还多。这些简单的塑料飞盘可以飞行很长的距离,似乎飞盘悬停在空中时能抵抗重力。大多数人认为飞盘背后的物理是理所当然的,但飞盘的运动模型一般可以只使用<strong>气动升降</strong>和<strong>陀螺稳定性</strong>两个物理概念来解释。现代飞盘的历史可以追溯到1871年William Russell Frisbie在Bridgeportd的Connecticut开了一个名叫The Frisbie Pie的小面包公司。Frisbie的馅饼在附近的耶鲁大学很受欢迎,学生们开始享受折腾空的饼罐。这开始变得受欢迎,学生开始称罐子为“Frisbies”和抛罐子的行为叫“Frisbieing”。第一次生产实际的塑料飞盘是在1958年Fred Morrison买了“飞盘”的专利后,但直到1958年当Wham-O发布了他们的商标“飞盘”时才真正流行起来。最近,Hummel已经对飞盘进行了研究以确定作用在飞盘上的所有力的大小以及涉及投掷飞盘的生物力学的系数<a href="http://biosport.ucdavis.edu/research-projects/frisbee-flight-simulation-and-throw-biomechanics/HummelThesis.pdf">Hummel(2003)</a>。此外,还有一个未发表的研究,研究飞盘的其他方面,包含直接从飞盘飞行获取数据的方法。</p><h2 id="飞盘飞行理论"><a href="#飞盘飞行理论" class="headerlink" title="飞盘飞行理论"></a>飞盘飞行理论</h2><p>飞盘包含的两个主要物理概念是空气动力学升降(或伯努利原理)和陀螺稳定性。旋转飞盘可以被看作是自由飞行中的机翼,伯努利原理是提供升力的原因,并且飞盘的角动量提供其稳定性。</p><h3 id="空气动力"><a href="#空气动力" class="headerlink" title="空气动力"></a>空气动力</h3><p>作用在飞盘上的两个主要空气动力是阻力和升力。为了确定这些力的大小,将使用两种非常常见的物理关系来描述。为了计算阻力,我们首先必须找到系统的雷诺数,以便知道应用哪个阻力关系。雷诺数<code>R</code>由下式给出:<br>$$ \mathbf{R} = \mathbf{\frac{ρvd}{η}} \tag1 $$</p><blockquote><ul><li><strong>ρ</strong> 表示流体的密度(对于飞盘,流体即空气)<br>(海平面,空气的密度约等于$1.23 kg/m^3$)</li><li><strong>v</strong> 表示飞盘与流体(空气)的相对速度</li><li><strong>d</strong> 表示物体的特征尺寸(对于飞盘,特征尺寸是它的直径)</li><li><strong>η</strong> 表示流体(空气)的粘稠度<br>通常取$η=1.73×10^−5 Ns/m^2$</li></ul></blockquote><p>对于这个量级的雷诺数,将使用Prandtl关系来计算阻力$F_d$,并且由下式给出:<br>$$ \mathbf{F}_{d} = \mathbf{\frac{-\mathbf{C_{D}}ρπ r^2v^2}{2}} = \mathbf{\frac{-\mathbf{C_{D}}ρπ Av^2}{2}} \tag2 $$<br>系数$C_{D}$是随物体变化的阻力系数,并且在<a href="http://biosport.ucdavis.edu/research-projects/frisbee-flight-simulation-and-throw-biomechanics/HummelThesis.pdf">Hummel(2003)</a>中给出为仅取决于攻角α的二次函数。攻角α是飞盘平面和相对速度矢量之间的夹角。<br>$$ \mathbf{C_{D}} = \mathbf{C_{D0}}+\mathbf{C_{Dα}}(\mathbf{α}-\mathbf{α_{0}}) \tag3$$<br>系数$C_{D0}$,$α_{0}$和$C_{Dα}$是常数并且取决于飞盘本身。<br>飞盘受到的升力与飞机机翼上的升力非常相似,并且使用伯努利原理计算。伯努利原理众所周知,它表明流体在同一流线上的任何点的速度,压力和高度之间存在着关系。流体以快速流动的那一侧压力比慢速流动的那一侧压力更低。公式:<br>$$ \frac{\mathbf{v^2_{1}}}{2} + \frac{\mathbf{p}<em>{1}}{ρ} + \mathbf{g}\mathbf{h</em>{1}} = \frac{\mathbf{v^2_{2}}}{2} + \frac{\mathbf{p_{2}}}{ρ} + \mathbf{g}\mathbf{h_{2}} \tag4 $$</p><blockquote><ul><li><strong>v</strong> 流体速度</li><li><strong>p</strong> 流体压力</li><li><strong>ρ</strong> 流体密度</li><li><strong>g</strong> 重力加速度</li><li><strong>h</strong> 流体高度</li></ul></blockquote><p>下标1和2指沿着相同流线的流体中的不同点。这个方程通常被称为伯努利方程。在上方流动的空气和在飞盘下方流动的空气之间的高度差是可忽略的,因此两个高度相关项抵消。我们还假定飞盘上面流动的空气的速度与飞盘下面的空气的速度成正比,因为路径长度的差是恒定的(i.e. $v_{1}=C_{v_{2}}$),等式如下:<br>$$ \frac{\mathbf{C^2v^2_{2}}}{2} + \frac{\mathbf{p_{1}}}{ρ} = \frac{\mathbf{v^2_{2}}}{2} + \frac{\mathbf{p_{2}}}{ρ} \tag5 $$</p><p>设置$F_{L}/A=p_{1}-p_{2},F_{L}-升力,A-飞盘面积,F_{L}$等式:<br>$$ \mathbf{F_{L}}=\frac{1}{2}\mathbf{ρ}\mathbf{v^2A}\mathbf{C_{L}} \tag6 $$<br>在确定(6)所需的整个步骤中,系数C是并入系数C_{L}。C_{L}是<a href="http://biosport.ucdavis.edu/research-projects/frisbee-flight-simulation-and-throw-biomechanics/HummelThesis.pdf">Hummel(2003)</a>给出的攻角α的线性函数。$$ \mathbf{C_{L}}=\mathbf{C_{L0}}+\mathbf{C_{Lα}}\mathbf{α} \tag7 $$</p><blockquote><p>$C_{L0}$和$C_{Lα}$是取决于飞盘物理特性的常数。</p></blockquote><h3 id="陀螺稳定性"><a href="#陀螺稳定性" class="headerlink" title="陀螺稳定性"></a>陀螺稳定性</h3><p>人们发现趣飞盘的旋转是飞盘飞行的机制中的必要组成部分,没有旋转,飞盘只会像落叶一样飘向地面,不能产生长距离,稳定的飞行。 这是由于前面部分中描述的空气动力不直接集中在飞盘上的事实造成的。一般来说,盘前半部的升力稍大于后半部的升力,这会在飞盘上产生扭矩(见图1)。<br> <img src="/2017/02/24/%E9%A3%9E%E7%9B%98%E8%BF%90%E5%8A%A8%E6%A8%A1%E5%9E%8B%E7%9A%84%E7%89%A9%E7%90%86%E5%AD%A6/frisbee_1.png" alt="frisbee"><br> 图1.偏心中心压力(COP)和质心(COM)导致施加在飞盘上的扭矩<br>当飞盘不旋转时,这个小扭矩会使飞盘前半部分翻转,会使飞盘稳定性降低。当飞盘高速旋转抛出的,那么它有较大角动量,使其具有在正或负垂直方向上的向量。当施加小扭矩时,扭矩矢量指向右侧侧面(当从后面观察时)。这可以使用righthad规则:<br>$$ \vec{\tau}=\vec{r}×\vec{F} \tag8 $$<br>来源于:<br>$$ \vec{\tau} = \frac{d\vec{L}}{dt} \tag{9}$$<br>角动量向量将开始向右驱动,即投掷飞盘时飞盘会靠右或者靠左飞行,正是这个原因。因此,给予飞盘的初始角动量越大,它的飞行会更稳定。</p><h2 id="飞盘飞行的数值建模"><a href="#飞盘飞行的数值建模" class="headerlink" title="飞盘飞行的数值建模"></a>飞盘飞行的数值建模</h2><p>为了模拟飞盘的飞行,编写了一个<strong>C++<strong>程序,该程序使用了应用于上一节中所描述的力的数值技术欧拉法(参见附录中的代码)。 为了实现这一点,不同的力被分成水平和垂直分量,并且每个分量应用欧拉方法。应当注意,在该模型中,假设飞盘被给予足够的初始旋转,以便保持稳定的飞行。 在应用欧拉方法中,飞盘的轨迹被分成离散的时间步长</strong>Δt</strong>,并且在每个步骤处新的水平速度v和水平位置x,定义:<br>$$ \mathbf{v_{i+1}} = \mathbf{v_{i}} + \mathbf{Δv} \tag{10} $$<br>$$ \mathbf{x}_{i+1} = \mathbf{x}_{i} + \mathbf{Δx} \tag{11}$$<br>其中<strong>Δv</strong>和<strong>Δx</strong>分别是速度和位置的变化。与方程(11)类似的方程可以与垂直方向一起使用位置y,而不是x。<br>通过求解得到<strong>Δv</strong>以下关系:<br>$$ \mathbf{F_{x}} = \mathbf{F_{D}} \tag{12}$$<br>$$ \mathbf{m}\frac{Δ\mathbf{v_{x}}}{Δt} = \frac{1}{2}\mathbf{ρ}{\mathbf{v_{x}}}^2\mathbf{A}\mathbf{C_{D} }\tag{13} $$<br>$$ \mathbf{Δv_{x}} = \frac{1}{2m}\mathbf{ρ}{\mathbf{v_{x}}}^2\mathbf{A}\mathbf{C_{D}}\mathbf{Δt} \tag{14} $$<br>其中<strong>F_{D}<strong>是阻力,那么:<br>$$ \mathbf{F_{y}} = \mathbf{F_{g}} + \mathbf{F_{l}} \tag{15}$$<br>$$ \mathbf{m}\frac{Δ\mathbf{v_{x}}}{Δt} = \mathbf{mg}\frac{1}{2}\mathbf{ρ}{\mathbf{v_{x}}}^2\mathbf{A}\mathbf{C_{D}} \tag{16}$$<br>$$ \mathbf{Δv_{y}} = (g+\frac{1}{2m}\mathbf{ρ}{\mathbf{v_{x}}}^2\mathbf{A}\mathbf{C_{L}})Δt \tag{17}$$<br>其中下标</strong>x</strong>和<strong>y</strong>分别表示<strong>水平速度</strong>和<strong>垂直速度</strong>,F_{g}是重力。<br><strong>Δx</strong>和<strong>Δy</strong>,如</p><p>$$ \mathbf{Δx}=\mathbf{v_{x}}\mathbf{Δt} \tag{18} $$<br>$$ \mathbf{Δy}=\mathbf{v_{y}}\mathbf{Δt} \tag{19} $$</p><p>写的程序包含一个模拟方法,需要五个输入参数,初始y(垂直)位置,初始y速度,初始x速度(初始x位置始终设置为零),攻角(以度为单位)和Δt。 除攻角以外的所有单位均为SI单位。在所有试验中,使用Δt= 0.001s。 试验Δt= 0.001s和测试Δt= 0.002s和结果之间的差异是不明显的。 (注:在模拟中的系数值使用:$C_{D0} = 0.08,C_{Dα}= 2.72,C_{L0} = .15,C_{Lα}= 1.4)$</p><h2 id="仿真结果"><a href="#仿真结果" class="headerlink" title="仿真结果"></a>仿真结果</h2><p>当进行模拟时,所有试验的初始高度为<strong>1m</strong>,初始x速度为<strong>14m/s</strong>,以及0m/s的初始y速度,这被认为是标准投掷飞盘的速度。试验使用从<strong>0°</strong>到<strong>45°</strong>的攻角。这是唯一的参数被改变,因为升力系数和阻力仅取决于攻角。从图2,图3可以看出和图4攻角对飞盘轨迹有很大的影响。具有低攻角(一般小于5度)升力非常小,飞盘快速下落到地面后短距离,通常小于20米。具有较大的攻角,更大的升力显然和飞盘更大高度和进一步行进,达40米。达到最大距离的攻角约为12°它行驶40米,最大高度为7.7米。在较大的攻角发射飞盘高度会更高,但由于阻力大,飞行距离较小。试验进行不同的初始速度跟随与具有初始速度的趋势相似的趋势速度为<strong>14m/s</strong>。在较低的速度下,提升力大大降低,飞盘会更快地掉到地上。在更高的速度提升力更大,轨迹越来越长。</p><p> <img src="/2017/02/24/%E9%A3%9E%E7%9B%98%E8%BF%90%E5%8A%A8%E6%A8%A1%E5%9E%8B%E7%9A%84%E7%89%A9%E7%90%86%E5%AD%A6/frisbee_2.png" alt="frisbee"><br> Figure 3. Plot of height(m) versus distance(m) for a Frisbee with initial velocity 14<br>m/s and angle of attack 7.5◦<br> <img src="/2017/02/24/%E9%A3%9E%E7%9B%98%E8%BF%90%E5%8A%A8%E6%A8%A1%E5%9E%8B%E7%9A%84%E7%89%A9%E7%90%86%E5%AD%A6/frisbee_3.png" alt="frisbee"><br> Figure 2. Plot of height(m) versus distance(m) for a Frisbee with initial velocity 14<br>m/s and angle of attack 5◦<br> <img src="/2017/02/24/%E9%A3%9E%E7%9B%98%E8%BF%90%E5%8A%A8%E6%A8%A1%E5%9E%8B%E7%9A%84%E7%89%A9%E7%90%86%E5%AD%A6/frisbee_4.png" alt="frisbee"><br> Figure 4. Plot of height(m) versus distance(m) for a Frisbee with initial velocity 14<br>m/s and angle of attack 10◦ </p><p>C++代码如下:<br><code>Frisbee.hpp文件</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">ifndef</span> FRISBEE_H</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> FRISBEE_H</span><br><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> PI 3.1415926</span><br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Frisbee</span><br>{<br><span class="hljs-keyword">public</span>:<br> <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">simulate</span><span class="hljs-params">(<span class="hljs-type">double</span> y0, <span class="hljs-type">double</span> vx0, <span class="hljs-type">double</span> vy0, <span class="hljs-type">double</span> alpha, <span class="hljs-type">double</span> deltaT)</span></span>;<br><span class="hljs-keyword">private</span>:<br> <span class="hljs-type">static</span> <span class="hljs-type">double</span> x;<br> <span class="hljs-type">static</span> <span class="hljs-type">double</span> y;<br> <span class="hljs-type">static</span> <span class="hljs-type">double</span> vx;<br> <span class="hljs-type">static</span> <span class="hljs-type">double</span> vy;<br> <span class="hljs-type">static</span> <span class="hljs-type">const</span> <span class="hljs-type">double</span> g; <span class="hljs-comment">//</span><br> <span class="hljs-type">static</span> <span class="hljs-type">const</span> <span class="hljs-type">double</span> m; <span class="hljs-comment">//</span><br> <span class="hljs-type">static</span> <span class="hljs-type">const</span> <span class="hljs-type">double</span> RHO; <span class="hljs-comment">//</span><br> <span class="hljs-type">static</span> <span class="hljs-type">const</span> <span class="hljs-type">double</span> AREA; <span class="hljs-comment">//</span><br> <span class="hljs-type">static</span> <span class="hljs-type">const</span> <span class="hljs-type">double</span> CL0; <span class="hljs-comment">//</span><br> <span class="hljs-type">static</span> <span class="hljs-type">const</span> <span class="hljs-type">double</span> CLA; <span class="hljs-comment">//</span><br> <span class="hljs-type">static</span> <span class="hljs-type">const</span> <span class="hljs-type">double</span> CD0; <span class="hljs-comment">//</span><br> <span class="hljs-type">static</span> <span class="hljs-type">const</span> <span class="hljs-type">double</span> CDA; <span class="hljs-comment">//</span><br> <span class="hljs-type">static</span> <span class="hljs-type">const</span> <span class="hljs-type">double</span> ALPHA0; <span class="hljs-comment">//</span><br>};<br><br><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span><br></code></pre></td></tr></table></figure><p><code>Frisbee.cpp文件</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string">"Frisbee.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><math.h></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><iostream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><fstream></span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><string></span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><br><span class="hljs-type">const</span> <span class="hljs-type">double</span> Frisbee::g = <span class="hljs-number">-9.81</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">double</span> Frisbee::m = <span class="hljs-number">0.175</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">double</span> Frisbee::RHO = <span class="hljs-number">1.23</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">double</span> Frisbee::AREA = <span class="hljs-number">0.0568</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">double</span> Frisbee::CL0 = <span class="hljs-number">0.1</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">double</span> Frisbee::CLA = <span class="hljs-number">1.4</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">double</span> Frisbee::CD0 = <span class="hljs-number">0.08</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">double</span> Frisbee::CDA = <span class="hljs-number">2.72</span>;<br><span class="hljs-type">const</span> <span class="hljs-type">double</span> Frisbee::ALPHA0 = <span class="hljs-number">-4</span>;<br><span class="hljs-type">double</span> Frisbee::vx = <span class="hljs-number">0</span>;<br><span class="hljs-type">double</span> Frisbee::vy = <span class="hljs-number">0</span>;<br><span class="hljs-type">double</span> Frisbee::x = <span class="hljs-number">0</span>;<br><span class="hljs-type">double</span> Frisbee::y = <span class="hljs-number">0</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">Frisbee::simulate</span><span class="hljs-params">(<span class="hljs-type">double</span> y0, <span class="hljs-type">double</span> vx0, <span class="hljs-type">double</span> vy0, <span class="hljs-type">double</span> alpha, <span class="hljs-type">double</span> deltaT)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-comment">//通过S. A. Hummel给出的公式计算计算升力系数</span><br> <span class="hljs-type">double</span> cl = CL0 + CLA*alpha*PI / <span class="hljs-number">180</span>;<br> <span class="hljs-comment">//通过Prantl’s关系计算阻力</span><br> <span class="hljs-type">double</span> cd = CD0 + CDA*<span class="hljs-built_in">pow</span>((alpha - ALPHA0)*PI / <span class="hljs-number">180</span>, <span class="hljs-number">2</span>);<br> <span class="hljs-comment">//初始化x位置</span><br> x = <span class="hljs-number">0</span>;<br> <span class="hljs-comment">//初始化y位置</span><br> y = y0;<br> <span class="hljs-comment">//初始化x方向速度 vx = vx0</span><br> vx = vx0;<br> <span class="hljs-comment">//初始化x方向速度 vy = vy0</span><br> vy = vy0;<br> <span class="hljs-keyword">try</span>{<br> <span class="hljs-comment">//将计算结果输出到一个word表格</span><br> <span class="hljs-comment">//PrintWriter pw = new PrintWriter(new BufferedWriter</span><br> <span class="hljs-comment">// (new FileWriter("frisbee.csv")));</span><br><br> ofstream fout;<br> fout.<span class="hljs-built_in">open</span>(<span class="hljs-string">"frisbee.csv"</span>, ios_base::app);<br> <span class="hljs-keyword">if</span> (!fout.<span class="hljs-built_in">is_open</span>())<br> {<br> <span class="hljs-keyword">throw</span> <span class="hljs-string">"文件打开失败!"</span>;<br> }<br><br> <span class="hljs-comment">//循环计数</span><br> <span class="hljs-type">int</span> k = <span class="hljs-number">0</span>;<br> <span class="hljs-comment">//一个while循环,执行迭代直到y位置达到零(即飞盘击中地面)</span><br> <span class="hljs-keyword">while</span> (y > <span class="hljs-number">0</span>){<br> <span class="hljs-comment">//在y方向上的速度变化获得将净力等于重力和提升力的总和并求解delta v</span><br> <span class="hljs-type">double</span> deltavy = (RHO*<span class="hljs-built_in">pow</span>(vx, <span class="hljs-number">2</span>)*AREA*cl / <span class="hljs-number">2</span> / m + g)*deltaT;<br> <span class="hljs-comment">//通过求解增量v的力方程(存在的唯一力是牵引力)获得x方向上的速度变化。</span><br> <span class="hljs-type">double</span> deltavx = -RHO*<span class="hljs-built_in">pow</span>(vx, <span class="hljs-number">2</span>)*AREA*cd*deltaT;<br> <span class="hljs-comment">//新的位置和速度使用简单的力学计算。</span><br> vx = vx + deltavx;<br> vy = vy + deltavy;<br> x = x + vx*deltaT;<br> y = y + vy*deltaT;<br> <span class="hljs-comment">//只有每十次迭代的输出将被发送到电子表格,以便减少数据点的数量。</span><br> <span class="hljs-keyword">if</span> (k % <span class="hljs-number">10</span> == <span class="hljs-number">0</span>){<br> <span class="hljs-comment">//pw.print(x + "," + y + "," + vx);</span><br> fout << x << <span class="hljs-string">","</span> << y << <span class="hljs-string">","</span> << vx;<br> <span class="hljs-comment">//pw.println();</span><br> <span class="hljs-comment">//fout << "\r\n";</span><br> fout << <span class="hljs-string">"\n"</span>;<br> fout.<span class="hljs-built_in">flush</span>();<br> }<br> k++;<br> }<br> fout.<span class="hljs-built_in">close</span>();<br> }<br> <span class="hljs-built_in">catch</span> (string s){<br> cout << s + <span class="hljs-string">"Error, file frisbee.csv is in use."</span>;<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> Frisbee::<span class="hljs-built_in">simulate</span>(<span class="hljs-number">1</span>, <span class="hljs-number">16.5</span>, <span class="hljs-number">0</span>, <span class="hljs-number">6</span>, <span class="hljs-number">0.001</span>);<br><br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>因为实际需要进行翻译,如有不足,欢迎指正.</p>]]></content>
<categories>
<category>数学建模</category>
</categories>
<tags>
<tag>飞盘</tag>
<tag>建模</tag>
</tags>
</entry>
<entry>
<title>Opencv计算算法运行时间</title>
<link href="/2017/02/23/Opencv%E8%AE%A1%E7%AE%97%E7%AE%97%E6%B3%95%E8%BF%90%E8%A1%8C%E6%97%B6%E9%97%B4/"/>
<url>/2017/02/23/Opencv%E8%AE%A1%E7%AE%97%E7%AE%97%E6%B3%95%E8%BF%90%E8%A1%8C%E6%97%B6%E9%97%B4/</url>
<content type="html"><![CDATA[<h2 id="最初的C接口是-cvGetTickCount-和cvGetTickFrequency-,但注意,此时得到的单位是us级的统计时间。使用如下"><a href="#最初的C接口是-cvGetTickCount-和cvGetTickFrequency-,但注意,此时得到的单位是us级的统计时间。使用如下" class="headerlink" title="最初的C接口是 cvGetTickCount()和cvGetTickFrequency(),但注意,此时得到的单位是us级的统计时间。使用如下:"></a>最初的C接口是<code> cvGetTickCount()</code>和<code>cvGetTickFrequency()</code>,<strong>但注意,此时得到的单位是us级的统计时间。</strong>使用如下:</h2><figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">double</span> t = (<span class="hljs-type">double</span>)<span class="hljs-built_in">cvGetTickCount</span>();<br><span class="hljs-comment">// 算法过程</span><br>t = (<span class="hljs-type">double</span>)<span class="hljs-built_in">cvGetTickCount</span>() - t;<br><span class="hljs-built_in">printf</span>( <span class="hljs-string">"run time = %gms\n"</span>, t/(<span class="hljs-built_in">cvGetTickFrequency</span>()*<span class="hljs-number">1000</span>) );<br><span class="hljs-built_in">printf</span>( <span class="hljs-string">"run time = %gs\n"</span>, t/(<span class="hljs-built_in">cvGetTickFrequency</span>()*<span class="hljs-number">1000000</span>) );<br></code></pre></td></tr></table></figure><h2 id="在Opencv到了2-x之后在命名空间cv中又多了几个函数"><a href="#在Opencv到了2-x之后在命名空间cv中又多了几个函数" class="headerlink" title="在Opencv到了2.x之后在命名空间cv中又多了几个函数:"></a>在Opencv到了2.x之后在命名空间cv中又多了几个函数:</h2><ul><li><code>getTickCount()</code>:返回CPU自某个时间(如启动电脑)以来走过的时钟周期数。</li><li><code>getTickFrequency()</code>返回CPU一秒中所走的时钟周期数。所以可以以秒为单位对某运算时间计时。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-type">double</span> start = <span class="hljs-built_in">static_cast</span><<span class="hljs-type">double</span>>(<span class="hljs-built_in">getTickCount</span>());<br><span class="hljs-type">double</span> time = ((<span class="hljs-type">double</span>)<span class="hljs-built_in">getTickCount</span>() - start) / <span class="hljs-built_in">getTickFrequency</span>();<br>cout << <span class="hljs-string">"所用时间为:"</span> << time << <span class="hljs-string">"秒"</span> << endl;<br></code></pre></td></tr></table></figure><h2 id="同时C-接口中还将上面的函数封装为了一个类TickMeter,使用起来比较方便,示例:"><a href="#同时C-接口中还将上面的函数封装为了一个类TickMeter,使用起来比较方便,示例:" class="headerlink" title="同时C++接口中还将上面的函数封装为了一个类TickMeter,使用起来比较方便,示例:"></a>同时C++接口中还将上面的函数封装为了一个类TickMeter,使用起来比较方便,示例:</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span><span class="hljs-string"><opencv2/contrib/contrib.hpp></span> </span><br><br>TickMeter tm;<br>tm.<span class="hljs-built_in">start</span>();<br><span class="hljs-comment">//算法程序</span><br>tm.<span class="hljs-built_in">stop</span>();<br>cout << tm.<span class="hljs-built_in">getCounter</span>() << endl;<span class="hljs-comment">//返回内部计数器值</span><br>cout << tm.<span class="hljs-built_in">getTimeMicro</span>() << endl;<span class="hljs-comment">//返回us</span><br>cout << tm.<span class="hljs-built_in">getTimeMilli</span>() << endl;<span class="hljs-comment">//返回ms</span><br>cout << tm.<span class="hljs-built_in">getTimeSec</span>() << endl;<span class="hljs-comment">//返回s</span><br>cout << tm.<span class="hljs-built_in">getTimeTicks</span>() << endl;<span class="hljs-comment">//返回计数次数</span><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Opencv</category>
</categories>
<tags>
<tag>Opencv</tag>
<tag>计算时间</tag>
</tags>
</entry>
<entry>
<title>Opencv图像增强算法实现(直方图均衡化、Laplace、Log、Gamma)</title>
<link href="/2017/02/21/Opencv%E5%9B%BE%E5%83%8F%E5%A2%9E%E5%BC%BA%E7%AE%97%E6%B3%95%E5%AE%9E%E6%B3%95/"/>
<url>/2017/02/21/Opencv%E5%9B%BE%E5%83%8F%E5%A2%9E%E5%BC%BA%E7%AE%97%E6%B3%95%E5%AE%9E%E6%B3%95/</url>
<content type="html"><![CDATA[<p>增强图像中的有用信息,它可以是一个失真的过程,其目的是要改善图像的视觉效果,针对给定图像的应用场合,有目的地强调图像的整体或局部特性,将原来不清晰的图像变得清晰或强调某些感兴趣的特征,扩大图像中不同物体特征之间的差别,抑制不感兴趣的特征,使之改善图像质量、丰富信息量,加强图像判读和识别效果,满足某些特殊分析的需要。</p><h2 id="直方图均衡化图像增强"><a href="#直方图均衡化图像增强" class="headerlink" title="直方图均衡化图像增强"></a>直方图均衡化图像增强</h2><p>图像对比度增强的方法可以分成两类:一类是<strong>直接对比度增强方法</strong>;另一类是<strong>间接对比度增强方法</strong>。<br><strong>直方图拉伸</strong>和<strong>直方图均衡化</strong>是两种最常见的间接对比度增强方法。<br><strong>直方图拉伸</strong>是通过对比度拉伸对直方图进行调整,从而“扩大”前景和背景灰度的差别,以达到增强对比度的目的,这种方法可以利用线性或非线性的方法来实现。<br><strong>直方图均衡化</strong>则通过使用累积函数对灰度值进行“调整”以实现对比度的增强。</p><blockquote><ul><li><strong>优点</strong>:这种方法对于背景和前景都太亮或者太暗的图像非常有用,这种方法尤其是可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。这种方法的一个主要优势是它是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大。</li></ul></blockquote><ul><li><strong>缺点</strong>:缺点是它对处理的数据不加选择,它可能会增加背景杂讯的对比度并且降低有用信号的对比度;变换后图像的灰度级减少,某些细节消失;某些图像,如直方图有高峰,经处理后对比度不自然的过分增强。<br> 彩色图像的直方图均衡化实现:<figure class="highlight cpp"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></div></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/opencv.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/highgui/highgui.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/imgproc/imgproc.hpp"</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> Mat src = <span class="hljs-built_in">imread</span>(<span class="hljs-string">"elephant.jpg"</span>);<br> Mat src_RGB[<span class="hljs-number">3</span>], dst;<br> <span class="hljs-built_in">split</span>(src, src_RGB);<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">3</span>; i++)<br> {<br> <span class="hljs-built_in">equalizeHist</span>(src_RGB[i], src_RGB[i]);<br> }<br><br> <span class="hljs-built_in">merge</span>(src_RGB, <span class="hljs-number">3</span>, dst);<br> <span class="hljs-built_in">imwrite</span>(<span class="hljs-string">"elephant_hist.jpg"</span>, dst);<br> <span class="hljs-built_in">waitKey</span>();<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure>直方图均衡化前后对比如下:<br><img src="/2017/02/21/Opencv%E5%9B%BE%E5%83%8F%E5%A2%9E%E5%BC%BA%E7%AE%97%E6%B3%95%E5%AE%9E%E6%B3%95/elephant_hist.jpg" alt="直方图均衡化"></li></ul><h2 id="Laplace图像增强"><a href="#Laplace图像增强" class="headerlink" title="Laplace图像增强"></a>Laplace图像增强</h2><p>拉普拉斯算子可以增强局部的图像对比度。<br>Laplace 8邻域卷积核:<br> 0 -1 0<br>-1 5 -1<br> 0 -1 0<br>采用filter2D函数实现对图像的卷积:</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/opencv.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/highgui/highgui.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/imgproc/imgproc.hpp"</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> Mat src = <span class="hljs-built_in">imread</span>(<span class="hljs-string">"cow.jpg"</span>);<br> Mat dst;<br> Mat kernel = (<span class="hljs-built_in">Mat_</span><<span class="hljs-type">int</span>>(<span class="hljs-number">3</span>, <span class="hljs-number">3</span>) << <span class="hljs-number">0</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">5</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">0</span>);<br> <span class="hljs-built_in">filter2D</span>(src, dst, src.<span class="hljs-built_in">depth</span>(), kernel);<br> <span class="hljs-built_in">imshow</span>(<span class="hljs-string">"dst"</span>,dst);<br> <span class="hljs-built_in">waitKey</span>();<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>图像进行Laplace卷积前后对比如下:<br><img src="/2017/02/21/Opencv%E5%9B%BE%E5%83%8F%E5%A2%9E%E5%BC%BA%E7%AE%97%E6%B3%95%E5%AE%9E%E6%B3%95/cow_laplace.jpg" alt="Laplace变换"></p><h2 id="对数Log变换图像增强"><a href="#对数Log变换图像增强" class="headerlink" title="对数Log变换图像增强"></a>对数Log变换图像增强</h2><p>对数变换可以将图像的低灰度值部分扩展,显示出低灰度部分更多的细节,将其高灰度值部分压缩,减少高灰度值部分的细节,从而达到强调图像低灰度部分的目的。变换方法:<br> $$ s=c·\log_v(1+v·r) \longrightarrow r\in [0,1]$$<br>对于不同的底数,底数越大,对低灰度部分的扩展就越强,对高灰度部分的压缩也就越强。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/opencv.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/highgui/highgui.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/imgproc/imgproc.hpp"</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv;<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> Mat src = <span class="hljs-built_in">imread</span>(<span class="hljs-string">"darknight.jpg"</span>);<br> <span class="hljs-function">Mat <span class="hljs-title">dst</span><span class="hljs-params">(src.size(), CV_32FC3)</span></span>;<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < src.rows; i++)<br> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">0</span>; j < src.cols; j++)<br> {<br> dst.<span class="hljs-built_in">at</span><Vec3f>(i, j)[<span class="hljs-number">0</span>] = <span class="hljs-built_in">log</span>(<span class="hljs-number">1</span> + src.<span class="hljs-built_in">at</span><Vec3b>(i, j)[<span class="hljs-number">0</span>]);<br> dstdst.<span class="hljs-built_in">at</span><Vec3f>(i, j)[<span class="hljs-number">1</span>] = <span class="hljs-built_in">log</span>(<span class="hljs-number">1</span> + src.<span class="hljs-built_in">at</span><Vec3b>(i, j)[<span class="hljs-number">1</span>]);<br> dst.<span class="hljs-built_in">at</span><Vec3f>(i, j)[<span class="hljs-number">2</span>] = <span class="hljs-built_in">log</span>(<span class="hljs-number">1</span> + src.<span class="hljs-built_in">at</span><Vec3b>(i, j)[<span class="hljs-number">2</span>]);<br> }<br> }<br> <span class="hljs-built_in">normalize</span>(dst, dst, <span class="hljs-number">0</span>, <span class="hljs-number">255</span>, CV_MINMAX);<br> <span class="hljs-built_in">convertScaleAbs</span>(dst, dst);<br> <span class="hljs-built_in">imshow</span>(<span class="hljs-string">"darknight_log.jpg"</span>,dst);<br> <span class="hljs-built_in">waitKey</span>();<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>对图片进行Log变换前后对比如下:<br><img src="/2017/02/21/Opencv%E5%9B%BE%E5%83%8F%E5%A2%9E%E5%BC%BA%E7%AE%97%E6%B3%95%E5%AE%9E%E6%B3%95/darknight_log.jpg" alt="Log变换"></p><h2 id="伽马变换的图像增强"><a href="#伽马变换的图像增强" class="headerlink" title="伽马变换的图像增强"></a>伽马变换的图像增强</h2><p>伽马变换主要用于图像的校正,将灰度过高或者灰度过低的图片进行修正,增强对比度。变换公式就是对原图像上每一个像素值做乘积运算:<br> $$ s=cr^\gamma \longrightarrow r\in[0,1] $$<br>伽马变换对于图像对比度偏低,并且整体亮度值偏高(对于于相机过曝)情况下的图像增强效果明显。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs cpp"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/opencv.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/highgui/highgui.hpp"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"opencv2/imgproc/imgproc.hpp"</span></span><br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> std;<br><span class="hljs-keyword">using</span> <span class="hljs-keyword">namespace</span> cv;<br><span class="hljs-meta">#<span class="hljs-keyword">define</span> Gamma 3</span><br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> Mat src = <span class="hljs-built_in">imread</span>(<span class="hljs-string">"src.jpg"</span>);<br> <span class="hljs-function">Mat <span class="hljs-title">dst</span><span class="hljs-params">(src.size(), CV_32FC3)</span></span>;<br> <br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> i = <span class="hljs-number">0</span>; i < src.rows;i++)<br> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> j = <span class="hljs-number">0</span>; j < src.cols; j++)<br> {<br> dst.<span class="hljs-built_in">at</span><Vec3f>(i, j)[<span class="hljs-number">0</span>] = <span class="hljs-built_in">pow</span>(src.<span class="hljs-built_in">at</span><Vec3b>(i, j)[<span class="hljs-number">0</span>], Gamma);<br> dst.<span class="hljs-built_in">at</span><Vec3f>(i, j)[<span class="hljs-number">1</span>] = <span class="hljs-built_in">pow</span>(src.<span class="hljs-built_in">at</span><Vec3b>(i, j)[<span class="hljs-number">1</span>], Gamma);<br> dst.<span class="hljs-built_in">at</span><Vec3f>(i, j)[<span class="hljs-number">2</span>] = <span class="hljs-built_in">pow</span>(src.<span class="hljs-built_in">at</span><Vec3b>(i, j)[<span class="hljs-number">2</span>], Gamma);<br> }<br> }<br> <span class="hljs-built_in">normalize</span>(dst, dst, <span class="hljs-number">0</span>, <span class="hljs-number">255</span>, CV_MINMAX);<br> <span class="hljs-built_in">convertScaleAbs</span>(dst, dst);<br><br> <span class="hljs-built_in">waitKey</span>();<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br>}<br></code></pre></td></tr></table></figure><p>图像进行Laplace卷积前后对比如下:<br><img src="/2017/02/21/Opencv%E5%9B%BE%E5%83%8F%E5%A2%9E%E5%BC%BA%E7%AE%97%E6%B3%95%E5%AE%9E%E6%B3%95/gamma_cvt.jpg" alt="伽马变换"></p>]]></content>
<categories>
<category>Opencv</category>
</categories>
<tags>