简体   繁体   中英

Three argument pow for arrays

pow accepts a third argument for modulo pow(x, y, z) that is more efficient computation than x ** y % z . How can you do that with arrays? What I've tried:

>>> import numpy as np
>>> A = np.array(range(10))
>>> pow(A, 23, 13)
TypeError: unsupported operand type(s) for pow(): 'numpy.ndarray', 'int', 'int'

Though ndarray implements __pow__ , invoking directly doesn't do anything:

>>> A.__pow__(23, 13)
NotImplemented

Using the exponentiation and modulo in two step gives incorrect results (guess it is overflowing the dtype)

>>> print(*(A ** 23 % 13))  # wrong result!
0 1 7 9 10 8 11 12 0 6
>>> print(*[pow(int(a), 23, 13) for a in A])  # correct result
0 1 7 9 10 8 11 2 5 3

The actual array is large so I can not use dtype "object" nor have the looping directly in Python.

How to compute 3-arg pow for numpy arrays?

Find the greatest n such that 2^n is not greater than your exponent. Then calculate A^{2^n} by repeatedly squaring and taking modulus for n steps. Then multiply this matrix with the matrix you obtain by recursively calling this same algorithm for (your exponent - 2^n) .

I know this makes many calls but since the most operations are running in Numpy, it is probably fast. You can record the matrices A^{2^n} (mod k) to make it even faster. Observe that the number of recursive calls is at most log_2(exponent)+1 .

If you did not find a better answer try the idea suggested in here . The problem with your original solution is overflow of np.int64/np.float64 in numpy unlike int in python (which expands as much as your available memory)

((A ** 13 % 13) * (A ** 10 % 13)) % 13

output:

array([ 0,  1,  7,  9, 10,  8, 11,  2,  5,  3])

If your array overflows, you would need to break down power to smaller elements so that it does not over flow. For example, you can break into smaller pieces like this:

n = 1
B = A % 13
while 2*n < 23:
    B = B * B % 13
    n *= 2
B = B * (A ** (23-n) % 13) % 13
B

You can use map() and lambda to accomplish this. You can then consume the results one at a time. Try:

result_iter = map(lambda x: pow(int(x), 23, 13), A))

results = list(result_iter)
print(results)

Output:

[0, 1, 7, 9, 10, 8, 11, 2, 5, 3]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM