07_算中的实际应用

bitmap算法(位图算法)

  • 问题描述:假设针对用户-特征问题,一个用户有很多特征,同时有很多用户。那么如何存储、表达是个问题,因为如果用数据库来操作,select和join都需要内存比较大,且计算也复杂,性能差。所以引入位图bitmap,因为在位上的操作性能是最高的。
  • 步骤
    • 首先建立用户名和用户ID的映射。在这里插入图片描述
    • 让每个标签存储包含此标签的所有用户ID,每个标签都是一个独立bitmap,每个标签:比如性别,有两个标签男女,所以此有两个bitmap。
    • 例如程序员和00后这两个群体,各自的bitmap分别如下:在这里插入图片描述
    • 查找使用苹果手机的程序员用户在这里插入图片描述
  • 优点:占用内存少,因为是以bit为存储单位的,还有就是计算性能高。
  • 代码
// 每一个word是一个long类型元素,对应一个64位二进制数据
private long[] words;
// bitmap的位数大小
private int size;

public MyBitmap(int size)
{
    this.size = size;
    this.words = new long[(getWordIndex(size - 1) + 1)];
}

/*判断bitmap某一位的状态*/
public boolean getBit(int bitIndex)
{
    if(bitIndex < 0 || bitIndex > size - 1)
    {
        throw new IndexOutOfBoundsException("超过Bitmap有效范围");
    }
    int wordIndex = getWordIndex(bitIndex);
    return (words[wordIndex] & (1L << bitIndex)) != 0;
}

/*把bitmap某一位设置为true  位图的第bitIndex*/
public void setBit(int bitIndex)
{
    if(bitIndex < 0 || bitIndex > size - 1)
    {
        throw new IndexOutOfBoundsException("超过Bitmap有效范围");
    }
    int wordIndex = getWordIndex(bitIndex);
    words[wordIndex] |= (1L << bitIndex);
}

/*定位bitmap某一位所对应的word  位图的第bitIndex位*/
private int getWordIndex(int bitIndex)
{
    // 右移6位,相当于除以64
    return bitIndex >> 6;
}

public static void main(String[] args){
    /* 针对用户-特征模型,这个地方128是用户数量,如果人数增多,可以增加long[n]
    n的大小,然后一个MyBitmap是一个特征,下面bitMap.setBit(126)的意思是该特征
    第126个客户具有,所以这个位置设成true。如果有新特征,需要再new一个MyBitmap,然后如果有新人,需要增加128的数值。*/
    MyBitmap bitMap = new MyBitmap(128);
    bitMap.setBit(126);
    bitMap.setBit(75);
    System.out.println(bitMap.getBit(126));
    System.out.println(bitMap.getBit(78));
}

LRU算法:Least Recently Used 最近最少使用

  • 问题描述:
    在这里插入图片描述
    中间新建一个哈希表的目的是用作换成,这样不用每次都去数据库中读取,因为这样会导致性能低,但是当访问哈希表次数变多以后,出现内存溢出,而且也会导致读取缓慢。
  • 解决方法:LRU算法
    • 建立LRU哈希表在这里插入图片描述
    • 往哈希表中插入数据:
      • 如果新插入的不存在于哈希表中,则需要从数据库中读取出来,插入到缓存中,此时插入到最右边,最左边是最少被访问到得。在这里插入图片描述
      • 如果新插入的存在于哈希表中,这时需要把已经存在的从哈希表中删除,然后移动到最后的最右边的位置。在这里插入图片描述
    • 假设当再插入时,缓存容量达到了上限,必须先删除掉最少被访问的数据,那么位于哈希表最左端的数据就会被删除。在这里插入图片描述
  • 代码
private Node head;
private Node end;
private int limit;  // 缓存存储上限

class Node{
    Node(String key, String value)
    {
        this.key = key;
        this.value = value;
    }
    public Node pre;
    public Node next;
    public String key;
    public String value;
}

/*删除节点*/
private String removeNode(Node node){
    if(node == head && node == end)
    {
        // 移除唯一节点
        head = null;
        end = null;
    }
    else if(node == end)
    {
        // 移除尾部节点
        end = end.pre;
        end.next = null;
    }
    else if(node == head)
    {
        // 移除头节点
        head = head.next;
        head.pre = null;
    }
    else
    {
        // 移除中间节点
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }
    return node.key;
}


/*尾部插入节点*/
private void addNode(Node node)
{
    if(end != null)
    {
        end.next = node;
        node.pre = end;
        node.next = null;
    }
    end = node;
    if(head == null)
    {
        hed = node;
    }
}

/*刷新被访问节点的位置*/
private void refreshNode(Node node)
{
    // 如果访问的是尾部节点,则无需移动节点
    if(node == end)
    {
        return;
    }
    // 移除节点
    removeNode(node);
    // 在尾部增加节点
    addNode(node);
}

private HashMap<String, Node> hashMap;
public LRUCache(int limit)
{
    this.limit = limit;
    hashMap = new HashMap<String, Node>();
}

public String get(String key){
    Node node = hashMap.get(key);
    if(node == null)
    {
        return null;
    }
    refreshNode(node);
    return node.value;
}

public void put(String key, String value)
{
    Node node = hashMap.get(key);
    if(node == null)
    {
        // 如果key不存在,则不插入key-value
        if(hashMap.size() >= limit)
        {
            String oldKey = removeNode(head);
            hashMap.remove(oldKey);
        }
        node = new Node(key, value);
        addNode(node);
        hashMap.put(key, node);
    }
    else{
        // 如果key存在,则刷新key-value
        node.value= value;
        refreshNode(node);
    }
}

public void remove(String key)
{
    Node node = hashMap.get(key);
    removeNode(node);
    hashMap.remove(key);
}

public static void main(String[] args)
{
    LRUCache lruCache = new LRUCache(5);
    lruCache.put("001", "用户1信息");
    lruCache.put("002", "用户1信息");
    lruCache.put("003""用户1信息");
    lruCache.put("004", "用户1信息");
    lruCache.put("005", "用户1信息");
    lruCache.get("002";
    lruCache.put("004", "用户2信息更新");
    lruCache.put("006", "用户6信息");
    System.out.println(lruCache.get("001"));
    System.out.println(lruCache.get("006"));
}

抢红包

  • 问题描述:微信抢红包,但是得满足
    • 所有抢得金额之和要等于红包金额,不能多也不能少。
    • 每个人至少抢到1分钱。
    • 要保证红包拆分的金额尽可能分布均匀,不要出现两极分化太严重的情况发生。
  • 解决方法:二倍均值法,假设红包剩余金额为m元,剩余人数为n,那么有如下公式:
    每次抢到的金额 = 随机区间[0.01, m/n * 2 -0.01] 元
    这个公式保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。
  • 代码
/*拆分红包 
totalAmout 总金额
totalPeopleNum 总人数
*/
public static List<Integer> divideRedPackage(Integer totalAmout, Integer totalPeopleNum)
{
    List<Integer> amoutList = new ArrayList<Integer>();
    Integer restAmout = totalAmout;
    Integer restPeopleNum = totalPeopleNum;
    Random random = new Random();
    for(int i = 0; i < totalPeopleNum - 1; i++)
    {
        // 随机范围:[1, 剩余人均金额的2倍 - 1] 分
        int amout = random.nextInt(restAmout / restPeopleNum * 2 - 1) + 1;
        restAmout -= amout;
        restPeopleNum--;
        amounmtList.add(amout);
    }
    amoutList.add(restAmount);
    return amoutList;
}

/*测试*/
public static void main(String[] args)
{
    List<Integer> amoutList = divideRedPackage(1000, 10);
    for(Integer amout : amoutList){
        System.out.println("抢到金额:" + new BigDecimal(amout).divide(new BigDecimal(100)));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值