写在前面
说起破解手机密码,大家可能第一印象想到的方法是暴力穷举,就是列举出所有的可能性,然后一个一个的试,直到正确为止。但是现在的手机都有保护机制,就像《爱情公寓5》里面第七集一样,猜错几次之后,可能要等几个小时后你才能猜下一次了。
当然,像破解手机密码这种涉嫌违法的事,其实我也不会 咱当然不能干
纳尼?博主那你博客标题是怎么肥事咧?
好吧,我承认我有些标题党了 不要急,请听我娓娓道来!
事出有因
原来,疫情在家期间,我突然发现我表妹和我叔叔,正在桌子上写着什么。
同时,桌子上还摆着一张报纸。我一问才知道他们在做报纸上的一道题目,但是做了很久都没有结果!题目如下:
小王的手机又换了新密码。他发现,有三个特点使新密码很好记:首先,原来的密码和新换的密码都是四个数字;其次,新密码正好是原来密码的四倍;再次,原来的密码从后面倒着写正好是新的密码。
所以,他不费劲就会记住新密码,那么,小王的新手机密码究竟是多少?
等等,博主你不说是老王吗?怎么变成小王了?
有道是,十年生死两茫茫,转眼小王变老王。
本博主本着"快乐编程,用编程改变世界"的理念。当然要用编程解决实际问题,解救人民于水火之中了。然后火速编程,很快就得出了结果,这速度简直让一旁苦算的表妹叹为观止。
方法一
噔噔蹬蹬,代码如下:
/**
* @author guqueyue
* @Date 2020/1/28
* 方法一
**/
public class Test99 {
public static void main(String[] args) {
// 原来的密码和新换的密码都是四个数字, 所以范围在[0000, 9999]
for (int i = 0000; i <= 9999; i++) {
/**
* 分别求出四位密码的千、百、十、个位的数字
*/
int q = i % 10000 / 1000; // int q = i / 1000;
int b = i % 1000 / 100;
int s = i % 100 / 10;
int g = i % 10 / 1; // int g = i % 10;
// 原来的密码从后面倒着写正好是新的密码
int newPwd = g * 1000 + s * 100 + b * 10 + q;
// 新密码正好是原来密码的四倍
if (4 * i == newPwd && i != newPwd){
System.out.println("原密码为:" + i);
System.out.println("新密码为:" + newPwd);
}
}
}
}
Output:
方法二
其实方法一和方法二的唯一差别,就在于求出四位密码的千、百、十、个位的数字部分
/**
* 分别求出四位密码的千、百、十、个位的数字
*/
int q = i / 1000;
int b = i / 100 % 10;
int s = i / 10 % 10;
int g = i / 1 % 10; // int g = i % 10
觉得不信的,可以自行实验一番,效果是一样的。
除法和取余的特性
到这里,大家可能会有些许疑惑,到底是啥原理呢?
其实聪明的朋友可能已经发现了:
方法一和方法二的差别无非就是先除还是先取余的差别,就这么简单!
首先,我们来了解一下Java中除法与日常生活中存在的不同特性:
若是 整型 / 整型 == 整型,直接取整,舍弃掉后面的小数 ,如在Java中: 5 / 3 =1;
若是 整型 / 浮点型 == 浮点型,如在Java中:5 / 3.0 = 1.6666666666666667。
也就是说,当参与/运算的两个操作数都是整数时,表示整数除法;否则,表示浮点除法。
代码示例如下:
/**
* @author guqueyue
* @Date 2020/3/16
* 除法
**/
public class Test101 {
public static void main(String[] args) {
System.out.println("5 / 3 = " + (5 / 3));
System.out.println("5 / 3.0 = " + (5 / 3.0));
}
}
Output:
我们再来看两行代码以及它们的输出结果:
System.out.print(1314/10);
System.out.print(1314%10);
Output:
What? 1314先除10,再余10,然后拼接出来还是1314?
难道是1314这个数字有魔法,会一生一世永恒不变的吗?当然不是
不要急,我们再来看一组代码:
/**
* @author guqueyue
* @Date 2020/3/21
* 除法和取余
**/
public class Test102 {
public static void main(String[] args) {
System.out.println("=====除法和取余的数字为10=====");
System.out.print(1314/10 + " ");
System.out.println(1314%10);
System.out.println("=====除法和取余的数字为100=====");
System.out.print(1314/100 + " ");
System.out.println(1314%100);
System.out.println("=====除法和取余的数字为1000=====");
System.out.print(1314/1000 + " ");
System.out.println(1314%1000);
}
}
Output:
所以,大家发现了吗?
当我们对于十进制的数,进行进制n倍数的整除或者取余的时候,只需从右往左数n位。此时,若是整除,则舍弃右边;若是取余,则舍弃左边。其他解码规则为乘法的数字同样适用,如计时所用的60进制。
所以当我们需要获取一个数字不同单位上的具体数字时,其实只需要把左右两边的数字舍弃即可,要么先舍弃右边,要么先舍弃左边,其实就是先除还是先取余的区别!
如当我们需要获取520的百位数字:
System.out.println(520 / 10 % 10); // 先舍弃右边(0),再舍弃左边(5)
System.out.println(520 % 100 / 10); // 先舍弃左边(5),再舍弃右边(0)
秒杀倒计时
嗯~~~,这下我明白了,可是这跟秒杀倒计时有什么关系呢?
不急,我们先打开京东的首页,找到京东"秒杀",点击右键选择"检查",看一下来自京东程序员小哥的秒杀倒计时是怎么写的。
简直是毫不费劲的就根据命名规则就找到了京东小哥.js文件的src:
http://misc.360buyimg.com/mtd/pc/index_2019/1.0.0/static/js/seckill.chunk.js
复制粘贴,然后打开浏览器输入查看:
结果,一看傻眼了,被加密了,什么东西都看不出来(•́へ•́╬)
但是别慌,我把自己以前练手写的倒计时的代码供大家参考:
/**
* 倒计时方法
*/
function djs() {
/**
* 分别获取当前时间以及商品秒杀开始时间相差的毫秒数,便于计算倒计时
*/
var times = startTime.getTime() - now.getTime();
if (times <= 0){
// 秒杀已经开始,商品可以秒杀
goBtn();
return;
}
// 秒杀还未开始,计算倒计时,还剩多少h/m/s/ms
var h = parseInt(times/1000/60/60);
var m = parseInt(times/1000/60%60);
var s = parseInt(times/1000%60);
var ms = parseInt(times%1000);
// 自定义的方法,格式化倒计时时间,若数字小于10补0,如3:23:9, 格式化为03:23:09
var djsTime = formatTime(h) + ":" + formatTime(m) + ":" + formatTime(s);
// 渲染页面
$("#djs").html(djsTime);
// 通过定时器使得倒计时每秒准确刷新
mytime = setTimeout(function () {
if (count >= 60){
console.log("主动获取服务器的时间!");
// 重新获取服务器的时间
getServerNow();
count = 0;
} else {
now.setSeconds(now.getSeconds() + 1);
now.setMilliseconds(now.getMilliseconds() + ms);
count++;
}
djs();
}, 1000 + ms)
}
其他的代码我们暂时不用管它,我们现在只需要注意这段代码:
// 秒杀还未开始,计算倒计时,还剩多少h/m/s/ms
var h = parseInt(times/1000/60/60);
var m = parseInt(times/1000/60%60);
var s = parseInt(times/1000%60);
var ms = parseInt(times%1000);
其中times是用于计算秒杀倒计时商品的剩余时间(毫秒数),
parseInt()函数可解析一个字符串,并返回一个整数,默认采用十进制解析。
那么朋友们,你们看出来这段代码的意思了吗?
为了直观显示,我把这段代码改动一下:
// 秒杀还未开始,计算倒计时,还剩多少ms,方便调整定时器
var ms = parseInt(times % 1000);
// 离秒杀开场总计还剩余多少秒
var ss = parseInt(ss/1000);
// 计算倒计时,还剩多少h/ms/s
var h = parseInt(ss/60/60);
var m = parseInt(ss/60%60); // var m = parseInt(ss%3600/60);
var s = parseInt(ss%60);
其实原理和之前是一样的,只不过ms到s的进制为1000,h、m、s之间是60进制而已!