我预感到我今天要和面试官就一道简单的算法题进行讨(si)论(bi)了,先上 v 站和你们讨论一下,寻找一下底气。

2015-05-15 08:14:37 +08:00
 jadetang
今天面试,有一道面试题是这样的:
“首歌曲都是一个评分,现在有2000首歌曲,要求实现一个随机播放器,每首歌曲播放的概率应该正比于它的评分,例如评分9.1的歌曲,和评分7.9的歌曲,播放的次数应该是91:79”
当时给出了一个比较复杂,但是有缺陷的接,之后面试官给出了答案,当时也没有细想,回家路上想出了更好的解法,就写在了我的博客上,顺便让面试官看一下(面试通过不通过无所谓,程序的孰优孰劣必须要分个清楚)。http://www.cnblogs.com/javanerd/p/4504482.html
总的来说我的解法:

拿空间换时间,评分9.1的歌曲,复制91份,评分7.9的歌曲复制79份,放到一个数组中,随机从里面拿。优点就是浅显易懂,实现起来也容易(当时是面试哦,手写代码,人都会紧张的吧)。缺点是空间复杂度不好,如果全是9.9分的歌曲,那么就悲剧了。但是每次随机选取的歌曲的时间还是O(1),所以随着听得歌曲越多,效率其实越高。


面试官的解法:
将评分和歌曲序号维护成一个区间和歌曲序号的map,然后生成一个随机数,落在哪个区间里面,就是哪首歌。那么问题来了?如果是一个区间做key的map,我只有一个随机数,如何正确的拿到value?是不是应该说的是线段树?如果用线段树,是不是要先排序,才能找到最大的评分的歌曲。

面试官的小弟已经回复我了,估计一会面试官也会看到,我已经做好讨(si)论(bi)的准备了,求V友给点信心。

另外,求一个珠三角地区后端的工作,能用scala就最好不过了,java也行。
9211 次点击
所在节点    求职
67 条回复
jadetang
2015-05-15 09:20:53 +08:00
@Septembers
目前看来,你这个答案最靠谱的,果然V站牛人多,谢谢了。
zhsso
2015-05-15 09:21:35 +08:00
我的理解
[1,2,3,4,5,6] ---> [1,3,6,10,15,21]
1-21随机取一个ra
取min(a[n] >= ra)
stiekel
2015-05-15 09:22:00 +08:00
这是一个典型的权重算法。比较经典和方便的方法是:
1、遍历,求出所有项的总权重
2、遍历,给每一项的权重,加一个0到总权重之间的随机数,注意,每项加的都是随机数,大小是不一样的。得出新权重
3、从所有项中,找出新权重最大的那一项。
难道我也要写一篇吗?
stiekel
2015-05-15 09:22:57 +08:00
这个算法,只需要遍历两次。
jadetang
2015-05-15 09:23:31 +08:00
@Andiry
啊,我想通了,确实这样能达到Log(n)的速度。
Andiry
2015-05-15 09:24:24 +08:00
算错了,a[2] = 170
lijia18
2015-05-15 09:38:57 +08:00
这种问题还值得搞个算法啊
至少要把加like的时间当参数传进来才是好一点的算法啊
很多人在某个时间段喜欢听某种音乐啊
越新发布的音乐在收藏里随机到的几率越高啊
把这些元素都考虑进去再做算法吧
其他啥好友相似口味这种忽悠的我就不说了
ffffwh
2015-05-15 09:39:05 +08:00
@crisrock
这种常见做法是把0变成0.1之类的
wshcdr
2015-05-15 09:43:39 +08:00
你的解法没有什么扩展性
hualuogeng
2015-05-15 10:00:19 +08:00
楼主使用的是整数域, 然后用数组做了一个全map,其它的和面试官的没有什么本质区别。用空间换时间,查询的效率应该最高的。
但是在评分改变时会有一些额外的开销,比如评分减少了,那么需要重新构建数组。而且在sNullp所说的评分值进一步细化情况下,其空间开销是数量级增长的。

而面试官的方法使用的是实数域,带来的优势是在评分细化时,其时空开销是不变的,对评分改变的额外开销也比较少。当然,只要不使用全map方式,从随机值到区间的查找肯定不是O(1),楼主的查询效率肯定要更好。

就本题的限定条件而言,我觉得楼主的算法更优。
但是如果是实际的开发需求,我会更倾向于面试官的做法,因为1. 有更好的扩展性; 2. 修改的影响小;3. 在2000首这样的数量级上,即使遍历,查询效率也应该够用。
kifile
2015-05-15 10:06:07 +08:00
感觉两个解法一样的啊,只不过你是属于在一个池子里复制多个相同对象,而面试官则是把你那些对象的数字给他抽离了,使用开区间来处理,其实思想都是同一个,只不过他的更省空间一点。
jadetang
2015-05-15 10:09:13 +08:00
@hualuogeng
评分减少了,只需要扫一遍数组,把对应的元素个数给删除出去就行。如果评分增多了,就加元素。

每个算法都有优缺点,只是看特殊的场景下选择最合适的。

受教了。
binux
2015-05-15 10:14:03 +08:00
面试官的解法明显是 Pre-calculated

我经常喜欢问的一个类似的题目是,怎样从不定多带权数列中,随机选取 n 个,只遍历一次。
zjuster
2015-05-15 10:23:27 +08:00
@jadetang 这样把1和0粗暴对待太书面化了。实际上的算法应考虑边界值。比如新加入的歌曲,还没来得及评分,那么就是0。其他都有{1,0.1,0.9,0.7...}的评分,那么0就相当于被无视了。
jadetang
2015-05-15 10:25:23 +08:00
@zjuster 面试题毕竟是面试题,不是实际开发啊,如果实际开发的话,很多条件要考虑的,比如,能不能更新评分,能不能新加歌曲,评分精确到小数点以后几位,播放的概率之比允许的偏差等等。
hualuogeng
2015-05-15 10:32:07 +08:00
@jadetang 对于连续存储的数组来说,增删的代价都不小。就算是scala的变长数组,在非尾端删除都会带来其后数组元素的移动。
xua131988
2015-05-15 10:43:06 +08:00
scala大爱。。
Septembers
2015-05-15 10:45:03 +08:00
@jadetang
对于音乐应用这些处理都可以压到后台预处理
但是考虑到 嵌入式设备 的特殊性,算法的各方面复杂度应该压到最低(电池啊 电池啊 电池啊
jadetang
2015-05-15 11:01:11 +08:00
@xua131988 安利一下我写的一个scala sql的实现,可以在内存中对于Map执行sql语句 https://github.com/jadetang/scala_sql
Septembers
2015-05-15 11:05:12 +08:00
@jadetang scala看起来做DSL似乎很简单

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/191209

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX