It was asked to find a way to check whether a number is in the Fibonacci Sequence or not.
The constraints are
1≤T≤10^5
1≤N≤10^10
where the T is the number of test cases,
and N is the given number, the Fibonacci candidate to be tested.
I wrote it the following using the fact a number is Fibonacci if and only if one or both of (5*n2 + 4) or (5*n2 – 4) is a perfect square :-
import java.io.*;
import java.util.*;
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for(int i = 0 ; i < n; i++){
int cand = sc.nextInt();
if(cand < 0){System.out.println("IsNotFibo"); return; }
int aTest =(5 * (cand *cand)) + 4;
int bTest = (5 * (cand *cand)) - 4;
int sqrt1 = (int)Math.sqrt(aTest);// Taking square root of aTest, taking into account only the integer part.
int sqrt2 = (int)Math.sqrt(bTest);// Taking square root of bTest, taking into account only the integer part.
if((sqrt1 * sqrt1 == aTest)||(sqrt2 * sqrt2 == bTest)){
System.out.println("IsFibo");
}else{
System.out.println("IsNotFibo");
}
}
}
}
But its not clearing all the test cases? What bug fixes I can do ?
A much simpler solution is based on the fact that there are only 49 Fibonacci numbers below 10^10.
Precompute them and store them in an array or hash table for existency checks.
The runtime complexity will be O(log N + T):
Set<Long> nums = new HashSet<>();
long a = 1, b = 2;
while (a <= 10000000000L) {
nums.add(a);
long c = a + b;
a = b;
b = c;
}
// then for each query, use nums.contains() to check for Fibonacci-ness
If you want to go down the perfect square route, you might want to use arbitrary-precision arithmetics:
// find ceil(sqrt(n)) in O(log n) steps
BigInteger ceilSqrt(BigInteger n) {
// use binary search to find smallest x with x^2 >= n
BigInteger lo = BigInteger.valueOf(1),
hi = BigInteger.valueOf(n);
while (lo.compareTo(hi) < 0) {
BigInteger mid = lo.add(hi).divide(2);
if (mid.multiply(mid).compareTo(x) >= 0)
hi = mid;
else
lo = mid.add(BigInteger.ONE);
}
return lo;
}
// checks if n is a perfect square
boolean isPerfectSquare(BigInteger n) {
BigInteger x = ceilSqrt(n);
return x.multiply(x).equals(n);
}
Your tests for perfect squares involve floating point calculations. That is liable to give you incorrect answers because floating point calculations typically give you inaccurate results. (Floating point is at best an approximate to Real numbers.)
In this case sqrt(n*n) might give you n - epsilon for some small epsilon and (int) sqrt(n*n) would then be n - 1 instead of the expected n.
Restructure your code so that the tests are performed using integer arithmetic. But note that N < 1010 means that N2 < 1020. That is bigger than a long ... so you will need to use ...
UPDATE
There is more to it than this. First, Math.sqrt(double) is guaranteed to give you a double result that is rounded to the closest double value to the true square root. So you might think we are in the clear (as it were).
But the problem is that N multiplied by N has up to 20 significant digits ... which is more than can be represented when you widen the number to a double in order to make the sqrt call. (A double has 15.95 decimal digits of precision, according to Wikipedia.)
On top of that, the code as written does this:
int cand = sc.nextInt();
int aTest = (5 * (cand * cand)) + 4;
For large values of cand, that is liable to overflow. And it will even overflow if you use long instead of int ... given that the cand values may be up to 10^10. (A long can represent numbers up to +9,223,372,036,854,775,807 ... which is less than 1020.) And then we have to multiply N2 by 5.
In summary, while the code should work for small candidates, for really large ones it could either break when you attempt to read the candidate (as an int) or it could give the wrong answer due to integer overflow (as a long).
Fixing this requires a significant rethink. (Or deeper analysis than I have done to show that the computational hazards don't result in an incorrect answer for any large N in the range of possible inputs.)
According to this link a number is Fibonacci if and only if one or both of (5*n2 + 4) or (5*n2 – 4) is a perfect square so you can basically do this check.
Hope this helps :)
Use binary search and the Fibonacci Q-matrix for a O((log n)^2) solution per test case if you use exponentiation by squaring.
Your solution does not work because it involves rounding floating point square roots of large numbers (potentially large enough not to even fit in a long), which sometimes will not be exact.
The binary search will work like this: find Q^m: if the m-th Fibonacci number is larger than yours, set right = m, if it is equal return true, else set left = m + 1.
As it was correctly said, sqrt could be rounded down. So:
Even if you use long instead of int, it has 18 digits.
even if you use Math.round(), not simply (int) or (long). Notice, your function wouldn't work correctly even on small numbers because of that.
double have 14 digits, long has 18, so you can't work with squares, you need 20 digits.
BigInteger and BigDecimal have no sqrt() function.
So, you have three ways:
write your own sqrt for BigInteger.
check all numbers around the found unprecise double sqrt() for being a real sqrt. That means also working with numbers and their errors simultaneously. (it's horror!)
count all Fibonacci numbers under 10^10 and compare against them.
The last variant is by far the simplest one.
Looks like to me the for-loop doesn't make any sense ?
When you remove the for-loop for me the program works as advertised:
import java.io.*;
import java.util.*;
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int cand = sc.nextInt();
if(cand < 0){System.out.println("IsNotFibo"); return; }
int aTest = 5 * cand *cand + 4;
int bTest = 5 * cand *cand - 4;
int sqrt1 = (int)Math.sqrt(aTest);
int sqrt2 = (int)Math.sqrt(bTest);
if((sqrt1 * sqrt1 == aTest)||(sqrt2 * sqrt2 == bTest)){
System.out.println("IsFibo");
}else{
System.out.println("IsNotFibo");
}
}
}
You only need to test for a given candidate, yes? What is the for loop accomplishing? Could the results of the loop be throwing your testing program off?
Also, there is a missing } in the code. It will not run as posted without adding another } at the end, after which it runs fine for the following input:
10 1 2 3 4 5 6 7 8 9 10
IsFibo
IsFibo
IsFibo
IsNotFibo
IsFibo
IsNotFibo
IsNotFibo
IsFibo
IsNotFibo
IsNotFibo
Taking into account all the above suggestions I wrote the following which passed all test cases
import java.io.*;
import java.util.*;
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long[] fib = new long[52];
Set<Long> fibSet = new HashSet<>(52);
fib[0] = 0L;
fib[1] = 1L;
for(int i = 2; i < 52; i++){
fib[i] = fib[i-1] + fib[i - 2];
fibSet.add(fib[i]);
}
int n = sc.nextInt();
long cand;
for(int i = 0; i < n; i++){
cand = sc.nextLong();
if(cand < 0){System.out.println("IsNotFibo");continue;}
if(fibSet.contains(cand)){
System.out.println("IsFibo");
}else{
System.out.println("IsNotFibo");
}
}
}
}
I wanted to be on the safer side hence I choose 52 as the number of elements in the Fibonacci sequence under consideration.
Related
The purpose of this class is to calculate the nth number of the Lucas Sequence. I am using data type long because the problems wants me to print the 215th number. The result of the 215th number in the Lucas Sequence is: 855741617674166096212819925691459689505708239. The problem I am getting is that at some points, the result is negative. I do not understand why I am getting a negative number when the calculation is always adding positive numbers. I also have two methods, since the question was to create an efficient algorithm. One of the methods uses recursion but the efficiency is O(2^n) and that is of no use to me when trying to get the 215th number. The other method is using a for loop, which the efficiency is significantly better. If someone can please help me find where the error is, I am not sure if it has anything to do with the data type or if it is something else.
Note: When trying to get the 91st number I get a negative number and when trying to get the 215th number I also get a negative number.
import java.util.Scanner;
public class Problem_3
{
static long lucasNum;
static long firstBefore;
static long secondBefore;
static void findLucasNumber(long n)
{
if(n == 0)
{
lucasNum = 2;
}
if(n == 1)
{
lucasNum = 1;
}
if(n > 1)
{
firstBefore = 1;
secondBefore = 2;
for(int i = 1; i < n; i++)
{
lucasNum = firstBefore + secondBefore;
secondBefore = firstBefore;
firstBefore = lucasNum;
}
}
}
static long recursiveLucasNumber(int n)
{
if(n == 0)
{
return 2;
}
if(n == 1)
{
return 1;
}
return recursiveLucasNumber(n - 1) + recursiveLucasNumber(n - 2);
}
public static void main(String[] args)
{
System.out.println("Which number would you like to know from "
+ "the Lucas Sequence?");
Scanner scan = new Scanner(System.in);
long num = scan.nextInt();
findLucasNumber(num);
System.out.println(lucasNum);
//System.out.println(recursiveLucasNumber(num));
}
}
Two observations:
The answer you are expecting (855741617674166096212819925691459689505708239) is way larger than you can represent using a long. So (obviously) if you attempt to calculate it using long arithmetic you are going to get integer overflow ... and a garbage answer.
Note: this observation applies for any algorithm in which you use a Java integer primitive value to represent the Lucas numbers. You would run into the same errors with recursion ... eventually.
Solution: use BigInteger.
You have implemented iterative and pure recursion approaches. There is a third approach: recursion with memoization. If you apply memorization correctly to the recursive solution, you can calculate LN in O(N) arithmetical operations.
Java data type long can contain only 64-bit numbers in range -9223372036854775808 .. 9223372036854775807. Negative numbers arise due to overflow.
Seems you need BigInteger class for arbitrary-precision integer numbers
I wasn't aware of the lucas numbers before this thread, but from wikipedia it looks like they are related to the fibonacci sequence with (n = nth number, F = fibonacci, L = lucas):
Ln = F_(n-1) + F_(n+1)
Thus, if your algorithm is too slow, you could use the closed form fibonacci and than compute the lucas number from it, alternative you could also use the closed form given in the wikipedia article directly (see https://en.wikipedia.org/wiki/Lucas_number).
Example code:
public static void main(String[] args) {
long n = 4;
double fibo = computeFibo(n);
double fiboAfter = computeFibo(n + 1);
double fiboBefore = computeFibo(n - 1);
System.out.println("fibonacci n:" + Math.round(fibo));
System.out.println("fibonacci: n+1:" + Math.round(fiboAfter));
System.out.println("fibonacci: n-1:" + Math.round(fiboBefore));
System.out.println("lucas:" + (Math.round(fiboAfter) + Math.round(fiboBefore)));
}
private static double computeFibo(long n) {
double phi = (1 + Math.sqrt(5)) / 2.0;
double psi = -1.0 / phi;
return (Math.pow(phi, n) - Math.pow(psi, n)) / Math.sqrt(5);
}
To work around the long size limit you could use java BigDecimal (https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html). This is needed earlier in this approach as the powers in the formula will grow very quickly.
Let M(n,k) be the sum of all possible multiplications of k distinct factors with largest possible factor n, where order is irrelevant.
For example, M(5,3) = 225 , because:
1*2*3 = 6
1*2*4 = 8
1*2*5 = 10
1*3*4 = 12
1*3*5 = 15
1*4*5 = 20
2*3*4 = 24
2*3*5 = 30
2*4*5 = 40
3*4*5 = 60
6+8+10+12+15+20+24+30+40+60 = 225.
One can easily notice that there are C(n,k) such multiplications, corresponding to the number of ways one can pick k objects out of n possible objects. In the example above, C(5,3) = 10 and there really are 10 such multiplications, stated above.
The question can also be visualized as possible n-sized sets containing exactly k 0's, where each cell that does not contain 0 inside it, has the value of its index+1 inside it. For example, one possible such set is {0,2,3,0,5}. From here on, one needs to multiply the values in the set that are different than 0.
My approach is a recursive algorithm. Similiarly to the above definition of
M(n,k), I define M(n,j,k) to be the sum of all possible multiplications of exactly k distinct factors with largest possible factor n, AND SMALLEST possible factor j. Hence, my approach would yield the desired value if ran on
M(n,1,k). So I start my recursion on M(n,1,k), with the following code, written in Java:
public static long M (long n, long j, long k)
{
if (k==1)
return usefulFunctions.sum(j, n);
for (long i=j;i<=n-k+1+1;i++)
return i*M(n,i+1,k-1);
}
Explanation to the code:
Starting with, for example, n=5 , j=1, k=3, the algorithm will continue to run as long as we need more factors, (k>=1), and it is made sure to run only distinct factors thanks to the for loop, which increases the minimal possible value j as more factors are added. The loop runs and decreases the number of needed factors as they are 'added', which is achieved through applying
M(n,j+1,k-1). The j+1 assures that the factors will be distinct because the minimal value of the factor increases, and k-1 symbolizes that we need 1 less factor to add.
The function 'sum(j,n)' returns the value of the sum of all numbers starting from j untill n, so sum(1,10)=55. This is done with a proper, elegant and simple mathematical formula, with no loops: sum(j,n) = (n+1)*n/2 - (i-1)*i/2
public static long sum (long i, long n)
{
final long s1 = n * (n + 1) / 2;
final long s2 = i * (i - 1) / 2;
return s1 - s2 ;
}
The reason to apply this sum when k=1, I will explain with an example:
Say we have started with 1*2. Now we need a third factor, which can be either of 3,4,5. Because all multiplications: 1*2*3, 1*2*4, 1*2*5 are valid, we can return 1*2*(3+4+5) = 1*2*sum(3,5) = 24.
Similiar logic explains the coefficient "i" next to the M(n,j+1,k-1).
say we have now the sole factor 2. Hence we need 2 more factors, so we multiply 2 by the next itterations, which should result in:
2*(3*sum(4,5) + 4*sum(5,5))
However, for a reason I can't explain yet, the code doesn't work. It returns wrong values and also has "return" issues that cause the function not to return anything, don't know why.
This is the reason i'm posting this question here, in hope someone will aid me. Either by fixing this code or sharing a code of his own. Explaining where I'm going wrong will be most appreciable.
Thanks a lot in advance, and sorry for this very long question,
Matan.
-----------------------EDIT------------------------
Below is my fixed code, which solves this question. Posting it incase one should ever need it :) Have fun !
public static long M(long n, long j, long k)
{
if (k == 0)
return 0;
if (k == 1)
return sum(j,n);
else
{
long summation = 0;
for (long i=j; i<=n; i++)
summation += i*M(n, i+1, k-1);
return summation;
}
}
I see that u got ur answer and i really like ur algorithm but i cant control myself posting a better algorithm . here is the idea
M(n,k) = coefficient of x^k in (1+x)(1+2*x)(1+3*x)...(1+n*x)
u can solve above expression by divide and conquer algorithm Click Here to find how to multiply above expression and get polynomial function in the form of ax^n + bx^(n-1)....+c
overall algorithm time complexity is O(n * log^2 n)
and best part of above algorithm is
in the attempt of finding solution for M(n,k) , u will find solution for M(n,x) where 1<=x<=n
i hope it will be useful to know :)
I am not sure about your algorithm, but you certainly messed up with your sum function. The problem you have is connected to type casting and division of integer numbers. Try something like this:
public static long sum (long i, long n)
{
final long s1 = n * (n + 1) / 2;
final long s2 = (i * i - i) / 2;
return s1 - s2 ;
}
You have a problem with your sum function : here is the correct formula:
public static long sum (long i, long n)
{
double s1 = n*(n+1)/2;
double s2 = i*(i-1)/2;
return (long)(s1-s2);
}
Here the full solution :
static int n = 5;
static long k = 3;
// no need to add n and k them inside your M function cause they are fixed.
public static long M (long start) // start = 1
{
if(start > k) // if start is superior to k : like your example going from 1..3 , then you return 0
return 0;
int res = 0; // res of your function
for(long i=start+1;i<n;i++){
res+=start*i*sum(i+1,n); // here you take for example 1*2*sum(3,5) + 1*3*sum(4,5).... ect
}
return res+M(start+1); // return res and start again from start+1 wich would be 2.
}
public static long sum (long i, long n)
{
if(i>n)
return 0;
double s1 = n*(n+1)/2;
double s2 = i*(i-1)/2;
return (long)(s1-s2);
}
public static void main(String[] args) {
System.out.println(M(1));
}
Hope it helped
I am currently taking pre-calculus and thought that I would make a quick program that would give me the results of factorial 10. While testing it I noticed that I was getting incorrect results after the 5th iteration. However, the first 4 iterations are correct.
public class Factorial
{
public static void main(String[] args)
{
int x = 1;
int factorial;
for(int n = 10; n!=1; n--)
{
factorial = n*(n-1);
x = x * factorial;
System.out.printf("%d ", x);
}
}//end of class main
}//end of class factorial
That is an Integer Overflow issue. Use long or unsigned long instead of int. (And as #Dunes suggested, your best bet is really BigInteger when working with very large numbers, because it will never overflow, theoretically)
The basic idea is that signed int stores numbers between -2,147,483,648 to 2,147,483,647, which are stored as binary bits (all information in a computer are stored as 1's and 0's)
Positive numbers are stored with 0 in the most significant bit, and negative numbers are stored with 1 in the most significant bit. If your positive number gets too big in binary representation, digits will carry over to the signed bit and turn your positive number into the binary representation of a negative one.
Then when the factorial gets bigger than even what an unsigned int can store, it will "wrap around" and lose the carry-over from its most significant (signed) bit - that's why you are seeing the pattern of sometimes alternating positive and negative values in your output.
You're surpassing the capacity of the int type (2,147,483,647), so your result is wrapping back around to the minimum int value. Try using long instead.
Having said the that, the method you are currently employing will not result in the correct answer: actually, you are currently computing 10! ^ 2.
Why complicate things? You could easily do something like this:
long x = 1L;
for(int n = 1; n < 10; n++)
{
x *= n;
System.out.println(x);
}
1
2
6
24
120
720
5040
40320
362880
which shows successive factorials until 10! is reached.
Also, as others have mentioned, if you need values bigger than what long can support you should use BigInteger, which supports arbitrary precision.
Your formula for the factorial is incorrect. What you will have is this:
Step 1 : n*(n-1) = 10 * 9 = 90 => x = 1*90 = 90
Step 2 : n*(n-1) = 9 * 8 = 72 => x = 90*72 = 6480 or, it should be : 10 * 9 * 8 => 720
But the wrong results are coming from the fact that you reached the maximum value for the type int as pointed out by others
Your code should be
public class Factorial
{
public static void main(String[] args)
{
double factorial = 1;
for(int n = factorial; n>=1; n--)
{
factorial = factorial * n;
System.out.printf("%d ", factorial );
}
}
}
In addition to what the other answers mention about the overflow, your factorial algorithm is also incorrect. 10! should calculate 10*9*8*7*6*5*4*3*2*1, you are doing (10*9)*(9*8)*(8*7)*(7*6)*...
Try changing your loop to the following:
int x = 1;
for(int n = 10; n > 1 ; n--)
{
x = x * n;
System.out.printf("%d ", x);
}
You will eventually overflow if you try to calculate the factorial of higher numbers, but int is plenty large enough to calculate the factorial of 10.
I am currently taking pre-calculus and thought that I would make a quick program that would give me the results of factorial 10. While testing it I noticed that I was getting incorrect results after the 5th iteration. However, the first 4 iterations are correct.
public class Factorial
{
public static void main(String[] args)
{
int x = 1;
int factorial;
for(int n = 10; n!=1; n--)
{
factorial = n*(n-1);
x = x * factorial;
System.out.printf("%d ", x);
}
}//end of class main
}//end of class factorial
That is an Integer Overflow issue. Use long or unsigned long instead of int. (And as #Dunes suggested, your best bet is really BigInteger when working with very large numbers, because it will never overflow, theoretically)
The basic idea is that signed int stores numbers between -2,147,483,648 to 2,147,483,647, which are stored as binary bits (all information in a computer are stored as 1's and 0's)
Positive numbers are stored with 0 in the most significant bit, and negative numbers are stored with 1 in the most significant bit. If your positive number gets too big in binary representation, digits will carry over to the signed bit and turn your positive number into the binary representation of a negative one.
Then when the factorial gets bigger than even what an unsigned int can store, it will "wrap around" and lose the carry-over from its most significant (signed) bit - that's why you are seeing the pattern of sometimes alternating positive and negative values in your output.
You're surpassing the capacity of the int type (2,147,483,647), so your result is wrapping back around to the minimum int value. Try using long instead.
Having said the that, the method you are currently employing will not result in the correct answer: actually, you are currently computing 10! ^ 2.
Why complicate things? You could easily do something like this:
long x = 1L;
for(int n = 1; n < 10; n++)
{
x *= n;
System.out.println(x);
}
1
2
6
24
120
720
5040
40320
362880
which shows successive factorials until 10! is reached.
Also, as others have mentioned, if you need values bigger than what long can support you should use BigInteger, which supports arbitrary precision.
Your formula for the factorial is incorrect. What you will have is this:
Step 1 : n*(n-1) = 10 * 9 = 90 => x = 1*90 = 90
Step 2 : n*(n-1) = 9 * 8 = 72 => x = 90*72 = 6480 or, it should be : 10 * 9 * 8 => 720
But the wrong results are coming from the fact that you reached the maximum value for the type int as pointed out by others
Your code should be
public class Factorial
{
public static void main(String[] args)
{
double factorial = 1;
for(int n = factorial; n>=1; n--)
{
factorial = factorial * n;
System.out.printf("%d ", factorial );
}
}
}
In addition to what the other answers mention about the overflow, your factorial algorithm is also incorrect. 10! should calculate 10*9*8*7*6*5*4*3*2*1, you are doing (10*9)*(9*8)*(8*7)*(7*6)*...
Try changing your loop to the following:
int x = 1;
for(int n = 10; n > 1 ; n--)
{
x = x * n;
System.out.printf("%d ", x);
}
You will eventually overflow if you try to calculate the factorial of higher numbers, but int is plenty large enough to calculate the factorial of 10.
I've managed to get a version of Eulers Totient Function working, albeit one that works for smaller numbers (smaller here being smaller compared to the 1024 bit numbers I need it to calculate)
My version is here -
public static BigInteger eulerTotientBigInt(BigInteger calculate) {
BigInteger count = new BigInteger("0");
for(BigInteger i = new BigInteger("1"); i.compareTo(calculate) < 0; i = i.add(BigInteger.ONE)) {
BigInteger check = GCD(calculate,i);
if(check.compareTo(BigInteger.ONE)==0) {//coprime
count = count.add(BigInteger.ONE);
}
}
return count;
}
While this works for smaller numbers, it works by iterating through every possible from 1 to the number being calculated. With large BigIntegers, this is totally unfeasible.
I've read that it's possible to divide the number on each iteration, removing the need to go through them one by one. I'm just not sure what I'm supposed to divide by what (some of the examples I've looked at are in C and use longs and a square root - as far as I know I can't calculate an accurate an accurate square root of a BigInteger. I'm also wondering that if for modular arithmetic such as this, does the function need to include an argument stating what the mod is. I'm totally unsure on that so any advice much appreciated.
Can anyone point me in the right direction here?
PS I deleted this question when I found modifying Euler Totient Function. I adapted it to work with BigIntegers -
public static BigInteger etfBig(BigInteger n) {
BigInteger result = n;
BigInteger i;
for(i = new BigInteger("2"); (i.multiply(i)).compareTo(n) <= 0; i = i.add(BigInteger.ONE)) {
if((n.mod(i)).compareTo(BigInteger.ZERO) == 0)
result = result.divide(i);
while(n.mod(i).compareTo(BigInteger.ZERO)== 0 )
n = n.divide(i);
}
if(n.compareTo(BigInteger.ONE) > 0)
result = result.subtract((result.divide(n)));
return result;
}
And it does give an accurate result, bit when passed a 1024 bit number it runs forever (I'm still not sure if it even finished, it's been running for 20 minutes).
There is a formula for the totient function, which required the prime factorization of n.
Look here.
The formula is:
phi(n) = n * (p1 - 1) / p1 * (p2 - 1) / p2 ....
were p1, p2, etc. are all the prime divisors of n.
Note that you only need BigInteger, not floating point, because the division is always exact.
So now the problem is reduced to finding all prime factors, which is better than iteration.
Here is the whole solution:
int n; //this is the number you want to find the totient of
int tot = n; //this will be the totient at the end of the sample
for (int p = 2; p*p <= n; p++)
{
if (n%p==0)
{
tot /= p;
tot *= (p-1);
while ( n % p == 0 )
n /= p;
}
}
if ( n > 1 ) { // now n is the largest prime divisor
tot /= n;
tot *= (n-1);
}
The algorithm you are trying to write is equivalent to factoring the argument n, which means you should expect it to run forever, practically speaking until either your computer dies or you die. See this post in mathoverflow for more information: How hard is it to compute the Euler totient function?.
If, on the other hand, you want the value of the totient for some large number for which you have the factorization, pass the argument as sequence of (prime, exponent) pairs.
The etfBig method has a problem.
Euler's product formula is n*((factor-1)/factor) for all factors.
Note: Petar's code has it as:
tot /= p;
tot *= (p-1);
In the etfBig method, replace result = result.divide(i);
with
result = result.multiply(i.subtract(BigInteger.ONE)).divide(i);
Testing from 2 to 200 then produces the same results as the regular algorithm.