一道关于随机算法的面试题(转)

简介: 今天碰到了一道面试题:原题大致是,每首歌曲都是一个评分,现在有2000首歌曲,要求实现一个随机播放器,每首歌曲播放的概率应该正比于它的评分,例如评分9.1的歌曲,和评分7.9的歌曲,播放的次数应该是91:79。

今天碰到了一道面试题:原题大致是,每首歌曲都是一个评分,现在有2000首歌曲,要求实现一个随机播放器,每首歌曲播放的概率应该正比于它的评分,例如评分9.1的歌曲,和评分7.9的歌曲,播放的次数应该是91:79。

面试官给的答案是大致如此:

先把评分从小到大排序,之后把根据每首歌的评分,生成一个半闭开区间,然后生成一个随机数,看随机数落在哪个区间,就是选择的那首歌。例如,有三首歌,评分是[1,2,3] 那么应该是生成三个区间 [0-1,1-3,3-6],之后生成一个0-6之间的随机数,随机数落在哪个区间,就选择对应的歌曲。考虑排序的效率,这是一个nLogn的算法。

但是,这个算法是有纰漏的,没有考虑到评分重复的情况,如果三首歌的评分是[1,2,2],那么应该是生成两个区间[0-1,1-5], 如果落在第二个区间,还需要从两首评分为2的歌曲里面随机选出一首来。这样的话,实现起来就相当复杂了。

最后,如果照上面那样考虑,就完整了,但是实现起来的话,会发现,没有很好的数据结构能判断哪个随机数是落在哪个区间的,除非遍历所有的区间。

那么,优雅又高效的解法是什么样的,假定每个评分只到小数点后一位,那么其实,利用空间换取时间的思路,只需要把每首歌按照他的评分多少相应的复制多少重复的歌曲,并且把所有的歌曲都扔到一个池子里面,之后从池子里面等概率的选取一首歌就行了。在最坏的情况下,2000首歌曲的评分都是9.9,那么每首歌需要复制99首,空间效率是On,时间复杂度为O1

算法的scala实现如下:

复制代码
class RandomSong(val rate: Array[Double]) {
  val rateWithIndex = rate.map(x => (x * 10).toInt).zipWithIndex
  val songPool = rateWithIndex.flatMap { case (rate, index) => Array(index).padTo(rate, index)}

  def pickSong:Int = songPool(Random.nextInt(songPool.size))

}
复制代码

测试

复制代码
object main {
  def main(args: Array[String]) {
    val r = new RandomSong(Array(0.9,0.9,0.1,0.2))
    var count: Map[Int, Int] = Map()
    1 to 10000 foreach { x =>
      val song = r.pickSong
      count.get(song) match {
        case None => count += (song -> 1)
        case Some(n) => count += (song -> (1 + n))
      }
    }
    println("count = " + count)
  }
}
复制代码

结果

count = Map(2 -> 477, 1 -> 4312, 3 -> 970, 0 -> 4241)

ps:我是回家路上才想起这种解法的,我和我老婆说,化学系毕业的她直接就给出了正确的解法,哎,被数学学霸碾压的滋味就是这么销魂。

 

更新:早上和V站的V友讨论以后,发现面试官说的那种映射是可以实现的,例如有三首歌,评分是[1,2,3]那么区间段是[0-1,2-4,4-6]这个时候,只需要存一个数组[1,4,6],之后用2分查找就能得出正确的结论了,当然还需要考虑评分重复的情况。

rangeMap guava中有现成的实现,我还是太年轻啊。此外,这种加权随机的算法,早有研究

http://www.electricmonk.nl/Writings/HomePage?action=download&upname=weighted_random_dist.pdf

http://www.electricmonk.nl/log/2009/12/23/weighted-random-distribution/

http://www.cnblogs.com/javanerd/p/4504482.html

 

相关文章
|
6月前
|
算法
面试场景题:如何设计一个抢红包随机算法
本文详细解析了抢红包随机算法的设计与实现,涵盖三种解法:随机分配法、二倍均值法和线段切割法。随机分配法通过逐次随机分配金额确保总额不变,但易导致两极分化;二倍均值法优化了金额分布,使每次抢到的金额更均衡;线段切割法则将总金额视为线段,通过随机切割点生成子金额,手气最佳金额可能更高。代码示例清晰,结果对比直观,为面试中类似算法题提供了全面思路。
1126 16
|
负载均衡 NoSQL 算法
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
这篇文章是关于Java面试中Redis相关问题的笔记,包括Redis事务实现、集群方案、主从复制原理、CAP和BASE理论以及负载均衡算法和类型。
一天五道Java面试题----第十天(简述Redis事务实现--------->负载均衡算法、类型)
|
8月前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
248 16
|
11月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩分享分库分表的基因算法设计,涵盖分片键选择、水平拆分策略及基因法优化查询效率等内容,助力面试者应对大厂技术面试,提高架构设计能力。
美团面试:百亿级分片,如何设计基因算法?
|
11月前
|
算法 前端开发 Java
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
这篇文章总结了单链表的常见面试题,并提供了详细的问题分析、思路分析以及Java代码实现,包括求单链表中有效节点的个数、查找单链表中的倒数第k个节点、单链表的反转以及从尾到头打印单链表等题目。
119 1
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
|
11月前
|
机器学习/深度学习 算法 Java
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
|
11月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩在读者群中分享了关于分库分表的基因算法设计,旨在帮助大家应对一线互联网企业的面试题。文章详细介绍了分库分表的背景、分片键的设计目标和建议,以及基因法的具体应用和优缺点。通过系统化的梳理,帮助读者提升架构、设计和开发水平,顺利通过面试。
美团面试:百亿级分片,如何设计基因算法?
|
11月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
821 2
|
12月前
|
机器学习/深度学习 JavaScript 算法
面试中的网红虚拟DOM,你知多少呢?深入解读diff算法
该文章深入探讨了虚拟DOM的概念及其diff算法,解释了虚拟DOM如何最小化实际DOM的更新,以此提升web应用的性能,并详细分析了diff算法的实现机制。
|
消息中间件 存储 算法
这些年背过的面试题——实战算法篇
本文是技术人面试系列实战算法篇,面试中关于实战算法都需要了解哪些内容?一文带你详细了解,欢迎收藏!

热门文章

最新文章