博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
初探12306售票算法(一)- 理论(转)
阅读量:6849 次
发布时间:2019-06-26

本文共 5983 字,大约阅读时间需要 19 分钟。

1.以G71列车为例,首先对车次站台进行占位编码(从1开始到最后一站递加)

       

   对以上占位简单描述以下:G71总共18个站点那么我们的单个座位的座位标识可以用十八位长度的二进制字符串表示10000000000000000每一位代表一个站点,每天放票前初始化到下面的订票表中,数据如下余票根据座位标识中的0的个数决定最大余票数量

 订票表中的始发受限站点和终到受限站点可以灵活搭配(这个就可以实现限制站点发售)

 限售渠道十进制 7 代表 1(车站)| 2(互联网)|4(电话)=7 即该票允许  车站, 互联网, 电话同时出售

    那么还可以是 1|4 = 5  即该票只接受在车站和电话预定

   扩展 8(代售点) 16 (手机端) 

2.查询余票

如果我们要用互联网渠道查询日期为2016-06-11,始发站保定东站(3)到韶关站(15)的G71二等座F座位余票情况只需要执行如下sql(该SQL可以实现选座位和选车厢等功能)

select GUID,车次编码,车次类型,座位类型,车厢号码,座位编码,座位位置,车票版本号 from 订票表where  座位标识=~((~坐标标识)|000111111111111000)and 发车日期='2016-06-11'and 车次编码='G71'and 始发受限车站=始发受限车站|000100000000000000and 终到受限车站=终到受限车站|000000000000001000and 车次类型='二等座'and 座位位置='F' and 受限渠道=2|受限渠道 order by 余票数量 asc ,车厢 asc  --先卖余票少的,防止打乱更多的长途票

 

3.预定票

  3.1根据第二步中查询条件获取一条记录然后将车票状态改为锁定

  3.2待锁定成功后进行支付

     

update 订票表 set 座位标识=座位标识 | 000111111111111000,车票版本号=车票版本号+1,余票数量 = 余票数量-(15-3)      where GUID = Md5(车次+座位编码+发车日期) and 座位标识=~((~坐标标识)|000111111111111000)--乐观锁        根据更新结果是否为1则可以判定购票成功

 

  3.2支付成功后然后将保定到韶关的票保存到另外一张客户的车票表中

  3.3如果指定时间没有支付,那么直接调用退票sql

4.退票

  获得该车次保定到韶关的票 (000111111111111000)与对应的票进行异或^运算,则即可回归票池子了

5.选座

      

update 订票表 set 座位标识=座位标识 ^000111111111111000,余票数量 = 余票数量+(15-3),车票版本号=车票版本号+1 where GUID = Md5(车次+座位编码+发车日期)  and 车票版本号=取得的版本号 --乐观锁
根据返回结果为1则标识退票成功

 

以下为相关java代码 

 

1 import java.math.BigDecimal;  2   3 public class MainTest {  4     public static void main(String[] args) {  5         String ticketFlag = "100000000000000000";  6         int beginStation = 3;  7         int endStation = 15;  8         long beginTime = System.currentTimeMillis();  9         String result = orderTicket(ticketFlag, beginStation, endStation); 10         if (result.equals(ticketFlag)) { 11             System.out.println("订票失败"); 12         } else { 13             System.out.println("订票后的结果:" + result); 14             // 如果要取消的话,就进行这个操作 15             String b = buildTicket(ticketFlag.length(), beginStation, 16                     endStation); 17             System.out.println("释放后的结果:" + releaseTicket(ticketFlag, b)); 18  19         } 20         long endTime = System.currentTimeMillis(); 21         System.out.println("耗时:" + (endTime - beginTime)); 22     } 23  24     /** 25      * 订票 26      *  27      * @param ticketFlag 28      * @param beginStation 29      * @param endStation 30      * @return 31      */ 32     private static String orderTicket(String ticketFlag, int beginStation, 33             int endStation) { 34         String result = ""; 35         if (checkCanTicket(ticketFlag, beginStation, endStation)) { 36             String b = buildTicket(ticketFlag.length(), beginStation, 37                     endStation); 38  39             String currentTicked = toTicket(ticketFlag, b); 40             System.out.println("预占票前结果:" + ticketFlag); 41             result = currentTicked; 42         } else { 43             result = ticketFlag; 44         } 45         ; 46         return result; 47     } 48  49     /** 50      * 取消已定票 51      *  52      * @param ticketFlag 53      * @param b 54      * @return 55      */ 56     private static String releaseTicket(String ticketFlag, String b) { 57         StringBuilder tempSt = new StringBuilder(""); 58         int length = ticketFlag.length(); 59         for (int i = 0; i < length; i++) { 60             char tempA = ticketFlag.charAt(i); 61             char tempB = b.charAt(i); 62             if (tempA == '1' && tempB == '1') { 63                 tempSt.append("0"); 64             } else { 65                 tempSt.append(tempA); 66             } 67         } 68         return tempSt.toString(); 69     } 70  71     /** 72      * 创建区间占位票 73      *  74      * @param length 75      * @param beginStation 76      * @param endStation 77      * @return 78      */ 79     private static String buildTicket(int length, int beginStation, 80             int endStation) { 81         StringBuilder st = new StringBuilder(""); 82         for (int i = 0; i < length; i++) { 83             if (i >= beginStation && i < endStation) { 84                 st.append("1"); 85             } else { 86                 st.append("0"); 87             } 88         } 89         System.out.println("创建区间票:" + st.toString()); 90         return st.toString(); 91     } 92  93     /** 94      * 生成订票后的结果 95      *  96      * @param ticketFlag 97      * @param b 98      * @return 99      */100     private static String toTicket(String ticketFlag, String b) {101         StringBuilder tempSt = new StringBuilder("");102         int length = ticketFlag.length();103         for (int i = 0; i < length; i++) {104             char tempA = ticketFlag.charAt(i);105             char tempB = b.charAt(i);106             if (tempA == '1' || tempB == '1') {107                 tempSt.append("1");108             } else {109                 tempSt.append(tempA);110             }111         }112         return tempSt.toString();113     }114 115     /**116      * 是否可以订票117      * 118      * @param ticketFlag119      * @param beginStation120      * @param endStation121      * @return122      */123     private static boolean checkCanTicket(String ticketFlag, int beginStation,124             int endStation) {125         boolean result = false;126         String tempTicket = ticketFlag.substring(beginStation, endStation);127         BigDecimal b = new BigDecimal(tempTicket);128         if (b.equals(new BigDecimal("0"))) {129             result = true;130         }131         return result;132     }133 134 }

 

 

 

本文原创:转载请注明出处 http://www.cnblogs.com/feichengwurao/p/5191253.html

 

说一下本人的几点改进想法:
1.guid 可以用 车次+车厢+座位编码+始发时间+(后4位递增的尾数序列) 的联合编码来替换,作为 “行健”。
2. 设计的这张表是针对票的,那么就专注票本身建模就好,始发受限站点 和 终到受限站点 最好不要用 和售票逻辑耦合的二进制码作为字段,直接使用站点自己的编码更好。
3.实现本文,生成韶关后面的票的算法,大可简单粗暴一些。把车次经过的站再设计一张表,存每个站的站编码和站位一一对应关系。生成后半段的票的时候,直接从这个表里去查那些站该座还可以卖就好了。当然站点编码和站位的关系是静态数据,放到读多写少的缓存里查询更快一些。
4. 还有就是,这个新生成的票,最...

非常感谢@MarsLeno提供的改进想法
1.GUID本身就是Md5(车次+座位编码+发车日期)生成的,这样可以保证此票的唯一性,在高并发的情况下可以防止重复插入
2.设计这张表的并不是针对票的,而是针对售票整个业务,在大数据量的情况下可以避免多表交叉查询,提高查询效率,而受限字段只是便于进行查询的一种手段。
针对3,4,5 采用更新的手段,只是针对一条数据的更新操作,在大数据量的情况下更新的效率会比插入一条效率高很多

 

你可能感兴趣的文章
sql help cs
查看>>
关于COUNT STOPKEY的工作机制(转载)
查看>>
如何理解VB窗体中的scale类属性及width height属性之间的关系
查看>>
大叔也说Xamarin~Android篇~日志的记录
查看>>
(原創) 為什麼要學[計算機組織]? (日記)
查看>>
JS实现Trim()
查看>>
Linux信号说明列表
查看>>
大端和小端
查看>>
无法解析或打开软件包的列表或是状态文件
查看>>
Ubuntu 12.04搭建Ruby on Rails开发环境
查看>>
Linux原子操作
查看>>
C++ 使用STL string 实现的split,trim,replace-修订
查看>>
2011年7月10个非常棒的jQuery插件
查看>>
.NET简谈事务、分布式事务处理
查看>>
我是如何推理出王珞丹住址的zz
查看>>
C#泛型列表List<T>基本用法总结
查看>>
《UNIX环境高级编程》单个源码编译方法
查看>>
追涨必须具备的四个条件
查看>>
最大存款方式
查看>>
GridView删除时激发了未处理的事件“RowDeleting"
查看>>