一、模拟客户需求

1.1 客户A需求:要求每次都按照下图的概率随机,数量不限,每个用户只能抽一次,抽奖结果的分布与抽奖概率近似。

1.2 客户B需求:固定奖项10个,抽奖次数不限,每个用户只能抽一次,抽完为止,抽奖结果必须是固定的那几个奖项。

二、需求分析

算法1:对于客户A,由于抽奖次数无限次,出奖分布遵守中奖概率设定值。所以必须在用户每一次的抽奖行为之前都按照中奖概率随机出就可以了,这样随着样本的增多,概率分布越来越趋近于设定的抽奖概率。

算法2:对于客户B,由于奖项固定,需要将奖项硬编码(或者存储到DB),奖项抽出来一个就将该奖项从列表中删除。奖项就抽完后就直接返回未中奖。

三、算法实现

3.1先定义个类,用于存储奖项和概率的关系:

//算法1.每次都完全随机的算法
//作者:deepleo
//博客:http://www.deepleo.com/
//邮箱:yemor@qq.com
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RandomAlgorithm
{
    /// <summary>
    /// 奖品
    /// </summary>
    public class Prize
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { set; get; }
        /// <summary>
        /// 中奖概率
        /// </summary>
        public decimal Probability { set; get; }
    }
}

3.2 实现算法1

//算法1.每次都完全随机的算法
//作者:deepleo
//博客:http://www.deepleo.com/
//邮箱:yemor@qq.com
using System;
using System.Collections.Generic;

namespace RandomAlgorithm
{
    public class EveryRandomAlgo
    {
        public List<Prize> Prizes
        {
            private set;
            get;
        }
        public EveryRandomAlgo(List<Prize> prizes)
        {
            this.Prizes = prizes;
        }

        /// <summary>
        /// 随机下一次奖品
        /// </summary>
        /// <returns></returns>
        public string Next()
        {
            long tick = DateTime.Now.Ticks;
            Random ran = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));
            var rnum = ran.Next(0, 9999);//将0~100映射到0~10000,提高精度到小数点后2位;生成随机数rnum;
            //Console.WriteLine(rnum);
            var randomProbability = (decimal)rnum / 100;//再换算为0~100范围
            var target = 0;//命中index
            var end = Prizes[0].Probability;
            for (int k = -1; k < Prizes.Count - 1; k++)
            {
                var min = k < 0 ? 0 : Prizes[k].Probability;//最小中奖概率
                if (randomProbability >= min && randomProbability <= end)//随机出来的中奖概率位于中奖概率区间内
                {
                    target = k + 1;
                    break;
                }
                end += Prizes[k + 1].Probability;//最大中奖概率累计值
            }
            return Prizes[target].Name;
        }
    }
}

 3.3 实现算法2

//算法2.从数据表中随机取出一条记录的伪随机算法
//作者:deepleo
//博客:http://www.deepleo.comchuangshi88.cn/
//邮箱:yemor@qq.com

using System.Collections.Generic;

namespace RandomAlgorithm
{
    public class DefineByDBAlgo
    {
        public List<Prize> Prizes
        {
            private set;
            get;
        }
        private Queue<string> _dbRecords;//存储在数据表中的奖项记录数据
        public DefineByDBAlgo(List<Prize> prizes)
        {
            this.Prizes = prizes;
            _dbRecords = new Queue<string>();
            //按照中奖概率初始化奖项数据
            _dbRecords.Enqueue(Prizes[0].Name);
            _dbRecords.Enqueue(Prizes[0].Name);
            _dbRecords.Enqueue(Prizes[0].Name);
            _dbRecords.Enqueue(Prizes[0].Name);
            _dbRecords.Enqueue(Prizes[0].Name);
            _dbRecords.Enqueue(Prizes[1].Name);
            _dbRecords.Enqueue(Prizes[1].Name);
            _dbRecords.Enqueue(Prizes[1].Name);
            _dbRecords.Enqueue(Prizes[1].Name);
            _dbRecords.Enqueue(Prizes[2].Name);

        }

        /// <summary>
        /// 随机下一次奖品
        /// </summary>
        /// <returns></returns>
        public string Next()
        {
            if (_dbRecords != null && _dbRecords.Count > 0)
            {
                lock (_dbRecords)
                {
                    var random = _dbRecords.Dequeue(yxin7.com );//这里直接取出最上面的那一条记录。保证出奖顺序与数据库记录一致,如果要随机,只需要更改数据库记录顺序或者这里使用随机Index
                    return random;
                }
            }
            return "";
        }
    }
}

3.4 调用代码

//摘要:抽奖随机算法
//作者:deepleo
//博客:http://www.deepleo.comsbsbo.cc/
//邮箱:yemor@qq.com

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace RandomAlgorithm
{
    class Program
    {
        static void Main(string[] args)
        {

            var prizes = new List<Prize>();
            prizes.Add(new Prize { Name = "三等奖50%", Probability = 50 });
            prizes.Add(new Prize { Name = "二等奖40%", Probability = 40 });
            prizes.Add(new Prize { Name = "一等奖10%", Probability = 10 });
            var algo1 = new EveryRandomAlgo(prizes);
            var result1 = new Dictionary<string, int>();
            var count = 5000;
            for (int i = 0; i < count; i++)
            {
                var next = algo1.Next();
               // Console.WriteLine("【{0}】算法1随机结果:{1}", i + 1, string.IsNullOrEmpty(next) ? "奖项已发完" : next);
                if (!result1.Keys.Any(x => x == next))
                {
                    result1.Add(next, 1);
                }
                else
                {
                    result1[next]++;
                }
                Thread.Sleep(1);
            }
            foreach (var key in result1.Keys)
            {
                Console.WriteLine("算法1随最终结果:{0}:{1}个", key, result1[key].ToString());
            }
            Console.WriteLine("=====================================");
            var algo2 = new DefineByDBAlgo(prizes);
            var result2 = new Dictionary<string, int>();
            for (int i = 0; i < count; i++)
            {
                var next = algo2.Next();
                //Console.WriteLine("【{0}】算法2随机结果:{1}", i + 1, string.IsNullOrEmpty(next110shenbo.cc) ? "奖项已发完" : next);
                if (!result2.Keys.Any(x => x == next))
                {
                    result2.Add(next, 1);
                }
                else
                {
                    result2[next]++;
                }
            }
            foreach (var key in result2.Keys)
            {
                Console.WriteLine("算法2随最终结果:{0}:{1}个", key, result2[key].ToString());
            }
            Console.ReadKey();
        }

    }

}

四、实验结果

1. 抽奖次数为10次的实验结果:

算法1,二等奖和三等奖的个数分别为:3,7。与设定的中奖概率偏差较大。

算法2,符合预期(由于将所有的奖项都放在最上面,所以奖项都抽出来了)。

2.抽奖次数为100次的实验结果:

算法1,二等奖和三等奖的个数分别为:57,43。与设定的中奖概率偏差较小,已经比较接近设定值。

算法2,符合预期,后面的99个由于奖项已经被抽完,所以都是未中奖的。

3.抽奖次数为1000次的实验结果:

算法1,二等奖和三等奖的个数分别为:497,503。与设定的中奖概率偏差非常接近了。

算法2,符合预期,后面的990个由于奖项已经被抽完,所以都是未中奖的。

五、总结

实际上这是一个比较简单的算法,唯一需要注意的的是:在构造Random对象时如果seed是一样的就很容易产生随机出来的结果是一样的。

所以代码中Thread.Sleep(1);以保证随机的结果分布均匀,但是这样又限制了算法1的出奖速度,不知道各位有没有更好的解决方案。

源代码下载:博客园下载地址

抽奖随机算法的技术探讨与C#实现的更多相关文章

  1. 微信红包中使用的技术:AA收款+随机算法

    除夕夜你领到红包了吗?有的说“我领了好几K!”“我领了几W!” 土豪何其多,苦逼也不少!有的说“我出来工作了,没压岁钱了,还要发红包”.那您有去抢微信红包吗?微信群中抢“新年红包”春节爆红.618微信 ...

  2. 权重随机算法的java实现

    一.概述 平时,经常会遇到权重随机算法,从不同权重的N个元素中随机选择一个,并使得总体选择结果是按照权重分布的.如广告投放.负载均衡等. 如有4个元素A.B.C.D,权重分别为1.2.3.4,随机结果 ...

  3. java实现权重随机算法

    权重随机算法在抽奖,资源调度等系统中应用还是比较广泛的,一个简单的按照权重来随机的实现,权重为几个随机对象(分类)的命中的比例,权重设置越高命中越容易,之和可以不等于100: 简单实现代码如下: im ...

  4. 七雄Q传封包辅助技术探讨回忆贴

    前言 网页游戏2013年左右最火的类型最烧钱游戏,当年的我也掉坑了.为了边玩还满足码农精神我奋力的学习如何来做外挂.2013年我工作的第二个年头.多一半…介绍下游戏<七雄Q传>是北京游戏谷 ...

  5. 权重随机算法Java实现

    权重随机算法在抽奖,资源调度等系统中应用还是比较广泛的,一个简单的按照权重来随机的实现,权重为几个随机对象(分类)的命中的比例,权重设置越高命中越容易,之和可以不等于100: 简单实现代码如下: ? ...

  6. POJ 3318 Matrix Multiplication(随机算法)

    题目链接 随机算法使劲水...srand((unsigned)time(0))比srand(NULL)靠谱很多,可能是更加随机. #include <cstdio> #include &l ...

  7. hdu 4712 (随机算法)

    第一次听说随机算法,在给的n组数据间随机取两个组比较,当随机次数达到一定量时,答案就出来了. #include<stdio.h> #include<stdlib.h> #inc ...

  8. hdu 4712 Hamming Distance ( 随机算法混过了 )

    Hamming Distance Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) ...

  9. HDU4712+随机算法

    随机算法 求n个20位的2进制串的MinDist. Dist:两个串的异或结果中1的个数 /* 随机算法 */ #include<algorithm> #include<iostre ...

随机推荐

  1. [翻译]比较ADO.NET中的不同数据访问技术(Performance Comparison:Data Access Techniques)

    Performance Comparison: Data Access Techniques Priya DhawanMicrosoft Developer Network January 2002 ...

  2. PING的原理以及ICMP协议

    主要内容: 1.ping的原理以及工作过程 2.ICMP协议 3.ICMP的应用:ping,traceroute 1.ping的原理以及工作过程  ping的原理  ping 程序是用来探测主机到主机 ...

  3. PHP 根据类名和方法名已面向对象的方式执行函数。

    <?php echo 'testClass'; echo '<br><hr>'; $className = 'TestClass'; $methodName = 'c_o ...

  4. Javascript(JS)对Cookie的读取、删除、写入操作帮助方法

    var CookieUtils = { get: function (name) { var cookieName = encodeURIComponent(name) + '=', cookieSt ...

  5. MyEclipse卡死解决

    MyEclipse卡死解决 在用[MyEclipse] 写代码很容易卡死机,尤其是在对JSP文件的<%%>之间写代码的时候,只要一弹出智能提示就立刻卡死,程序失去响应,我以为是MyEcli ...

  6. 语义化的HTML

    div:division(分开, 分割, 区分,分配, 分界线),标签可以把文档分割为独立的.不同的部分.作为块级元素的代表,因为没有固定的格式表现,目前滥用情况最严重.ul:Unordered Li ...

  7. ArcSDE 10.2建立SDE服务

    从ArcGIS 10.1开始,arcgis官方推荐以直连方式连接SDE,因此在SDE安装时不再自动安装SDE服务,以下是手动安装SDE服务的方法 环境 服务端: oracle 11.2 64位,Arc ...

  8. 从Windows 服务器通过sync向Linux服务器定时同步文件

    本文解决的是Windows 下目录及文件向Linux同步的问题,Windows向 Windows同步的请参考:http://www.idcfree.com/article-852-1.html 环境介 ...

  9. PhoneGap+jQuery Mobile+Rest 访问远程数据

    最近研究Mobile Web技术.发现了一个好东西-PhoneGap! 发现用PhoneGap+jQuery Mobile是一个很完美的组合! 本实例通俗易懂.适合广大开发人群:高富帅.白富美.矮穷戳 ...

  10. iOS开发——测试篇&breakpoints、lldb 和 chisel 的详解

    breakpoints.lldb 和 chisel 的详解 Breakpoints BreakPoint分类 breakpoint也是有分类的,我这里的文章内大致按使用的方式分为了 Normal Br ...