快手
昨天(12月22日)晚上,快手突发了严重的网络安全事件。
大量露骨色情内容短时间内侵入多个直播间,引发用户恐慌与舆论哗然。
更加具体的:上千个账号几乎在同一时间开启直播,播放违规内容,整个过程持续超过十分钟。
这不是偶然的事故,而是精心策划的攻击活动。
综合目前网上的信息,总的来说,攻击者的核心步骤di分为如下几步:
-
批量化生产新账号:通过自动化脚本和接码平台,像流水线般的生产账号
-
提高新账号的平台信任度:新号直接直播,将会很难得到平台推流,违规信息如果进不到真实用户眼里,攻击将没有意义。于是攻击者的第二步,是对这些新号执行"自动化养号"操作,提高平台的信任度
-
同步进行违规直播:先通过服务器下发视频内容,然后通过推流工具,以及特定协议推送到快手流媒体服务器。这需要对快手的 API 接口(创建直播房间),以及直播传输协议十分了解
-
攻击快手的举报接口:使得真实用户看到违规内容后,无法进行举报,相当于"废掉"快手的借助用户反馈,建立的安全审核机制
其中第三点,是技术方面最难的地方,但第二和第四点,是攻击里面"含金量"最高的地方,也是攻击能够成功的关键,一定程度,属于"社会学"范畴。
目前得到的消息,是快手报警了,但更具体的事故报告还未释出,希望后面会有吧。
...
回归主题。
来一道和「快手」相关的算法题。
题目描述
平台:LeetCode
题号:633
给定一个非负整数 c,你要判断是否存在两个整数 a 和 b,使得 。
示例 1:
输入:c = 5
输出:true
解释:1 * 1 + 2 * 2 = 5
示例 2:
输入:c = 3
输出:false
示例 3:
输入:c = 4
输出:true
示例 4:
输入:c = 2
输出:true
示例 5:
输入:c = 1
输出:true
提示:
基本分析
根据等式 ,可得知 a 和 b 的范围均为 。
基于此我们会有以下几种做法。
枚举
我们可以枚举 a ,边枚举边检查是否存在 b 使得等式成立。
这样做的复杂度为 。
Java 代码:
class Solution {
public boolean judgeSquareSum(int c) {
int max = (int)Math.sqrt(c);
for (int a = 0; a <= max; a++) {
int b = (int)Math.sqrt(c - a * a);
if (a * a + b * b == c) return true;
}
return false;
}
}
C++ 代码:
class Solution {
public:
bool judgeSquareSum(int c) {
int maxv = sqrt(c);
for (int a = 0; a <= maxv; a++) {
int b = sqrt(c - a * a);
if (a * a + b * b == c) return true;
}
return false;
}
};
Python 代码:
class Solution:
def judgeSquareSum(self, c: int) -> bool:
maxv = int(math.sqrt(c))
for a in range(maxv + 1):
b = int(math.sqrt(c - a * a))
if a * a + b * b == c:
return True
return False
TypeScript 代码:
function judgeSquareSum(c: number): boolean {
const maxv = Math.floor(Math.sqrt(c));
for (let a = 0; a <= maxv; a++) {
const b = Math.floor(Math.sqrt(c - a * a));
if (a * a + b * b === c) return true;
}
return false;
};
- 时间复杂度:
- 空间复杂度:
双指针
由于 a 和 b 的范围均为 ,因此我们可以使用「双指针」在 范围进行扫描:
- : 找到符合条件的
a和b,返回 - : 当前值比目标值要小,
a++ - : 当前值比目标值要大,
b--
Java 代码:
class Solution {
public boolean judgeSquareSum(int c) {
int a = 0, b = (int)Math.sqrt(c);
while (a <= b) {
int cur = a * a + b * b;
if (cur == c) return true;
else if (cur > c) b--;
else a++;
}
return false;
}
}
C++ 代码:
class Solution {
public:
bool judgeSquareSum(int c) {
int a = 0, b = sqrt(c);
while (a <= b) {
long cur = a * a * 1L + b * b;
if (cur == c) return true;
else if (cur > c) b--;
else a++;
}
return false;
}
};
Python 代码:
class Solution:
def judgeSquareSum(self, c: int) -> bool:
a, b = 0, int(math.sqrt(c))
while a <= b:
cur = a * a + b * b
if cur == c:
return True
elif cur > c:
b -= 1
else:
a += 1
return False
TypeScript 代码:
function judgeSquareSum(c: number): boolean {
let a = 0, b = Math.floor(Math.sqrt(c));
while (a <= b) {
let cur = a * a + b * b;
if (cur === c) return true;
else if (cur > c) b--;
else a++;
}
return false;
};
- 时间复杂度:
- 空间复杂度:
费马平方和
费马平方和 : 奇质数能表示为两个平方数之和的充分必要条件是该质数被 4 除余 1 。
翻译过来就是:当且仅当一个自然数的质因数分解中,满足 4k+3 形式的质数次方数均为偶数时,该自然数才能被表示为两个平方数之和。
因此我们对 c 进行质因数分解,再判断满足 4k+3 形式的质因子的次方数是否均为偶数即可。
Java 代码:
public class Solution {
public boolean judgeSquareSum(int c) {
for (int i = 2, cnt = 0; i * i <= c; i++, cnt = 0) {
while (c % i == 0 && ++cnt > 0) c /= i;
if (i % 4 == 3 && cnt % 2 != 0) return false;
}
return c % 4 != 3;
}
}
C++ 代码:
class Solution {
public:
bool judgeSquareSum(int c) {
for (int i = 2, cnt = 0; i * i <= c; i++, cnt = 0) {
while (c % i == 0 && ++cnt > 0) c /= i;
if (i % 4 == 3 && cnt % 2 != 0) return false;
}
return c % 4 != 3;
}
};
Python 代码:
class Solution:
def judgeSquareSum(self, c: int) -> bool:
for i in range(2, int(c**0.5) + 1, 1):
cnt = 0
while c % i == 0:
c //= i
cnt += 1
if i % 4 == 3 and cnt % 2 != 0:
return False
return c % 4 != 3
TypeScript 代码:
function judgeSquareSum(c: number): boolean {
for (let i = 2, cnt = 0; i * i <= c; i++, cnt = 0) {
while (c % i === 0 && ++cnt > 0) c /= i;
if (i % 4 === 3 && cnt % 2 !== 0) return false;
}
return c % 4 !== 3;
};
- 时间复杂度:
- 空间复杂度:
我猜你问
- 三种解法复杂度都一样,哪个才是最优解呀?
前两套解法是需要「真正掌握」的,而「费马平方和」更多的是作为一种拓展。
你会发现从复杂度上来说,其实「费马平方和」并没有比前两种解法更好,但由于存在对 c 除质因数操作,导致「费马平方和」实际表现效果要优于同样复杂度的其他做法。但这仍然不成为我们必须掌握「费马平方和」的理由。
三者从复杂度上来说,都是 算法,不存在最不最优的问题。
- 是否有关于「费马平方和」的证明呢?
想要看 莱昂哈德·欧拉 对于「费马平方和」的证明在 这里,我这里直接引用 费马 本人的证明:
我确实发现了一个美妙的证明,但这里空白太小写不下。
- 我就是要学「费马平方和」,有没有可读性更高的代码?
有的,在这里。喜欢的话可以考虑背过:
Java 代码:
public class Solution {
public boolean judgeSquareSum(int c) {
for (int i = 2; i * i <= c; i++) {
int cnt = 0;
while (c % i == 0) {
cnt++;
c /= i;
}
if (i % 4 == 3 && cnt % 2 != 0) return false;
}
return c % 4 != 3;
}
}