1
lululau 2022-02-11 10:36:38 +08:00 via iPhone
硬盘多大?
|
2
himarrin 2022-02-11 11:04:15 +08:00
忙猜 bitmap
|
3
Jooooooooo 2022-02-11 11:05:29 +08:00
搜索指的是?
|
4
cnoder 2022-02-11 11:22:43 +08:00
找最大 /最小的 n 个是经典题
|
5
siriussilen 2022-02-11 11:27:13 +08:00
字典树呗…
|
6
xiao109 2022-02-11 11:27:57 +08:00
我感觉这些算法题都是三板斧,就那几个固定的套路
|
7
stone666 2022-02-11 11:36:35 +08:00 2
布隆过滤器
|
8
williamjing OP @lululau 不允许用其他条件,只能在内存中操作。
|
9
williamjing OP @Jooooooooo 给定一个卡号,查看是否在其中。
|
10
williamjing OP @siriussilen 具体讲讲?
|
11
williamjing OP @xiao109 给个思路?
|
12
williamjing OP @stone666 有误识别率,有没有一个完全准确的算法?
|
13
TomVista 2022-02-11 11:54:48 +08:00 1
一个深度 16 的 10 叉 树,
|
14
Jooooooooo 2022-02-11 12:09:20 +08:00
@williamjing 噢, 搜下布隆过滤器.
|
15
williamjing OP 我觉得这个问题可以转换为两个子问题,1. 4 亿个卡号如何存储; 2. 查找。解决了存储问题也就解决了查找问题。
|
16
sNullp 2022-02-11 12:15:20 +08:00
512M = 512*1024*1024*8 = 4294967296bits
|
17
williamjing OP @himarrin 为了解决存储问题,看来只能往 bitmap 方向考虑。
|
18
williamjing OP @sNullp 对,每个卡号最多只能用 10 bits 来存储。
|
19
sadfQED2 2022-02-11 12:17:49 +08:00 via Android
字典树+1
|
20
sNullp 2022-02-11 12:19:49 +08:00 via iPhone
@williamjing 每个卡号明明只占 1bit
|
21
williamjing OP @TomVista 没那么大内存,你这个都到 10^17bits 了
|
22
williamjing OP @sNullp Roaring BitMap
|
23
gps949 2022-02-11 12:25:43 +08:00
感觉问题很多信息都没说,在缺信息的情况下甚至搞不懂为什么要用 512MB 那么大内存。比如假设搜索空间 4 亿个卡号一个挨一个连续存放在硬盘上,每个卡号是 16 个 char (单字节)连续存放。所谓搜索只是判断是否存在而不用给出其他信息。
那么,不考虑程序自身运行需要,仅考虑存放数据,实际使用内存有 1 ( char )+16 (目标卡号 16 位)+1/8=17.125B 就够了啊? |
24
ynyounuo 2022-02-11 12:29:00 +08:00
如果保证全部卡号都是 valid 的情况下
前 6 - 8 位的 BIN/IIN 进行重编码,末位 Luhn 算法验证直接省略; 这样就只剩下中间的 7 - 9 位数字了 |
25
gps949 2022-02-11 12:29:19 +08:00
|
26
TomVista 2022-02-11 12:32:17 +08:00
银行卡号的前 11 位组成 是有限的,远小于 10^11,
|
27
kimera 2022-02-11 13:55:30 +08:00
可以利用哈希函数的均匀分布特性
每个卡号 16 位,40 亿个总共 3.7G ,限制内存 512M 1 ,哈希函数把 4 亿份数据分为 N 份,使得每一份内存不会超。这里取 N=10 2 ,计算 hash ( cardno )%10, 把所有的卡号分为 10 份,每份大约 4 亿个卡号,占用内存 0.37G 3 ,根据待查找 cardno2, 找到对应是属于 10 份中第几份,把数据加载到内存,验证是否存在就可以了 |
28
freelancher 2022-02-11 14:40:37 +08:00
512MB 内存*1024*1024*8=4294,967,296 字节。42 亿个 bit 。 16 位数字用 10bit 存储。数据格式用 int 长整形。 只要 8bit.
数值太接近了。感觉就是大学的作业题目。 放到内存一般用 redis 或者其它数据库。 可以先用冒泡排序从小到大排列,再用数据库自己带的索引来查找,效率也不会低。 |
29
Morton996 2022-02-11 14:41:04 +08:00
听说过基数树没有呢?这还要叫大神
|
30
freelancher 2022-02-11 14:41:51 +08:00
或者用二分查找法。应该能明白吧。我怎么感觉好像在做作业?
|
31
freelancher 2022-02-11 14:50:31 +08:00
首先是解决如何存。4 亿数据,每个用 10bit.那就只能用数据库里的 long int ( 8K )长整形存在内存里了。
存好了之后,要如何找呢?就可以用冒泡排序自己用顺序排列到数据库里,用索引来查找。 或者瞎存。用二分查找的递归来找对应的数据。 大概思路就是这样。也不知道说得对吗? |
32
freelancher 2022-02-11 14:52:56 +08:00
最后二句错了。二分查找适合有序的数据。
|
33
williamjing OP @gps949 没说就是没有其余信息啊,不让用磁盘。
|
34
gps949 2022-02-11 15:20:10 +08:00
@williamjing 那我问你,4 亿条数据最开始在哪里?比如,在纸上,然后呢,从纸上如何录入到的计算机?
数据的全流程存在三个阶段:计算机外、进入计算机( I/O )、计算机内。第一个阶段可以不考虑,不属于计算机范畴。 如果你说系统是无盘(硬盘)的,那么,有没有 I/O ?如果也没 I/O ,那说明数据没有从计算机外进入计算机,天生在计算机内产生的,那就得说明直接在计算机内产生的方式和形式。 |
35
gps949 2022-02-11 15:23:06 +08:00
续#34
想问“4 亿条 16 位数字信用卡号如何完全存储在 512M 内存中”就直接问,别问“如何用 512MB 内存对 4 亿个银行卡号(每个卡号 16 位阿拉伯数字)进行搜索”。两个问题无论本身的清晰程度还是含义都是有区别的 |
36
williamjing OP @kimera 不允许使用磁盘。
|
37
williamjing OP @gps949 #34 #35 ,本题是开放性试题,数据本身在磁盘上,但是要求在算法运行阶段,不允许再次读取硬盘,也就是说相当于一次性全部读到内存中。
|
38
williamjing OP @freelancher #28 计算过程不允许再次访问磁盘,要一次性加载到内存中。4 亿数据用冒泡?
|
39
Marinata 2022-02-11 15:58:04 +08:00
@freelancher 8bit 最高 11111111 ,老哥,8byte ( 64bit )才能存长整形
|
40
williamjing OP @Morton996 是个可行方案,但是基数选择多少呢?建树指针也算内存哦。
|
41
freelancher 2022-02-11 16:06:56 +08:00
@Marinata 可能我记错了。
|
42
kalsosadie165123 2022-02-11 16:28:18 +08:00
可以用布隆过滤器,不过有一定误差率。
布隆过滤器判断值存在,则很有可能存在; 判断不存在则一定不存在。 |
43
lis66951735 2022-02-11 17:42:48 +08:00
布隆过滤器啊
|
44
kalago 2022-02-11 18:57:58 +08:00 1
看了下楼主说的 roaring bitmap 方式:
1. 银行卡前 8 位划分 1 0000 0000 个桶; 2. 后 8 位用 bitmap 来存储,申请 27 bit 大小空间,最大无符号整数 1 3421 7728; 3. 理想情况下就是占用了 27 0000 0000 的大小空间。 4. 这种方式纯存储占用 322mb 大小。 不知道描述的对不对。以前不知道 Roaring Bitmap 这个操作,感觉是对 bitmap 做了 2 级索引? |
45
2dot71828 2022-02-11 20:19:09 +08:00
布隆过滤器不行吧?空间浪费太严重,试试布谷鸟过滤器?
|
46
lrjia 2022-02-11 20:29:35 +08:00 2
从信息论的角度估算一下,如果认为卡号在 10^16 次方范围内随机分布需要空间大约为 2500MB
$log_2((10^{16})^{4 * 10 ^ 8})= 21260339807 \ bit = 2534MB$ |
47
misdake 2022-02-11 21:41:14 +08:00 1
@kalago 每个 bucket 后 8 位十进制应该用长度 10^8 的 bitmap 来存储吧,存在添 1 ,不存在添 0 ,这样才是 bitmap 。27bit 只能存储 1 个 8 位十进制数字。
我看的 roaring bitmap 例子里,就是给后 16 位 2 进制数分配了长度 2^16(即 65536)的 bitmap 来存储。 |
48
StrorageBox 2022-02-11 22:22:01 +08:00 via iPhone
典型 bitmap 问题啊
|
49
raycool 2022-02-11 23:18:27 +08:00
卡号的前几位肯定是固定的吧
所以可以进一步节省空间 |
50
binux 2022-02-12 01:27:44 +08:00 via Android
@lrjia 同意信息论的分析,即使考虑数据没有重复,C(10^16, 4*10^8) 的量级上差太多,结果量级没什么区别。
也就是说,假如卡号没规律,这 4 亿的组合放在 512M 空间中一定会有歧义。 |
51
misdake 2022-02-12 02:25:52 +08:00
我现在设计的最小需要 1.43GB ,平均下来每个卡号使用了 30.725bit
把 10^16 的空间分成 10^7 个 bucket ,卡号剩余 9 位数,用 30bit 来覆盖。卡号排序后,将这 4*10^8 个卡号的 30bit 数据紧密排列,4*10^8*30bit bucket 数据,每个 bucket 存储起始 index ,index 最大 4*10^8 ,用 29bit 来覆盖,这部分是 10^7*29bit 两项加一起 1.43GB 另外沿着信息的那条路去估算 log2(C(10^16, 4*10^8)),我把分子的里面改成 10^16-4*10^8 找下限,再用积分模拟分母,最后得到的下限大约 1.21156GB |
52
macg0406 2022-02-12 07:18:28 +08:00 via Android
接信息论的分析,假如卡号前 9 位互不相同,如 1 到 4 亿,那么后 7 位就满足的随机分布,这种情况表示 4 亿个卡号需要的空间是 4e8 * log 2(10^7),约 1.16G 。所以 512M 肯定是不够的。
|
53
dujiaoxi 2022-02-12 08:58:42 +08:00
假如已有的卡号和要判断的卡号都在 10^16 空间上随机分布没有规律,题目上已有的 4 亿个数字占整个 10^16 比例是亿分之 4 ,那么就有一个很好的解决方法了,直接 return false ,时间复杂度 O(1),不需要读取硬盘,不需要额外内存,误判率只有亿分之 4 ,基本可以满足生产需求了(雾)
|
54
xuanbg 2022-02-12 09:55:51 +08:00
4 亿地址要用去 29 位,如果是 32 位系统,剩下 3 位只够 0-7 。所以如果是 32 位系统就没戏。
64 位的话就可以随便搞了,可以表示 20 位的 10 进制数字,16 位卡号离上限还差远着呢。一个 LONG 数组把 4 亿卡号作为 10 进制数字怼进去,挨个比对就行了。 |
55
williamjing OP @misdake #51 我觉得还是没有解决稀疏性这个问题,其实 10^7 个 bucket 是相当稀疏的,比如中国的卡都是 62 开头。空的 bucket 后面的 30bits 甚至不用创建。
|
56
williamjing OP @binux 而现实是卡号是有规律的,单纯用信息论分析有点脱离实际。卡号空间 10^17 ,但是全球才不到 100 亿人即 10^10 ,相当稀疏。
|
57
williamjing OP @xuanbg 好家伙,合着服务器单独为了一个查询算法要时刻保持 4* 10^8 * 8B 这么大内存?然后还是 O(n)的效率...
|
58
williamjing OP @dujiaoxi 误判率很低,但是 F1-score 完犊子了...
|
59
williamjing OP @macg0406 信息论可以给我们提供数据分布的描述,但是本题已经分析很多了,现在最重要的是解决数据表示问题。用 10 进制表示是不够的,只能考虑 bitmap 。
|
60
binux 2022-02-12 10:41:37 +08:00 via Android
@williamjing 那什么规律你说啊,实际有效的卡号空间有多少?即使全球 100 亿人,但是卡号是随机生成的,一样百搭。
|
61
williamjing OP @binux 银联都是 62 开头,算不算规律?
|
62
winnie2012 2022-02-12 11:02:04 +08:00
排序好,使用差值来做 bitmap 存储吧
|
63
dujiaoxi 2022-02-12 11:46:16 +08:00
@williamjing 也就是你出了个残缺的需求让开发去实现算法呗。卡号的生成方式,有效取值范围,什么都没说,上来就 16 位 10 进制,还🦐限定 512mb 内存。然后一点一点的加限定条件补完需求改题目,不如直接加个允许有亿分之 4 的误判率好了。建议题目改成 4 亿个 16 位 10 进制的数,无压缩的编码在 512 兆储存空间,求允许数最杂乱的规律是什么。参考其他人的解法,全随机分布目前最少的需要 1.43g ,然后如果都是同一个值只要 54bit
|
64
dujiaoxi 2022-02-12 11:55:07 +08:00
看了上面人对信息的分析,也可以建议把题目改成如何用 1bit 区分 012 这三个数,解决了可以干翻香农公式了
|
65
misdake 2022-02-12 12:25:07 +08:00
作为算法问题,最好是能把所有的前提都说明。让人自己摸索规律的话,这就是一个工程问题了,不同的人心中的规律不同,难以形成共识,结论的含金量也就大大降低。
|
66
MegrezZhu 2022-02-12 12:33:08 +08:00
直接用卡号数字做 offset ,每个卡号在对应的 offset 上用 1bit 标记是否存在,这样就只需要 4 亿多 bit……
|
67
MegrezZhu 2022-02-12 12:34:10 +08:00
啊,看错题了,原来四亿是卡号数量不是总可能数量……告辞
|
68
hefish 2022-02-12 12:44:59 +08:00
谢谢 ls 的大神们帮大学生完成了一个课题的思路。
|
69
williamjing OP @misdake #65 为什么总觉题是我条件没说够?都说了啊,这就是所有条件。第二,这个确实是个开放性的算法题,如果有一个好的解决方案,那不就称为经典算法了吗?还用得着讨论吗?
|
70
moshiyeap100 2022-02-12 16:02:38 +08:00
512MB 大小足够标识所有 QQ 号码的存在,QQ 号码的理论最大值为 2^32 - 1 ,大概是 43 亿左右。
|
71
binux 2022-02-13 00:42:59 +08:00 via Android
@williamjing 既然你坚持这是全部条件,那就不能假设卡号的规律了,也就不能降低卡号空间了;既然是开放性问题,证明无解也是答案啊。
怎么?你一边说这是所有条件,一边加条件,一边开放,又不接受无解?你想要五彩斑斓的黑吗? |
72
wa007 2022-02-13 10:20:46 +08:00 via iPhone
@williamjing 字典数是一种数据结构,你了解下就知道怎么用在这个问题上了
|
73
williamjing OP @binux 你应该去工地,正好缺个抬杠的。
|
74
williamjing OP @wa007 #72 谢谢 我去了解一下
|
75
KousukeSakurako 2022-02-15 11:36:41 +08:00 via iPhone
普通的字典树很有可能会超出内存,因为无论是 map ,还是 hash map 的内存开销都非常大, 我认为 Succinct Trie 值得一试
正巧最近写了一个 Succinct Trie 的 Golang 实现,楼主愿意的话可以看看 https://github.com/NobeKanai/sutrie 思路是来自于这个博客 http://stevehanov.ca/blog/index.php?id=120 |
76
williamjing OP @KousukeSakurako #75 感谢
|