# sympy/sympy

### Subversion checkout URL

You can clone with
or
.

# A faster totient function#1086

Closed
wants to merge 7 commits into from
+4 −28

### 3 participants

The old totient function was iterating over all numbers less than n and calculating whether the gcd is 1 which would take O{n*logn} time.

The new totient function iterates over the primes less than sqrt(n) (which is cached I think) and calculates the totient function. This will take O{sqrt(n)} time.

 catchmrbharath `A faster totient function` `2acb375`
sympy/ntheory/residue_ntheory.py
 ((5 lines not shown)) n = int_tested(n) if n < 1: raise ValueError("n must be a positive integer") - tot = 0 - for x in xrange(1, n): - if igcd(x, n) == 1: - tot += 1 + tot = n + for x in sieve.primerange(1, int(sqrt(n))):
 Collaborator smichr added a note Feb 26, 2012 it's kind to use p (for prime) for the loop variable. to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/ntheory/residue_ntheory.py
 ((5 lines not shown)) n = int_tested(n) if n < 1: raise ValueError("n must be a positive integer") - tot = 0 - for x in xrange(1, n): - if igcd(x, n) == 1: - tot += 1 + tot = n + for x in sieve.primerange(1, int(sqrt(n))): + if n % x == 0:
 Collaborator smichr added a note Feb 26, 2012 Could you import multiplicity from factor_ and do this: m = multiplicity(p, n): if m: factor = p**m tot -= tot//factor n //= factor to join this conversation on GitHub. Already have an account? Sign in to comment
 catchmrbharath `replaced the code finding multiplicity by the multiplicity function i…` `…n factor_` `01efb81`

@smichr I have used the multiplicity function in the new commit.

Collaborator
commented on `sympy/ntheory/residue_ntheory.py` in `01efb81`

dedent 4 spaces.

Collaborator
commented on `sympy/ntheory/residue_ntheory.py` in `01efb81`

a space goes after the comma

 catchmrbharath `dedent 4 spaces and added spaces after commas` `e0717c9`
commented on `01efb81`

@catchmrbharath Are you sure about the logic(or the catch)?
First, primerange(1, int(sqrt(n)) will exclude the last prime which you should consider. For example,

```In [1]: sieve.primerange(1,7)
Out[1]: <generator object primerange at 0x9fbce64>

In [2]: for x in _ :
...:     print x
...:
...:
2
3
5```

Second, what about the prime which are larger than sqrt(n) but divides n? Ex. n = 20 and from your logic, it only consider prime 2, 3 but not 5 which is a factor of 20 and thats why the following difference.

```In [11]: totient_(20) # Your modified function
Out[11]: 12

In [12]: t = 0;

In [13]: for i in range(1, 20):
....:     if igcd(i, 20) == 1:
....:         t = t + 1
....:
....:

In [14]: t == totient_(20)
Out[14]: False

In [15]: t
Out[15]: 8```

@smichr Why do we have two separate function viz totient and totient_ ? And those two are at different locations sympy/ntheory/factor_.py and sympy/ntheory/residue_ntheory.py respectively.

 catchmrbharath `fixed the sqrt(n) inclusive error. Also fixed the algorithm` `6c2fb70`

@hector1618
About the sqrt(n) problem : You were right. I have fixed it.

About the second problem : The idea is that there can be maximum of one prime factor greater than sqrt(n) . That is taken care by the `if n > 1 : tot -= tot // n`. The proof goes like this: if p1 and p2 are prime factors greater than sqrt(n) then p1*p2 will be greater than n, and hence its not possible.

I introduced an error when I changed the function to include multiplicity. I should have done `tot -= tot // p rather than tot -= tot //factor. It still passed the tests though.

I have fixed both the issues. Also as hector1618 said, there are two totient functions.

@catchmrbharath : I got the logic. Good work.
I think its good to go in.

Collaborator
Collaborator
Collaborator
commented on `6c2fb70`

The test that identified the logical error should probably be added, too. Also, `dumb` is sometimes faster than `smart`, so if a change is going to be made we should make sure that our assumptions about the smarter being faster are correct. In this case they aren't:

```>>> t=time();jnk=[tot(i) for i in xrange(1,1000)];print time()-t
1.98099994659
>>> t=time();jnk=[tot(i) for i in xrange(1,1000)];print time()-t
1.02399992943
>>> t=time();jnk=[totient(i) for i in xrange(1,1000)];print time()-t
0.0240001678467
>>> t=time();jnk=[totient_(i) for i in xrange(1,1000)];print time()-t
2.27099990845```
Collaborator

Maybe your first approach (without multiplicity) was better. But compare it and post the result here.

Collaborator

I think I had the two functions (totient_ and totient0 reversed. You were replacing `totient_`; it's simple and slow. Your modifications are an improvement, but not better than the existing totient (unless you can come up with something better). So I think we should just delete `totient_`, update imports and documentation, and thank you and @hector1618 for helping us get rid of the inefficient algorithm.

 catchmrbharath `removed totient_ function and fixed all imports` `7437725`

The other totient function uses the same algorithm which I use. So I think that is the best solution.

There were no tests for totient_ . The method was only used in residue_ntheory file. So fixing it was easy. I have removed the totient_ function from init.py also. What should I do to update the documentation?

Collaborator
 catchmrbharath `deleted totient_ function documentation` `ea71a93`

yeah done.

sympy/ntheory/residue_ntheory.py
 @@ -56,10 +34,11 @@ def n_order(a, n): >>> n_order(4, 7) 3 """ + from sympy.ntheory import totient
 Collaborator smichr added a note Feb 29, 2012 Here and below, import at the top (`from factor_ import factorint, trailing, totient`). It's only a last restort that we import in the function. to join this conversation on GitHub. Already have an account? Sign in to comment
 catchmrbharath `imported totient at the top rather than inside functions` `dcc3d48`
Collaborator

I rebased and squashed and added int_tested to totient and committed from here. So thanks: this is in.

closed this
Commits on Feb 26, 2012
1. catchmrbharath authored
Commits on Feb 27, 2012
1. catchmrbharath authored
`…n factor_`
2. catchmrbharath authored
Commits on Feb 28, 2012
1. catchmrbharath authored
2. catchmrbharath authored
3. catchmrbharath authored
Commits on Mar 1, 2012
1. catchmrbharath authored