-
-
Notifications
You must be signed in to change notification settings - Fork 49.6k
Add narcissistic number finder with dynamic programming #13971
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,107 @@ | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
| 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[num_digits][digit] = digit^num_digits | ||||||||||||||||||||||||||
| # 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): | ||||||||||||||||||||||||||
| if number == 0: | ||||||||||||||||||||||||||
| narcissistic_nums.append(0) | ||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||
|
Comment on lines
+73
to
+75
|
||||||||||||||||||||||||||
| if number == 0: | |
| narcissistic_nums.append(0) | |
| continue |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Converting the number to a string just to count digits is inefficient. Consider using math.log10 or iteratively dividing by 10 to count digits, which would be more efficient and avoid the string conversion overhead.
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable name 'temp' is unclear. Consider a more descriptive name like 'remaining' or 'remaining_digits' to better convey that it represents the remaining portion of the number being processed.
| temp = number | |
| digit_sum = 0 | |
| while temp > 0: | |
| digit = temp % 10 | |
| digit_sum += get_digit_power(digit, num_digits) | |
| temp //= 10 | |
| remaining = number | |
| digit_sum = 0 | |
| while remaining > 0: | |
| digit = remaining % 10 | |
| digit_sum += get_digit_power(digit, num_digits) | |
| remaining //= 10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment describes the cache structure as "cache[num_digits][digit]" which suggests a nested dictionary structure, but the actual implementation uses a flat dictionary with tuple keys: power_cache[(power, digit)]. The comment should accurately reflect the implementation, such as "cache[(power, digit)] = digit^power".