两次笔试都遇到这个问题,出镜率挺高(事实上任何一种一页纸能写完的算法都可能在面试笔试中遇到,加油骚年!)
分析:本质上是1~n的随机序列问题
方法2(推荐):先对数组进行初始化,然后为每个位置生成一个随机位置进行交换,共交换n次,其中n越大越接近随机。这也是C++的STL<algorithm>库中的random_shuffle()函数的思想
注意:rand()%(i+1); 对i+1取余而不是对n
void swap(int &a, int &b){ int tmp = a; a = b; b = tmp; } void shuffle_2(int a[], int n){ for(int i=0; i<n; i++) a[i] = i+1; for(int i=n-1;i>0; --i){ //倒序显然比正序好, i取不到0,a[0]没必要交换 int num =rand()%(i+1); //注意这里是对i+1取余 从a[0]到a[i]中取待交换位置,不是对54 if(num!=i) swap(a[num],a[i]); } }
想法一:将54个数依次放到随机的位置,关键是每次找到一个新的随机位置
①每次产生一个0~53的随机数,看这个位置是否有数,如果已经被占了,则继续;否则选定此位置,数组值设置为n(代表某张牌如红桃K)
②反复执行,直到所有位置都有数。
以下是代码(测试通过)
void shuffle(int arr[], int n){ int pos,card; memset(arr, 0, sizeof(int)*n); //类似malloc,只是分配空间并初始化为0 for(card=1;card<=n; card++){ do{ pos = rand()%(n); //产生 0~53的随机数, 随机取位置,也可以下边这样写,显然取余简单 //pos = rand()/(RAND_MAX + 1)* 53 } while (arr[pos]!=0); arr[pos] = card; } } void main() { const int num=54; int a[num]; shuffle(a,num); for(int i=0;i<54;i++) cout << a[i] << " "; cout << endl; }
问题:越到后边,寻找新的随机位置会越难,如果n数量级非常大不可取
参考:洗牌程序的两种实现方法比较
随机问题之--洗牌算法(JS版本)