论坛首页 Java企业应用论坛

迅雷的一个面试题

浏览 54548 次
精华帖 (8) :: 良好帖 (3) :: 新手帖 (0) :: 隐藏帖 (1)
作者 正文
   发表时间:2012-03-01  
select row_num from (select *,rownum as row_num from tableName)  where id=#id#
0 请登录后投票
   发表时间:2012-03-01  
phk070832 写道
case0079 写道
为什么不能用数据库解决呢?
又不是说只有一个数据库。



数据库能比内存快吗



两亿条数据显然是放在磁盘上的。数据库最终也是以文件形式放在磁盘上的。所以根本没有必要争论是什么数据源,放在数据库里根本没什么不妥。

sql语句最终也是一段代码,一个算法的实现。在数据库中score会按btree排序的。如果数据库的count算法实现的好的话就可以用sql来计算排名。
0 请登录后投票
   发表时间:2012-03-01  
HADOOP
0 请登录后投票
   发表时间:2012-03-01  
DDT_123456 写道
HADOOP


如果数据没有排序的话可以考虑用HADOOP
0 请登录后投票
   发表时间:2012-03-02  
看了不少高人的回复,有点自己的想法,之前先有几个问题要先确认一下,1、数据源的是否可以直接获取某行记录?2、id是否指的用户的ID 还是只在这个表中做索引用的标识?(这个问题好像也没什么意义,呵呵)
如果可以直接获取指定行的记录,那就用二分法直接查就好了:既然已知数据有2E条,那取第1E行记录比较……下略;
如果不可以真接获取到指定行记录,那单条记录的宽度是否一样(好像这也问的有点多余),如果一样,那设 w=单记录宽度,row=1E * w,取row到row+w之间的数据……下面和前面提到的一样。
其实就是用二分法比较,这个和内存没多大关系。想提高效率的话,前面有高人也提过了,就是建立一个分数区段的索引,根据内存的大小确定区段的长度就好了。
如果数据源只是一个流的话,那就慢慢比吧,没好办法。
0 请登录后投票
   发表时间:2012-03-02   最后修改:2012-03-02
正解!内存中常数时间(微秒级)解决问题。

hardPass 写道
tsxm 写道
hardPass 写道
就一定要用数据表来完成么?
CPU,内存,外存
同学们啊!

桶排序呗,
直接在程序中搞个数组,数组的下标就是分数,元素是每个分数对应的人数。

具体的,如果不需要精确计算,为了减少同步及遍历计算的性能损失,可以为每个数组元素设定一个预留值。



都说内存不够了,用外存估计速度太慢



1G内存还不够?如果现在排名第一的是1亿分,也就是一个长度为1亿的数组而已
300MB左右就够了,剩下的内存还可以再维护个计数缓存的数组(每个元素是上面数组希格玛,0到i),数组长度是上面数组一样

0 请登录后投票
   发表时间:2012-03-02  
因为是已经排序过的所以可以用分端映射 + 2分法.
将2E条数据按每段1000w条分成20组,每组只记录Min Score和Max Score. 这步可以是比较占用资源的,可以后台定时Build.
然后查询的时候用2分法查询Segment,如果命中在哪个Segment里就在Segment里查询.

将Segment分的越小查询速度越快,但占用资源就越多.
0 请登录后投票
   发表时间:2012-03-02  
jiuyuehe 写道
shoushou2001 写道
假设:
分区:1              2           3            4        5。。。
分数:[0, 1000),[1000, 2000),[2000, 3000),[3000, 4000),[)。。。

分别给id、score加索引,用户一登录,就知道id,通过id查找score,即可知道该score所在的分区,如score=2300,位于分区3.

通过select计算3以后的比2300大的共有多少个,可以通过分数范围,一个分区一个分区地求,最后累加的结果再加1就是该用户的排名。
---------------
允许扔XX,但请手下留情


id 都被分开了,怎么通过找id?



成千上万的用户积分在不断变化   定时更新这些分区表是不是很麻烦呢?
0 请登录后投票
   发表时间:2012-03-07  
CshBBrain 写道
kidding87 写道
终于有人说了我说的东西。。。
你们难道就没发现别人说的是数据源,不是数据库
那么多数据select * from table where id=?没有索引这个的效率都低死,数据库还不是一个个找


的确,题目强调了是【数据源】,并且是有序的数据源,没有说具体的存储形式,用数据库,用文件系统,还是用缓存服务器集群......;其实大多数人一看到2亿条数据都想到自己采用怎样形式去存这个数据了,而忽略题目的本意是怎样快速有效的找出用户的排名;依我个人的愚见,用户登录系统时找到用户的分数排名,要到数据库去找,或是到文件系统去找花费不菲而且效率也不见得好,一般会放到缓存服务器集群中。当然其实数据源的存储形式我们不用去操心。用户登录肯定已经获取到id和自己的分数,我们就假设已经知道用户id和用户分数(这是比较合理的假设)。

数据源既然【有序】那么我们肯定就有办法(是什么办法根据数据源具体的存储形式可以写个方法<当然有可能数据源本身可以提供>)取到指定位置的元素。

1.获取数据源的最后一个元素和数据源的第一个元素的 score, 将这2个score 相减 得到的 最大分数与最小分数的差值 S1。
2.用得到的分数差值 除以 数据源的元素数量 得到 平均每个用户之间的 分数差 V1;
3.将用户的分数US 减去 数据源中最小分数 得到用户与最小 分数之间的差值 S2,
4.W = S2 / V1 ,W 为我们估算的用户在所有用户中的排名数量
5.取出W处的元素的score WS
6.如果 用户的分数US 小于 WS,则从 W处开始向前查找用户的排名,如果US大于 WS 则从W处开始向后查找用户的排名,用户的排名就在附近,不用加载太多数据。如果US的只等于WS,那么恭喜你了。
7.这个算法没有考虑分数相同并列排名的处理,我想迅雷也许也没有处理并列排名的情况,只是获取数据源中的具体位置。
8.如果分数分布特别不均匀的话,可以递归使用上面的方法多算几次,直到US 和 WS 相差非常小(小到一个阀值)再从W处开始向前或向后查找定位。


同意这个方案
0 请登录后投票
   发表时间:2012-03-16   最后修改:2012-03-16
废话不说了,上代码,有兴趣朋友跑一下.
产生模拟文件(1,600,000,000B) + 实际算法。
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Random;

//@author piaoboyi@gmail.com
public class BinarySearch {
    public static int nCount = (int) 2e8;

    //!!!!!!!!!!!!!!!!!! Choose disk that is large enough !!!!!!!!!!!!!!
    public static String path = new String("D:/data.dat");

    public static String testPath = new String("D:/test.dat");

    public static Random r = new Random();

    public static void BuildFile() throws IOException {
        // Be sure to use buffer.
        DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
                new FileOutputStream(new File(path))));
        int nCurrentScore = 0;
        for (int i = 0; i < nCount; i++) {
            // int nID = r.nextInt(100);
            // nID is not so useful.
            int nID = 12341231;
            out.writeInt(nID);
            // Becareful to generate the score. because score is small that sizeof(int)/2.
            // Which makes random number should < 20.
            nCurrentScore += r.nextInt(5);
            out.writeInt(nCurrentScore);
        }
        out.close();
    }

    public static void BuildTestFile() throws IOException {
        DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
                new FileOutputStream(new File(testPath))));
        // 25|1
        // 31|2
        // 17|3
        // ...
        out.writeInt(25);
        out.writeInt(1);
        out.writeInt(31);
        out.writeInt(2);
        out.writeInt(17);
        out.writeInt(3);

        out.close();
    }

    public static long BSearch(String path, int nScore) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(path, "r");
        assert (raf.length() % 8 == 0);

        long l = 0, r = raf.length() / 8 - 1;
        while (l <= r) {
            long m = (l + r) / 2;
            long pointer = m * 8;
            raf.seek(pointer);

            @SuppressWarnings("unused")
            int id = raf.readInt();// id.
            int score = raf.readInt();

            if (nScore == score) {
                raf.close();
                return m;
            }
            if (nScore < score)
                r = m - 1;
            else
                l = m + 1;
        }

        raf.close();
        return -1L;
    }

    public static void main(String[] args) throws IOException {
        // BuildTestFile();
        long a = System.currentTimeMillis();
        BuildFile();
        long b = System.currentTimeMillis();
        System.out.println((b-a)/(1000));
        System.out.println("Rank is " + BSearch(path, 0));
    }
}

数据文件产生时间大概100s,大家耐心点哈!
典型磁盘访问时间是10ms,最多31次IO也就310ms.经测试,哥磁盘比典型磁盘快点.
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics