首先来看一下效果:
这是主页面,点击Go按钮开始产生随机数,从而决定选中哪个奖品。
如果是单纯的产生一个随机数,直接调用Qt封装好的函数即可。然而这个例子比较有意思的点是:
在生成一个1到9之间的随机数时,可以保证每个数生成的概率都相等。比如说,生成9次,每次生成的值各不相同。直观点说,当我们有九次机会抽奖时,可以保证每次抽到的奖都不重复,不会出现第一次抽到苹果,第二次还抽到苹果的情况。其实这个思路源于leetcode 519题,我当时读完这道题,感觉可以做一个抽奖游戏,这便来了。
那具体思路是怎样的呢?
系统中的随机函数可以在给定范围内产生一个随机值,比如我们要一个[1,9]间的随机值,可以这样写:
rand() % 9 + 1;
我们还是利用这个函数,不过需要增加一个映射关系。具体:
在产生随机值时,让第一次产生1-9间的随机值,第二次产生1-8间的随机值,第三次产生1-7间的随机值,依次。。。
第一次产生随机值时,范围为[1, 9]:若随机值为3,返回3,同时建立一个hash[3] = 9的映射。
第二次产生随机值时,范围为[1, 8]:若随机值还为3,因为产生的随机值不能重复,这时返回hash[3]即9,同时更改hash[3]的映射为8,(当下次还产生3时,返回8)
第三次产生随机值时,范围为[1, 7]:若随机值为6,返回6,同时建立hash[6] = 7的映射。
第四次产生随机值时,范围为[1, 6]:若随机值为4,返回4,建立4到6的映射,但6已经被选过了,所以建立hash[4] = hash[6] == 7的映射关系。(当下次还产生4时,返回7)
依次类推,是不是很巧妙。
总结一下:
当产生一个随机值时,如果它之前没有产生过,直接返回它。如果它之前产生过了,则返回它的映射值(它的映射值一定是在右边部分的)同时产生随机数的范围从右向左不断缩小,右边部分的值,只能通过映射的关系取出。
在第四次结束后,如果今后都不产生4了,是不是也就拿不到7了?
其实并不然,当有需要建立某个数到4的映射关系时,发现4的映射是7,这时就能拿到7了。所以可以保证每个数都可以被取到。
看一下代码实现:
int Widget::customRandom() { int iReturn; int iRandom = QRandomGenerator::global()->bounded(m_iPrizeNumber) + 1; //返回未产生过的随机值 if (m_hashMap.count(iRandom)) { iReturn = m_hashMap[iRandom]; } else { iReturn = iRandom; } //建立映射关系 if (m_hashMap.count(m_iPrizeNumber)) { m_hashMap[iRandom] = m_hashMap[m_iPrizeNumber]; } else { m_hashMap[iRandom] = m_iPrizeNumber; } //范围不断缩小 m_iPrizeNumber--; return iReturn; }
这便是整个产生随机值的过程了,也是例子最重要的部分了。
剩下的便是Qt界面相关了,主要用到定时器,不断的给每个方框加边框,产生动画效果。以及当选中某个方框时,方框中的图片(QLabel)显示出来。默认方框中的QLabel都是隐藏的,即看不见奖品。
源码(包含可执行程序):
https://gitee.com/gao-yuelong/qtdemo/tree/master/Lucky
欢迎试玩。