摇号中签生成随机号

来源:互联网 发布:编程教学 编辑:程序博客网 时间:2024/04/29 12:07

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

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

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

package com.fbd.core.util;import java.util.ArrayList;import java.util.Iterator;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;import java.util.Map.Entry;import com.fbd.core.exception.ApplicationException;/** * 摇号中签工具类 生成中签号码 *  * @author Lip * */public class LotterySystem {// 已经选中的尾号的数量// public static long chooseNum = 0;public static void main(String[] args) {// 起始申购序列号long start = 10000000001L;// 实际申购数量long purchaseNum =675893;// 实际发行long distributeNum = 12345;Map<String, Integer> distributeMap = getLottery(purchaseNum, distributeNum);int total = 0;Iterator iterator = distributeMap.entrySet().iterator();while (iterator.hasNext()) {Entry entry = (Entry) iterator.next();System.out.println(entry.getKey() + ":" + entry.getValue());total += (int) entry.getValue();}System.out.println("中签数量:" + total);}/** * 得到各个尾数的中签数量 *  * @param purchaseNum * @param distributeNum * @return */public static Map<String, Integer> getLottery(long purchaseNum, long distributeNum) {// 中签尾数及数量Map<String, Integer> distributeMap = new LinkedHashMap<>();if (purchaseNum <= distributeNum) {int n1 = (int) (purchaseNum % 10);int n2 = (int) (purchaseNum / 10);for (int i = 0; i < 10; i++) {if (i >= n1)distributeMap.put(i + "", n2);elsedistributeMap.put(i + "", n2 + 1);}return distributeMap;}long chooseNum = 0;double allocationRate = distributeNum * 1.0 / purchaseNum;// 0.001204177...System.out.println("中签率:" + allocationRate);int len = getDigitNum(purchaseNum);long distributeX = (long) (allocationRate * Math.pow(10, len));// 1204177List<Integer> digitList = getEachDigit(distributeX, len);// 1,2,0,4,1,7,7int lenX = getDigitNum(distributeX);List<Long> distributeList = new ArrayList<>();for (int i = 0; i < digitList.size(); i++) {int rate = digitList.get(i);// 尾号余数如232,158 也可以中奖long temp = (long) (purchaseNum % Math.pow(10, len - lenX + 1 + i));for (int j = 0; j < rate; j++) {if (chooseNum == distributeNum)return distributeMap;// 该随机号有多少个String lotteryNum = getRandom(distributeList, len - lenX + 1 + i);int number = (int) (purchaseNum * Math.pow(10, -(len - lenX + 1 + i)));long lotteryLong = Long.parseLong(lotteryNum);if (lotteryLong <= temp && lotteryLong > 0) {number++;}if (chooseNum + number <= distributeNum)chooseNum += number;elsebreak;distributeList.add(lotteryLong);distributeMap.put(lotteryNum, number);}}int left = (int) (distributeNum - chooseNum);while (left > 0)// 每次产生一个号码{String lotteryNum = getRandom(distributeList, len);long lotteryLong = Long.parseLong(lotteryNum);if (lotteryLong > purchaseNum || lotteryLong == 0) {continue;}distributeList.add(lotteryLong);distributeMap.put(lotteryNum, 1);left--;}return distributeMap;}/** * 得到一个数的位数 *  * @param value * @return */public static int getDigitNum(long value) {return String.valueOf(value).length();}/** * 得到一个num位的随机数 *  * @param except * @param num * @return */public static String getRandom(List<Long> except, int num) {boolean confict = true;long obj = 0l;while (confict) {obj = (long) (Math.random() * Math.pow(10, num));while (except.contains(obj) || obj == 0) {// obj肯定不在except中obj = (long) (Math.random() * Math.pow(10, num));}confict = false;int len = getLen(obj);for (long temp : except) {int len2 = getLen(temp);if (len2 == len) {continue;}if (Math.abs(obj - temp) % Math.pow(10, len2) == 0) // 有冲突{confict = true;break;}}}return String.format("%0" + num + "d", obj);}/** * 得到一个整数的位数 *  * @param num * @return */public static int getLen(long num) {int len = 0;while (num != 0) {num /= 10;len++;}return len;}/** * 得到每位的中签比率 *  * @param value * @param len * @return */public static List<Integer> getEachDigit(long value, int len) {String valueS = String.valueOf(value);List<Integer> result = new ArrayList<>();for (int i = 0; i < valueS.length() - 1; i++) {result.add(Integer.parseInt(valueS.charAt(i) + ""));}return result;}}
生成的中签尾号完全是随机的,如下图:

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

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

算法原理:

  1. 计算中签率R=12345/675893=0.018264...
  2. 拆分小数位,百分位是为1,说明两位数的中签尾号有1个,千分位为8,说明三位数的中签尾号有8个....依次类推,直到产生足够的中签尾号
摇号结束后,知道了所有的中签尾号,也知道每个用户的购买数,那么可以计算每个用户的中签数量。我是利用存储过程来计算用户的中签数量的:
BEGINDECLARE v_num varchar(11);  DECLARE v_len int;  DECLARE done INT;  DECLARE v_result int;DECLARE v_start_result int;DECLARE v_end_result int;DECLARE v_num_pow int;  DECLARE cur_success CURSOR FOR SELECT number from lottery_number where project_id=projectId;  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;  set v_result = 0;set v_start_result=0;set v_end_result=0;  OPEN cur_success;  BEGIN_success: LOOP     FETCH cur_success INTO v_num;     IF done THEN         LEAVE BEGIN_success;     ELSE         set v_len = LENGTH(v_num); set v_num_pow=POWER(10,v_len);         set v_start_result=v_start_result+FLOOR(startNum/v_num_pow); IF startNum % v_num_pow>v_num THEN  set v_start_result=v_start_result + 1; END IF;         set v_end_result=v_end_result+FLOOR(endNum/v_num_pow);  IF endNum%v_num_pow>=v_num THEN  set v_end_result=v_end_result+1;  END IF;     END IF;  END LOOP BEGIN_success;  CLOSE cur_success;SET v_result=v_end_result-v_start_result;RETURN v_result;END
原理其实很简单,每个用户都有一个起始配号,一个结束配号,那么只需要计算0到起始配号之间的中签数量n1,再计算0到结束配号之间的中签数量n2,那么n2-n1+1就是用户的中签数量。

1 0
原创粉丝点击