![ca762103fa6c7a869e79a000191db568.png](https://i-blog.csdnimg.cn/blog_migrate/66fcfe9f2109f7a5907ec8324774f8e7.jpeg)
前几天想到一个有趣的问题,我们每个手机都可以设置手势密码,那么手势密码一共有几种呢?
我们先了解下手势密码设置的规则:密码必须不小于4个点,点在第一次经过时不允许被跳过。
![c1ef5662880e1987e2a48da0b366ad95.png](https://i-blog.csdnimg.cn/blog_migrate/eb763473229e5712c96ab47f9f2ed4b1.png)
如图,我们把每个点编上序号。
则以下这种是不合法的。
![8261542b686e90a7cedb5dd7d54186f5.png](https://i-blog.csdnimg.cn/blog_migrate/c3e12108623fc39fead3e5a2092dd0d6.jpeg)
5被连接了两次
而
![51e78ff1713fbde595739668e1e6930c.png](https://i-blog.csdnimg.cn/blog_migrate/4c8984e73e1f7fb44a3599878181ef7f.jpeg)
这种是合法的,因为5被跳过了。而被跳过的点不被记为总点数。
用数字表示的话,例如1923则不能为四个点连接的情况,因为1和9之间有5,所以实际上是15923。
如果用纯理性分析的话,这无疑是个极为复杂的数学题,很多数学技巧似乎都被限制住。但是,我们有计算机这个无比强大的工具。这里,我选择用C语言处理。
宏定义
#define SIZE 10000
#define XSIZE 5
#define SIZEX 11111
以五个数字组成的密码举例,SIZE为
主函数部分
void main(){
int n = 0;
int i = SIZEX, j = 0;
for (i; i < SIZE*10; i++) {
if(check(i)) n++;
}
printf("%d",n);
}
主函数部分十分简单,就是简单的循环遍历。
check函数部分
int check(int i)
{
int k = 0, j = SIZE;
int a[XSIZE];
while (k<XSIZE) {
a[k] = i / j;
i = i - a[k] * j;
j = j / 10;
k++;
}
for (k=0; k < XSIZE; k++) {
for (j = k + 1; j < XSIZE; j++) {
if (a[k] == a[j]) return 0;
}
if (a[k] == 0) return 0;
}
for ( k = 0; k+1< XSIZE; k++){
if ((a[k] + a[k + 1]) == 4) {
if (!check1(a, k)) return 0;
}
if ((a[k] + a[k + 1]) == 10) {
if (!check1(a, k)) return 0;
}
if ((a[k] + a[k + 1]) == 16) {
if (!check1(a, k)) return 0;
}
if (a[k] - a[k + 1] == 6 || a[k + 1] - a[k] == 6) {
if (!check1(a, k)) return 0;
}
}
return 1;
}
该函数大致分为三个部分,第一部分,对传入的i分离i的各个位数的数字,并存入数组中。第二部分,设置两个循环判断数组中是否有相同的数字,第三部分,设置循环,遍历相邻的两个数字,判断这两个数字是否满足可能非法的条件,即两个数字间是否包含第三个数字,然后传入check1函数,判断两个数字前是否有中间包含的第三个数字,存在的话说明是合法的。
check1函数
int check1(int *a, int k) {
for (int i = 0; i < k; i++) {
if (a[i] == (a[k] + a[k + 1]) / 2) return 1;
}
return 0;
}
结论
运行程序,发现各个数字的个数,其中
4位的有1624个
5位的有7152个
6位的有26016个
7位的有72912个
8位的有140704个
9位的有140704个
总共有389112种!
值得一提的是,6位以下的个数甚至每到10000种,所以设置密码最好设置6位以上的,当然不怕忘记的话,可以设置9位的变态密码。
完。