1
x = 2**1000000
n = 2**100000000

(x**2-2)%n is too slow. I found pow() but I can't use it because I can't subtract 2. (pow(x, 2)-2)%n and (x*x-2)%n are also slow. When I tested (x*x-2) it was fast but when I added the modulo operator it was slow. Is there a way to compute (x**2-2)%n faster?

Johnny P.
  • 27
  • 1
  • 9

5 Answers5

9

Are you running this in the interpreter? I did some testing and the main slowdown seemed to come from the interpreter trying to display the result.

If you assign the expression to a variable, the interpreter won't try to display the result, and it will be very quick:

x = 2**1000000
n = 2**100000000
result = (x**2-2)%n

Addendum:

I was also originally thinking along the same lines as MikeW's answer, and if you wanted every part of the code to be fast, you could take advantage of Python's internal base 2 representation of integers and use bitwise left shifts:

x = 1 << 1000000
n = 1 << 100000000

This comes with the caveat that this only works because x and n are powers of 2, and you have to be more careful to avoid making an off-by-one error. This answer is a good explanation of how bitshifts basically work, but Python is bit different than other languages like C, C++, or Java because Python integers are unlimited precision, so you can never left shift a bit completely away like you could in other languages.

Graham
  • 3,153
  • 3
  • 16
  • 31
  • Is x = 1 << 1000000 equal to x = 2**1000000? – Johnny P. Dec 11 '18 at 13:38
  • @JohnnyP. Yes, you can test it yourself: `2**100000000 == 1 << 100000000` – Graham Dec 11 '18 at 13:39
  • @JohnnyP. Internally, *all* numbers are binary to Python. As demonstrated by the above comparison, the numbers are identical. The only difference is that using a bitshift is faster than individually doing each multiplication by 2 (I'll add a link explaining binary in a second) – Graham Dec 11 '18 at 13:44
  • It runs very fast. – Johnny P. Dec 11 '18 at 13:51
  • @Graham and x**2, how is it with << operator. – Johnny P. Dec 13 '18 at 14:23
  • @JohnnyP. We can understand this by comparing base 10 (our normal number system) to binary (the way computers represent data): In base 10, each place-value represents a power of 10: `1` is 10^0, `10` is 10^1, and `100` is 10^2. In binary, each place-value is a power of 2: `1` is 2^0 (1), `10` is 2^2 (4), and `100` is 2^3 (8). When a value is left-shifted (`<<`), the base binary representation's right side is padded with 0s, which is equivalent to multiplication by 2. This is like how adding 0s in base 10 is multiplication by 10: adding a 0 to `10` result in `100`, i.e. 10 times greater. – Graham Dec 13 '18 at 18:32
1

If x is always a power of 2, and n is always a power of 2, then you can you can compute it easily and quickly using bit operations on a byte array, which you can then reconstitute into a "number".

If 2^N is (binary) 1 followed by N zeroes, then (2^N)^2 is (binary) 1 followed by 2N zeros.

2^3 squared is b'1000000'

If you have a number 2^K (binary 1 followed by K zeroes), then 2^K - 2 will be K-1 1s (ones) followed by a zero.

eg 2^4 is 16 =  b'10000', 2^4 - 2 is b'1110'

If you require "% 2^M" then in binary, you just select the last (lower) M bits, and disregard the rest .

9999 is       b'10011100001111'
9999 % 2^8 is       b'00001111'

'

Hence combining the parts, if x=2^A and n=2^B, then

(x^2 - 2 ) % n

will be: (last B bits of) (binary) (2*A - 1 '1's followed by a '0')

MikeW
  • 5,504
  • 1
  • 34
  • 29
1

Some module rules :

1) (a+b)mod(n) = amod(n)+bmod(N)

2) (a.b)mod(n) = amod(n).bmod(n)

So you can transform your equation into :

(x**2-2)%n ==> (x.x - 2)%n ==> (x%n).(x%n) - (2%n)

If n is always greater than 2, (2%n) is 2 itself.

solving (x%n) :

If x and n are always in 2**value ; if x > n then (x%n)= 0 is the answer and if x < n (x%n)=x

So the answer is either 0-(2%n) or x**2-(2%n)

Vijay Kalmath
  • 178
  • 11
  • These rules are incorrect, at least as written here. For example (3+4) mod 5 = 2, while (3 mod 5) + (4 mod 5) = 7. You need to replace "=" with "≡". Or, equivalently, add one more "mod(n)" at the end. – vog Dec 11 '18 at 14:10
0

If you want to compute (x ** y - z) % n
it will be equivalent to ((x ** y) % n - z) % n

Python pow function includes as optional parameter a modulo, as it is very often used and can be computed in an optimized way. So you should use:

(pow(x, y, n) - z) % n
ch7kor
  • 184
  • 5
0

OP says in comment : it's slow because I assign x to the answer and I repeat the process.

I try this :

x = 2**(1000*1000)
n = 2**(100*1000*1000)

import time
t0=time.time()
for i in range(6):
    x = (x*x-2)%n   
    t1=time.time()
    print(i,t1-t0)
    t0=t1

print(x<n)

"""    
0 0.0
1 0.4962291717529297
2 0.5937404632568359
3 1.9043104648590088
4 5.708504915237427
5 16.74528479576111  
True   
"""

It shows that in this problem, it's just slow because x grows, doubling the number of digit at each loop :

In [5]: %timeit  u=x%n
149 ns ± 6.42 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

the %n takes absolutely no time if x<n.

B. M.
  • 18,243
  • 2
  • 35
  • 54