example_4.md 20.1 KB

【LP专题-4】视频流量调控:保障每个视频播放量满足预期的基础上目标最大化

使用过程提示

Tips:

  • 您可点击右下角“下一步”,查看后续的建模讲解和源代码。
  • 本教程文档是 .md 格式,此讲解窗口采用阿里云 CloudShell 的teachme指令打开,如命令行输入 teachme doc/fileName.md 命令打开。
  • CloudShell 默认模式是登录失效后线上机器内信息删除,建议您在本地机器上编辑和保存自己的代码,然后通过 CloudShell 左上角上传文件的方式上传,也可进行下载。CloudShell 窗口关闭后原登录信息会清除,建议同时开两个窗口,防止误操作关闭;同时不建议离开窗口太久导致登录失效。
  • 如果想体验运行MindOpt求解器代码,请①在天池-决策优化MindOpt网址申请免费的授权Token,并替换掉示例代码里的Token;②并安装MindOpt优化求解器的SDK:安装指导。如已安装,可跳过。

^___^ 还没体验过安装和运行求解器的同学,可以直接点击链接:一键安装MindOpt求解器,并一键用令行求解.mps或.lp文件~~

案例问题背景

在很多视频在线流量调控场景,需要在保证每个视频内容播放量的同时,使得播放总量最大化。如广告、通知、宣发内容等。而线上流量是有限的,且不同的宣发内容对用户的吸引力不一样。 我们把问题模型建立如下: image.png

示例数据

保量约束表

item0 item1 item2
下界 0 0 1
上界

CTR 预估表

user0 user1
item1 0.52 0.92
item2 0.31 0.93
item3 0.82 0.91

Tips:案例内容可能参考自互联网,如有侵权,请联系我们删除。

代码示例-C语言-MakeFile 文件

本案例的makefile文件同案例2营养调配中的示例,可直接修改makefile中的TARGET = example_2变为TARGET = example_4_xxx来编辑。

step 1:建立一个makefile文件

如下。除示例nano指令外,编辑文件可使用vi、vim、nano、emacs等不同编辑器指令,或touch创建文件,也可采用CloudShell右上角的“笔”的编辑图标来选择对应的文档进行新增和修改。

mkdir c
cd c
mkdir tmp
nano makefile

然后复制下面的makefile的代码粘贴至makefile文件里(注意根据软件版本或安装地址来调整内容):

.RECIPEPREFIX = >
INCPATHS=-I$${HOME}/mindopt/linux64-x86/include
LIBPATHS=-L$${HOME}/mindopt/linux64-x86/lib

MINDOPTLIB=-lmindopt
CCOPT=
LDOPT=-Wl,-rpath-link,$${HOME}/mindopt/linux64-x86/lib -Wl,-rpath,'$$ORIGIN/../../linux64-x86/lib' -Wl,--no-as-needed -pthread -lc -lm -ldl
CC=cc -m64
LD=cc -m64

# the build target executable:
TARGET = example_4submit

all: $(TARGET)

$(TARGET).o: $(TARGET).c
>$(CC) -c -g $(INCPATHS) $(CCOPT) -o $@ $<

$(TARGET): $(TARGET).o
>$(CC) -g $(LIBPATHS) $(LDOPT) -o $@ $< $(MINDOPTLIB)

根据提示来修改和保存文件,如ctrl+o保存,然后看到对应名字OK后摁enter即可,如ctrl+x退出,如有修改保存按Y,再看到对应名字OK后摁enter即可退出。

也可采用CloudShell右上角的“笔”的编辑图标来选择对应的文档进行修改。

step 2:运行提交问题的代码

上文中makefile编译的文件名默认为 example_4submit.c。可以通过以下指令新建文件,然后复制下面几页对应的C代码保存。(除示例nano指令外,编辑文件可使用vi、vim、nano、emacs等不同编辑器指令,或touch创建文件,也可采用CloudShell右上角的“笔”的编辑图标来选择对应的文档进行新增和修改。)

nano example_4submit.c

复制后面的提交任务的submit代码粘贴,注意根据提示修改代码中的Token或jobID等信息。可根据nano的快捷键提示来修改和保存文件,如ctrl+o保存,再看到对应名字OK后摁enter即可,如ctrl+x退出,如有修改保存按Y,再看到对应名字OK后摁enter即可退出。

也可采用CloudShell右上角的“笔”的编辑图标来选择对应的文档进行修改。

此时得到文件如下示意:

ls

example_4submit.c makefile tmp

make编译代码和运行方法:

make
./example_4submit

运行后,会获取到一个 job ID,这个ID是求解任务的识别号。请复制此job ID,后面的代码要用

注:问题提交远端计算Server后,会需要求解时间,请耐心等待。由于计算资源限制,同一Token同时间只能求解一个问题,请勿同时段提交太多次。如果代码运行失败,可能是mindopt环境变量链接失败,可重新走一遍安装步骤(安装指导)。

step 3:运行获取结果的代码

** 类似于step 1-2来创建代码文件和修改makefile。

如创建获取结果的代码文件(除示例指令外,编辑文件可使用vim、vi、nano、emacs、touch等不同编辑器指令):

nano example_4retrieve.c

复制后面的获取结果的Retrieve代码粘贴,把jobID、Token修改编辑完。(可根据nano的快捷键提示来修改和保存文件,如ctrl+o保存,再看到对应名字OK后摁enter即可,如ctrl+x退出,如有修改保存按Y,再看到对应名字OK后摁enter即可退出。)

此时得到文件如下示意:

ls

example_4retrieve.c  example_4submit  example_4submit.c  example_4submit.o  makefile  tmp

替换前面makefile代码里面的文件名为要编译的文件,如 TARGET = example_4submit ,更改为 TARGET = example_4retrieve 。可用以下nano指令编辑,也可采用CloudShell右上角的“笔”的编辑图标来选择对应的文档进行修改。

nano makefile

然后make编译代码和运行方法:

make
./example_4retrieve

注:问题提交远端计算Server后,会需要求解时间,请耐心等待。由于计算资源限制,同一Token同时间只能求解一个问题,请勿同时段提交太多次。如果代码运行失败,可能是mindopt环境变量链接失败,可重新走一遍安装步骤(安装指导)。

代码示例-C语言-提交任务程序(Submit)

Tips:  如果想体验运行MindOpt求解器代码,请先在天池-决策优化MindOpt-免费使用上申请授权Token,并替换掉示例代码里的Token。

make编译的方法见前页。

/**
 *  Description
 *  -----------
 *  example_4_submit.c
 *  Modeling the task, submit the problem to remote server.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "Mindopt.h"

/*example_4_submit.c. Macro to check the return code */
#define MDO_CHECK_CALL(MDO_CALL)                                    \
    code = MDO_CALL;                                                \
    if (code != MDO_OKAY)                                           \
    {                                                               \
        Mdo_explainResult(model, code, str);                        \
        Mdo_freeMdl(&model);                                        \
        fprintf(stderr, "===================================\n");   \
        fprintf(stderr, "Error   : code <%d>\n", code);             \
        fprintf(stderr, "Reason  : %s\n", str);                     \
        fprintf(stderr, "===================================\n");   \
        return (int)code;                                           \
    }

int main(void) {
#define num_users 2
#define num_items 3

  /* Variables. */
  char str[1024] = {"\0"};
  MdoMdl *model = NULL;
  MdoResult code = MDO_OKAY;
  MdoStatus status = MDO_UNKNOWN;
  const char * model_file = "./tmp/my_model.mdo";
  const char * param_file = "./tmp/my_param.mdo";
  char job_id[1024]= { "\0" };
  double val, soln[num_items * num_users];
  int i, j;

  const char user[num_users][30] =
      {
          "user1", "user2"
      };
  const char item[num_items][30] =
      {
          "item1", "item2", "item3"
      };
  const double ctr[num_items][num_users] =
      {
          {0.52, 0.92},
          {0.31, 0.93},
          {0.82, 0.91}
      };
  const double lb[num_items][num_users] =
      {
          {0, 0},
          {0, 0},
          {0, 0},
      };
  const double ub[num_items][num_users] =
      {
          {1.0, 1.0},
          {1.0, 1.0},
          {1.0, 1.0}
      };

  const double l_volume[num_items] =
      {
          0.0, 0.0, 1
      };
  const double r_volume[num_items] =
      {
          MDO_INFINITY, MDO_INFINITY, MDO_INFINITY
      };

  const double user_constraint[num_items] =
      {
          1.0, 1.0, 1.0
      };

  /*------------------------------------------------------------------*/
  /* Step 1. Create a model and change the parameters.                */
  /*------------------------------------------------------------------*/
  /* Create an empty model. */
  MDO_CHECK_CALL(Mdo_createMdl(&model));

  /*------------------------------------------------------------------*/
  /* Step 2. Input model.                                             */
  /*------------------------------------------------------------------*/
  /* Change to minimization problem. */
  Mdo_setIntAttr(model, "MinSense", MDO_NO);

  /* Add variables. */
  for (i = 0; i < num_items; ++i) {
    for (j = 0; j < num_users; ++j) {
      char *name = (char *) malloc(strlen(item[i]) + strlen(user[j]));
      sprintf(name, "%s%s", item[i], user[j]);
      MDO_CHECK_CALL(Mdo_addCol(model, lb[i][j], ub[i][j], ctr[i][j], 0, NULL, NULL, name, MDO_NO));
    }
  }

  /* Add constraints, guaranteed value. */
  for (i = 0; i < num_items; ++i) {
    int idx_item[num_users];
    for (int u = 0; u < num_users; ++u) {
      idx_item[u] = i * num_users + u;
    }
    MDO_CHECK_CALL(Mdo_addRow(model, l_volume[i], r_volume[i], num_users, idx_item, ctr[i], item[i]));
  }

  /* Add constraints, for user.*/
  for (i = 0; i < num_users; ++i) {
    int idx_user[num_items];
    for (int u = 0; u < num_items; ++u) {
      idx_user[u] = u * num_users + i;
    }
    MDO_CHECK_CALL(Mdo_addRow(model, 1.0, 1.0, num_items, idx_user, user_constraint, user[i]));
  }

  /*------------------------------------------------------------------*/
  /* Step 3. Serialize the model and the parameters for later use.    */
  /*------------------------------------------------------------------*/

  /* Input parameters. */
  Mdo_setIntParam(model, "NumThreads", 4);
  Mdo_setRealParam(model, "MaxTime", 3600);

  MDO_CHECK_CALL(Mdo_writeTask(model, model_file, MDO_YES, MDO_NO, MDO_NO));
  MDO_CHECK_CALL(Mdo_writeTask(model, param_file, MDO_NO, MDO_YES, MDO_NO));

  /*------------------------------------------------------------------*/
  /* Step 4. Input parameters related to the remote computing server. */
  /*------------------------------------------------------------------*/
  /* Input parameters related to remote computing. */
  Mdo_setStrParam(model, "Remote/Token", "xxxxxxxxYourTokenxxxxxx");
  Mdo_setStrParam(model, "Remote/Desc", "tianchi example4 LLTK");
  Mdo_setStrParam(model, "Remote/Server", "mindopt.tianchi.aliyun.com");
  Mdo_setStrParam(model, "Remote/File/Model", model_file);
  Mdo_setStrParam(model, "Remote/File/Param", param_file);

  /*------------------------------------------------------------------*/
  /* Step 5. Upload the serialized model and parameters to server, and*/
  /*         then optimize the model.                                 */
  /*------------------------------------------------------------------*/
  MDO_CHECK_CALL(Mdo_submitTask(model, job_id));
  if (job_id[0] == '\0')
  {
    printf("ERROR: Invalid job ID: %s\n", job_id);
  }
  else
  {
    printf("Job was submitted to server successfully.\n");
    printf("User may query the optimization result with the following job ID: %s\n", job_id);
  }

  /*------------------------------------------------------------------*/
  /* Step 6. Free the model.                                          */
  /*------------------------------------------------------------------*/
  /* Free the model. */
  Mdo_freeMdl(&model);

  return (int) code;
}

代码示例-C语言-取结果任务程序(Retrieve)

Tips:  如果想体验运行MindOpt求解器代码,请先在天池-决策优化MindOpt-免费使用上申请授权Token,并替换掉示例代码里的Token。

make编译的方法见前页。

/**
 *  Description
 *  -----------
 *  example_4_retrieve.c
 *  Check the solution status, download the solution, and populate the result.
 */
#include <stdio.h>
#include "Mindopt.h"
#include <string.h>
#if defined(__APPLE__) || defined(__linux__)
#   include <unistd.h>
#   define SLEEP_10_SEC sleep(10)
#else
#   include <windows.h>
#   define SLEEP_10_SEC Sleep(10000)
#endif

#define num_users 2
#define num_items 3

/* Macro to check the return code */
#define MDO_CHECK_CALL(MDO_CALL)                                    \
    code = MDO_CALL;                                                \
    if (code != MDO_OKAY)                                           \
    {                                                               \
        Mdo_explainResult(model, code, str);                        \
        Mdo_freeMdl(&model);                                        \
        fprintf(stderr, "===================================\n");   \
        fprintf(stderr, "Error   : code <%d>\n", code);             \
        fprintf(stderr, "Reason  : %s\n", str);                     \
        fprintf(stderr, "===================================\n");   \
        return (int)code;                                           \
    }

int main(
    int argc,
    char * argv[])
{
  /* Variables. */
  char         str[1024] = { "\0" };
  char         status[1024] = { "\0" };
  const char * model_file = "./tmp/my_model.mdo";
  const char * param_file = "./tmp/my_param.mdo";
  const char * soln_file = "./tmp/my_soln.mdo";
  MdoResult    code = MDO_OKAY;
  MdoStatus    model_status = MDO_UNKNOWN;
  char         model_status_details[1024] = { "\0" };
  MdoResult    result = MDO_OKAY;
  char         result_details[1024] = { "\0" };
  MdoBool      has_soln = MDO_NO;
  MdoMdl *     model = NULL;
  char         job_id[1024] = { "\0" };
  double       val = 0.0;
  double       soln[num_items * num_users];
  int          i,j;

  /*------------------------------------------------------------------*/
  /* Step 1. Create a model and change the parameters.                */
  /*------------------------------------------------------------------*/
  /* Create an empty model. */
  MDO_CHECK_CALL(Mdo_createMdl(&model));

  /*------------------------------------------------------------------*/
  /* Step 2. Read the serialized model and parameters -- this is      */
  /*         required while populating the optimization result.       */
  /*------------------------------------------------------------------*/
  MDO_CHECK_CALL(Mdo_readTask(model, model_file, MDO_YES, MDO_NO, MDO_NO));
  MDO_CHECK_CALL(Mdo_readTask(model, param_file, MDO_NO, MDO_YES, MDO_NO));

  /*------------------------------------------------------------------*/
  /* Step 3. Input parameters related to the remote computing server. */
  /*------------------------------------------------------------------*/
  Mdo_setStrParam(model, "Remote/Token", "xxxxxxxxxYourTokenxxxxxxxx");
  Mdo_setStrParam(model, "Remote/Server", "mindopt.tianchi.aliyun.com");
  Mdo_setStrParam(model, "Remote/File/Soln", soln_file);
  strcpy(job_id, "xxxxxxxxxxxxxYourJobIDxxxxxxxxxx");

  /*------------------------------------------------------------------*/
  /* Step 4. Check the solution status periodically, and              */
  /*         download the its upon availability.                      */
  /*------------------------------------------------------------------*/
  do
  {
    MDO_CHECK_CALL(Mdo_retrieveTask(model, job_id, status, &model_status, &result, &has_soln));

    /* Sleep for 10 seconds. */
    SLEEP_10_SEC;
  }
  while (status[0] == 'S'); /* Continue the loop if the status is in either Submitted status or Solving status. */

  /*------------------------------------------------------------------*/
  /* Step 5. Un-serialize the solution and then populate the result.  */
  /*------------------------------------------------------------------*/
  printf("\nPopulating optimization results.\n");
  Mdo_explainStatus(model, model_status, model_status_details);
  Mdo_explainResult(model, result, result_details);

  printf(" - Job status             : %s\n", status);
  printf(" - Model status           : %s (%d)\n", model_status_details, model_status);
  printf(" - Optimization status    : %s (%d)\n", result_details, result);
  printf(" - Solution availability  : %s\n", has_soln ? "available" : "not available");
  if (has_soln)
  {
    printf("\nPopulating solution.\n");
    MDO_CHECK_CALL(Mdo_readTask(model, soln_file, MDO_NO, MDO_NO, MDO_YES));
    Mdo_displayResults(model);

    Mdo_getRealAttr(model, "PrimalObjVal", &val);
    printf(" - Primal objective value : %e\n", -val);
    Mdo_getRealAttr(model, "DualObjVal", &val);
    printf(" - Dual objective value   : %e\n", -val);
    Mdo_getRealAttr(model, "SolutionTime", &val);
    printf(" - Solution time          : %e sec.\n", val);

    MDO_CHECK_CALL(Mdo_getRealAttrArray(model, "PrimalSoln", 0, num_items * num_users, soln));
    printf("Best solution: (item_id, user_id):X(i,u)\n");
    for (i = 0; i < num_items; ++i) {
      for (j = 0; j < num_users; ++j)
        if (soln[i * num_users + j] > 0.01) {
          printf(" - (%d,%d): %.2f\n", i, j, soln[i * num_users + j]);
        }
    }

  }

  /*------------------------------------------------------------------*/
  /* Step 6. Free the model.                                          */
  /*------------------------------------------------------------------*/
  /* Free the model. */
  Mdo_freeMdl(&model);

  return (int)code;
}

求解结果

如下结果说明总点击量预估为1.75, 求解后的分配结果为将第1号视频播放给第1号用户的概率为0.8, 将第2号视频播放给第0号用户的概率为1, 将第2号视频播放给第1号用户的概率为0.2,从结果上看,该分配方式,满足所有约束条件。有兴趣的用户可以自行调节本例中涉及的参数,观察效果的变化。

(1)提交任务程序日志: 截屏2020-12-30 下午2.58.59.png (2)取结果程序日志: image.png