Given an integer, write a function to determine if it is a power of three.
Follow up:
Could you do it without using any loop / recursion?
循环和递归的解法就不提了。这道题有这几种解法:
Method 1
算出Int最大值范围内的3的最大乘方数是:1162261467,判断n能不能整除它。
public boolean isPowerOfThree(int n) {
// 1162261467 is 3^19, 3^20 is bigger than int
return ( n>0 && 1162261467%n==0);
}
Method 2
如果 log10(n) / log10(3)
得到的是整数,那么n就是3的成分。 但是这里 需要注意的是, java的Math.log(x)方法默认的是自然数底数,即以e作为底数。但是这里不能够使用自然数底数,因为java在这里会有 round off error for n=243
,更像是一个巧合。当 n=243
, 我们得到的是下面的结果。
log(243) = 5.493061443340548 log(3) = 1.0986122886681098
==> log(243)/log(3) = 4.999999999999999
log10(243) = 2.385606273598312 log10(3) = 0.47712125471966244
==> log10(243)/log10(3) = 5.0
这是因为 log(3)
实际上轻微地比它真正的值要大,导致了 求比结果 比 真正的值更小。
public boolean isPowerOfThree(int n) {
return (Math.log10(n) / Math.log10(3)) % 1 == 0;
}
如果害怕出现这种round off error的巧合出错,可以用下面两种方案来弥补:
public boolean isPowerOfThree(int n) {
return n==0 ? false : n==Math.pow(3, Math.round(Math.log(n) / Math.log(3)));
}
或者
public boolean isPowerOfThree(int n) {
return n>0 && Math.abs(Math.log10(n)/Math.log10(3)-Math.ceil(Math.log10(n)/Math.log10(3))) < Double.MIN_VALUE;
}
Method 3 Cheating
Method
cheating method其实并不是一个好方法,但是对于这样的 乘方
问题,如果我们需要检查很多次,那么先把所有可能值存到hashset中也不失为一种好办法。
public boolean isPowerOfThree(int n) {
HashSet<Integer> set = new HashSet<>(Arrays.asList(1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049, 177147, 531441, 1594323, 4782969, 14348907, 43046721, 129140163, 387420489, 1162261467));
return set.contains(n);
}
Method 4
思路是把 n 转化为三进制的表示,然后检查 该三进制字符串 是否呈现 100.....的格式(其中0的个数>=0)。format 10*
where 0*
means k
zeros with k>=0
.
public boolean isPowerOfThree(int n) {
return Integer.toString(n, 3).matches("10*");
}
这道题有solutions:https://leetcode.com/problems/power-of-three/solution/
Solution
Approach #1 Loop Iteration [Accepted]
Java
public class Solution { public boolean isPowerOfThree(int n) { if (n < 1) { return false; } while (n % 3 == 0) { n /= 3; } return n == 1; } }
Complexity Analysis
-
Time complexity : O(logb(n)). In our case that is O(log3n). The number of divisions is given by that logarithm.
-
Space complexity : O(1). We are not using any additional memory.
Approach #2 Base Conversion 进制转换 [Accepted]
在十进制中,所有10的乘方的数字 都以 1 开头,之后跟着 0 (比如 10, 100, 1000)。 这对于其他进制和对应的乘方也适用。所以我们可以把 n 转化为三进制的表达形式,如果它的格式是100...0, 那么它就是3的乘方。
使用以下的正则表达式来判断:字符串是否以 1 开头 ^1
, 后面跟着 0个或者更多个数字 0 0*
,并且字符串中不包含其他的东西 $
.
Java
public class Solution { public boolean isPowerOfThree(int n) { return Integer.toString(n, 3).matches("^10*$"); } }
Complexity Analysis
-
Time complexity : O(log3n).
Assumptions:
Integer.toString()
- Base conversion is generally implemented as a repeated division. The complexity of should be similar to our approach #1: O(log3n).String.matches()
- Method iterates over the entire string. The number of digits in the base 3 representation ofn
is O(log3n).
-
Space complexity : O(log3n).
We are using two additional variables,
- The string of the base 3 representation of the number (size log3n)
- The string of the regular expression (constant size)
Approach #3 Mathematics [Accepted]
log方法:
只有当 i
是整数时,n
才是3的乘方。判断一个数是不是整数,我们可以用 % 1
来取出小数部分,检查小数部分是否是0。
Java
public class Solution { public boolean isPowerOfThree(int n) { return (Math.log10(n) / Math.log10(3)) % 1 == 0; } }
Attention
当我们开始使用 double
时,就有可能产生精度错误,这种解法就有问题。这意味着当比较 double
数字时,我们不能使用 ==
。因为 Math.log10(n) / Math.log10(3)
的结果可能是 5.0000001
或者4.9999999
。为了解决这个问题,我们可以使用 epsilon
来避免这个问题。令final double epsilon=0.00000001;(epsilon通常表示表示大于零的最小正Double值。这里可以自己定义,或者直接令epsilon=Double.MIN_VALUE。)
需要注意的是,Integer.MIN_VALUE自不必说,就是32位整型能存储的最小数字:0x80000000,是一个负数。但是Double.MIN_VALUE却是一个正数,Double.MIN_VALUE表示的是64位双精度值能表示的最小正数。如果需要用到Double的负无穷,可以用Double.NEGATIVE_INFINITY。
Java
return (Math.log(n) / Math.log(3) + epsilon) % 1 <= 2 * epsilon;
Complexity Analysis
-
Time complexity : Unknown The expensive operation here is
Math.log
, which upper bounds the time complexity of our algorithm. The implementation is dependent on the language we are using and the compiler[3] -
Space complexity : O(1). We are not using any additional memory. The
epsilon
variable can be inlined.
Approach #4 Integer Limitations [Accepted]
3⌊log3MaxInt⌋=3⌊19.56⌋=319=1162261467
Java
public class Solution { public boolean isPowerOfThree(int n) { return n > 0 && 1162261467 % n == 0; } }
Complexity Analysis
-
Time complexity : O(1). We are only doing one operation.
-
Space complexity : O(1). We are not using any additional memory.
Performance Measurements
Iterations | 106 | 107 | 108 | 109 | Maxint |
---|---|---|---|---|---|
Java Approach #1 (Naive) | 0.04 | 0.07 | 0.30 | 2.47 | 5.26 |
Java Approach #2 (Strings) | 0.68 | 4.02 | 38.90 | 409.16 | 893.89 |
Java Approach #3 (Logarithms) | 0.09 | 0.50 | 4.59 | 45.53 | 97.50 |
Java Approach #4 (Fast) | 0.04 | 0.06 | 0.08 | 0.41 | 0.78 |
References
- [1] https://en.wikipedia.org/wiki/Fast_inverse_square_root
- [2] https://en.wikipedia.org/wiki/Regular_expression
- [3] http://developer.classpath.org/doc/java/lang/StrictMath-source.html
- [4] http://www.cut-the-knot.org/recurrence/conversion.shtml
- [5] http://www.cnblogs.com/liujinhong/p/6432107.html Java输出double类型中的最小正数和最大正数。