摇号中签生成随机号

在现在很多类似于股票市场的交易中,很多项目发行都需要进行申购,等到申购结束,进行摇号,根据中签尾号确定每个用户的中签数量。

如果用户U1购买了10个产品,那么他申购的产品尾号就是10000001到10000010,用户U2再购买5个,那么U2的产品尾号10000011到10000015。

现在假如发行项目A,发行量为12345,申购量为675893。随机生成中签尾号:

[java]  view plain  copy
  1. package com.fbd.core.util;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Iterator;  
  5. import java.util.LinkedHashMap;  
  6. import java.util.List;  
  7. import java.util.Map;  
  8. import java.util.Map.Entry;  
  9.   
  10. import com.fbd.core.exception.ApplicationException;  
  11.   
  12. /** 
  13.  * 摇号中签工具类 生成中签号码 
  14.  *  
  15.  * @author Lip 
  16.  * 
  17.  */  
  18. public class LotterySystem {  
  19.     // 已经选中的尾号的数量  
  20.     // public static long chooseNum = 0;  
  21.   
  22.     public static void main(String[] args) {  
  23.         // 起始申购序列号  
  24.         long start = 10000000001L;  
  25.         // 实际申购数量  
  26.         long purchaseNum =675893;  
  27.         // 实际发行  
  28.         long distributeNum = 12345;  
  29.         Map<String, Integer> distributeMap = getLottery(purchaseNum, distributeNum);  
  30.         int total = 0;  
  31.         Iterator iterator = distributeMap.entrySet().iterator();  
  32.         while (iterator.hasNext()) {  
  33.             Entry entry = (Entry) iterator.next();  
  34.             System.out.println(entry.getKey() + ":" + entry.getValue());  
  35.             total += (int) entry.getValue();  
  36.         }  
  37.         System.out.println("中签数量:" + total);  
  38.     }  
  39.   
  40.     /** 
  41.      * 得到各个尾数的中签数量 
  42.      *  
  43.      * @param purchaseNum 
  44.      * @param distributeNum 
  45.      * @return 
  46.      */  
  47.     public static Map<String, Integer> getLottery(long purchaseNum, long distributeNum) {  
  48.         // 中签尾数及数量  
  49.         Map<String, Integer> distributeMap = new LinkedHashMap<>();  
  50.         if (purchaseNum <= distributeNum) {  
  51.             int n1 = (int) (purchaseNum % 10);  
  52.             int n2 = (int) (purchaseNum / 10);  
  53.             for (int i = 0; i < 10; i++) {  
  54.                 if (i >= n1)  
  55.                     distributeMap.put(i + "", n2);  
  56.                 else  
  57.                     distributeMap.put(i + "", n2 + 1);  
  58.             }  
  59.             return distributeMap;  
  60.         }  
  61.         long chooseNum = 0;  
  62.         double allocationRate = distributeNum * 1.0 / purchaseNum;// 0.001204177...  
  63.         System.out.println("中签率:" + allocationRate);  
  64.         int len = getDigitNum(purchaseNum);  
  65.         long distributeX = (long) (allocationRate * Math.pow(10, len));// 1204177  
  66.         List<Integer> digitList = getEachDigit(distributeX, len);// 1,2,0,4,1,7,7  
  67.         int lenX = getDigitNum(distributeX);  
  68.         List<Long> distributeList = new ArrayList<>();  
  69.         for (int i = 0; i < digitList.size(); i++) {  
  70.             int rate = digitList.get(i);  
  71.             // 尾号余数如232,158 也可以中奖  
  72.             long temp = (long) (purchaseNum % Math.pow(10, len - lenX + 1 + i));  
  73.             for (int j = 0; j < rate; j++) {  
  74.                 if (chooseNum == distributeNum)  
  75.                     return distributeMap;  
  76.                 // 该随机号有多少个  
  77.                 String lotteryNum = getRandom(distributeList, len - lenX + 1 + i);  
  78.                 int number = (int) (purchaseNum * Math.pow(10, -(len - lenX + 1 + i)));  
  79.                 long lotteryLong = Long.parseLong(lotteryNum);  
  80.                 if (lotteryLong <= temp && lotteryLong > 0) {  
  81.                     number++;  
  82.                 }  
  83.                 if (chooseNum + number <= distributeNum)  
  84.                     chooseNum += number;  
  85.                 else  
  86.                     break;  
  87.                 distributeList.add(lotteryLong);  
  88.                 distributeMap.put(lotteryNum, number);  
  89.             }  
  90.         }  
  91.         int left = (int) (distributeNum - chooseNum);  
  92.         while (left > 0)// 每次产生一个号码  
  93.         {  
  94.             String lotteryNum = getRandom(distributeList, len);  
  95.             long lotteryLong = Long.parseLong(lotteryNum);  
  96.             if (lotteryLong > purchaseNum || lotteryLong == 0) {  
  97.                 continue;  
  98.             }  
  99.             distributeList.add(lotteryLong);  
  100.             distributeMap.put(lotteryNum, 1);  
  101.             left--;  
  102.         }  
  103.         return distributeMap;  
  104.     }  
  105.   
  106.     /** 
  107.      * 得到一个数的位数 
  108.      *  
  109.      * @param value 
  110.      * @return 
  111.      */  
  112.     public static int getDigitNum(long value) {  
  113.         return String.valueOf(value).length();  
  114.     }  
  115.   
  116.     /** 
  117.      * 得到一个num位的随机数 
  118.      *  
  119.      * @param except 
  120.      * @param num 
  121.      * @return 
  122.      */  
  123.     public static String getRandom(List<Long> except, int num) {  
  124.         boolean confict = true;  
  125.         long obj = 0l;  
  126.         while (confict) {  
  127.             obj = (long) (Math.random() * Math.pow(10, num));  
  128.             while (except.contains(obj) || obj == 0) {// obj肯定不在except中  
  129.                 obj = (long) (Math.random() * Math.pow(10, num));  
  130.             }  
  131.             confict = false;  
  132.             int len = getLen(obj);  
  133.             for (long temp : except) {  
  134.                 int len2 = getLen(temp);  
  135.                 if (len2 == len) {  
  136.                     continue;  
  137.                 }  
  138.                 if (Math.abs(obj - temp) % Math.pow(10, len2) == 0// 有冲突  
  139.                 {  
  140.                     confict = true;  
  141.                     break;  
  142.                 }  
  143.             }  
  144.         }  
  145.         return String.format("%0" + num + "d", obj);  
  146.     }  
  147.   
  148.     /** 
  149.      * 得到一个整数的位数 
  150.      *  
  151.      * @param num 
  152.      * @return 
  153.      */  
  154.     public static int getLen(long num) {  
  155.         int len = 0;  
  156.         while (num != 0) {  
  157.             num /= 10;  
  158.             len++;  
  159.         }  
  160.         return len;  
  161.     }  
  162.   
  163.     /** 
  164.      * 得到每位的中签比率 
  165.      *  
  166.      * @param value 
  167.      * @param len 
  168.      * @return 
  169.      */  
  170.     public static List<Integer> getEachDigit(long value, int len) {  
  171.         String valueS = String.valueOf(value);  
  172.         List<Integer> result = new ArrayList<>();  
  173.         for (int i = 0; i < valueS.length() - 1; i++) {  
  174.             result.add(Integer.parseInt(valueS.charAt(i) + ""));  
  175.         }  
  176.         return result;  
  177.     }  
  178.   
  179. }  
生成的中签尾号完全是随机的,如下图:

一个特殊的情况需要注意,那就是申购总量很少,小于发行量,那么相当于每个尾号都是中签的,当然,在实际中,这种情况不可能存在,出现那么也意味着该项目失败了。不过本文解决了申购量小于等于发行量的特殊情况。

该项目的中签率很低,用户U1和U2都不会中签。

算法原理:

  1. 计算中签率R=12345/675893=0.018264...
  2. 拆分小数位,百分位是为1,说明两位数的中签尾号有1个,千分位为8,说明三位数的中签尾号有8个....依次类推,直到产生足够的中签尾号
摇号结束后,知道了所有的中签尾号,也知道每个用户的购买数,那么可以计算每个用户的中签数量。我是利用存储过程来计算用户的中签数量的:
[sql]  view plain  copy
  1. BEGIN  
  2.     DECLARE v_num varchar(11);  
  3.   DECLARE v_len int;  
  4.   DECLARE done INT;  
  5.   DECLARE v_result int;  
  6.     DECLARE v_start_result int;  
  7.     DECLARE v_end_result int;  
  8.     DECLARE v_num_pow int;  
  9.   DECLARE cur_success CURSOR FOR SELECT number from lottery_number where project_id=projectId;  
  10.   DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;  
  11.   set v_result = 0;  
  12.     set v_start_result=0;  
  13.     set v_end_result=0;  
  14.   OPEN cur_success;  
  15.   BEGIN_success: LOOP  
  16.      FETCH cur_success INTO v_num;  
  17.      IF done THEN  
  18.          LEAVE BEGIN_success;  
  19.      ELSE  
  20.          set v_len = LENGTH(v_num);  
  21.                  set v_num_pow=POWER(10,v_len);  
  22.          set v_start_result=v_start_result+FLOOR(startNum/v_num_pow);  
  23.                  IF startNum % v_num_pow>v_num THEN   
  24.                          set v_start_result=v_start_result + 1;  
  25.                  END IF;  
  26.          set v_end_result=v_end_result+FLOOR(endNum/v_num_pow);  
  27.                   IF endNum%v_num_pow>=v_num THEN   
  28.                          set v_end_result=v_end_result+1;  
  29.                   END IF;  
  30.      END IF;  
  31.   END LOOP BEGIN_success;  
  32.   CLOSE cur_success;  
  33.     SET v_result=v_end_result-v_start_result;  
  34.     RETURN v_result;  
  35. END  
原理其实很简单,每个用户都有一个起始配号,一个结束配号,那么只需要计算0到起始配号之间的中签数量n1,再计算0到结束配号之间的中签数量n2,那么n2-n1+1就是用户的中签数量。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值