问题
I know this is a classic problem. I solved it in Java. I have my solution below. However, when I used this solution in codefights.com, It went beyond the execution time limit. I would appreciate if anyone could give me suggestions on improving this code in any way possible. Please feel free to criticize my code so that I can improve on my coding skills. Thanks
You're given number n.
Return n as a product of its prime factors.
Example
For n = 22 the output should be "2*11".
For n = 120 the output should be "2*2*2*3*5".
For n = 17194016 the output should be "2*2*2*2*2*7*59*1301".
[input] integer n
An integer number smaller than 109. [output] string
The Prime factors of n which splitted by * symbol. prime factors should be in increasing order.
Solution (JAVA):
public String primefactors(int n) {
String factors = "";
for (int i = 2; i <= n / 2; i++) {
if (isPrime(i)) {
while (n % i == 0) {
n /= i;
if (isPrime(n) && n != 1) {
factors = factors + Integer.valueOf(i).toString() + "*"
+ Integer.valueOf(n).toString();
break;
} else if (n == 1)
factors = factors + Integer.valueOf(i).toString();
else
factors = factors + Integer.valueOf(i).toString() + "*";
}
}
}
return factors;
}
public boolean isPrime(int n) {
boolean prime = true;
if (n == 1)
return false;
else if (n % 2 == 0 && n!=2)
return false;
else if (n % 3 == 0 && n!=3)
return false;
else {
for (int j = 2; j < n / 2; j++) {
if (n % j == 0) {
return false;
}
}
}
return prime;
}
回答1:
Since n
is smaller than a fixed number (109), simply use a table containing all prims <= 109, instead of generating them dynamically. Or atleast generate the prims first using the sieve of erathostenes or atkin. The hardcoded table would be preferable, but using a sieve for generating the table dynamically would aswell speed things up alot. The isPrime()
function you implemented is a performance killer.
回答2:
The function isPrime()
is called too much times in primefactors
. For example, i == 2
and there are many divisors 2
in n
. The top call (isPrime(i))
is fine. However, inside the loop while (n % i == 0)
you check isPrime(n)
after each dividing n /= 2;
. So, if initial n
is 100
the function isPrime()
is called for 50
and on the next loop for 25
. That does not make any sense. I think that it is the biggest problem here, since even if isPrime
works in linear time it is too much to call it many times in the internal loop.
It is possible to exit from the loop for i
in two cases: n
is equal 1
after divisions or n
is for sure prime if i
is larger than sqrt(n)
.
public String primefactors(int n) {
String factors = "";
int max_divisor = sqrt(n);
for (int i = 2; i <= max_divisor; i++) {
if (isPrime(i)) {
while (n % i == 0) {
n /= i;
if (n == 1)
factors = factors + Integer.valueOf(i).toString();
else
factors = factors + Integer.valueOf(i).toString() + "*";
}
max_divisor = sqrt(n);
}
}
// check for the last prime divisor
if (n != 1)
factors = factors + Integer.valueOf(n).toString();
return factors;
}
Even after that improvement (and sqrt(n)
as the max limit in isPrime()
) your algorithm will have linear complexity O(n)
, since there are at most sqrt(n)
loops for i
and the maximum number of probes for prime in isPrime
is also sqrt(n)
.
Yes, it can be done better by choosing better algorithms for isPrime()
. Even if you are not allowed to use hardcoded table of prime numbers it is possible to generate such look up table in runtime (if there are enough memory). So, it is possible to use automatically generated list of primes organized in ascending order to probe given number up to sqrt(n
). If i
becomes larger than sqrt(n)
it means that the next prime is found and it should be appended to the look up table and isPrime()
should return true
.
Example
Suppose isPrime
is called for 113
. At that moment the look up table has a list of previous prime numbers: 2,3,5,7,11,13...
. So, we try to divide 113
by items from that list up to sqrt(113)
(while (i <= 10)
). After trying 2,3,5,7
the next item on the list 11
is too large, so 113
is appended to the list of primes and the function returns true
.
The other algorithms may give better performance in the worst case. For example the sieve of Eratosthenes or sieve of Atkin can be used to effectively precomputed list of prime numbers up to given n
with the best O(n)
complexity for the best implementation. Here you need to find all primes up to sqrt(n)
, so it takes O(sqrt(n))
to generate such list. Once such list is generated you need to try to divide your input by numbers is the list that takes at most sqrt(n)
probes. So, the algorithm complexity is O(sqrt(n))
. However, suppose that your input is 1024
that is 2
to the power of 10
. In that case the first algorithm will be better, since it will not go to primes larger than 2
.
Do you really need the function isPrime()?
With flexible thinking If we look closer it appears that you do not have to search for all primes in some range. You only needed to find all prime divisors of one given integer. However if we try to divide n
by all integers in range up to sqrt(n)
that is also good solution. Even if such integer is not prime it will be skipped due to the condition n % i == 0
, since all primes lower than the integer under test are already removed from n
, so that simple modular division does here the same as isPrime()
. The full solution with O(sqrt(n))
complexity:
public String primefactors(int n) {
String factors = "";
int max_divisor = sqrt(n);
for (int i = 2; i <= max_divisor; i++) {
while (n % i == 0) {
n /= i;
max_divisor = sqrt(n);
if (n == 1)
factors = factors + Integer.valueOf(i).toString();
else
factors = factors + Integer.valueOf(i).toString() + "*";
}
}
// check for the last prime divisor
if (n != 1)
factors = factors + Integer.valueOf(n).toString();
return factors;
}
It is also possible to split the function to avoid if (n == 1)
check in the inner loop, however it does not change the algorithm complexity.
来源:https://stackoverflow.com/questions/32914103/returning-string-of-prime-factors-in-java