From 60aee30548f427916274a7f99a6c7d342d991fdd Mon Sep 17 00:00:00 2001 From: ykiko Date: Thu, 28 Mar 2024 16:54:47 +0800 Subject: [PATCH] adoping a more efficient division algorithm for bigint. --- python/_long.py | 76 ++++++++++++++++++++++++++++++++++++++++++++---- tests/09_long.py | 2 +- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/python/_long.py b/python/_long.py index e2ff938f..eac349c0 100644 --- a/python/_long.py +++ b/python/_long.py @@ -99,12 +99,78 @@ def ulong_divmodi(a: list, b: int): ulong_unpad_(res) return res, carry +def ulong_lshift_(a: list, shift: int): + if shift == 0: + return + bits = shift % PyLong_SHIFT + carry = 0 + for i in range(len(a) - 1, -1, -1): + new_carry = (a[i] >> (PyLong_SHIFT - bits)) & PyLong_MASK + a[i] = (a[i] << bits) & PyLong_MASK | carry + carry = new_carry + if carry != 0: + a.insert(0, carry) + +def ulong_rshift_(a: list, shift: int): + if shift == 0: + return + bits = shift % PyLong_SHIFT + carry = 0 + for i in range(len(a)): + new_carry = (a[i] << (PyLong_SHIFT - bits)) & PyLong_MASK + a[i] = (a[i] >> bits) | carry + carry = new_carry + if carry != 0: + a.pop(0) + def ulong_divmod(a: list, b: list): - q = [0] - while ulong_cmp(a, b) >= 0: - ulong_inc_(q) - a = ulong_sub(a, b) - return q, a + + if ulong_cmp(a, b) < 0: + return [0], a + + if len(b) == 1: + quotient = [0] * len(a) + remainder = 0 + for i in range(len(a)-1, -1, -1): + temp = (remainder << PyLong_SHIFT) + a[i] + quotient[i] = temp // b[0] + remainder = temp % b[0] + return quotient, [remainder] + + shift = PyLong_SHIFT - (b[-1].bit_length() - 1) + a = a.copy() + b = b.copy() + ulong_lshift_(a, shift) + ulong_lshift_(b, shift) + + quotient = [0] * (len(a) - len(b) + 1) + + for j in range(len(a)-len(b), -1, -1): + temp = ((a[j+len(b)] << PyLong_SHIFT) + a[j+len(b)-1]) // b[-1] + temp = min(temp, PyLong_MASK) + while temp*b[-1] > ((a[j+len(b)] << PyLong_SHIFT) + a[j+len(b)-1] - temp*a[j+len(b)-2] + (b[-2] if len(b) > 1 else 0)): + temp -= 1 + + carry = 0 + for i in range(len(b)): + carry += a[i+j] - temp*b[i] + a[i+j] = carry & PyLong_MASK + carry >>= PyLong_SHIFT + a[j+len(b)] += carry + + if a[j+len(b)] < 0: + carry = 0 + for i in range(len(b)): + carry += a[i+j] + b[i] + a[i+j] = carry & PyLong_MASK + carry >>= PyLong_SHIFT + a[j+len(b)] += carry + else: + quotient[j] = temp + + ulong_unpad_(a) + ulong_rshift_(a, shift) + return quotient, a def ulong_floordivi(a: list, b: int): # b > 0 diff --git a/tests/09_long.py b/tests/09_long.py index 412a2d38..28d383b4 100644 --- a/tests/09_long.py +++ b/tests/09_long.py @@ -14,7 +14,7 @@ assert -a == -2 assert 1 + a == 3L assert 1 - a == -1L assert 2 * a == 4L - +assert 2L**500 // 3333L # __lshift__ and __rshift__ for i in range(29):