Extension:CentralNotice/Allocation system

Allocation is the process in which CentralNotice determines the percentage of page views a banner will receive. It is an iterative process across all active banners under a selection vector  and is moderated by campaign priority and the each campaign's 'maximum allocation of traffic' setting. By default each banner in a campaign is allocated equally, however a weight may be set per banner in a campaign that affects the relative allocation of each banner in a campaign.

Moderation by Priority
Higher priority levels may take up to a certain percentage of initially available free space. This means that two banners in two separate campaigns each having 100% requested allocation but at different priority levels will have different allocation with higher priority levels getting higher allocations. For the moment the table is defined as:


 * It may seem counter-intuitive that the lowest priority gets 100% maximum initial allocation; but it allows the algorithm to completely fill any remaining space with low priority banners.

Algorithm

 * is an array keyed on banner with subkeys:
 * - theoretical allocation of the banner with no other banners present
 * - allocation of the banner as it progresses through the algorithm


 * is the banner object with following properties:
 * relative weight of banner inside of campaign
 * associated campaign object with properties:
 * priority level of campaign
 * sum of all banner weights in the campaign under the selection vector
 * maximum requested allocation of traffic

The following pseudofunctions are ommited for clarity
 * - returns the total requested allocation (of type requested or actual) in a priority
 * - Returns the maximum initial allocation for a priority level
 * - Returns the set of banners that does not have Actual = Requested

Initialization
freeAllocation = 1 // Everything available

ForEach banner: // Calculate the theoretical maximum allocation for a banner bannerAlloc[banner].Requested = ( banner.Weight / banner.Campaign.SumOfWeights ) * banner.Campaign.MaxAllocation bannerAlloc[banner].Actual = bannerAlloc[banner].Requested

First pass: Initial even distribution, priority moderated
ForEach priority: // Calculate the initial actual allocation based on free space, cascading down priority levels // Evenly distribute the free space over all banners If TotalAllocInPri( priority, requested ) > ( MaxPriAlloc( priority ) * freeAllocation ): ForEach banner With banner.Priority = priority: bannerAlloc[banner].Actual *= TotalAllocInPri( priority, requested ) / ( MaxPriAlloc( priority ) * freeAllocation )

// Update the remaining allocation now that we've fixed this priority level freeAllocation -= TotalAllocInPri( priority, actual )

Second pass: Under-allocation correction
// If we have free allocation space, and there are banners which are not yet at their theoretical maximum // give them the space equally. While ( freeAllocation > 0% ) And ( Count( UnderAllocatedBanners( bannerAlloc ) ) > 0 ): ForEach UnderAllocatedBanners( bannerAlloc ) As banner: banner.Actual += ( freeAllocation / Count( UnderAllocatedBanners( bannerAlloc ) )

If banner.Actual > banner.Requested: // We just over allocated this banner. Set to theoretical maximum and leave the space for the next pass freeAllocation += ( banner.Actual - banner.Requested ) banner.Actual = banner.Requested