Repository: TheAlgorithms/Python

Issue:
Remove duplicate Collatz sequence implementation
### Feature description

I think one of [`3n_plus_1.py`](https://github.com/TheAlgorithms/Python/blob/master/maths/3n_plus_1.py) and [`collatz_sequence.py`](https://github.com/TheAlgorithms/Python/blob/master/maths/collatz_sequence.py) should be removed, as they're nearly identical implementations of the same algorithm for computing Collatz sequences.

I'm not sure which of the two files should be deleted. I believe that "the Collatz conjecture" is the more well-known name for the problem, but it seems that [`3n_plus_1.py`](https://github.com/TheAlgorithms/Python/blob/master/maths/3n_plus_1.py) is the older of the two files.


Discussion:
This I didn't see.. V C But I'm having trouble running and recognizing.. ??

Resolving patch:
diff --git a/maths/3n_plus_1.py b/maths/3n_plus_1.py
deleted file mode 100644
--- a/maths/3n_plus_1.py
+++ /dev/null
@@ -1,151 +0,0 @@
-from __future__ import annotations
-
-
-def n31(a: int) -> tuple[list[int], int]:
-    """
-    Returns the Collatz sequence and its length of any positive integer.
-    >>> n31(4)
-    ([4, 2, 1], 3)
-    """
-
-    if not isinstance(a, int):
-        msg = f"Must be int, not {type(a).__name__}"
-        raise TypeError(msg)
-    if a < 1:
-        msg = f"Given integer must be positive, not {a}"
-        raise ValueError(msg)
-
-    path = [a]
-    while a != 1:
-        if a % 2 == 0:
-            a //= 2
-        else:
-            a = 3 * a + 1
-        path.append(a)
-    return path, len(path)
-
-
-def test_n31():
-    """
-    >>> test_n31()
-    """
-    assert n31(4) == ([4, 2, 1], 3)
-    assert n31(11) == ([11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1], 15)
-    assert n31(31) == (
-        [
-            31,
-            94,
-            47,
-            142,
-            71,
-            214,
-            107,
-            322,
-            161,
-            484,
-            242,
-            121,
-            364,
-            182,
-            91,
-            274,
-            137,
-            412,
-            206,
-            103,
-            310,
-            155,
-            466,
-            233,
-            700,
-            350,
-            175,
-            526,
-            263,
-            790,
-            395,
-            1186,
-            593,
-            1780,
-            890,
-            445,
-            1336,
-            668,
-            334,
-            167,
-            502,
-            251,
-            754,
-            377,
-            1132,
-            566,
-            283,
-            850,
-            425,
-            1276,
-            638,
-            319,
-            958,
-            479,
-            1438,
-            719,
-            2158,
-            1079,
-            3238,
-            1619,
-            4858,
-            2429,
-            7288,
-            3644,
-            1822,
-            911,
-            2734,
-            1367,
-            4102,
-            2051,
-            6154,
-            3077,
-            9232,
-            4616,
-            2308,
-            1154,
-            577,
-            1732,
-            866,
-            433,
-            1300,
-            650,
-            325,
-            976,
-            488,
-            244,
-            122,
-            61,
-            184,
-            92,
-            46,
-            23,
-            70,
-            35,
-            106,
-            53,
-            160,
-            80,
-            40,
-            20,
-            10,
-            5,
-            16,
-            8,
-            4,
-            2,
-            1,
-        ],
-        107,
-    )
-
-
-if __name__ == "__main__":
-    num = 4
-    path, length = n31(num)
-    print(f"The Collatz sequence of {num} took {length} steps. \nPath: {path}")
diff --git a/maths/collatz_sequence.py b/maths/collatz_sequence.py
--- a/maths/collatz_sequence.py
+++ b/maths/collatz_sequence.py
@@ -1,43 +1,66 @@
+"""
+The Collatz conjecture is a famous unsolved problem in mathematics. Given a starting
+positive integer, define the following sequence:
+- If the current term n is even, then the next term is n/2.
+- If the current term n is odd, then the next term is 3n + 1.
+The conjecture claims that this sequence will always reach 1 for any starting number.
+
+Other names for this problem include the 3n + 1 problem, the Ulam conjecture, Kakutani's
+problem, the Thwaites conjecture, Hasse's algorithm, the Syracuse problem, and the
+hailstone sequence.
+
+Reference: https://en.wikipedia.org/wiki/Collatz_conjecture
+"""
+
 from __future__ import annotations
 
+from collections.abc import Generator
 
-def collatz_sequence(n: int) -> list[int]:
+
+def collatz_sequence(n: int) -> Generator[int, None, None]:
     """
-    Collatz conjecture: start with any positive integer n. The next term is
-    obtained as follows:
-        If n term is even, the next term is: n / 2 .
-        If n is odd, the next term is: 3 * n + 1.
-
-    The conjecture states the sequence will always reach 1 for any starting value n.
-    Example:
-    >>> collatz_sequence(2.1)
+    Generate the Collatz sequence starting at n.
+    >>> tuple(collatz_sequence(2.1))
     Traceback (most recent call last):
         ...
-    Exception: Sequence only defined for natural numbers
-    >>> collatz_sequence(0)
+    Exception: Sequence only defined for positive integers
+    >>> tuple(collatz_sequence(0))
     Traceback (most recent call last):
         ...
-    Exception: Sequence only defined for natural numbers
-    >>> collatz_sequence(43)  # doctest: +NORMALIZE_WHITESPACE
-    [43, 130, 65, 196, 98, 49, 148, 74, 37, 112, 56, 28, 14, 7,
-     22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
+    Exception: Sequence only defined for positive integers
+    >>> tuple(collatz_sequence(4))
+    (4, 2, 1)
+    >>> tuple(collatz_sequence(11))
+    (11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1)
+    >>> tuple(collatz_sequence(31))     # doctest: +NORMALIZE_WHITESPACE
+    (31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274, 137,
+    412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593,
+    1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425,
+    1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644,
+    1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732,
+    866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53,
+    160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1)
+    >>> tuple(collatz_sequence(43))     # doctest: +NORMALIZE_WHITESPACE
+    (43, 130, 65, 196, 98, 49, 148, 74, 37, 112, 56, 28, 14, 7, 22, 11, 34, 17, 52, 26,
+    13, 40, 20, 10, 5, 16, 8, 4, 2, 1)
     """
-
     if not isinstance(n, int) or n < 1:
-        raise Exception("Sequence only defined for natural numbers")
+        raise Exception("Sequence only defined for positive integers")
 
-    sequence = [n]
+    yield n
     while n != 1:
-        n = 3 * n + 1 if n & 1 else n // 2
-        sequence.append(n)
-    return sequence
+        if n % 2 == 0:
+            n //= 2
+        else:
+            n = 3 * n + 1
+        yield n
 
 
 def main():
     n = 43
-    sequence = collatz_sequence(n)
+    sequence = tuple(collatz_sequence(n))
     print(sequence)
-    print(f"collatz sequence from {n} took {len(sequence)} steps.")
+    print(f"Collatz sequence from {n} took {len(sequence)} steps.")
 
 
 if __name__ == "__main__":


Test patch: