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: []
}