广告中的去重选择
在广告展示策略中,有一些去重的需求。本文中介绍一个去重中的细节问题。
举一个广告主去重的例子。如果在页面上一次可以展示三个广告,如果我们展示的广告中有两上广告是同一个广告主的,那么广告主会很不开心。因为他会感觉自己的展示次数是虚的,对他来讲是一次展示,但给他的报表里却说是两次。
再举一个素材去重的例子。如果一个用户在网页浏览时,看到大量相似素材非常接近的广告,他会有点反感,并且通常不会去点击多个相似素材的广告,这对广告主,广告受众,广告平台都是不利的。
看到这个问题,第一反应会是这算什么问题呀,简单去重一下不就可以吗?是的,去重一下是可以解决,但这是最优解吗?
我举一个例子说明,下图中的每一个块代表一个候选广告,一个广告有两个属性,用两个颜色表示,相同属性中相同颜色不能重复,一共需要2个广告。
现在看简单去重的逻辑,我们先选择了广告1,广告1的第一个属性(深棕色)与广告2的第一个属性冲突,广告1的第二个属性(深蓝色)与广告3第二个属性冲突。所以广告1只能与广告4搭配,它们两个的价值之和为9+1=10。
而换一个思路,不一定要选择第一个广告后去重,那么我们选择广告2和广告3,它们的价值之和是8+7=15。也就是说简单的去重逻辑不是最优策略。
有了上述的问题,我们将它形式化成一个算法问题。有N个候选广告,每个广告一次展示的价值为Vi (i=[1, … N]),N个候选广告已经按价值排序,一次展示需要K个广告。现要找出K个价值之和最大的广告。
再对上面的问题进行一点解释,候选广告已经按价值排序,这是广告系统中的逻辑决定的。
看完问题之后,第一反应应该是这不就是0/1背包问题吗?但仔细想一下,与0/1背包问题还是有点不一样,1. 0/1背包问题中的weight在这里是相同的,一个广告就占一个展示的位置,广告与广告所占的空间相同(我们现在正在搞不相素材规格的混排问题,就不发散了),但这个区别只是说它是0/1背包问题的一个特例。2. N个候选已经按价值排序过了,按价值排序了,那么就可以提前剪枝了。
Talk is cheap, Show me the Code
AD_COUNT = ad_list.size();
CURRENT_MAX_VALUE = 0;
REQUIRED_AD_COUNT = 4;
FETCH_LIST = []
FindMaxValue(index, sum_value, fetch_list)
if index >= AD_COUNT
if sum_value > CURRENT_MAX_VALUE
CURRENT_MAX_VALUE = sum_value
FETCH_LIST = fetch_list;
return ;
if fetch_list.size() == REQUIRED_AD_COUNT
if sum_value > CURRENT_MAX_VALUE
CURRENT_MAX_VALUE = sum_value
FETCH_LIST = fetch_list;
return ;
if (Pruning(index, fetch_count, sum_value))
return ;
value = ad_list[index].value;
copy_sum_value = sum_value;
copy_index = index
copy_fetch_list = fetch_list.deep_copy();
FindMaxValue(index + 1, sum_value, fetch_list);
copy_sum_value += value;
copy_fetch_list fetch_list.append(index);
copy_index += 1;
FindMaxValue(copy_index, copy_sum_value, copy_fetch_list);
Pruning(index, fetch_count sum_value)
int max_value = sum_value;
remain_count = REQUIRED_AD_COUNT - fetch_count;
for i = index to remain_count
if index >= AD_COUNT &&
max_value < CURRENT_MAX_VALUE
return true;
max_value += ad_list[i].value;
if max_value < CURRENT_MAX_VALUE
return true;
return false;