|
| 1 | +""" |
| 2 | +Kaprekar's Constant (6174) - also known as Kaprekar's Routine |
| 3 | +
|
| 4 | +Kaprekar's constant is a special number discovered by Indian mathematician |
| 5 | +D. R. Kaprekar in 1949. It is notable for the following property: |
| 6 | +
|
| 7 | +1. Take any four-digit number with at least two different digits (leading zeros allowed) |
| 8 | +2. Arrange the digits in descending order to form the largest possible number |
| 9 | +3. Arrange the digits in ascending order to form the smallest possible number |
| 10 | +4. Subtract the smaller number from the larger number |
| 11 | +5. Repeat the process with the result |
| 12 | +
|
| 13 | +This process will always reach 6174 in at most 7 iterations, and once 6174 is |
| 14 | +reached, the process will continue to yield 6174. |
| 15 | +
|
| 16 | +Example: |
| 17 | + 3524 -> 5432 - 2345 = 3087 |
| 18 | + 3087 -> 8730 - 0378 = 8352 |
| 19 | + 8352 -> 8532 - 2358 = 6174 |
| 20 | + 6174 -> 7641 - 1467 = 6174 (repeats) |
| 21 | +
|
| 22 | +Reference: https://en.wikipedia.org/wiki/6174_(number) |
| 23 | +OEIS: https://oeis.org/A099009 |
| 24 | +""" |
| 25 | + |
| 26 | + |
| 27 | +def kaprekar_routine(number: int) -> int: |
| 28 | + """ |
| 29 | + Perform one iteration of Kaprekar's routine. |
| 30 | +
|
| 31 | + Args: |
| 32 | + number: A 4-digit number (0-9999) |
| 33 | +
|
| 34 | + Returns: |
| 35 | + The result of one Kaprekar iteration |
| 36 | +
|
| 37 | + >>> kaprekar_routine(3524) |
| 38 | + 3087 |
| 39 | + >>> kaprekar_routine(3087) |
| 40 | + 8352 |
| 41 | + >>> kaprekar_routine(8352) |
| 42 | + 6174 |
| 43 | + >>> kaprekar_routine(6174) |
| 44 | + 6174 |
| 45 | + >>> kaprekar_routine(1) |
| 46 | + 999 |
| 47 | + >>> kaprekar_routine(1111) |
| 48 | + 0 |
| 49 | + """ |
| 50 | + if not isinstance(number, int) or number < 0 or number > 9999: |
| 51 | + msg = f"Input must be a non-negative integer between 0 and 9999, got {number}" |
| 52 | + raise ValueError(msg) |
| 53 | + |
| 54 | + # Convert to 4-digit string with leading zeros |
| 55 | + digits = str(number).zfill(4) |
| 56 | + |
| 57 | + # Check if all digits are the same (repdigit) |
| 58 | + if len(set(digits)) == 1: |
| 59 | + return 0 |
| 60 | + |
| 61 | + # Sort digits in descending and ascending order |
| 62 | + descending = int("".join(sorted(digits, reverse=True))) |
| 63 | + ascending = int("".join(sorted(digits))) |
| 64 | + |
| 65 | + return descending - ascending |
| 66 | + |
| 67 | + |
| 68 | +def kaprekar_constant(number: int, max_iterations: int = 7) -> tuple[int, list[int]]: |
| 69 | + """ |
| 70 | + Apply Kaprekar's routine until reaching the constant 6174 or max iterations. |
| 71 | +
|
| 72 | + Args: |
| 73 | + number: A 4-digit number (0-9999) |
| 74 | + max_iterations: Maximum number of iterations to perform (default: 7) |
| 75 | +
|
| 76 | + Returns: |
| 77 | + A tuple containing: |
| 78 | + - The number of iterations taken to reach 6174 (or -1 if not reached) |
| 79 | + - A list of all intermediate results |
| 80 | +
|
| 81 | + >>> kaprekar_constant(3524) |
| 82 | + (3, [3524, 3087, 8352, 6174]) |
| 83 | + >>> kaprekar_constant(6174) |
| 84 | + (0, [6174]) |
| 85 | + >>> kaprekar_constant(1234) |
| 86 | + (3, [1234, 3087, 8352, 6174]) |
| 87 | + >>> kaprekar_constant(1111) |
| 88 | + (-1, [1111, 0]) |
| 89 | + >>> kaprekar_constant(495) |
| 90 | + (4, [495, 9081, 9621, 8352, 6174]) |
| 91 | + >>> kaprekar_constant(9998) |
| 92 | + (5, [9998, 999, 8991, 8082, 8532, 6174]) |
| 93 | + """ |
| 94 | + if not isinstance(number, int) or number < 0 or number > 9999: |
| 95 | + msg = f"Input must be a non-negative integer between 0 and 9999, got {number}" |
| 96 | + raise ValueError(msg) |
| 97 | + |
| 98 | + if not isinstance(max_iterations, int) or max_iterations < 1: |
| 99 | + msg = f"max_iterations must be a positive integer, got {max_iterations}" |
| 100 | + raise ValueError(msg) |
| 101 | + |
| 102 | + sequence = [number] |
| 103 | + current = number |
| 104 | + |
| 105 | + # If already at Kaprekar's constant |
| 106 | + if current == 6174: |
| 107 | + return (0, sequence) |
| 108 | + |
| 109 | + for iteration in range(max_iterations): |
| 110 | + current = kaprekar_routine(current) |
| 111 | + sequence.append(current) |
| 112 | + |
| 113 | + # Check if we've reached the constant |
| 114 | + if current == 6174: |
| 115 | + return (iteration + 1, sequence) |
| 116 | + |
| 117 | + # Check if we've reached a repdigit (all same digits) which gives 0 |
| 118 | + if current == 0: |
| 119 | + return (-1, sequence) |
| 120 | + |
| 121 | + # Did not reach 6174 within max_iterations |
| 122 | + return (-1, sequence) |
| 123 | + |
| 124 | + |
| 125 | +def is_kaprekar_valid(number: int) -> bool: |
| 126 | + """ |
| 127 | + Check if a number is valid for Kaprekar's routine. |
| 128 | + A number is valid if it has at least two different digits. |
| 129 | +
|
| 130 | + Args: |
| 131 | + number: A 4-digit number (0-9999) |
| 132 | +
|
| 133 | + Returns: |
| 134 | + True if the number is valid for Kaprekar's routine, False otherwise |
| 135 | +
|
| 136 | + >>> is_kaprekar_valid(3524) |
| 137 | + True |
| 138 | + >>> is_kaprekar_valid(1111) |
| 139 | + False |
| 140 | + >>> is_kaprekar_valid(1000) |
| 141 | + True |
| 142 | + >>> is_kaprekar_valid(1) |
| 143 | + True |
| 144 | + >>> is_kaprekar_valid(6174) |
| 145 | + True |
| 146 | + """ |
| 147 | + if not isinstance(number, int) or number < 0 or number > 9999: |
| 148 | + msg = f"Input must be a non-negative integer between 0 and 9999, got {number}" |
| 149 | + raise ValueError(msg) |
| 150 | + |
| 151 | + # Convert to 4-digit string with leading zeros |
| 152 | + digits = str(number).zfill(4) |
| 153 | + |
| 154 | + # Check if at least two different digits exist |
| 155 | + return len(set(digits)) > 1 |
| 156 | + |
| 157 | + |
| 158 | +def main() -> None: |
| 159 | + """ |
| 160 | + Demonstrate Kaprekar's constant with user input and examples. |
| 161 | + """ |
| 162 | + print("Kaprekar's Constant (6174) - Demonstration\n") |
| 163 | + print("=" * 50) |
| 164 | + |
| 165 | + # Example demonstrations |
| 166 | + examples = [3524, 1234, 6174, 9998, 1111, 5] |
| 167 | + |
| 168 | + for num in examples: |
| 169 | + print(f"\nStarting number: {num:04d}") |
| 170 | + if not is_kaprekar_valid(num): |
| 171 | + print(" ❌ Invalid: All digits are the same (repdigit)") |
| 172 | + continue |
| 173 | + |
| 174 | + iterations, sequence = kaprekar_constant(num) |
| 175 | + if iterations == -1: |
| 176 | + print(f" ❌ Did not reach 6174. Sequence: {sequence}") |
| 177 | + elif iterations == 0: |
| 178 | + print(" ✓ Already at Kaprekar's constant!") |
| 179 | + else: |
| 180 | + print(f" ✓ Reached 6174 in {iterations} iteration(s)") |
| 181 | + print(f" Sequence: {' -> '.join(str(n) for n in sequence)}") |
| 182 | + |
| 183 | + # Interactive mode |
| 184 | + print("\n" + "=" * 50) |
| 185 | + print("\nTry your own number (0-9999):") |
| 186 | + try: |
| 187 | + user_input = int(input("Enter a 4-digit number: ")) |
| 188 | + if 0 <= user_input <= 9999: |
| 189 | + if not is_kaprekar_valid(user_input): |
| 190 | + print("Invalid: All digits are the same!") |
| 191 | + else: |
| 192 | + iterations, sequence = kaprekar_constant(user_input) |
| 193 | + print(f"\nSequence: {' -> '.join(str(n) for n in sequence)}") |
| 194 | + if iterations != -1: |
| 195 | + print(f"Reached 6174 in {iterations} iteration(s)!") |
| 196 | + else: |
| 197 | + print("Number must be between 0 and 9999") |
| 198 | + except (ValueError, EOFError): |
| 199 | + print("Invalid input or no input provided") |
| 200 | + |
| 201 | + |
| 202 | +if __name__ == "__main__": |
| 203 | + import doctest |
| 204 | + |
| 205 | + doctest.testmod() |
| 206 | + main() |
0 commit comments