mirror of
https://github.com/TheAlgorithms/Python.git
synced 2026-03-13 09:50:19 +08:00
* Add narcissistic number finder with dynamic programming * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update dynamic_programming/narcissistic_number.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dynamic_programming/narcissistic_number.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dynamic_programming/narcissistic_number.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
104 lines
3.5 KiB
Python
104 lines
3.5 KiB
Python
"""
|
|
Find all narcissistic numbers up to a given limit using dynamic programming.
|
|
|
|
A narcissistic number (also known as an Armstrong number or plus perfect number)
|
|
is a number that is the sum of its own digits each raised to the power of the
|
|
number of digits.
|
|
|
|
For example, 153 is a narcissistic number because 153 = 1^3 + 5^3 + 3^3.
|
|
|
|
This implementation uses dynamic programming with memoization to efficiently
|
|
compute digit powers and find all narcissistic numbers up to a specified limit.
|
|
|
|
The DP optimization caches digit^power calculations. When searching through many
|
|
numbers, the same digit power calculations occur repeatedly (e.g., 153, 351, 135
|
|
all need 1^3, 5^3, 3^3). Memoization avoids these redundant calculations.
|
|
|
|
Examples of narcissistic numbers:
|
|
Single digit: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
Three digit: 153, 370, 371, 407
|
|
Four digit: 1634, 8208, 9474
|
|
Five digit: 54748, 92727, 93084
|
|
|
|
Reference: https://en.wikipedia.org/wiki/Narcissistic_number
|
|
"""
|
|
|
|
|
|
def find_narcissistic_numbers(limit: int) -> list[int]:
|
|
"""
|
|
Find all narcissistic numbers up to the given limit using dynamic programming.
|
|
|
|
This function uses memoization to cache digit power calculations, avoiding
|
|
redundant computations across different numbers with the same digit count.
|
|
|
|
Args:
|
|
limit: The upper bound for searching narcissistic numbers (exclusive)
|
|
|
|
Returns:
|
|
list[int]: A sorted list of all narcissistic numbers below the limit
|
|
|
|
Examples:
|
|
>>> find_narcissistic_numbers(10)
|
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
>>> find_narcissistic_numbers(160)
|
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153]
|
|
>>> find_narcissistic_numbers(400)
|
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371]
|
|
>>> find_narcissistic_numbers(1000)
|
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407]
|
|
>>> find_narcissistic_numbers(10000)
|
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407, 1634, 8208, 9474]
|
|
>>> find_narcissistic_numbers(1)
|
|
[0]
|
|
>>> find_narcissistic_numbers(0)
|
|
[]
|
|
"""
|
|
if limit <= 0:
|
|
return []
|
|
|
|
narcissistic_nums = []
|
|
|
|
# Memoization: cache[(power, digit)] = digit^power
|
|
# This avoids recalculating the same power for different numbers
|
|
power_cache: dict[tuple[int, int], int] = {}
|
|
|
|
def get_digit_power(digit: int, power: int) -> int:
|
|
"""Get digit^power using memoization (DP optimization)."""
|
|
if (power, digit) not in power_cache:
|
|
power_cache[(power, digit)] = digit**power
|
|
return power_cache[(power, digit)]
|
|
|
|
# Check each number up to the limit
|
|
for number in range(limit):
|
|
# Count digits
|
|
num_digits = len(str(number))
|
|
|
|
# Calculate sum of powered digits using memoized powers
|
|
remaining = number
|
|
digit_sum = 0
|
|
while remaining > 0:
|
|
digit = remaining % 10
|
|
digit_sum += get_digit_power(digit, num_digits)
|
|
remaining //= 10
|
|
|
|
# Check if narcissistic
|
|
if digit_sum == number:
|
|
narcissistic_nums.append(number)
|
|
|
|
return narcissistic_nums
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import doctest
|
|
|
|
doctest.testmod()
|
|
|
|
# Demonstrate the dynamic programming approach
|
|
print("Finding all narcissistic numbers up to 10000:")
|
|
print("(Using memoization to cache digit power calculations)")
|
|
print()
|
|
|
|
narcissistic_numbers = find_narcissistic_numbers(10000)
|
|
print(f"Found {len(narcissistic_numbers)} narcissistic numbers:")
|
|
print(narcissistic_numbers)
|