Skip to content

Commit a5c7d87

Browse files
authored
Merge branch 'master' into ty-invalid-type-form
2 parents 516d37c + 6029a91 commit a5c7d87

2 files changed

Lines changed: 86 additions & 11 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
== Evil Number ==
3+
An evil number is a non-negative integer that has an even number of 1s in its
4+
binary expansion. Numbers that are not evil are called odious numbers.
5+
6+
Examples of evil numbers: 0, 3, 5, 6, 9, 10, 12, 15, 17, 18, 20, 23, 24, 27, ...
7+
Reference: https://en.wikipedia.org/wiki/Evil_number
8+
"""
9+
10+
11+
def is_evil_number(number: int) -> bool:
12+
"""
13+
Check if a given number is an evil number.
14+
15+
A non-negative integer is evil if the sum of its binary digits (its Hamming weight)
16+
is even.
17+
18+
:param number: A non-negative integer.
19+
:return: True if number is an evil number, False otherwise.
20+
21+
>>> is_evil_number(0)
22+
True
23+
>>> is_evil_number(3)
24+
True
25+
>>> is_evil_number(5)
26+
True
27+
>>> is_evil_number(6)
28+
True
29+
>>> is_evil_number(9)
30+
True
31+
>>> is_evil_number(1)
32+
False
33+
>>> is_evil_number(2)
34+
False
35+
>>> is_evil_number(4)
36+
False
37+
>>> is_evil_number(7)
38+
False
39+
>>> is_evil_number(8)
40+
False
41+
>>> is_evil_number(-1)
42+
Traceback (most recent call last):
43+
...
44+
ValueError: Input must be a non-negative integer
45+
>>> is_evil_number(5.5)
46+
Traceback (most recent call last):
47+
...
48+
TypeError: Input must be an integer
49+
"""
50+
if not isinstance(number, int):
51+
raise TypeError("Input must be an integer")
52+
if number < 0:
53+
raise ValueError("Input must be a non-negative integer")
54+
55+
count = 0
56+
temp = number
57+
while temp > 0:
58+
if temp % 2 == 1:
59+
count += 1
60+
temp = temp // 2
61+
return count % 2 == 0
62+
63+
64+
if __name__ == "__main__":
65+
import doctest
66+
67+
doctest.testmod()

‎searches/exponential_search.py‎

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,29 @@ def binary_search_by_recursion(
4747
>>> binary_search_by_recursion([0, 5, 7, 10, 15], 16)
4848
-1
4949
"""
50-
if right is None:
51-
right = len(sorted_collection) - 1
5250
if list(sorted_collection) != sorted(sorted_collection):
5351
raise ValueError("sorted_collection must be sorted in ascending order")
54-
if right < left:
55-
return -1
52+
if right is None:
53+
right = len(sorted_collection) - 1
5654

57-
midpoint = left + (right - left) // 2
55+
# Recursive core: ``left`` and ``right`` are always concrete indices here, so the
56+
# window can only shrink. Keeping the recursion flat (no ``None`` sentinel, no
57+
# re-expansion) is what prevents the runaway recursion when the item sits below
58+
# ``sorted_collection[0]`` and ``right`` legitimately drops below ``left``.
59+
def _search(left: int, right: int) -> int:
60+
if right < left:
61+
return -1
5862

59-
if sorted_collection[midpoint] == item:
60-
return midpoint
61-
elif sorted_collection[midpoint] > item:
62-
return binary_search_by_recursion(sorted_collection, item, left, midpoint - 1)
63-
else:
64-
return binary_search_by_recursion(sorted_collection, item, midpoint + 1, right)
63+
midpoint = left + (right - left) // 2
64+
65+
if sorted_collection[midpoint] == item:
66+
return midpoint
67+
elif sorted_collection[midpoint] > item:
68+
return _search(left, midpoint - 1)
69+
else:
70+
return _search(midpoint + 1, right)
71+
72+
return _search(left, right)
6573

6674

6775
def exponential_search(sorted_collection: list[int], item: int) -> int:

0 commit comments

Comments
 (0)