题目分析
1. Pow(x, n)这题和Super Pow这题非常相似,都是要用快速幂的方法来完成的,因此放在一起来考虑。
2. Pow(x, n)这题第一次提交的时候我用的是非常简单的写法,直接把n分解成n = a0 * 2^0 + a1 * 2 ^ 1 + a2 * 2^2...an-1 * a ^ (n-1) ,然后迭代求出每个次方的值再求积,这个方法是logn的复杂度,能够满足题目的要求。
3. 但是却连续提交了好几次都没有通过,第一次是因为没有考虑到负数的问题,因此把负数求绝对值当成正数计算,再根据n的奇偶性得到原来的值。但是这样做也是不正确的,最主要的问题是没有考虑到测试数据中的特殊数据,也就是MIN_INT是在INT的取值范围内,但-MIN_INT是不在INT的取值范围内的,因此如果直接这样做会导致溢出,必须针对MIN_INT进行一下特殊判断才能得到正解。
4. 完成了Pow(x, n)直接开始做Super Pow,第一思路就是把问题转化到和Pow(x, n)一样的做法做,然后在每一步的地方先取模再做乘积就可以了,是比较朴素的快速幂取模。但是有一个问题在于题目中的数字是用高精度表示的,因此除二的操作不是特别好进行(写到这里我想到如果用Python来写会不会很简单。。。可不可以直接转化成大整数一直除以2做),但这也没有阻碍我的惯性思维,我写了一个高精度的除二运算,还有高精度的判断是否为零运算以及高精度判断奇偶性运算。然后把题目转化到了Pow(x, n)来做。然而这个做法是会超时的,主要是因为高精度判断是否为零的运算本身是O(log(10)n)级别的,高精度的除二运算也是O(log(10)n)级别的,而本身外部的大循环也是O(log(2)n)级别的,因此整个算法是O(log(2)^2(n))级别的。
5. 因此在加了一堆的优化试图通过没成功后我发现其实不需要进行复杂的除二操作,直接用十进制的每一位展开快速幂就可以解决这个问题,是O(log(10)n)的,可以直接通过。
题目代码
double myPow(double x, int n) {
if (n == INT_MIN) return 1.0/(myPow(x, INT_MAX) * x);
double result = (abs(n) & 1) == 1 ? x : 1;
for (int i = 1; i <<= 1; i <= abs(n)) {
x *= x;
if ((i & abs(n)) > 0)
result *= x;
}
return n > 0?result:1,0/result;
}
较差的做法:用了复杂的高精度操作
bool isEven(vector<int> &b) {
return (b[b.size() - 1] % 2 == 0);
}
bool isZero(vector<int> &b) {
return count_if(b.cbegin(), b.cend(), [](int b1) {return b1 != 0; }) == 0;
}
vector<int>& div2(vector<int>& b) {
int carry = 0;
for (int i = 0; i < b.size(); i++) {
b[i] = b[i] + carry * 10;
carry = b[i] % 2;
b[i] = b[i] / 2;
}
return b;
}
int superPow(int a, vector<int>& b) {
int result = 1;
while (!isZero(b)) {
if (!isEven(b))
result = ((result % 1337) * (a % 1337)) % 1337;
a = ((a % 1337) * (a % 1337)) % 1337;
b = div2(b);
}
return result;
}
较好的做法:直接针对十进制位求快速幂
int powx(int a, int b) {
int temp = 1;
a %= 1337;
for (int i = 0; i < b; i++)
temp = (temp * a) %1337;
return temp;
}
int superPow(int a, vector<int>& b) {
int result = 1;
for (int i = b.size() -1 ; i >= 0; i--) {
if (b[i] != 0) result = (result * powx(a, b[i])) % 1337;
a = powx(a, 10);
}
return result;
}