0%

RTB实时竞价

在RTB场景下,最优出价算法会受到计费方式的影响。

常见的计费方式有两种:

  • 二价计费拍卖(SPA)

    二价计费拍卖下,流量方会根据出价对所有参竞广告排序,出价最高的广告获得曝光机会,按第二高的出价计费。

  • 一价计费拍卖(FPA)

    而在一价计费拍卖下,按照最高出价来计费。

理想二价环境下,由于实际支付的价格和出价无关,DSP的最佳策略是直接使用真实出价。但一价拍卖下,真实出价并不是DSP的最佳选择。假设DSP的真实出价是X,而外部最高出价是Y,其最优策略是将出价略高于Y,这样既能确保赢得竞标,又不会支付过高的费用。

哈夫曼树

霍夫曼树也叫哈夫曼树,也称为最优二叉树,是一种带权路径最短的二叉树。在信息检索时很常用。

概念

  • 节点之间的路径长度:从一个节点到另一个节点之间的分支数量称为两个节点之间的路径长度
  • 树的路径长度:从根节点到树中每一个节点的路径长度之和
  • 节点的带权路径长度:从该节点到根节点之间的路径长度与节点上权的乘积
  • 树的带权路径:树中所有叶子节点的带权路径长度之和

对于n个叶子节点哈夫曼树来说,一共需要2*n-1个节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class HuffmanTree<E> {

static class Node<E> {
E data;
double weight;
Node<E> left;
Node<E> right;

public Node(E data, double weight) {
this.data = data;
this.weight = weight;
}

@Override
public String toString() {
return new StringJoiner(", ", Node.class.getSimpleName() + "[", "]")
.add("data=" + data)
.add("weight=" + weight)
// .add("left=" + left)
// .add("right=" + right)
.toString();
}
}

public static void main(String[] args) {
List<Node<String>> nodes = new ArrayList<>();
nodes.add(new Node<>("A", 40));
nodes.add(new Node<>("B", 8));
nodes.add(new Node<>("C", 10));
nodes.add(new Node<>("D", 30));
nodes.add(new Node<>("E", 10));
nodes.add(new Node<>("F", 2));

Node<String> root = createTree(nodes);

System.out.println(breadthFirst(root));

}

private static <E> List<Node<E>> breadthFirst(Node root) {
Queue<Node<E>> queue = new ArrayDeque<>();

List<Node<E>> result = new ArrayList<>();

if (root != null) {
queue.add(root);

while (!queue.isEmpty()) {
Node<E> node = queue.poll();
result.add(node);

if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
}

return result;
}

private static <E> Node<E> createTree(List<Node<E>> nodes) {
while (nodes.size() > 1) {
nodes.sort(Comparator.comparingDouble(o -> -o.weight));

Node<E> left = nodes.get(nodes.size() - 1);
Node<E> right = nodes.get(nodes.size() - 2);
Node<E> parent = new Node<E>(null, left.weight + right.weight);
parent.left = left;
parent.right = right;
nodes.remove(nodes.size() - 1);
nodes.remove(nodes.size() - 1);
nodes.add(parent);
}
return nodes.get(0);
}
}

结果为

1
[Node[data=null, weight=100.0], Node[data=A, weight=40.0], Node[data=null, weight=60.0], Node[data=null, weight=30.0], Node[data=D, weight=30.0], Node[data=C, weight=10.0], Node[data=null, weight=20.0], Node[data=null, weight=10.0], Node[data=E, weight=10.0], Node[data=F, weight=2.0], Node[data=B, weight=8.0]]

哈夫曼编码

使用哈夫曼树可以解决报文编码问题,相当于进行了压缩。如对字符串”abcdabcaba”进行编码,使用频率作为权值,a、b、c、d的权值分别为4、3、2、1。从哈夫曼树根节点开始,对左子树分配代码0,对右子树分配代码1,得到的哈夫曼树为

哈夫曼编码

然后将字符串”abcdabcaba”按照该二进制码生成为0101101110101100100,仅为19个字符

循环队列

用数组实现队列时,如果不移动,随着数组的不断读写,会出现假满队列的情况。即尾数组已满,但头数组还是空的。循环队列解决了这个问题,在逻辑上把数组的头尾相连,形成循环队列,当数组尾满的时候,要判断数组头是否为空,不为空继续存放数据。

优点

  • 可以有效的利用资源

缺点

  • 由于入队时尾指针向前追赶头指针,出队时头指针向前追赶尾指针,造成队空或队满时头尾指针均相等。无法通过front==rear来判断队列是空还是满

广播域和冲突域

路由器是用于连接多个逻辑上分开的网络,路由器的每个端口所连接的网络都独自构成一个广播域。

集线器内部,各接口都是通过背板总线连接在一起的,在逻辑上构成一个共享的总线,集线器和所有接口的主机共同构成了一个冲突域。

交换机上每个接口都是自己的一个冲突域。

矩阵

对称矩阵

存下三角区和主对角线

  • 就是Aij=Aji 所以存储的时候只需要存 主对角线+下三角区
  • i>=j时按行存储从0开始 Aij=(i+1)i/2+j+1 j>=i时根据Aji=Aij求解就行了

三对角矩阵

  • 就是只有三条斜对角线,旁边的三角形(都是0)不要,也不需要存储
  • 按行存储从0开始的 Aij=2i+j+1

稀疏矩阵

存储矩阵一般是采用二维数组,优点是可以随机访问每一个元素,因而能够较容易的实现矩阵的各种运算。但对于稀疏矩阵来说,会重复存储很多0,浪费空间。

  • 矩阵很大,存储的东西很少

稀疏矩阵的三元组表是对稀疏矩阵的压缩存储方式

  • 三元顺序表是三元组表的顺序存储结构
  • 十字链表是三元组表的链式存储结构