一、模拟客户需求

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. session与缓存

    分布式系统开发常见问题-1. session的复制与共享 2. 分布式缓存的设计 1. session的复制与共享 在web应用中,为了应对大规模访问,必须实现应用的集群部署.要实现集群部署主要需要实 ...

  2. IL学习资料

    读懂IL代码就这么简单 由浅入深CIL系列 .net IL 指令速查

  3. JOIN 相关内容

    1.left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录  2.right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录 3.inner join ...

  4. MySQL 日期时间

    NOW()函数以`'YYYY-MM-DD HH:MM:SS'返回当前的日期时间,可以直接存到DATETIME字段中.CURDATE()以’YYYY-MM-DD’的格式返回今天的日期,可以直接存到DAT ...

  5. 转载:DIV+CSS有可能遇到的问题

    [总结]DIV+CSS有可能遇到的问题 一.超链接访问过后hover样式就不出现的问题? 被点击访问过的超链接样式不在具有hover和active了,解决方法是改变CSS属性的排列顺序: L-V-H- ...

  6. 以静态变量保存 Spring ApplicationContext

    package com.thinkgem.jeesite.common.utils; import java.net.HttpURLConnection; import java.net.URL; i ...

  7. c#获取或修改配置文件

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.X ...

  8. document.body为null的问题

    虽然body是JS中的DOM技术中所有浏览器支持的属性,但在我们的代码编写中,还是会碰到document.is null问题 例如:我们可以使用alert(document.body);的时候,就会提 ...

  9. 让AllocateHwnd接受一般函数地址作参数

    http://www.xuebuyuan.com/1889769.html Classes单元的AllocateHWnd函数是需要传入一个处理消息的类的方法的作为参数的,原型: function Al ...

  10. ArcGIS Desktop 10.0 直连 ArcSDE 10.2

    环境 客户端:win7 64位 sp1,oracle11.2 32位客户端,ArcGIS Desktop 10.0 服务端:win7 64位 sp1,oracle11.2 64位服务端,ArcSDE ...