﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-yx-随笔分类-随机算法</title><link>http://www.cppblog.com/csu-yx-2013/category/18786.html</link><description>Algorithm Study And So On</description><language>zh-cn</language><lastBuildDate>Sun, 15 Sep 2013 10:07:12 GMT</lastBuildDate><pubDate>Sun, 15 Sep 2013 10:07:12 GMT</pubDate><ttl>60</ttl><item><title>如何生成均匀随机排列(等概率生成排列)</title><link>http://www.cppblog.com/csu-yx-2013/archive/2012/02/26/166565.html</link><dc:creator>yx</dc:creator><author>yx</author><pubDate>Sun, 26 Feb 2012 08:07:00 GMT</pubDate><guid>http://www.cppblog.com/csu-yx-2013/archive/2012/02/26/166565.html</guid><wfw:comment>http://www.cppblog.com/csu-yx-2013/comments/166565.html</wfw:comment><comments>http://www.cppblog.com/csu-yx-2013/archive/2012/02/26/166565.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/csu-yx-2013/comments/commentRss/166565.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/csu-yx-2013/services/trackbacks/166565.html</trackback:ping><description><![CDATA[&nbsp; &nbsp; &nbsp; 这个算法的应用，比如洗牌，这个大家都非常熟悉。很久以前用的是最原始的方法，就是一直rand()未出现的牌，直至生成所有的牌。<br />这当然是一个while(1)循环，很烂的算法吧。后面听说直接交换牌，打乱即可了。但是打乱后生成的排列是随机的么，是等可能随机的么。<br />其实，这个问题上算法导论上早已经有了答案了，看过算法导论之后觉得没看之前真的是算法修养太差了。<br />&nbsp; &nbsp; &nbsp; 算法的伪代码如下图所示：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.cppblog.com/images/cppblog_com/csu-yx/RandomizeInPlace.jpg" width="419" height="126" alt="" /><br />&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; &nbsp; &nbsp; 具体c++实现如下：<br />#include &lt;stdio.h&gt;<div>#include &lt;stdlib.h&gt;</div><div>#include &lt;assert.h&gt;</div><div>#include &lt;time.h&gt;</div><div></div><div>// void Swap(int&amp; nOne, int&amp; nTwo)</div><div>// {</div><div>// <span style="white-space:pre">	</span>nOne = nOne + nTwo;</div><div>// <span style="white-space:pre">	</span>nTwo = nOne - nTwo;</div><div>// <span style="white-space:pre">	</span>nOne = nOne - nTwo;</div><div>// }</div><div></div><div>void Swap(int&amp; nOne, int&amp; nTwo)</div><div>{</div><div>&nbsp; &nbsp; int nTemp;</div><div>&nbsp; &nbsp; nTemp = nOne;</div><div>&nbsp; &nbsp; nOne = nTwo;</div><div>&nbsp; &nbsp; nTwo = nTemp;</div><div>}</div><div></div><div>//返回一个在区间[nBeg, nEnd]内的随机数</div><div>int Random(int nBeg, int nEnd)</div><div>{</div><div>&nbsp; &nbsp; assert(nEnd &gt;= nBeg);</div><div>&nbsp; &nbsp; if (nBeg == nEnd)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return nBeg;</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; else</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return rand() % (nEnd - nBeg + 1) + nBeg;</div><div>&nbsp; &nbsp; }</div><div>}</div><div></div><div>void RandomizeInPlace(int* pnA, int nLen)</div><div>{</div><div>&nbsp; &nbsp; static bool s_bFirst = false;</div><div>&nbsp; &nbsp; if (!s_bFirst)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; srand(time(NULL));</div><div>&nbsp; &nbsp; &nbsp; &nbsp; s_bFirst = true;</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; for (int i = 0; i &lt; nLen; ++i)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; Swap(pnA[i], pnA[Random(i, nLen - 1)]);</div><div>&nbsp; &nbsp; }</div><div>}</div><div></div><div>int main()</div><div>{</div><div>&nbsp; &nbsp; int nArray[20];</div><div>&nbsp; &nbsp; int i, j;</div><div></div><div></div><div>&nbsp; &nbsp; for (i = 1; i &lt;= 20; ++i)</div><div>&nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; int nCnt = i;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; while (nCnt--)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (j = 0; j &lt; i; ++j)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nArray[j] = j;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RandomizeInPlace(nArray, i);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (j = 0; j &lt; i; ++j)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("%d ", nArray[j]);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("\n");</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; printf("\n");</div><div>&nbsp; &nbsp; }</div><div></div><div>&nbsp; &nbsp; return 0;</div><div>}</div><div><br />&nbsp;&nbsp;&nbsp;运行效果图片如下：<br /><img src="http://www.cppblog.com/images/cppblog_com/csu-yx/RandomizeInPlaceRun.jpg" width="445" height="428" alt="" /><br />&nbsp;&nbsp;&nbsp;根据运行结果大致就可以感觉到，生成的排列都是随机的。<br />&nbsp; &nbsp;这里要多说一句那就是我注释的那个交换函数其实是有bug的，也许这才是不提倡使用这个交换方法的真正原因，而不仅仅是<br />难以理解。用同一个变量去调用该函数，会将该变量置0，而不是保持原来的值！！！<br /><br />&nbsp; &nbsp;至于如何证明这个算法生成的均匀随机的排列，可以参考算法导论5.3节最后一部分。<br />&nbsp; &nbsp;证明的大致思路是利用循环不变式的证明方法：证明i次循环后得到某个排列的概论是(n -i)! / n!，那么n次循环后得到最终那个排列的<br />概论就是1/n!，这样就证明了该算法能够得到均匀随机排列。<br />&nbsp; &nbsp;这个算法其实就是随机化算法的一种，其实快排也有所谓的随机化版本，改动的地方只是随机选择了中轴元素而已，这个<br />在算法导论上也有介绍。</div><img src ="http://www.cppblog.com/csu-yx-2013/aggbug/166565.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/csu-yx-2013/" target="_blank">yx</a> 2012-02-26 16:07 <a href="http://www.cppblog.com/csu-yx-2013/archive/2012/02/26/166565.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>