作者: Phill King

原创文章,转载请注明出处。

我们每次购买火车票,在查询的时候都可以及时的看到余票数。这个余票数是怎么计算出来的?有没有好的算法可以很快的计算出余票数目。本文对这个问题做了详细的分析,并给出了具体的代码。

声明:

本文详细分析了火车票的余票的算法,不考虑现实中一列火车包含不同等级座位和站票,以及预分配等情况,采用一个简化的模型来做基本分析。最后用一个高效率的算法来获取所有站点的余票数。

本文和12306无关。

-----------------------------------------------------------------------------------------------------

火车票余票问题的描述:

假设有一列火车,一共有M个座位,沿途经过S个站点。在出售一定数量的车票后,求该火车在沿途各站点的余票数。

首先我们对购票的情况和余票的关系做一个分析。

以M=5个座位, S=6站 为例

该表中的列代表所购票对应的站点段,一共有6站,对应的站点段数为5. 用户购买某张票如(3-6)站,即意味者占用了(3-4),(4-5),(5-6)的站点段。

该表中的行代表了座位号,对应购票时的座位。下文的总票数即为总的座位数。

对于(1-2)段来说,余票数就是总票数减去被占用的座位数。 示例中是 5-2 = 3;

对于(1-3)段来说,余票数就是总票数减去  (1-2) ∪ (2-3)的占用座位数。 示例中是 5-3 = 2;

对于(2-4)段来说,  余票数就是总票数减去  (2-3) ∪ (3-4)的占用座位数。  示例中是 5-4 = 1;

...

对应的余票表:

(行表头对应出发站,列表头对应抵达站)

因为余票即为总票数减去占用座位数,下面我们用更直观的占用座位表格来分析。

座位占用表: ( Si代表 从i到i+1站的座位占用集合)

我们可以看到对于某两站的座位占用集合有相应的规律,如

S(1-3) = S1 ∪ S2;

S(2-5) = S2 ∪ S3 U S4

S(i-j) = Si ∪ S(i+1) ... ∪ S(j-1)

我们可以根据座位占用的情况推导出余票计算的公式:

对于某两站i,j的余票计算可推出公式:

T(i,j) = 总票数 – (Si ∪ S(i+1) ∪  … ∪ S(j -1))

或者 T(i,j) = 总票数 – (S左 ∪ S下)

测试代码采用stl的Set来存储座位号,在100个站点和1000个座位下,更新购买2000张票计算整张余票表大约需要0.8秒。

可以看出性能不够理想。

性能优化:

由于集合运算比较耗时,我们考虑用位图法提升效率。

如果某个座位被出售,既标识相应的位(bit)为1.

比如(1-2)段的2座和5座已售,则可以用10010来表示。

假设火车共有1000个座位,如果用64位整数代表,则只需 大小为 1000/64 +1 = 16的数组即可表示。

uint64_t seat[16]

每两个站点之间的座位占用情况都需要一个数组来存储。一个数组的大小为128bytes。

这样计算集合只需要做或运算。大大提高效率。

用bit来表示购票的座位占用情况:

(1-2)站, 座位的分布对应的值是 二进制 10010

(2-3)站, 座位的分布对应的值是 二进制 10110

(3-4)站, 座位的分布对应的值是 二进制 10111

(4-5)站, 座位的分布对应的值是 二进制 01011

(5-6)站, 座位的分布对应的值是 二进制 01101

(1-3)站, 座位的分布对应的值是 二进制 10010 | 10110 = 10110  。 占用的座位数是3, 所以余票数是5-3= 2

以此类推

对应的余票计算公式:

每站对应的座位占用用一组64位整形数组表示,设为Bi

某两站i,j的余票数T(i,j) = 总票数  –  Bi|B(i+1)  | B(j-1) 中1的个数。

座位占用示意表:

采用位图(bitmap)法,运行效率提高很多,同时占用的内存空间也大大降低。

在100个站点,1000个座位的情况下测试更新购买2000张票之后的余票表格只需要5毫秒。

如果只是计算某两个站点的余票数更是只需要几微秒。

算法复杂度分析:

设购票数为T, 站点数为S, 时间复杂度为 T*S + S*(S+1)/2. 因为S是固定的,所以时间复杂度是O(N)

T*S的部分是更新购票信息,如果单独计算更新余票的时间复杂度,则为O(1);

空间复杂度:

设总座位数为M, 每两个站点的售票信息占用M/64*8 bytes. 整个个余票表占用M/8 * S(S+1)/2 bytes空间。

结合实际情况的一些测试数据(在本人的机器上):

50个站点,1000个座位的火车,批量更新2000张票,共耗时1.2毫秒。任意两个站点的座位占用数据可以压缩到大小为16的64位整形数组里,占用空间为128bytes。 整张表格的占用空间为128*50*49/2 = 154k bytes.

所以火车票更新余票按照此算法从时间和空间上分析,是非常快和省空间的。

最后是示例代码:

#include <vector>
#include <iostream>
#include <unordered_set>
#include <iomanip>
#include <chrono>
#include <ctime>
#include <cstdlib>
#include <set>
#include <fstream>using namespace std;#define BITS_WIDTH 64const int MAX_STATION = 100;
const int TOTAL_SEAT = 1000; // 总座位数,即每一站的总票数struct ticket_info
{int start = -1;int end = -1;int number = -1;
};typedef struct seat_type
{uint64_t & operator[](int i){return seat[i];};uint64_t seat[32] = {0};int get_bits(uint64_t n){int bits=0;while(n>0){n= n&(n-1);bits++;}return bits;}int get_count(){int count = 0;for(int i=0; i<32;i++)count += get_bits(seat[i]);return count;}}seat_type;class ticket_box
{std::vector< std::vector<seat_type> > tickets_count;const int ticket_total = TOTAL_SEAT ;const int station_number = MAX_STATION;
public://逐票更新int buy_ticket(ticket_info& new_ticket){int start = new_ticket.start;int end = new_ticket.end ;int number = new_ticket.number;int base = number/BITS_WIDTH;int offset = number%BITS_WIDTH;uint64_t update_bit = 1<<offset;for(int i=0; i<=start; i++)for(int j=start;j<station_number;j++){tickets_count[i][j][base] |= update_bit;}for(int i=start+1;i<=end;i++)for(int j=i;j<station_number;j++){tickets_count[i][j][base] |= update_bit;}return 0;}//批量更新余票int buy_tickets(std::vector<ticket_info>& tickets){for(auto ticket:tickets){int base = ticket.number/BITS_WIDTH;int offset = ticket.number%BITS_WIDTH;uint64_t update_bit = 1<<offset;for(int i=ticket.start; i<=ticket.end;i++){tickets_count[i][i][base] |= update_bit;}}for(int i=0; i<station_number-1; i++)for(int j=i+1, index=1; j<station_number; j++){for(int k=0; k<32; k++){tickets_count[i][j][k] = tickets_count[i][j-1][k] | tickets_count[i+index][j][k];}index++;}return 0;}//输出余票int output_remain_tickets(){std::cout<<std::setw(4)<<" ";for(int i=0; i< station_number; i++){std::cout<<std::setw(4)<<i+2<<" ";}std::cout<<endl;for(int i=0; i< station_number; i++){std::cout<<std::setw(4)<<"-----";}std::cout<<endl;for(int i=0; i< station_number&&i<12; i++){std::cout<<std::setw(3)<<i+1<<" ";for(int k=0; k<i; k++){std::cout<<std::setw(4)<<"     ";}for(int j=i; j<station_number&&j<12; j++){std::cout<<std::setw(4)<<TOTAL_SEAT - tickets_count[i][j].get_count()<<" ";}std::cout<<endl;}return 0;}};

以下给出一个test case和对应的输出

此case中站点数为12,总座位数为1000。

(起始站,终点站,座位号)
7,10,1
10,12,1
3,6,2
6,8,2
2,4,3
4,6,3
1,5,4
5,8,4
2,4,5
4,5,5
8,9,6
4,6,7
6,7,7
5,7,8
7,9,8
10,11,9
1,3,10
3,4,102    3    4    5    6    7    8    9   10   11   12
-------------------------------------------------------1  998  996  995  994  993  993  992  991  991  990  990 2       996  995  994  993  993  992  991  991  990  990 3            995  994  993  993  992  991  991  990  990 4                 995  994  994  993  992  992  991  991 5                      995  995  994  993  993  992  992 6                           996  995  994  994  993  993 7                                996  995  995  994  994 8                                     997  997  996  996 9                                          999  998  998 10                                               998  998 11                                                    999

本文考虑了有座位号的余票计算,可以涵盖不同类型有座位号的余票更新, 比如卧铺,一等座,二等座等。但是对于站票等没有座位号的情况,余票计算的方式会略有不同。如果用户购买的是没有座位的站票,购票只有出发站和到达站的信息,相应的余票应该如何计算呢?这个简单的问题就留给读者思考吧。

火车票余票问题的算法解析相关推荐

  1. 火车票余票问题的算法解析(续)

    作者: Phill King 邮箱: phillking1982@163.com 原创文章,转载请注明出处. 在之前的文章中,我分析了火车票余票的问题,提供了一个高效的算法.在本文中,我们继续讨论无座 ...

  2. Python爬虫入门(一)火车票余票实时提醒

    Python爬虫入门(一)火车票余票实时提醒 火车票余票实时提醒 最近开始学习爬虫了,参考的教材是<Python网络爬虫从入门到精通>吕云翔,张扬,韩延刚等,机械工业出版社.本篇博客是实战 ...

  3. python爬虫实现火车票余票查询

    python爬虫实现火车票余票查询 获取终端输入的命令行参数 重构请求url,解析返回的json数据 获取终端输入的命令行参数 例如:python3 tickets.py -dg 成都 南京 2016 ...

  4. 项目实战一 12306火车票余票查询软件

    1.安装docopt.urllib.requests 2.实现程序基础框架 # -*- coding:utf-8 -*-""" Train tickets query p ...

  5. Python爬虫----12306火车票余票查询器

    12306火车票余票查询器 文章同步更新:http://www.riba2534.cn/?p=305 今天写了一个12306火车票余票查询器的爬虫,在这里记录一下过程. 首先先看一下最终效果: 比如想 ...

  6. [置顶] 火车票余票接口API使用方法

    之前有很多人和我要火车票余票接口的api,铁道部频繁升级程序,导致余票的数据一直失败,参看文章: 快春运了,做个火车余票查询接口,余票来源12306,图是百度地图, 这个虽然叫做火车余票接口,但是我要 ...

  7. 火车票余票接口和火车票接口查询出来喽

    火车票余票接口 经过本人无数次的尝试终于成功获取到12306官网的余票数据.

  8. 火车票余票接口API使用方法

    之前有很多人和我要火车票余票接口的api,铁道部频繁升级程序,导致余票的数据一直失败,参看文章: 快春运了,做个火车余票查询接口,余票来源12306,图是百度地图, 这个虽然叫做火车余票接口,但是我要 ...

  9. 实验楼 python 火车票余票查询

    Python 实现火车票查询工具 核心的几个地方: 1:正则表达式的构建 2:json数据的解析 一.实验简介 当你想查询一下火车票信息的时候,你还在上 12306 官网吗?或是打开你手机里的 APP ...

最新文章

  1. GitHub换帅!开源大神辞任CEO,竞品GitLab刚完成IPO
  2. NFV — 高性能 NFVI
  3. vue中怎么清空tab选项卡的缓存_vue Tab切换以及缓存页面处理的几种方式
  4. 实现串匹配的并行算法_5-1-KMP模式匹配
  5. aspnet登录界面代码_SPA+.NET Core3.1 GitHub第三方授权登录
  6. 如何通过Port-isolate实现二层网络相互隔离
  7. js 字符转换,小驼峰转大写字母开头并且加空格 changeDate -》 Change Date
  8. Spring3.0包描述
  9. 如何用firebug调试js
  10. [Python] 维度交换函数:transpose(m,n,r)和permute(m,n,r)
  11. 关于项目部署到外网后,访问域名失败的原因之一
  12. php显示jquery未定义,运行PHP脚本时,jQuery函数表示未定义
  13. 基于Paddle Serving百度智能边缘BIE的边缘AI解决方案
  14. ABC分析做法、步骤、Pareto图制作方法解说
  15. 谷歌退出中国谁对谁错
  16. 哈雷haley教你如何用你的手机测试你的移动端项目
  17. 同宇新材再更新招股书:继续冲刺创业板上市,计划募资13亿元
  18. html浏览器兼容性问题总结,常见的浏览器兼容性问题(小结)
  19. python解一元二次方程ax^2+bx_python 练习题:定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程ax^2+bx+c=0的两个解...
  20. 旅游景区|“沉浸式夜游”如何玩?深圳光语数字

热门文章

  1. XJOI 7810 计数问题
  2. 实现不同海拔高度空气参数自由
  3. 淘宝API接口如何调取产品数据?
  4. JavaScript(js)加密解密视频文件
  5. esp01s作为ap,收发数据
  6. NO、NC、COM、FG端的含义
  7. 2022秋招系列------海康威视
  8. win10设置新建文本文档的快捷一点的方式
  9. 如何用MATLAB检测一张图片中某种颜色的占比
  10. 锐捷客户端下虚拟机VMware无法联网的问题