bitmap算法(位图算法)
- 问题描述:假设针对用户-特征问题,一个用户有很多特征,同时有很多用户。那么如何存储、表达是个问题,因为如果用数据库来操作,select和join都需要内存比较大,且计算也复杂,性能差。所以引入位图bitmap,因为在位上的操作性能是最高的。
- 步骤
- 首先建立用户名和用户ID的映射。
- 让每个标签存储包含此标签的所有用户ID,每个标签都是一个独立bitmap,每个标签:比如性别,有两个标签男女,所以此有两个bitmap。
- 例如程序员和00后这两个群体,各自的bitmap分别如下:
- 查找使用苹果手机的程序员用户
- 优点:占用内存少,因为是以bit为存储单位的,还有就是计算性能高。
- 代码
private long[] words;
private int size;
public MyBitmap(int size)
{
this.size = size;
this.words = new long[(getWordIndex(size - 1) + 1)];
}
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;
}
public void setBit(int bitIndex)
{
if(bitIndex < 0 || bitIndex > size - 1)
{
throw new IndexOutOfBoundsException("超过Bitmap有效范围");
}
int wordIndex = getWordIndex(bitIndex);
words[wordIndex] |= (1L << bitIndex);
}
private int getWordIndex(int bitIndex)
{
return bitIndex >> 6;
}
public static void main(String[] args){
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)
{
if(hashMap.size() >= limit)
{
String oldKey = removeNode(head);
hashMap.remove(oldKey);
}
node = new Node(key, value);
addNode(node);
hashMap.put(key, node);
}
else{
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] 元
这个公式保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。
- 代码
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++)
{
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)));
}
}