项目作者: shawpan

项目描述 :
Campaign scheduling API
高级语言: Java
项目地址: git://github.com/shawpan/campaign-allocator.git
创建时间: 2017-03-28T14:32:11Z
项目社区:https://github.com/shawpan/campaign-allocator

开源协议:MIT License

下载


CampaignAllocator

Problem definition and goal

Given monthly inventory amount and a list of campaigns with curstomer name, unit amount of impressions and corresponding price, find the best set of campaigns that will generate maximum revenue not exceeding monthly inventory.

Response time goals:

  • not more than 3 seconds (best case)
  • 1 minute (worst case)

Memory goals:

  • Heap size less than 4GB

Algorithm/ Solution

This is a case of classic 0-1 Knapsack problem with repeating elements. Dynamic Programming (DP) works best with O(inventoryAmount * numberOfCampaigns) runtime and O(inventoryAmount) memory. However when the numbers are too large it becomes a NP-Complete, even NP-Hard problem. Therefore, different algorithms should be used based on request data such as DP for tractable size and branch-bound approach for large numbers within close range having tractable number of campaigns.

DP Approach

DP solution is implemented in CampaignAllocationAlgorithmDP class with some further optimization. Steps

  • Scale the inventory numbers dividing by Greatest Common Divisor of monthly inventory amount and all the unit impression amount of campaigns. This will significantly reduce the run time and memory unless GCD is 1. If the monthly inventory is still too large after scaling, consider scale factor = 100 which will make the DP solution feasible under current constraints but lose data upto 2 digits a nd find a near optimal solution.
    Runt time is O(inventoryAmount/ inventoryScaleFactor * numberOfCampaigns) and memory is 2 * O(inventoryAmount/ inventoryScaleFactor)

TODO Improvements

  • Investigate other approaches like branch-bound, genetic algorithm etc.
  • Investigate to use distributed/ concurrent implementations.

Sample results

Test case 1 response time 33ms

Input

  1. curl -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -H "Postman-Token: aba79d72-a914-6eec-6d99-ad59ec6280f0" -d '{
  2. "monthlyInventory": 32356000,
  3. "campaigns": [{
  4. "customerName": "customer1",
  5. "impressions": 2000000,
  6. "revenue": 200
  7. }, {
  8. "customerName": "customer2",
  9. "impressions": 3500000,
  10. "revenue": 400
  11. },{
  12. "customerName": "customer3",
  13. "impressions": 2300000,
  14. "revenue": 210
  15. },{
  16. "customerName": "customer4",
  17. "impressions": 8000000,
  18. "revenue": 730
  19. },{
  20. "customerName": "customer5",
  21. "impressions": 10000000,
  22. "revenue": 1000
  23. },{
  24. "customerName": "customer6",
  25. "impressions": 1500000,
  26. "revenue": 160
  27. },{
  28. "customerName": "customer7",
  29. "impressions": 1000000,
  30. "revenue": 100
  31. }]
  32. }
  33. ' "http://127.0.0.1:8080/campaign-allocation"

Output

  1. {
  2. "totalImpressions": 32000000,
  3. "totalRevenue": 3620,
  4. "campaigns": [
  5. {
  6. "customerName": "customer1",
  7. "count": 0,
  8. "impressions": 2000000,
  9. "revenue": 200,
  10. "totalImpressions": 0,
  11. "totalRevenue": 0
  12. },
  13. {
  14. "customerName": "customer2",
  15. "count": 8,
  16. "impressions": 3500000,
  17. "revenue": 400,
  18. "totalImpressions": 28000000,
  19. "totalRevenue": 3200
  20. },
  21. {
  22. "customerName": "customer3",
  23. "count": 0,
  24. "impressions": 2300000,
  25. "revenue": 210,
  26. "totalImpressions": 0,
  27. "totalRevenue": 0
  28. },
  29. {
  30. "customerName": "customer4",
  31. "count": 0,
  32. "impressions": 8000000,
  33. "revenue": 730,
  34. "totalImpressions": 0,
  35. "totalRevenue": 0
  36. },
  37. {
  38. "customerName": "customer5",
  39. "count": 0,
  40. "impressions": 10000000,
  41. "revenue": 1000,
  42. "totalImpressions": 0,
  43. "totalRevenue": 0
  44. },
  45. {
  46. "customerName": "customer6",
  47. "count": 2,
  48. "impressions": 1500000,
  49. "revenue": 160,
  50. "totalImpressions": 3000000,
  51. "totalRevenue": 320
  52. },
  53. {
  54. "customerName": "customer7",
  55. "count": 1,
  56. "impressions": 1000000,
  57. "revenue": 100,
  58. "totalImpressions": 1000000,
  59. "totalRevenue": 100
  60. }
  61. ]
  62. }

Test case 2 response time 2288ms

Input

  1. curl -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -H "Postman-Token: 8c433918-be3b-db7f-0fbe-3d94de0d3190" -d '{
  2. "monthlyInventory": 50000000,
  3. "campaigns": [{
  4. "customerName": "customer1",
  5. "impressions": 1,
  6. "revenue": 0
  7. }, {
  8. "customerName": "customer2",
  9. "impressions": 2,
  10. "revenue": 2
  11. },{
  12. "customerName": "customer3",
  13. "impressions": 3,
  14. "revenue": 2
  15. },{
  16. "customerName": "customer4",
  17. "impressions": 70000,
  18. "revenue": 71000
  19. },{
  20. "customerName": "customer5",
  21. "impressions": 49000000,
  22. "revenue": 50000000
  23. }]
  24. }
  25. ' "http://127.0.0.1:8080/campaign-allocation"

Output

  1. {
  2. "totalImpressions": 50000000,
  3. "totalRevenue": 51014000,
  4. "campaigns": [
  5. {
  6. "customerName": "customer1",
  7. "count": 0,
  8. "impressions": 1,
  9. "revenue": 0,
  10. "totalImpressions": 0,
  11. "totalRevenue": 0
  12. },
  13. {
  14. "customerName": "customer2",
  15. "count": 10000,
  16. "impressions": 2,
  17. "revenue": 2,
  18. "totalImpressions": 20000,
  19. "totalRevenue": 20000
  20. },
  21. {
  22. "customerName": "customer3",
  23. "count": 0,
  24. "impressions": 3,
  25. "revenue": 2,
  26. "totalImpressions": 0,
  27. "totalRevenue": 0
  28. },
  29. {
  30. "customerName": "customer4",
  31. "count": 14,
  32. "impressions": 70000,
  33. "revenue": 71000,
  34. "totalImpressions": 980000,
  35. "totalRevenue": 994000
  36. },
  37. {
  38. "customerName": "customer5",
  39. "count": 1,
  40. "impressions": 49000000,
  41. "revenue": 50000000,
  42. "totalImpressions": 49000000,
  43. "totalRevenue": 50000000
  44. }
  45. ]
  46. }

Test case 3 response time 22ms
Input

  1. curl -X POST -H "Content-Type: application/json" -H "Cache-Control: no-cache" -H "Postman-Token: c00af6b4-ba69-14d9-e487-22bc4cb93e30" -d '{
  2. "monthlyInventory": 2000000000 ,
  3. "campaigns": [{
  4. "customerName": "customer1",
  5. "impressions": 1000000,
  6. "revenue": 5000
  7. }, {
  8. "customerName": "customer2",
  9. "impressions": 2000000,
  10. "revenue": 9000
  11. },{
  12. "customerName": "customer3",
  13. "impressions": 3000000,
  14. "revenue": 20000
  15. }]
  16. }' "http://127.0.0.1:8080/campaign-allocation"

Output

  1. {
  2. "totalImpressions": 2000000000,
  3. "totalRevenue": 13330000,
  4. "campaigns": [
  5. {
  6. "customerName": "customer1",
  7. "count": 2,
  8. "impressions": 1000000,
  9. "revenue": 5000,
  10. "totalImpressions": 2000000,
  11. "totalRevenue": 10000
  12. },
  13. {
  14. "customerName": "customer2",
  15. "count": 0,
  16. "impressions": 2000000,
  17. "revenue": 9000,
  18. "totalImpressions": 0,
  19. "totalRevenue": 0
  20. },
  21. {
  22. "customerName": "customer3",
  23. "count": 666,
  24. "impressions": 3000000,
  25. "revenue": 20000,
  26. "totalImpressions": 1998000000,
  27. "totalRevenue": 13320000
  28. }
  29. ]
  30. }

How to start the CampaignAllocator application

  1. Run mvn clean install or mvn package to build your application
  2. Start application with java -jar target/campaign-allocator-1.0-SNAPSHOT.jar server config.yml
  3. To check that your application is running enter url http://localhost:8080

Health Check

To see your applications health enter url http://localhost:8081/healthcheck