摇号中签生成随机号
在现在很多类似于股票市场的交易中,很多项目发行都需要进行申购,等到申购结束,进行摇号,根据中签尾号确定每个用户的中签数量。
如果用户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);
- else
- distributeMap.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));// 1204177
- List<Integer> digitList = getEachDigit(distributeX, len);// 1,2,0,4,1,7,7
- int 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;
- else
- break;
- 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都不会中签。
算法原理:
- 计算中签率R=12345/675893=0.018264...
- 拆分小数位,百分位是为1,说明两位数的中签尾号有1个,千分位为8,说明三位数的中签尾号有8个....依次类推,直到产生足够的中签尾号
摇号结束后,知道了所有的中签尾号,也知道每个用户的购买数,那么可以计算每个用户的中签数量。我是利用存储过程来计算用户的中签数量的:
- BEGIN
- DECLARE 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
相关推荐
- java自动生成流水号(格式:业务码+时间+当天自增号)
- 前端初学之利用html,css,js实现车牌摇号程序(一)
- STB学习——excel实现摇工号抽奖功能
- 技术宅男揭秘:北京机动车摇号真的公平?
- 利用Java线程及JFrame面板制作一个随机摇号小程序
- 动态生成像柜子管理的柜子号,用于DataGridView 绑定显示
- 微信公众号开发第一天,E-R图设计和SQL脚本生成
- 【JMeter】JMeter随机生成手机号后8位并去重,来进行注册手机号的压测
- 微信公众号设置菜单,网页授权,生成二维码,推送文本图文消息实例【附带源码】
- 流水号生成方式
- 愚人节导入_在愚人节的恶作剧破坏之后,如何重置键盘的映射?
- protobuf的安装 和使用