对“心理学测测你的本命专业”的实现与算法分析

1 研究对象

对象名称:心理学测测你的本命专业
对象链接:https://cdn-act.knowyourself.cc/life_profession/

2 简单测试

(1) 多次测试,每次测试选择固定的答案,发现结果始终相同。

3 Network分析

(1) 使用Microsoft Edge浏览器开发者工具下的“网络”(chrome开发者工具中叫做Network)分析数据包。

(2) 对答题过程进行抓包分析如下图,发现在答题过程中和“查看结果”都只进行了图片获取的请求。

(3) 根据以上发现和分析数据没有被发送到服务器,故业务逻辑是放在本地被执行的。

3 业务逻辑分析

(1) 在开发者工具下的控制台(chrome开发者工具中叫做Console)中发现打印了如下图信息,打印的信息包含答题记录和获取的专业名称的拼音。如土木工程学==tumugongcheng

(2) 上图中打印的答题记录长度是11,其中包括选择的性别和十道题目。同时可以看到打印这些信息的文件是“index.tsx”,并且发现有两个index.tsx文件。

(3) 对两个index.tsx分析
① 打印“答题记录”的index.tsx文件,如下图中红色框中是核心代码。核心代码实现的功能是:对输入信息规范化和提示,跳转到下一页。

② 打印“专业名拼音”的index.tsx文件,如下图,其核心功能是:获取答题记录通过result函数转换为整数,在通过这个整数在数组done_string_2中获取对应的专业。

数组done_string_2如下。

const done_string_2 = ["jingjixue", "zhexue", "faxue", "shehuixue", "jiaoyuxue", "hanyuyan", "waiguo", "xinwenxue", "lishixue", "shuxue", "wulixue", "huaxue", "shengmingkexue", "dilixue", "xinlixue", "jisuanjikexue", "tumugongcheng", "jianzhuxue", "jidiangongcheng", "nonglinxue", "yixue", "guanlixue", "yishu", "xijuyingshi", "biaoyan", "tiyuxue", "kaoguxue", "dianzijingji"]

数组done_string_2和函数result函数在代码中的位置如下图。

4 算法详解

(1) 根据上述分析,“心理学测测你的本命专业”的工作流程是,先获取用户答题记录,再将答题记录转换整数index,最后从数组done_string_2中获取索引为index的值。
(2) 代码中传递给函数result的实参是select.slice(1),使用单步调试获取变量select的内容
① 变量ctx获取用户的信息,如:姓名(name: han),专业(profession: 电子信息),答题记录(select_q_list)如下图。

select_q_list的内容如下图。

② 变量select的内容如下图,变量select对变量select_q_list进行了数据格式转换。

(3) 核心算法
算法位于result.js文件中。
(1) 算法中,变量config把每个问题的答案映射为一个数组,数组中每一个数字对应一个专业。变量config如下

  var config = {
    0: {
      0: [1, 0, 2, 3, 4, 5, 6, 7, 8, 21, 26],
      1: [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 26],
      2: [22, 23, 24, 25, 27],
    },
    1: {
      0: [0, 2, 6, 7, 20, 21, 25],
      1: [3, 4, 24],
      2: [5, 14, 22],
      3: [1, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 23, 26, 27],
    },
    2: {
      0: [1, 3, 4, 5, 9, 10, 14, 21, 22, 23, 24, 27],
      1: [0, 2, 6, 7, 8, 11, 12, 13, 15, 16, 17, 18, 19, 20, 25, 26],
    },
    3: {
      0: [0, 2, 3, 4, 6, 7, 20, 21, 24, 25],
      1: [1, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 26, 27],
    },
    4: {
      0: [1, 3, 4, 5, 9, 10, 14, 21, 22, 23, 24, 27],
      1: [0, 2, 6, 7, 8, 11, 12, 13, 15, 16, 17, 18, 19, 20, 25, 26],
    },
    5: {
      0: [1, 0, 2, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 23, 25, 26, 27],
      1: [3, 4, 5, 14, 22, 24],
    },
    6: {
      0: [1, 8, 9, 10, 11, 12, 13, 20, 26],
      1: [5, 22, 23, 24],
      2: [15, 16, 17, 18, 19, 25, 27],
      3: [3, 4, 6, 7, 14],
      4: [0, 2, 21],
    },
    7: {
      0: [1, 8, 9, 10, 11, 12, 13, 20, 26],
      1: [5, 22, 23, 24],
      2: [15, 16, 17, 18, 19, 25, 27],
      3: [3, 4, 6, 7, 14],
      4: [0, 2, 21],
    },
    8: {
      0: [4, 5, 7, 14, 21, 23, 6],
      1: [6, 15],
      2: [13],
      3: [10, 16, 17, 18],
      4: [12, 19, 20, 25],
      5: [1, 2, 7, 21],
      6: [0, 9, 15, 16, 18, 10, 14],
      7: [17, 22, 24, 23],
      8: [3, 8, 26, 13],
      9: [11, 12, 19, 20, 26],
      10: [25, 27],
      11: [22, 24],
    },
    9: {
      0: [],
      1: [],
      2: []
    }
  };

(2) 统计所有问题映射的数组,并把这些数组合并成一个数组存储在变量result中

  var result = [];
  for (var topic_num = 0; topic_num < select.length; topic_num++) {
    // 遍历本题答案
    for (var j = 0; j < select[topic_num].length; j++) {
      var user_select = select[topic_num][j];
      // 获取配置
      var tmp = config[topic_num][user_select];
      // 合并
      result = result.concat(tmp);
    }
  }

(3) 统计变量result中出现最多次的数字,并返回该数字

  var result_map = {};
  var max = -1;
  // 打乱一下result结果
  result.shuffle();
  for (var i = 0; i < result.length; i++) {
    if (typeof result_map[result[i]] == "undefined") {
      result_map[result[i]] = 0;
    }
    result_map[result[i]]++;
    if (max == -1) {
      max = result[i];
    } else {
      if (result_map[result[i]] > result_map[max]) {
        max = result[i];
      }
    }
  }

  //console.log(result);
  //console.log(result_map);
  //console.log(max);
  return max;

5 附录result.js完整代码

// 结果页id:
// 0:经济学
// 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:电子竞技

//10道题,选择传入数组,因为有多选,数组内套数组
//选择顺序从0开始算,0代表第一个选项,1代表第二个...
//例如 select :[[0],[1],[3],[0],[0,1,2,3],[0,1,2,3,4],[0]]
function getResult(select) {
  if (!Array.prototype.shuffle) {
    Array.prototype.shuffle = function () {
      for (var j, x, i = this.length; i; j = parseInt(Math.random() * i), x = this[--i], this[i] = this[j], this[j] = x)
        return this;
    };
  }
  var config = {
    0: {
      0: [1, 0, 2, 3, 4, 5, 6, 7, 8, 21, 26],
      1: [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 26],
      2: [22, 23, 24, 25, 27],
    },
    1: {
      0: [0, 2, 6, 7, 20, 21, 25],
      1: [3, 4, 24],
      2: [5, 14, 22],
      3: [1, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 23, 26, 27],
    },
    2: {
      0: [1, 3, 4, 5, 9, 10, 14, 21, 22, 23, 24, 27],
      1: [0, 2, 6, 7, 8, 11, 12, 13, 15, 16, 17, 18, 19, 20, 25, 26],
    },
    3: {
      0: [0, 2, 3, 4, 6, 7, 20, 21, 24, 25],
      1: [1, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 26, 27],
    },
    4: {
      0: [1, 3, 4, 5, 9, 10, 14, 21, 22, 23, 24, 27],
      1: [0, 2, 6, 7, 8, 11, 12, 13, 15, 16, 17, 18, 19, 20, 25, 26],
    },
    5: {
      0: [1, 0, 2, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 23, 25, 26, 27],
      1: [3, 4, 5, 14, 22, 24],
    },
    6: {
      0: [1, 8, 9, 10, 11, 12, 13, 20, 26],
      1: [5, 22, 23, 24],
      2: [15, 16, 17, 18, 19, 25, 27],
      3: [3, 4, 6, 7, 14],
      4: [0, 2, 21],
    },
    7: {
      0: [1, 8, 9, 10, 11, 12, 13, 20, 26],
      1: [5, 22, 23, 24],
      2: [15, 16, 17, 18, 19, 25, 27],
      3: [3, 4, 6, 7, 14],
      4: [0, 2, 21],
    },
    8: {
      0: [4, 5, 7, 14, 21, 23, 6],
      1: [6, 15],
      2: [13],
      3: [10, 16, 17, 18],
      4: [12, 19, 20, 25],
      5: [1, 2, 7, 21],
      6: [0, 9, 15, 16, 18, 10, 14],
      7: [17, 22, 24, 23],
      8: [3, 8, 26, 13],
      9: [11, 12, 19, 20, 26],
      10: [25, 27],
      11: [22, 24],
    },
    9: {
      0: [],
      1: [],
      2: []
    }
  };

  var result = [];
  for (var topic_num = 0; topic_num < select.length; topic_num++) {
    // 遍历本题答案
    for (var j = 0; j < select[topic_num].length; j++) {
      var user_select = select[topic_num][j];
      // 获取配置
      var tmp = config[topic_num][user_select];
      // 合并
      result = result.concat(tmp);
    }
  }
  var result_map = {};
  var max = -1;
  // 打乱一下result结果
  result.shuffle();
  for (var i = 0; i < result.length; i++) {
    if (typeof result_map[result[i]] == "undefined") {
      result_map[result[i]] = 0;
    }
    result_map[result[i]]++;
    if (max == -1) {
      max = result[i];
    } else {
      if (result_map[result[i]] > result_map[max]) {
        max = result[i];
      }
    }
  }

  //console.log(result);
  //console.log(result_map);
  //console.log(max);
  return max;
}

// test
// console.log(getResult([[0], [3], [0], [1], [0], [0], [0], [0], [0, 5, 6], [1]]));//期望结果 1
//getResult([[1],[3],[1],[1],[1],[0],[2],[2],[1,2,6],[1]]);//期望结果 15
//getResult([[2],[3],[0],[1],[0],[0],[1],[1],[0,7,10],[1]]);//期望结果 23

export default getResult;

6 结束语

把每个专业映射为数字,再把每个问题的答案映射为一组数字,最后统计出现次数最多的数字就是专业。
不得不说这种映射和众数的思想真是完美。大佬!就是牛!

思考题。为什么在变量config中对第十个问题答案的映射全是空?
第十个问题如下图

变量config中对第十个问题答案的映射

  9: {
      0: [],
      1: [],
      2: []
    }
原文地址:https://www.cnblogs.com/HanYG/p/14824633.html