Некоторые способы могут быть:
def mod_c0(a, b):
if b < 0:
b = -b
return -1 * (-a % b) if a < 0 else a % b
def mod_c1(a, b):
return (-1 if a < 0 else 1) * ((a if a > 0 else -a) % (b if b > 0 else -b))
def mod_c2(a, b):
return (-1 if a < 0 else 1) * (abs(a) % abs(b))
def mod_c3(a, b):
r = a % b
return (r - b) if (a < 0) != (b < 0) and r != 0 else r
def mod_c4(a, b):
r = a % b
return (r - b) if (a * b < 0) and r != 0 else r
def mod_c5(a, b):
return a % (-b if a ^ b < 0 else b)
def mod_c6(a, b):
a_xor_b = a ^ b
n = a_xor_b.bit_length()
x = a_xor_b >> n
return a % (b * (x | 1))
def mod_c7(a, b):
a_xor_b = a ^ b
n = a_xor_b.bit_length()
x = a_xor_b >> n
return a % ((-b & x) | (b & ~x))
def mod_c8(a, b):
q, r = divmod(a, b)
if (a >= 0) != (b >= 0) and r:
q += 1
return a - q * b
def mod_c9(a, b):
if a >= 0:
if b >= 0:
return a % b
else:
return a % -b
else:
if b >= 0:
return -(-a % b)
else:
return a % b
, которые все работают как положено, Например:
print(mod_c0(31, -3))
# 1
По существу, mod_c0()
реализует оптимизированную версию mod_c1()
и mod_c2()
, которые идентичны, за исключением того, что в mod_c1()
вызов (относительно дорогой) вызов abs()
заменяется троичным условным оператором с тем же семантием c. Вместо этого mod_c3()
и mod_c4()
пытаются напрямую исправить значение a % b
для случаев, когда это необходимо. Разница между ними заключается в том, как они обнаруживают противоположные знаки аргументов: (a < 0) != (b != 0)
против a * b < 0
. Подход mod_c5()
основан на ответе @ ArborealAnole и по существу использует побитовый xor для правильной обработки случаев, тогда как mod_c6()
и mod_c7()
совпадают с @ ответом ArborealAnole , но с использованием адаптивного правого сдвига с int.bit_length()
. Подход mod_c8()
использует исправленное определение целочисленного деления, чтобы зафиксировать значение модуля. Метод mod_c9()
основан на ответе @ NeverGoodEnough и по существу становится полностью условным.
Охватывает все знаковые случаи:
vals = (3, -3, 31, -31)
s = '{:<{n}}' * 4
n = 14
print(s.format('a', 'b', 'mod(a, b)', 'mod_c(a, b)', n=n))
print(s.format(*(('-' * (n - 1),) * 4), n=n))
for a, b in itertools.product(vals, repeat=2):
print(s.format(a, b, mod(a, b), mod_c0(a, b), n=n))
a b mod(a, b) mod_c(a, b)
------------- ------------- ------------- -------------
3 3 0 0
3 -3 0 0
3 31 3 3
3 -31 -28 3
-3 3 0 0
-3 -3 0 0
-3 31 28 -3
-3 -31 -3 -3
31 3 1 1
31 -3 -2 1
31 31 0 0
31 -31 0 0
-31 3 2 -1
-31 -3 -1 -1
-31 31 0 0
-31 -31 0 0
Немного больше тестов и тестов:
n = 100
k = 1
l = [x for x in range(-n, n + k, k)]
ll = [(a, b) for a, b in itertools.product(l, repeat=2) if b]
funcs = mod_c0, mod_c1, mod_c2, mod_c3, mod_c4, mod_c5, mod_c6, mod_c7, mod_c8, mod_c9
for func in funcs:
correct = all(func(a, b) == funcs[0](a, b) for a, b in ll)
print(func.__name__, 'correct:', all_equal)
%timeit [func(a, b) for a, b in ll]
print()
100 loops, best of 3: 6.6 ms per loop
mod_c1 correct: True
100 loops, best of 3: 7.86 ms per loop
mod_c2 correct: True
100 loops, best of 3: 8.49 ms per loop
mod_c3 correct: True
100 loops, best of 3: 7.56 ms per loop
mod_c4 correct: True
100 loops, best of 3: 7.5 ms per loop
mod_c5 correct: True
100 loops, best of 3: 7.94 ms per loop
mod_c6 correct: True
100 loops, best of 3: 13.4 ms per loop
mod_c7 correct: True
100 loops, best of 3: 16.8 ms per loop
mod_c8 correct: True
100 loops, best of 3: 12.4 ms per loop
mod_c9 correct: True
100 loops, best of 3: 6.48 ms per loop
Возможно, есть лучшие (более короткие ?, более быстрые?) Способы, учитывая, что реализация Python '* %
использование C %
выглядит намного проще:
((a % b) + b) % b
Чтобы получить представление о том, как вычисление C в стиле %
(mod_c*()
действует сверху) выступает против обычного %
или операций, необходимых для получения Python -стиля %
из C
:
def mod_py(a, b):
return a % b
def mod_c2py(a, b):
return ((a % b) + b) % b
%timeit [mod_py(a, b) for a, b in ll]
# 100 loops, best of 3: 5.85 ms per loop
%timeit [mod_c2py(a, b) for a, b in ll]
# 100 loops, best of 3: 7.84 ms per loop
Обратите внимание, конечно, что mod_c2py()
полезен только для того, чтобы получить ощущение какие характеристики мы можем ожидать от функции mod_c()
.
( EDITED для исправления некоторых из предложенных методов и включения некоторых моментов времени)
( EDITED-2 для добавления mod_c5()
решения)
( EDITED-3 для добавления mod_c6()
к mod_c9()
решениям)