根据权重随机选择

需求:

从一定数量中的歌曲中随机选择300首歌曲进行推荐,每首歌设置权重要提高某些歌曲的命中率。

思想:

  1. 假如n首歌曲总权重是total,将[1,total]范围内所有整数分成n个部分,每首歌占一个闭区间。如:歌曲songs={a,b,c,d},对应权重w={3,1,2,4},那么歌曲a对应[1,3],b对应[4,4],c对应[5,6],d对应[7,10],长度分别是3,1,2,4。左区间是之前所有元素之和加1,右区间是按顺序到此元素所有权重之和。
  2. 按这样的规律右边界可以用数组{3,4,6,10}表示,pre[i]=pre[i-1]+w[i]。
  3. 由于pre是递增的所以可以使用二分查找法快速找到元素,时间复杂度为O(logn)
  4. 核心算法可参照leetcode算法No.528。

实现:

  1. 将歌曲与权重对应关系加载到redis中
  2. 组装pre数组
  3. 利用二分查找法定位随机数所在区间w[i],w[i]对应songs[i]
  4. 根据命中的歌曲查询对应的权重并将该区间关闭,防止再次命中并不影响剩余歌曲的命中概率。
  5. 循环选中300首

代码:

//1、歌曲权重对应关系加载--部分
Map<String, String> generaSongMap = new HashMap<>();
List<Map<String, Object>> recommendSongs = slaveRecommendSongsMapper.selectSongWeightByState(1);
for (Map<String, Object> entry : recommendSongs) {
    generaSongMap.put(entry.get("sourceID").toString(), entry.get("weight").toString());
}

Map<String, String> songMap = MyJedis.hgetAll(ConstUtil.RedisCache.REDIS_MSUIC_SMART_VIDEO_CACHE, ConstUtil.RedisKey.SONG_RECOMMEND_GENERAL_WEIGHT_POOL);
if(songMap !=null && songMap.size()>0){
    Set<String> strings = songMap.keySet();
    Long[] songs = new Long[strings.size()];
    int k = 0;
    for(String id :strings){
        songs[k] = Long.valueOf(id);
        ++k;
    }
    Integer[] pre = new Integer[songs.length];
    pre[0] = Integer.valueOf(songMap.get(songs[0].toString()));
    for(int i = 1; i < songs.length; ++i){
        pre[i]= pre[i-1] + Integer.valueOf(songMap.get(songs[i].toString()));
    }
    
    for(int i = 0;i<count;i++){
        int total = pre[pre.length-1]; //总权重
        int x = (int) (Math.random()*total) +1;
        int preIndex = binarySearch(x, pre);
        Long songid = songs[preIndex];
        generalSongIds.add(songid.toString());
        int weight =Integer.parseInt(songMap.get(songid.toString()));
        for(int j = preIndex;j< pre.length;j++){
            pre[j] = pre[j]-weight;
        }
    }
 
//二分查找法
private int binarySearch(int x,Integer[] pre) {
        int low = 0, high = pre.length - 1;
        while (low < high) {
            int mid = (high - low) / 2 + low;
            if (pre[mid] < x) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        return low;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值