一日,光头程序员的女票在看某短视频APP的时候刷到了一个叫FLAME游戏的视频1,说是列出两个人的名字,然后经过如下的操作之后就可以算出两人的关系:
F 是朋友
L 是心爱的人
A 是喜欢的人
M 是结婚对象
E 是敌人
写下两人的英文名字,可以用拼音将你们名字中相同的字母划掉,然后剩下几个拼音就从剩的总数重复数FLAME(点到哪个划掉哪个,只剩最后一个字母)
字母对应着你们的关系
0x00 初次尝试
在没看到视频的当下,我直接拿起张三(Zhang San)和李四(Li Si)开始了第一轮的计算:
然后开始计算 FLAME:
- 去除第10个字母,也就是 FLAME;
- 接着还是去除第10个字母,也就是 FLAM;
- 故技重施,得到 FAM;
- AM 则去掉 M,得到最后的字母为 A。
也就是喜欢的人(Affection2)。
0x01 更进一步
女票表示,不对劲,去的有点少,不好数。
所以添加一条新规则 —— 可以去掉名字中所有重复的字母。
那么0x00中求出来的 Zhang an & Li i 就要进一步剔除,得到 Zhg & L;
这样就只要数4个字母了。
接着还是算FLAME的方法,每次去掉的位置为剩余字母数N对FLAME剩余字母数n的求余数,也就是:
当
N
=
1
N = 1
N=1且
n
∈
{
2
,
3
,
4
,
5
}
n\in\{2,3,4,5\}
n∈{2,3,4,5}时,
有
[
1
m
o
d
5
,
1
m
o
d
4
,
1
m
o
d
3
,
1
m
o
d
2
]
[{1}\mod{5},{1}\mod{4},{1}\mod{3},{1}\mod{2}]
[1mod5,1mod4,1mod3,1mod2],
即
[
1
,
1
,
1
,
1
]
[1,1,1,1]
[1,1,1,1],
也就是说四次全删除第一个字母即可,
通过求这四个数的最小公倍数,我们可以知道:
这样的规律每
3
×
4
×
5
=
60
3\times4\times5=60
3×4×5=60 次产生一个轮回;
那么,这回我就直接把一次轮回的所有可能都枚举出来了,也就是:
剩余字母数 | 你们的关系 |
---|---|
1 | E |
2 | F |
3 | L |
4 | L |
5 | M |
6 | L |
7 | E |
8 | F |
9 | A |
10 | A |
11 | E |
12 | F |
13 | E |
14 | F |
15 | L |
16 | A |
17 | E |
18 | F |
19 | E |
20 | F |
21 | M |
22 | M |
23 | E |
24 | F |
25 | M |
26 | L |
27 | A |
28 | L |
29 | E |
30 | F |
31 | E |
32 | F |
33 | M |
34 | A |
35 | M |
36 | L |
37 | E |
38 | F |
39 | L |
40 | L |
41 | E |
42 | F |
43 | E |
44 | F |
45 | A |
46 | M |
47 | E |
48 | F |
49 | E |
50 | F |
51 | A |
52 | A |
53 | E |
54 | F |
55 | M |
56 | L |
57 | M |
58 | M |
59 | E |
60 | F |
统计一下每个字母在这60次中出现的频次和概率:
字母 | 出现次数 | 概率 |
---|---|---|
F | 16 | 26.67% |
L | 10 | 16.67% |
A | 8 | 13.33% |
M | 10 | 16.67% |
E | 16 | 26.67% |
如果按拼音来说,最长的拼音可以有6个字母(如chuang),一个人名包括复姓,一般最多5个汉字,假设两人名字的字母没有一个重复的,才有可能到达最后的60,那么便不符合普遍现象;
所以,我们选取前 20 种情况,看看每种关系的概率如何:
字母 | 出现次数 | 概率 |
---|---|---|
F | 6 | 30% |
L | 4 | 20% |
A | 3 | 15% |
M | 1 | 5% |
E | 6 | 30% |
我们就会发现:
只有 第5次 时出现了M(结婚对象);
F(朋友)和E(敌人)平分秋色,占比最大;
L(心爱的人)还比A(喜欢的人)的概率更高点。
所以,这道题其实至少三成是道送命题,遇到的话,还请慎重把握。
0x02 举一反三
那么我们能不能在后面那个求FLAME的算法里动点手脚呢?
当然可以,同样是FLAME,再划掉字母后,将剩下的字母填充进剩下的空里,保证是5个字母直到5个字母变成同一个字母为止。
示例:
还是Zhang San和Li Si,根据0x01已知他们最后剩下4个字母,经过改造后的算法为
- 整体左移,且标定位置前后都移动,留出的空按从左到右的顺序填充剩下字母:
FLAME
FLAEF
FLAFL
LALAL
LLLLL - 或是后移,前不移:
FLAME
FLAEF
FLAFL
LLALA
AAAAA - 或是前移,后不移:
FLAME
FLAFE
LALAE
LLLEE
LLLLL - 或是前后都不移:
FLAME
FLAFE
LLAAE
LLLEE
LLLLL
那么同理,整体右移和按从右到左的顺序又能排列组合出共计 2 × 2 × 4 = 16 2\times2\times4=16 2×2×4=16 种不同的算法。
0x03 求本溯源
本着严谨的科学态度,咱打开了搜索引擎,倒是找到了一个关于FLAMES游戏的玩法说明2:
What is the full form of Full form for Flames?
FLAMES stands for Friends, Lover, Affection, Marriage, Enemy, Sister(sibling).
Calculating relationship using Flames Game (step by step) Manually:
- Write down your name and partner’s name.
- Strikeout common letters in both names.
- Get the count remaining letters (Assuming it as N).
- Remove the N’th letter from the word FLAMES (If N > 6; start the second loop)
- Continue until you left with a single letter, and it is the Flames relationship.
翻译一下就是:
FLAMES里每个字母具体代表了什么关系呢?
F是朋友,L是情人,A是在意的人,M是结婚对象,E是敌人,S是姐妹(有血缘关系的兄弟姐妹3)。
手把手教你算FLAMES游戏:
- 写下你和你伴侣的名字。
- 将两人名字中相同的字母去掉。
- 记下剩下的字母数 (假设这个数是N)。
- 从单词FLAMES中删除第N个字母 (如果N大于6,则再从头开始数)。
- 重复以上步骤直到剩下最后一个字母,而这个字母据说就是你们俩之间的关系。
0x04 得寸进尺
那么以后还可以开发一个什么uncopyrightable Game来检查你的一段话和别人的一段话有没有可能出现版权纠纷:
U: Unfortunate 不幸地有问题
N: No 无版权问题
C: Closer 几近出问题
O: Open 原作者很开放
Y: Yours 你原创的
… 编不下去了,有大佬高兴的话可以在评论区补全
想试试看的话可以看看后面参考资料里关于函数 loopModGame(string, n)
的JS实现。
0x05 参考资料
/**
* 返回FLAME游戏剩余字母数为n时的结果
*
* @author LearningPawn
* @param {Number} 两名字去重后的剩余字母数
* @return {string} 最终得出的字母
*/
function flamegame(n) {
var string = "FLAME";
var list = [n%5, n%4, n%3, n%2];
for (i in list) { // 逐个去除字母,余数为0时,表示去除最后一位
if(list[i] == 0) {
string = string.substring(0, string.length-1);
} else {
string = string.replace(string[list[i]-1], "");
}
}
return string;
}
/**
* FLAME游戏的推广
*
* @author LearningPawn
* @param {string} 关键字母字符串,如"FLAME"
* @param {Number} 两名字去重后的剩余字母数
* @return {string} 最终得出的字母
*/
function loopModGame(string, n) {
var length = string.length;
var list = [];
for (i = length; i > 1; i--) { // 求余到1时则无需再删字符
list.push(n%i);
}
console.log(list)
for (i in list) { // 逐个去除字母,余数为0时,表示去除最后一位
if(list[i] == 0) {
string = string.substring(0, string.length-1);
} else {
string = string.replace(string[list[i]-1], "");
}
}
return string;
}