Răsfoiți Sursa

adoping a more efficient division algorithm for bigint. (#232)

* adoping a more efficient division algorithm for bigint.

* add test case.

* modify the implementation to binary search.

* fix type error.

* fix some error.

* remove extra zero.

* add some test cases.

* add some cases

* Update 09_long.py

* Update 09_long.py

---------

Co-authored-by: BLUELOVETH <blueloveTH@foxmail.com>
ykiko 1 an în urmă
părinte
comite
cb15db1f0e
2 a modificat fișierele cu 45 adăugiri și 6 ștergeri
  1. 31 4
      python/_long.py
  2. 14 2
      tests/09_long.py

+ 31 - 4
python/_long.py

@@ -99,11 +99,38 @@ def ulong_divmodi(a: list, b: int):
     ulong_unpad_(res)
     return res, carry
 
+
 def ulong_divmod(a: list, b: list):
-    q = [0]
-    while ulong_cmp(a, b) >= 0:
-        ulong_inc_(q)
-        a = ulong_sub(a, b)
+
+    if ulong_cmp(a, b) < 0:
+        return [0], a
+
+    if len(b) == 1:
+        q, r = ulong_divmodi(a, b[0])
+        r, _ = ulong_fromint(r)
+        return q, r
+
+    max = (len(a) - len(b)) * PyLong_SHIFT + \
+        (a[-1].bit_length() - b[-1].bit_length())
+
+    low = [0]
+
+    high = (max // PyLong_SHIFT) * [0] + \
+        [(2**(max % PyLong_SHIFT)) & PyLong_MASK]
+
+    while ulong_cmp(low, high) < 0:
+        ulong_inc_(high)
+        mid, r = ulong_divmodi(ulong_add(low, high), 2)
+        if ulong_cmp(a, ulong_mul(b, mid)) >= 0:
+            low = mid
+        else:
+            high = ulong_sub(mid, [1])
+
+    q = [0] * (len(a) - len(b) + 1)
+    while ulong_cmp(a, ulong_mul(b, low)) >= 0:
+        q = ulong_add(q, low)
+        a = ulong_sub(a, ulong_mul(b, low))
+    ulong_unpad_(q)
     return q, a
 
 def ulong_floordivi(a: list, b: int):

+ 14 - 2
tests/09_long.py

@@ -15,7 +15,6 @@ assert 1 + a == 3L
 assert 1 - a == -1L
 assert 2 * a == 4L
 
-
 # __lshift__ and __rshift__
 for i in range(29):
     assert 1L << i == 2 ** i
@@ -35,4 +34,17 @@ assert s == [4, 6, 7, 2, 3]
 
 assert 1 < 2L < 3 < 6.6
 assert 1L < 2 < 9.6 >= 7 > 2L
-assert 1L < 2 < 3 < 6.6
+assert 1L < 2 < 3 < 6.6
+
+assert 10000000000000000000000L // 3333L == 3000300030003000300L
+assert 10000000000000000000000L % 3333L == 100L
+assert 2L ** 100 // 3L ** 50 == 1765780L
+assert 2L ** 200 // 3L ** 100 == 3117982410207L
+assert 2L ** 500 // 3L ** 200 == 12323863745843010927046405923587284941366070573310012484L
+assert 2L ** 500 % 3L ** 200 == 242990057207501525999897657892105676264485903550870122812212566096970021710762636168532352280892L
+
+assert 3L**500 // 3L**400 == 3L**100
+assert 4562645248L // 3L == 1520881749L
+assert 3L**10 // 2L**200 == 0
+assert 2L**500 % 3L**200 == 242990057207501525999897657892105676264485903550870122812212566096970021710762636168532352280892L
+assert divmod(10L**115, 3L**47) == (376098003656605353510839433041026531338835641081336270795298342081499446459650747504311257564L, 10150482013473427429132L)