• "top k"问题的深入探讨


          常常遇到这样的一个问题:在海量数据中找出出现频率最高的前K个数,或者从海量数据中找出最大的前K个数,这类问题通常称为“top K”问题,如:在搜索引擎中,统计搜索最热门的10个查询词;在歌曲库中统计下载率最高的前10首歌等等。

        ①: 本人初次学习软件工程,近来便遇到一个类似的问题,问题是关于统计一片文章中统计出现频率最高的前十个单词。其实刚拿到这个程序时,也觉得很容易:无非是文件的导入、单词的分类、以及排序算法。所以便开始编程(因为大学没有养成需求设计的习惯,更加注重编写代码),所以,编着编着便越遍越多,仔细看来不仅代码冗余,而且结构混乱。正如师所说,自己的代码不仅别人看不懂,自己几天后都不知道自己的某个类时期什么作用。于是我便开始着手分析程序(因为自己熟悉java,觉得java的封装做的好,其实论速度还是c语言比较快)。

        First:理解这道题做什么的?

                   这道小程序做的是统计一篇文章出现频率最高的十个词,及相同的次的次数排序。

       Second:理解这道题须解决哪些问题?

                  (1)文件的导入(其实很简单,一个BufferedReader便能搞定)。

                   (2)单词的读取,因为是一整篇文章,需要分割单词依据“,。?!”等这些进行分割。

                   (3)相同字数的统计。

                    (4)排序算法。

       Third:功能的实现?

                (1)文件的导入,依据java提供的基于字符型的文件的输入输出与缓冲流的应用(相信学过I/O的输入输出都应该会)

                       关键代码:

    File file=new File(path);    
    FileReader fileReader=new FileReader(file);     //建立文件输入流
    BufferedReader bufferedReader=new BufferedReader(fileReader);  //建立缓冲输入流
    View Code

                (2)单词的提取,其实刚开始本来想利用数组分割,但是觉得麻烦,后来查阅资料,以前学过的java正则表达式,起到了关键作用。
                  

          public String[] split(String regex,int limit)
    根据匹配给定的正则表达式来拆分此字符串。

    此方法返回的数组包含此字符串的子字符串,每个子字符串都由另一个匹配给定表达式的子字符串终止,或者由此字符串末尾终止。数组中的子字符串按它们在此字符串中出现的顺序排列。如果表达式不匹配输入的任何部分,那么所得数组只具有一个元素,即此字符串。

    关键代码:

    String []splittStr=str.split("[\s,.;!?]");      //利用java正则表达式实现单词的分离
    View Code

    (3)相同字数的统计:因为学过Hashmap集合类,想到了Hashmap中有个方法:

    containsKey(Object key)

              如果此映射包含对于指定键的映射关系,则返回 true。故可以利用此方法进行相同归类:

     关键代码:

     while((str=bufferedReader.readLine())!=null)
           {
               String []splittStr=str.split("[\s,.;!?]");      //利用java正则表达式实现单词的分离
           for(int i=0;i<splittStr.length;i++)
               {
                     if(!splittStr[i].equals(""))
            {
               splittStr[i]=splittStr[i].toLowerCase();
                  //利用hashmap的containsKey判断是否包含键的映射关系
                      if(!hashMap.containsKey(splittStr[i]))                          
                      {
                                   hashMap.put(splittStr[i], 1);
                                      }
                                  else 
                                   {
                 hashMap.put(splittStr[i], Integer.parseInt(""+hashMap.get(splittStr[i]))+1);
                                   }
             }
                  }
           }
    View Code

           (4)排序算法:将Map转化位List,利用sort(List<T> list, Comparator<? super T> c) 
                 根据指定比较器产生的顺序对指定列表进行排序。

             关键代码:

     List<Map.Entry<String,Integer>> list_Data = new ArrayList<Map.Entry<String, Integer>>(hashMap.entrySet());  
        Collections.sort(list_Data, new Comparator<Map.Entry<String, Integer>>()  
          {   
         public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2)  
                {  
                if(o2.getValue()!=null&&o1.getValue()!=null&&o2.getValue().compareTo(o1.getValue())>0){  
                        return 1;  
                     }else{  
                       return -1;  
                     }  
                  }  
             });
    View Code

       Forth:结果测试:

      ②:公司解决如此问题:

         针对top k类问题,通常比较好的方案是【分治 + trie树/hash + 小顶堆】,即先将数据集按照hash方法分解成多个小数据集,然后使用trie树或者 hash统计每个小数据集中的query词频,之后用小顶堆求出每个数据集中出频率最高的前K个数,最后在所有top K中求出最终的top K

          实际上,最优的解决方案应该是最符合实际设计需求的方案,在实际应用中,可能有足够大的内存,那么直接将数据扔到内存中一次性处理即可,也可能机器有多个核,这样可以采用多线程处理整个数据集。

    哈哈,到此本人第一次软件工程初步认识,希望大家多提意见多多进步。

    附源代码:

    package com.su.test;
    
    import java.io.BufferedReader;
    
    import java.io.BufferedWriter;
    
    import java.io.File;
    
    import java.io.FileReader;
    
    import java.io.FileWriter;
    
    import java.util.ArrayList;
    
    import java.util.Collections;
    
    import java.util.Comparator;
    
    import java.util.HashMap;
    
    import java.util.List;
    
    import java.util.Map;
    
    import java.util.Scanner;
    
    public class Test {
    
        //没有处理异常,将异常抛出
    
            public static void main(String[] args) throws Exception{
    
                  // TODO Auto-generated method stub
    
                 System.out.print("请输入所在文件的位置:");
    
                 Scanner scanner=new Scanner(System.in);   //文本输入
    
           String path=scanner.next();
    
                File file=new File(path);    
    
                FileReader fileReader=new FileReader(file);     //建立文件输入流
    
           BufferedReader bufferedReader=new BufferedReader(fileReader);  //建立缓冲输入流
    
           String str=null;
    
           HashMap<String,Integer> hashMap=new HashMap<String, Integer>();   //hashmap集合,利用键值:key-单词、value-出现次数
    
           while((str=bufferedReader.readLine())!=null)
    
           {
    
               String []splittStr=str.split("[\s,.;!?]");      //利用java正则表达式实现单词的分离
    
               for(int i=0;i<splittStr.length;i++)
    
               {
    
                                        if(!splittStr[i].equals(""))
    
                       {
    
                                             splittStr[i]=splittStr[i].toLowerCase();
    
                                             //利用hashmap的containsKey判断是否包含键的映射关系
    
                                             if(!hashMap.containsKey(splittStr[i]))                         
    
                         {
    
                                          hashMap.put(splittStr[i], 1);
    
                                          }
    
                                          else
    
                                         {
    
                        hashMap.put(splittStr[i], Integer.parseInt(""+hashMap.get(splittStr[i]))+1);
    
                      }
    
                       }
    
              }
    
           }
    
           /*
    
            * *对Hashmap进行排序(按value值进行排序)
    
            */
    
           List<Map.Entry<String,Integer>> list_Data = new ArrayList<Map.Entry<String, Integer>>(hashMap.entrySet());  
    
             Collections.sort(list_Data, new Comparator<Map.Entry<String, Integer>>()  
    
                 {   
    
                    public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2)  
    
                     {  
    
                     if(o2.getValue()!=null&&o1.getValue()!=null&&o2.getValue().compareTo(o1.getValue())>0){  
    
                      return 1;  
    
                     }else{  
    
                       return -1;  
    
                     }  
    
                  }  
    
               });
    
            int len=list_Data.size();   //获取list_Data的大小
    
            for(int i=0;i<10;i++)
    
            {
    
                System.out.print(list_Data.get(i)+"  ");
    
            }
    
           bufferedReader.close();   
    
        }
    
    }
    View Code

           

  • 相关阅读:
    创建可视化优秀网站的40个精美jquery插件推荐
    究极程序员跨过的艰难六步
    编写可移植的PHP代码
    程序员如何保持优秀
    网站安全检查列表
    PHP之谈(四)——smarty模板的学习
    PHP
    弱校ACM奋斗史
    学习PHP重在坚持
    About Me
  • 原文地址:https://www.cnblogs.com/sulindong/p/3574250.html
Copyright © 2020-2023  润新知