Python program to find Armstrong numbers in a certain rangeCalculation of the distance to the next space stationNecklace counting problem-with consecutive prime constraintFinding the smallest number whose digits sum to NArmstrong numbers in CProject Euler #43Find good numbers in a given rangeCodeFights Quora botSudoku-lite challengeFind “prime polynomials” for a user's given prime, bounds, and degreePython program for “0-1 knapsack problem”

Why didn't Voldemort recognize that Dumbledore was affected by his curse?

Need feedback - Can the composition/colors of this design be fixed if something is lacking or is not a better fit?

SQL counting distinct over partition

Certain search in list

What speaks against investing in precious metals?

Colloquialism for “see you later”

Determining the price of an option when it hasn't been traded recently

How can I get an unreasonable manager to approve time off?

Does Disney no longer produce hand-drawn cartoon films?

Shell script returning "Running: command not found". Not sure why

Pathfinder warbow concept review

When would it be advantageous not apply Training Ground's cost reduction?

Mathematically, why does mass matrix / load vector lumping work?

What is the purpose of the goat for Azazel, as opposed to conventional offerings?

Thread Pool C++ Implementation

Alternate way of computing the probability of being dealt a 13 card hand with 3 kings given that you have been dealt 2 kings

Is it possible to have the age of the universe be unknown?

Compiling C files on Ubuntu and using the executable on Windows

Is an entry level DSLR going to shoot nice portrait pictures?

Playing a Character as Unobtrusive and Subservient, Yet Not Passive

Why can my keyboard only digest 6 keypresses at a time?

Someone whose aspirations exceed abilities or means

Group Integers by Originality

How come the nude protesters were not arrested?



Python program to find Armstrong numbers in a certain range


Calculation of the distance to the next space stationNecklace counting problem-with consecutive prime constraintFinding the smallest number whose digits sum to NArmstrong numbers in CProject Euler #43Find good numbers in a given rangeCodeFights Quora botSudoku-lite challengeFind “prime polynomials” for a user's given prime, bounds, and degreePython program for “0-1 knapsack problem”






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








8












$begingroup$


I have written a Python program to check for Armstrong numbers in a certain range.



An Armstrong number of three digits is an integer such that the sum of the cubes of its digits is equal to the number itself. For example, 371 is an Armstrong number since 3**3 + 7**3 + 1**3 = 371.



Here is my code:



lower = int(input("Enter lower range: "))
upper = int(input("Enter upper range: "))

for num in range(lower, upper + 1):

order = len(str(num))

sum = 0

temp = num
while temp > 0:
digit = temp % 10
sum += digit ** order
temp //= 10

if num == sum:
print(num)


Here is an example output:



Enter lower range: 200
Enter upper range: 5000

370
371
407
1634


So, I would like to know whether I could make this program shorter and more efficient.



Any help would be highly appreciated.










share|improve this question











$endgroup$











  • $begingroup$
    "sum of the cubes of its digits"? The power should be equal to the number of digits.
    $endgroup$
    – Sedsarq
    May 22 at 15:36










  • $begingroup$
    @Sedsarq - taken from - pages.mtu.edu/~shene/COURSES/cs201/NOTES/chap04/arms.html
    $endgroup$
    – Justin
    May 22 at 15:38










  • $begingroup$
    @Sedsarq - Yes, you are also right - the power should be equal to the number of digits.
    $endgroup$
    – Justin
    May 22 at 15:42










  • $begingroup$
    @vurmux - I thought the performance tag was necessary as a larger range takes more time to print the output and therefore I needed the code to be as efficient as possible.
    $endgroup$
    – Justin
    May 22 at 15:46






  • 1




    $begingroup$
    You're right, so I rolled back the removal.
    $endgroup$
    – Mast
    May 22 at 15:49

















8












$begingroup$


I have written a Python program to check for Armstrong numbers in a certain range.



An Armstrong number of three digits is an integer such that the sum of the cubes of its digits is equal to the number itself. For example, 371 is an Armstrong number since 3**3 + 7**3 + 1**3 = 371.



Here is my code:



lower = int(input("Enter lower range: "))
upper = int(input("Enter upper range: "))

for num in range(lower, upper + 1):

order = len(str(num))

sum = 0

temp = num
while temp > 0:
digit = temp % 10
sum += digit ** order
temp //= 10

if num == sum:
print(num)


Here is an example output:



Enter lower range: 200
Enter upper range: 5000

370
371
407
1634


So, I would like to know whether I could make this program shorter and more efficient.



Any help would be highly appreciated.










share|improve this question











$endgroup$











  • $begingroup$
    "sum of the cubes of its digits"? The power should be equal to the number of digits.
    $endgroup$
    – Sedsarq
    May 22 at 15:36










  • $begingroup$
    @Sedsarq - taken from - pages.mtu.edu/~shene/COURSES/cs201/NOTES/chap04/arms.html
    $endgroup$
    – Justin
    May 22 at 15:38










  • $begingroup$
    @Sedsarq - Yes, you are also right - the power should be equal to the number of digits.
    $endgroup$
    – Justin
    May 22 at 15:42










  • $begingroup$
    @vurmux - I thought the performance tag was necessary as a larger range takes more time to print the output and therefore I needed the code to be as efficient as possible.
    $endgroup$
    – Justin
    May 22 at 15:46






  • 1




    $begingroup$
    You're right, so I rolled back the removal.
    $endgroup$
    – Mast
    May 22 at 15:49













8












8








8


1



$begingroup$


I have written a Python program to check for Armstrong numbers in a certain range.



An Armstrong number of three digits is an integer such that the sum of the cubes of its digits is equal to the number itself. For example, 371 is an Armstrong number since 3**3 + 7**3 + 1**3 = 371.



Here is my code:



lower = int(input("Enter lower range: "))
upper = int(input("Enter upper range: "))

for num in range(lower, upper + 1):

order = len(str(num))

sum = 0

temp = num
while temp > 0:
digit = temp % 10
sum += digit ** order
temp //= 10

if num == sum:
print(num)


Here is an example output:



Enter lower range: 200
Enter upper range: 5000

370
371
407
1634


So, I would like to know whether I could make this program shorter and more efficient.



Any help would be highly appreciated.










share|improve this question











$endgroup$




I have written a Python program to check for Armstrong numbers in a certain range.



An Armstrong number of three digits is an integer such that the sum of the cubes of its digits is equal to the number itself. For example, 371 is an Armstrong number since 3**3 + 7**3 + 1**3 = 371.



Here is my code:



lower = int(input("Enter lower range: "))
upper = int(input("Enter upper range: "))

for num in range(lower, upper + 1):

order = len(str(num))

sum = 0

temp = num
while temp > 0:
digit = temp % 10
sum += digit ** order
temp //= 10

if num == sum:
print(num)


Here is an example output:



Enter lower range: 200
Enter upper range: 5000

370
371
407
1634


So, I would like to know whether I could make this program shorter and more efficient.



Any help would be highly appreciated.







python performance python-3.x






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited May 22 at 18:26







Justin

















asked May 22 at 15:04









JustinJustin

1,318427




1,318427











  • $begingroup$
    "sum of the cubes of its digits"? The power should be equal to the number of digits.
    $endgroup$
    – Sedsarq
    May 22 at 15:36










  • $begingroup$
    @Sedsarq - taken from - pages.mtu.edu/~shene/COURSES/cs201/NOTES/chap04/arms.html
    $endgroup$
    – Justin
    May 22 at 15:38










  • $begingroup$
    @Sedsarq - Yes, you are also right - the power should be equal to the number of digits.
    $endgroup$
    – Justin
    May 22 at 15:42










  • $begingroup$
    @vurmux - I thought the performance tag was necessary as a larger range takes more time to print the output and therefore I needed the code to be as efficient as possible.
    $endgroup$
    – Justin
    May 22 at 15:46






  • 1




    $begingroup$
    You're right, so I rolled back the removal.
    $endgroup$
    – Mast
    May 22 at 15:49
















  • $begingroup$
    "sum of the cubes of its digits"? The power should be equal to the number of digits.
    $endgroup$
    – Sedsarq
    May 22 at 15:36










  • $begingroup$
    @Sedsarq - taken from - pages.mtu.edu/~shene/COURSES/cs201/NOTES/chap04/arms.html
    $endgroup$
    – Justin
    May 22 at 15:38










  • $begingroup$
    @Sedsarq - Yes, you are also right - the power should be equal to the number of digits.
    $endgroup$
    – Justin
    May 22 at 15:42










  • $begingroup$
    @vurmux - I thought the performance tag was necessary as a larger range takes more time to print the output and therefore I needed the code to be as efficient as possible.
    $endgroup$
    – Justin
    May 22 at 15:46






  • 1




    $begingroup$
    You're right, so I rolled back the removal.
    $endgroup$
    – Mast
    May 22 at 15:49















$begingroup$
"sum of the cubes of its digits"? The power should be equal to the number of digits.
$endgroup$
– Sedsarq
May 22 at 15:36




$begingroup$
"sum of the cubes of its digits"? The power should be equal to the number of digits.
$endgroup$
– Sedsarq
May 22 at 15:36












$begingroup$
@Sedsarq - taken from - pages.mtu.edu/~shene/COURSES/cs201/NOTES/chap04/arms.html
$endgroup$
– Justin
May 22 at 15:38




$begingroup$
@Sedsarq - taken from - pages.mtu.edu/~shene/COURSES/cs201/NOTES/chap04/arms.html
$endgroup$
– Justin
May 22 at 15:38












$begingroup$
@Sedsarq - Yes, you are also right - the power should be equal to the number of digits.
$endgroup$
– Justin
May 22 at 15:42




$begingroup$
@Sedsarq - Yes, you are also right - the power should be equal to the number of digits.
$endgroup$
– Justin
May 22 at 15:42












$begingroup$
@vurmux - I thought the performance tag was necessary as a larger range takes more time to print the output and therefore I needed the code to be as efficient as possible.
$endgroup$
– Justin
May 22 at 15:46




$begingroup$
@vurmux - I thought the performance tag was necessary as a larger range takes more time to print the output and therefore I needed the code to be as efficient as possible.
$endgroup$
– Justin
May 22 at 15:46




1




1




$begingroup$
You're right, so I rolled back the removal.
$endgroup$
– Mast
May 22 at 15:49




$begingroup$
You're right, so I rolled back the removal.
$endgroup$
– Mast
May 22 at 15:49










4 Answers
4






active

oldest

votes


















12












$begingroup$

Names



Using sum as a variable name is not adviced as it hides the sum builtin. I'd suggest sum_pow as an alternative.



Also, temp does not convey much information. I'd use remaining even though I am not fully convinced.



Extracting digits from a number



You've used divisions and modulo to compute the different digits for a number. You can use divmod which is a pretty unknown builtin which returns the result for both operations in one go.



That would give:



 remaining = num
while remaining:
remaining, digit = divmod(remaining, 10)
sum_pow += digit ** order


Also, a better alternative would be to avoid doing this ourselves: extracting the different digits is pretty much what str does for us. Also, we already call it anyway, we could make the most out of it and reuse the result.



 num_str = str(num)
order = len(num_str)
sum_pow = 0
for digit in num_str:
sum_pow += int(digit) ** order


More builtins



Disclaimer: next comment may be a bit overwhelming for a beginner. Don't worry, just take your time and read documentation online if need be.



We've already delegated most of the hard work to Python builtins but we can go further. The summing part could be handled by the sum builtin along with generator expressions.



for num in range(lower, upper + 1):
num_str = str(num)
order = len(num_str)
sum_pow = sum(int(digit) ** order for digit in num_str)
if num == sum_pow:
print(num)


Going further



A few other things could be improved from an organisation point of view:



  • split the code in functions

  • use the if name == "main" check

Going even further, we could write unit tests for the logic.






share|improve this answer









$endgroup$




















    9












    $begingroup$

    Python has a good standard module for work with decimal numbers: decimal. Your code (still C/C++-style) can be replaced with this code:



    import decimal

    # A separate function to check if the number is an Armstrong number
    def is_armstrong(number):
    # Get tuple of number digits
    num_digits = decimal.Decimal(number).as_tuple().digits
    # Return the result of check if it is an Armstrong number
    return number == sum(d ** len(num_digits) for d in num_digits)

    lower = int(input("Enter lower range: "))
    upper = int(input("Enter upper range: "))

    for num in range(lower, upper):
    if is_armstrong(num):
    print(num)



    About performance:



    I checked how long your code works for one million numbers:



    import datetime

    t1 = datetime.datetime.now()
    for num in range(1, 1000000):
    order = len(str(num))
    sum_ = 0
    temp = num
    while temp > 0:
    digit = temp % 10
    sum_ += digit ** order
    temp //= 10
    if num == sum_:
    q = num
    t2 = datetime.datetime.now()
    str(t2-t1)


    And it returned:



    '0:00:02.568923'



    Two and a half seconds. I think it is not the sort of code where one should worry about performance. Moreover, the complexity of each is_armstrong() call is O(log(N)) (we summarize powers O(1) of digits O(log(N))) for each number so the result complexity is O(N log(N)). For one BILLION numbers this script will work less than hour! It compares favorably with, for example, some kind of graph algorithms with O(N^3 E^2) complexity that works for days and every little improvement can save literally hours of CPU working.



    P.S. If you aren't familiar with Big O notation, check this article in Wikipedia.






    share|improve this answer











    $endgroup$








    • 1




      $begingroup$
      Good answer, I did not know the Decimal module. Minor point that could be improved: "return (True if expression else False)" could be "return bool(expresion)" or even "return express".
      $endgroup$
      – Josay
      May 22 at 16:40










    • $begingroup$
      Definetly! Thank you! I thought that it can be simplified but missed it somehow.
      $endgroup$
      – vurmux
      May 22 at 16:48










    • $begingroup$
      Considering that the Armstrong numbers are all found already and can be easily googled, it's likely this is more a programming challenge, where performance is the whole point. And percentage wise, there's a lot to shave off from 1M numbers in 2 seconds in this case.
      $endgroup$
      – Sedsarq
      May 23 at 7:20






    • 1




      $begingroup$
      Decimal is typically imported directly, from decimal import Decimal, rather than referenced from within the module. Same with datetime: from datetime import datetime.
      $endgroup$
      – jpmc26
      May 23 at 7:21



















    5












    $begingroup$

    It would save a lot of time to start from the other end, by making the sums first. Notice that a digit combination like (1, 2, 3) is shared by six numbers: 123, 132, 213, 231, 312, 321. They all have the same digit cube sum 1^3 + 2^3 + 3^3 = 36. Your code will iterate over those numbers and recalculate the same sum 6 times.



    Instead, you could use the digit combination (1, 2, 3) and calculate the digit cube sum 36 once. Then check if the digits in that sum is a permutation of those in the digit combination - if so, it's an Armstrong number. Then move to the next digit combination (1, 2, 4) to check another six numbers in one fell swoop.



    The digit combinations can be iterated over using itertools.combinations_with_replacement. Here's an example that generates all Armstrong numbers of length 1 to 10 (so under 10 billion), running in under 3 seconds on my machine:



    from itertools import combinations_with_replacement

    armstrongs = []
    for length in range(1, 11):
    found = []
    for digits in combinations_with_replacement(range(10), length):

    # make the digit power sum
    s = sum(pow(d, length) for d in digits)

    # collect the digits of the sum
    temp = s
    sumdigits = []
    while temp:
    temp, d = divmod(temp, 10)
    sumdigits.append(d)

    # compare the two digit groups. Notice that "digits" is already sorted
    if list(digits) == sorted(sumdigits):
    found.append(s)

    # the sum-first-method doesn't find Armstrong numbers in order, so
    # an additional sorting is thrown in here.
    armstrongs.extend(sorted(found))

    print(armstrongs)


    This could be optimized further, by for example checking if sumdigits has the right length before sorting. You could also check the digit d as it's chopped off from the sum and make sure it exists at all within digits. If not, the two digit groups are clearly different and you can move to the next iteration.




    Now, this example doesn't limit the results to a range. But it can quite easily be modified to do so: Check the lengths of the boundary numbers, then use those in the for length in range(1, 11): line to only generate Armstrong numbers of relevant length. So modify the top to:



    lower = 400
    upper = 15000

    lowerlength = len(str(lower))
    upperlength = len(str(upper))
    armstrongs = []
    for length in range(lowerlength, upperlength + 1):


    Then generate the numbers as before, and once you have them, filter down:



    armstrongs = [n for n in armstrongs if lower <= n <= upper]





    share|improve this answer











    $endgroup$












    • $begingroup$
      Upvoted! Thanks for the answer. It helped me a lot.
      $endgroup$
      – Justin
      May 23 at 7:08


















    4












    $begingroup$

    I'm going to focus on the performance aspect, as I believe other parts already have good answers. While the program may be simple and performant, it is still fun to go in and micro-optimize a little. In general I would recommend against it.



    Here is your code put into a function to make it clear what changes in the future. I've made the function return the numbers since that was easier for me to work with. I don't have anything new that I would suggest changing.



    def armstrong(lower, upper):
    armstrong_numbers = []
    for num in range(lower, upper + 1):
    order = len(str(num))
    sum = 0

    temp = num
    while temp > 0:
    digit = temp % 10
    sum += digit ** order
    temp //= 10

    if num == sum:
    armstrong_numbers.append(num)

    return armstrong_numbers


    Precompute



    A google search says another name for these numbers are narcissistic numbers and that there are only a finite number of them (88 to be exact). We could make a list of the numbers, loop over the list and return the numbers between lower and upper. This only works if someone else has done the work and generated all the numbers already.



    Precompute a little



    The above point could be useful though, the first 9 armstrong numbers are the numbers 1 through 9. Let's "precompute" as many of those as we need. We should really add a check to make sure lower <= upper, but alas...



    def armstrong(lower, upper):
    cutoff = min(10, upper + 1)
    armstrong_numbers = list(range(lower, cutoff))
    if lower < 10:
    lower = 10

    for num in range(lower, upper + 1):
    order = len(str(num))
    sum = 0

    temp = num
    while temp > 0:
    digit = temp % 10
    sum += digit ** order
    temp //= 10

    if num == sum:
    armstrong_numbers.append(num)

    return armstrong_numbers


    Optimize the powers



    Another good point that showed up when googling armstrong numbers is that no number bigger than 1060 can be an armstrong number. This means we can work out every possible answer for digitorder ahead of time and reuse it each loop. This should be useful as I think computing arbitrary powers is not as fast as looking up the answer in a table.



    As plainly as I can state it, there are only 10 digits, and order is at most 60, so we can make a table 10 * 60 big that stores all the answers to digitorder and use that instead.



    def armstrong(lower, upper):
    cutoff = min(10, upper + 1)
    armstrong_numbers = list(range(lower, cutoff))
    if lower < 10:
    lower = 10

    powers_table = [[d ** n for d in range(10)] for n in range(60)]

    for num in range(lower, upper + 1):
    order = len(str(num))
    row = powers_table[order] # We only care about one row of the table at a time.
    sum = 0

    temp = num
    while temp > 0:
    digit = temp % 10
    sum += row[digit]
    temp //= 10

    if num == sum:
    armstrong_numbers.append(num)

    return armstrong_numbers


    Check less numbers



    The last idea that I found online (see section 5) is to skip numbers with certain prefixes. We can guarantee a number will never work if it the sum of all of its digits except the last one is odd.



    The reason for this is as follows. Raising a number to a power won't change its parity. In other words if a number x is even, xn is also even. If x is odd xn is also odd. The sum of the digits raised to a power n will have the same parity as the sum of the digits. For example if we have the 3 digit number 18X. The sum of it's digits cubed is 1**3 (odd) + 83 (even) + X3 which is the same as 1 (odd) + 8 (even) + X.



    Assume the sum of all the digits of an armstrong number excluding the last digit is odd then we have either



    (A**n + B**n + C**n + ...W**n) + X**n == odd + X**n == odd if X is even or
    (A**n + B**n + C**n + ...W**n) + X**n == odd + X**n == even if X is odd


    But if the last digit (X) is even, the sum has to be even it to which it isn't.
    If the last digit is odd, the sum has to be odd, but it isn't. Either way, we get a contradiction, so our assumption must be wrong.



    The code is a bit messy, but it gives the idea. It agrees with the other snippets above for the query (1, 100000)



    def armstrong3(lower, upper): 
    cutoff = min(10, upper + 1)
    armstrong_numbers = list(range(lower, cutoff))
    if lower < 10:
    lower = 10

    powers_table = [[d ** n for d in range(10)] for n in range(60)]

    start, end = lower // 10, upper // 10
    for leading_digits in range(start, end + 1):
    if sum(c in "13579" for c in str(leading_digits)) % 2 == 1:
    # Skip numbers with an odd sum parity
    continue

    order = len(str(leading_digits)) + 1 # We will add a last digit later
    row = powers_table[order] # We only care about one row of the table at a time.
    sum = 0

    temp = leading_digits
    while temp > 0:
    digit = temp % 10
    sum += row[digit]
    temp //= 10

    for last_digit in range(10):
    final_total = sum + row[last_digit]
    if 10 * leading_digits + last_digit == final_total and final_total <= upper:
    armstrong_numbers.append(num)

    return armstrong_numbers


    Micro-benchmarked locally I get the following



    %timeit armstrong(1, 100000)
    143 ms ± 104 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    %timeit armstrong2(1, 100000)
    69.4 ms ± 2.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    %timeit armstrong3(1, 100000)
    14.9 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


    So the extra tuning was worth it.



    Other work



    I didn't get around to implementing the ideas at this github project. The code is in java and run with N < 10 at the smallest (above you can see my benchmarks were only for N < 5), so I don't think the performance is anywhere near as good as that code. It would be the next place to go if you are interested in pushing things further.



    I looked at using divmod instead of modding and dividing by 10. The performance was worse for me, so I chose not to use it.



    %timeit armstrong(1, 100000)
    143 ms ± 104 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
    %timeit armstrong_divmod(1, 100000)
    173 ms ± 5.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)





    share|improve this answer











    $endgroup$












    • $begingroup$
      Upvoted! Thanks for the amazing response. It helped me a lot.
      $endgroup$
      – Justin
      May 23 at 2:56











    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "196"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f220738%2fpython-program-to-find-armstrong-numbers-in-a-certain-range%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    4 Answers
    4






    active

    oldest

    votes








    4 Answers
    4






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    12












    $begingroup$

    Names



    Using sum as a variable name is not adviced as it hides the sum builtin. I'd suggest sum_pow as an alternative.



    Also, temp does not convey much information. I'd use remaining even though I am not fully convinced.



    Extracting digits from a number



    You've used divisions and modulo to compute the different digits for a number. You can use divmod which is a pretty unknown builtin which returns the result for both operations in one go.



    That would give:



     remaining = num
    while remaining:
    remaining, digit = divmod(remaining, 10)
    sum_pow += digit ** order


    Also, a better alternative would be to avoid doing this ourselves: extracting the different digits is pretty much what str does for us. Also, we already call it anyway, we could make the most out of it and reuse the result.



     num_str = str(num)
    order = len(num_str)
    sum_pow = 0
    for digit in num_str:
    sum_pow += int(digit) ** order


    More builtins



    Disclaimer: next comment may be a bit overwhelming for a beginner. Don't worry, just take your time and read documentation online if need be.



    We've already delegated most of the hard work to Python builtins but we can go further. The summing part could be handled by the sum builtin along with generator expressions.



    for num in range(lower, upper + 1):
    num_str = str(num)
    order = len(num_str)
    sum_pow = sum(int(digit) ** order for digit in num_str)
    if num == sum_pow:
    print(num)


    Going further



    A few other things could be improved from an organisation point of view:



    • split the code in functions

    • use the if name == "main" check

    Going even further, we could write unit tests for the logic.






    share|improve this answer









    $endgroup$

















      12












      $begingroup$

      Names



      Using sum as a variable name is not adviced as it hides the sum builtin. I'd suggest sum_pow as an alternative.



      Also, temp does not convey much information. I'd use remaining even though I am not fully convinced.



      Extracting digits from a number



      You've used divisions and modulo to compute the different digits for a number. You can use divmod which is a pretty unknown builtin which returns the result for both operations in one go.



      That would give:



       remaining = num
      while remaining:
      remaining, digit = divmod(remaining, 10)
      sum_pow += digit ** order


      Also, a better alternative would be to avoid doing this ourselves: extracting the different digits is pretty much what str does for us. Also, we already call it anyway, we could make the most out of it and reuse the result.



       num_str = str(num)
      order = len(num_str)
      sum_pow = 0
      for digit in num_str:
      sum_pow += int(digit) ** order


      More builtins



      Disclaimer: next comment may be a bit overwhelming for a beginner. Don't worry, just take your time and read documentation online if need be.



      We've already delegated most of the hard work to Python builtins but we can go further. The summing part could be handled by the sum builtin along with generator expressions.



      for num in range(lower, upper + 1):
      num_str = str(num)
      order = len(num_str)
      sum_pow = sum(int(digit) ** order for digit in num_str)
      if num == sum_pow:
      print(num)


      Going further



      A few other things could be improved from an organisation point of view:



      • split the code in functions

      • use the if name == "main" check

      Going even further, we could write unit tests for the logic.






      share|improve this answer









      $endgroup$















        12












        12








        12





        $begingroup$

        Names



        Using sum as a variable name is not adviced as it hides the sum builtin. I'd suggest sum_pow as an alternative.



        Also, temp does not convey much information. I'd use remaining even though I am not fully convinced.



        Extracting digits from a number



        You've used divisions and modulo to compute the different digits for a number. You can use divmod which is a pretty unknown builtin which returns the result for both operations in one go.



        That would give:



         remaining = num
        while remaining:
        remaining, digit = divmod(remaining, 10)
        sum_pow += digit ** order


        Also, a better alternative would be to avoid doing this ourselves: extracting the different digits is pretty much what str does for us. Also, we already call it anyway, we could make the most out of it and reuse the result.



         num_str = str(num)
        order = len(num_str)
        sum_pow = 0
        for digit in num_str:
        sum_pow += int(digit) ** order


        More builtins



        Disclaimer: next comment may be a bit overwhelming for a beginner. Don't worry, just take your time and read documentation online if need be.



        We've already delegated most of the hard work to Python builtins but we can go further. The summing part could be handled by the sum builtin along with generator expressions.



        for num in range(lower, upper + 1):
        num_str = str(num)
        order = len(num_str)
        sum_pow = sum(int(digit) ** order for digit in num_str)
        if num == sum_pow:
        print(num)


        Going further



        A few other things could be improved from an organisation point of view:



        • split the code in functions

        • use the if name == "main" check

        Going even further, we could write unit tests for the logic.






        share|improve this answer









        $endgroup$



        Names



        Using sum as a variable name is not adviced as it hides the sum builtin. I'd suggest sum_pow as an alternative.



        Also, temp does not convey much information. I'd use remaining even though I am not fully convinced.



        Extracting digits from a number



        You've used divisions and modulo to compute the different digits for a number. You can use divmod which is a pretty unknown builtin which returns the result for both operations in one go.



        That would give:



         remaining = num
        while remaining:
        remaining, digit = divmod(remaining, 10)
        sum_pow += digit ** order


        Also, a better alternative would be to avoid doing this ourselves: extracting the different digits is pretty much what str does for us. Also, we already call it anyway, we could make the most out of it and reuse the result.



         num_str = str(num)
        order = len(num_str)
        sum_pow = 0
        for digit in num_str:
        sum_pow += int(digit) ** order


        More builtins



        Disclaimer: next comment may be a bit overwhelming for a beginner. Don't worry, just take your time and read documentation online if need be.



        We've already delegated most of the hard work to Python builtins but we can go further. The summing part could be handled by the sum builtin along with generator expressions.



        for num in range(lower, upper + 1):
        num_str = str(num)
        order = len(num_str)
        sum_pow = sum(int(digit) ** order for digit in num_str)
        if num == sum_pow:
        print(num)


        Going further



        A few other things could be improved from an organisation point of view:



        • split the code in functions

        • use the if name == "main" check

        Going even further, we could write unit tests for the logic.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered May 22 at 16:17









        JosayJosay

        26.7k14088




        26.7k14088























            9












            $begingroup$

            Python has a good standard module for work with decimal numbers: decimal. Your code (still C/C++-style) can be replaced with this code:



            import decimal

            # A separate function to check if the number is an Armstrong number
            def is_armstrong(number):
            # Get tuple of number digits
            num_digits = decimal.Decimal(number).as_tuple().digits
            # Return the result of check if it is an Armstrong number
            return number == sum(d ** len(num_digits) for d in num_digits)

            lower = int(input("Enter lower range: "))
            upper = int(input("Enter upper range: "))

            for num in range(lower, upper):
            if is_armstrong(num):
            print(num)



            About performance:



            I checked how long your code works for one million numbers:



            import datetime

            t1 = datetime.datetime.now()
            for num in range(1, 1000000):
            order = len(str(num))
            sum_ = 0
            temp = num
            while temp > 0:
            digit = temp % 10
            sum_ += digit ** order
            temp //= 10
            if num == sum_:
            q = num
            t2 = datetime.datetime.now()
            str(t2-t1)


            And it returned:



            '0:00:02.568923'



            Two and a half seconds. I think it is not the sort of code where one should worry about performance. Moreover, the complexity of each is_armstrong() call is O(log(N)) (we summarize powers O(1) of digits O(log(N))) for each number so the result complexity is O(N log(N)). For one BILLION numbers this script will work less than hour! It compares favorably with, for example, some kind of graph algorithms with O(N^3 E^2) complexity that works for days and every little improvement can save literally hours of CPU working.



            P.S. If you aren't familiar with Big O notation, check this article in Wikipedia.






            share|improve this answer











            $endgroup$








            • 1




              $begingroup$
              Good answer, I did not know the Decimal module. Minor point that could be improved: "return (True if expression else False)" could be "return bool(expresion)" or even "return express".
              $endgroup$
              – Josay
              May 22 at 16:40










            • $begingroup$
              Definetly! Thank you! I thought that it can be simplified but missed it somehow.
              $endgroup$
              – vurmux
              May 22 at 16:48










            • $begingroup$
              Considering that the Armstrong numbers are all found already and can be easily googled, it's likely this is more a programming challenge, where performance is the whole point. And percentage wise, there's a lot to shave off from 1M numbers in 2 seconds in this case.
              $endgroup$
              – Sedsarq
              May 23 at 7:20






            • 1




              $begingroup$
              Decimal is typically imported directly, from decimal import Decimal, rather than referenced from within the module. Same with datetime: from datetime import datetime.
              $endgroup$
              – jpmc26
              May 23 at 7:21
















            9












            $begingroup$

            Python has a good standard module for work with decimal numbers: decimal. Your code (still C/C++-style) can be replaced with this code:



            import decimal

            # A separate function to check if the number is an Armstrong number
            def is_armstrong(number):
            # Get tuple of number digits
            num_digits = decimal.Decimal(number).as_tuple().digits
            # Return the result of check if it is an Armstrong number
            return number == sum(d ** len(num_digits) for d in num_digits)

            lower = int(input("Enter lower range: "))
            upper = int(input("Enter upper range: "))

            for num in range(lower, upper):
            if is_armstrong(num):
            print(num)



            About performance:



            I checked how long your code works for one million numbers:



            import datetime

            t1 = datetime.datetime.now()
            for num in range(1, 1000000):
            order = len(str(num))
            sum_ = 0
            temp = num
            while temp > 0:
            digit = temp % 10
            sum_ += digit ** order
            temp //= 10
            if num == sum_:
            q = num
            t2 = datetime.datetime.now()
            str(t2-t1)


            And it returned:



            '0:00:02.568923'



            Two and a half seconds. I think it is not the sort of code where one should worry about performance. Moreover, the complexity of each is_armstrong() call is O(log(N)) (we summarize powers O(1) of digits O(log(N))) for each number so the result complexity is O(N log(N)). For one BILLION numbers this script will work less than hour! It compares favorably with, for example, some kind of graph algorithms with O(N^3 E^2) complexity that works for days and every little improvement can save literally hours of CPU working.



            P.S. If you aren't familiar with Big O notation, check this article in Wikipedia.






            share|improve this answer











            $endgroup$








            • 1




              $begingroup$
              Good answer, I did not know the Decimal module. Minor point that could be improved: "return (True if expression else False)" could be "return bool(expresion)" or even "return express".
              $endgroup$
              – Josay
              May 22 at 16:40










            • $begingroup$
              Definetly! Thank you! I thought that it can be simplified but missed it somehow.
              $endgroup$
              – vurmux
              May 22 at 16:48










            • $begingroup$
              Considering that the Armstrong numbers are all found already and can be easily googled, it's likely this is more a programming challenge, where performance is the whole point. And percentage wise, there's a lot to shave off from 1M numbers in 2 seconds in this case.
              $endgroup$
              – Sedsarq
              May 23 at 7:20






            • 1




              $begingroup$
              Decimal is typically imported directly, from decimal import Decimal, rather than referenced from within the module. Same with datetime: from datetime import datetime.
              $endgroup$
              – jpmc26
              May 23 at 7:21














            9












            9








            9





            $begingroup$

            Python has a good standard module for work with decimal numbers: decimal. Your code (still C/C++-style) can be replaced with this code:



            import decimal

            # A separate function to check if the number is an Armstrong number
            def is_armstrong(number):
            # Get tuple of number digits
            num_digits = decimal.Decimal(number).as_tuple().digits
            # Return the result of check if it is an Armstrong number
            return number == sum(d ** len(num_digits) for d in num_digits)

            lower = int(input("Enter lower range: "))
            upper = int(input("Enter upper range: "))

            for num in range(lower, upper):
            if is_armstrong(num):
            print(num)



            About performance:



            I checked how long your code works for one million numbers:



            import datetime

            t1 = datetime.datetime.now()
            for num in range(1, 1000000):
            order = len(str(num))
            sum_ = 0
            temp = num
            while temp > 0:
            digit = temp % 10
            sum_ += digit ** order
            temp //= 10
            if num == sum_:
            q = num
            t2 = datetime.datetime.now()
            str(t2-t1)


            And it returned:



            '0:00:02.568923'



            Two and a half seconds. I think it is not the sort of code where one should worry about performance. Moreover, the complexity of each is_armstrong() call is O(log(N)) (we summarize powers O(1) of digits O(log(N))) for each number so the result complexity is O(N log(N)). For one BILLION numbers this script will work less than hour! It compares favorably with, for example, some kind of graph algorithms with O(N^3 E^2) complexity that works for days and every little improvement can save literally hours of CPU working.



            P.S. If you aren't familiar with Big O notation, check this article in Wikipedia.






            share|improve this answer











            $endgroup$



            Python has a good standard module for work with decimal numbers: decimal. Your code (still C/C++-style) can be replaced with this code:



            import decimal

            # A separate function to check if the number is an Armstrong number
            def is_armstrong(number):
            # Get tuple of number digits
            num_digits = decimal.Decimal(number).as_tuple().digits
            # Return the result of check if it is an Armstrong number
            return number == sum(d ** len(num_digits) for d in num_digits)

            lower = int(input("Enter lower range: "))
            upper = int(input("Enter upper range: "))

            for num in range(lower, upper):
            if is_armstrong(num):
            print(num)



            About performance:



            I checked how long your code works for one million numbers:



            import datetime

            t1 = datetime.datetime.now()
            for num in range(1, 1000000):
            order = len(str(num))
            sum_ = 0
            temp = num
            while temp > 0:
            digit = temp % 10
            sum_ += digit ** order
            temp //= 10
            if num == sum_:
            q = num
            t2 = datetime.datetime.now()
            str(t2-t1)


            And it returned:



            '0:00:02.568923'



            Two and a half seconds. I think it is not the sort of code where one should worry about performance. Moreover, the complexity of each is_armstrong() call is O(log(N)) (we summarize powers O(1) of digits O(log(N))) for each number so the result complexity is O(N log(N)). For one BILLION numbers this script will work less than hour! It compares favorably with, for example, some kind of graph algorithms with O(N^3 E^2) complexity that works for days and every little improvement can save literally hours of CPU working.



            P.S. If you aren't familiar with Big O notation, check this article in Wikipedia.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited May 23 at 14:20









            aschultz

            1731211




            1731211










            answered May 22 at 16:16









            vurmuxvurmux

            1,225214




            1,225214







            • 1




              $begingroup$
              Good answer, I did not know the Decimal module. Minor point that could be improved: "return (True if expression else False)" could be "return bool(expresion)" or even "return express".
              $endgroup$
              – Josay
              May 22 at 16:40










            • $begingroup$
              Definetly! Thank you! I thought that it can be simplified but missed it somehow.
              $endgroup$
              – vurmux
              May 22 at 16:48










            • $begingroup$
              Considering that the Armstrong numbers are all found already and can be easily googled, it's likely this is more a programming challenge, where performance is the whole point. And percentage wise, there's a lot to shave off from 1M numbers in 2 seconds in this case.
              $endgroup$
              – Sedsarq
              May 23 at 7:20






            • 1




              $begingroup$
              Decimal is typically imported directly, from decimal import Decimal, rather than referenced from within the module. Same with datetime: from datetime import datetime.
              $endgroup$
              – jpmc26
              May 23 at 7:21













            • 1




              $begingroup$
              Good answer, I did not know the Decimal module. Minor point that could be improved: "return (True if expression else False)" could be "return bool(expresion)" or even "return express".
              $endgroup$
              – Josay
              May 22 at 16:40










            • $begingroup$
              Definetly! Thank you! I thought that it can be simplified but missed it somehow.
              $endgroup$
              – vurmux
              May 22 at 16:48










            • $begingroup$
              Considering that the Armstrong numbers are all found already and can be easily googled, it's likely this is more a programming challenge, where performance is the whole point. And percentage wise, there's a lot to shave off from 1M numbers in 2 seconds in this case.
              $endgroup$
              – Sedsarq
              May 23 at 7:20






            • 1




              $begingroup$
              Decimal is typically imported directly, from decimal import Decimal, rather than referenced from within the module. Same with datetime: from datetime import datetime.
              $endgroup$
              – jpmc26
              May 23 at 7:21








            1




            1




            $begingroup$
            Good answer, I did not know the Decimal module. Minor point that could be improved: "return (True if expression else False)" could be "return bool(expresion)" or even "return express".
            $endgroup$
            – Josay
            May 22 at 16:40




            $begingroup$
            Good answer, I did not know the Decimal module. Minor point that could be improved: "return (True if expression else False)" could be "return bool(expresion)" or even "return express".
            $endgroup$
            – Josay
            May 22 at 16:40












            $begingroup$
            Definetly! Thank you! I thought that it can be simplified but missed it somehow.
            $endgroup$
            – vurmux
            May 22 at 16:48




            $begingroup$
            Definetly! Thank you! I thought that it can be simplified but missed it somehow.
            $endgroup$
            – vurmux
            May 22 at 16:48












            $begingroup$
            Considering that the Armstrong numbers are all found already and can be easily googled, it's likely this is more a programming challenge, where performance is the whole point. And percentage wise, there's a lot to shave off from 1M numbers in 2 seconds in this case.
            $endgroup$
            – Sedsarq
            May 23 at 7:20




            $begingroup$
            Considering that the Armstrong numbers are all found already and can be easily googled, it's likely this is more a programming challenge, where performance is the whole point. And percentage wise, there's a lot to shave off from 1M numbers in 2 seconds in this case.
            $endgroup$
            – Sedsarq
            May 23 at 7:20




            1




            1




            $begingroup$
            Decimal is typically imported directly, from decimal import Decimal, rather than referenced from within the module. Same with datetime: from datetime import datetime.
            $endgroup$
            – jpmc26
            May 23 at 7:21





            $begingroup$
            Decimal is typically imported directly, from decimal import Decimal, rather than referenced from within the module. Same with datetime: from datetime import datetime.
            $endgroup$
            – jpmc26
            May 23 at 7:21












            5












            $begingroup$

            It would save a lot of time to start from the other end, by making the sums first. Notice that a digit combination like (1, 2, 3) is shared by six numbers: 123, 132, 213, 231, 312, 321. They all have the same digit cube sum 1^3 + 2^3 + 3^3 = 36. Your code will iterate over those numbers and recalculate the same sum 6 times.



            Instead, you could use the digit combination (1, 2, 3) and calculate the digit cube sum 36 once. Then check if the digits in that sum is a permutation of those in the digit combination - if so, it's an Armstrong number. Then move to the next digit combination (1, 2, 4) to check another six numbers in one fell swoop.



            The digit combinations can be iterated over using itertools.combinations_with_replacement. Here's an example that generates all Armstrong numbers of length 1 to 10 (so under 10 billion), running in under 3 seconds on my machine:



            from itertools import combinations_with_replacement

            armstrongs = []
            for length in range(1, 11):
            found = []
            for digits in combinations_with_replacement(range(10), length):

            # make the digit power sum
            s = sum(pow(d, length) for d in digits)

            # collect the digits of the sum
            temp = s
            sumdigits = []
            while temp:
            temp, d = divmod(temp, 10)
            sumdigits.append(d)

            # compare the two digit groups. Notice that "digits" is already sorted
            if list(digits) == sorted(sumdigits):
            found.append(s)

            # the sum-first-method doesn't find Armstrong numbers in order, so
            # an additional sorting is thrown in here.
            armstrongs.extend(sorted(found))

            print(armstrongs)


            This could be optimized further, by for example checking if sumdigits has the right length before sorting. You could also check the digit d as it's chopped off from the sum and make sure it exists at all within digits. If not, the two digit groups are clearly different and you can move to the next iteration.




            Now, this example doesn't limit the results to a range. But it can quite easily be modified to do so: Check the lengths of the boundary numbers, then use those in the for length in range(1, 11): line to only generate Armstrong numbers of relevant length. So modify the top to:



            lower = 400
            upper = 15000

            lowerlength = len(str(lower))
            upperlength = len(str(upper))
            armstrongs = []
            for length in range(lowerlength, upperlength + 1):


            Then generate the numbers as before, and once you have them, filter down:



            armstrongs = [n for n in armstrongs if lower <= n <= upper]





            share|improve this answer











            $endgroup$












            • $begingroup$
              Upvoted! Thanks for the answer. It helped me a lot.
              $endgroup$
              – Justin
              May 23 at 7:08















            5












            $begingroup$

            It would save a lot of time to start from the other end, by making the sums first. Notice that a digit combination like (1, 2, 3) is shared by six numbers: 123, 132, 213, 231, 312, 321. They all have the same digit cube sum 1^3 + 2^3 + 3^3 = 36. Your code will iterate over those numbers and recalculate the same sum 6 times.



            Instead, you could use the digit combination (1, 2, 3) and calculate the digit cube sum 36 once. Then check if the digits in that sum is a permutation of those in the digit combination - if so, it's an Armstrong number. Then move to the next digit combination (1, 2, 4) to check another six numbers in one fell swoop.



            The digit combinations can be iterated over using itertools.combinations_with_replacement. Here's an example that generates all Armstrong numbers of length 1 to 10 (so under 10 billion), running in under 3 seconds on my machine:



            from itertools import combinations_with_replacement

            armstrongs = []
            for length in range(1, 11):
            found = []
            for digits in combinations_with_replacement(range(10), length):

            # make the digit power sum
            s = sum(pow(d, length) for d in digits)

            # collect the digits of the sum
            temp = s
            sumdigits = []
            while temp:
            temp, d = divmod(temp, 10)
            sumdigits.append(d)

            # compare the two digit groups. Notice that "digits" is already sorted
            if list(digits) == sorted(sumdigits):
            found.append(s)

            # the sum-first-method doesn't find Armstrong numbers in order, so
            # an additional sorting is thrown in here.
            armstrongs.extend(sorted(found))

            print(armstrongs)


            This could be optimized further, by for example checking if sumdigits has the right length before sorting. You could also check the digit d as it's chopped off from the sum and make sure it exists at all within digits. If not, the two digit groups are clearly different and you can move to the next iteration.




            Now, this example doesn't limit the results to a range. But it can quite easily be modified to do so: Check the lengths of the boundary numbers, then use those in the for length in range(1, 11): line to only generate Armstrong numbers of relevant length. So modify the top to:



            lower = 400
            upper = 15000

            lowerlength = len(str(lower))
            upperlength = len(str(upper))
            armstrongs = []
            for length in range(lowerlength, upperlength + 1):


            Then generate the numbers as before, and once you have them, filter down:



            armstrongs = [n for n in armstrongs if lower <= n <= upper]





            share|improve this answer











            $endgroup$












            • $begingroup$
              Upvoted! Thanks for the answer. It helped me a lot.
              $endgroup$
              – Justin
              May 23 at 7:08













            5












            5








            5





            $begingroup$

            It would save a lot of time to start from the other end, by making the sums first. Notice that a digit combination like (1, 2, 3) is shared by six numbers: 123, 132, 213, 231, 312, 321. They all have the same digit cube sum 1^3 + 2^3 + 3^3 = 36. Your code will iterate over those numbers and recalculate the same sum 6 times.



            Instead, you could use the digit combination (1, 2, 3) and calculate the digit cube sum 36 once. Then check if the digits in that sum is a permutation of those in the digit combination - if so, it's an Armstrong number. Then move to the next digit combination (1, 2, 4) to check another six numbers in one fell swoop.



            The digit combinations can be iterated over using itertools.combinations_with_replacement. Here's an example that generates all Armstrong numbers of length 1 to 10 (so under 10 billion), running in under 3 seconds on my machine:



            from itertools import combinations_with_replacement

            armstrongs = []
            for length in range(1, 11):
            found = []
            for digits in combinations_with_replacement(range(10), length):

            # make the digit power sum
            s = sum(pow(d, length) for d in digits)

            # collect the digits of the sum
            temp = s
            sumdigits = []
            while temp:
            temp, d = divmod(temp, 10)
            sumdigits.append(d)

            # compare the two digit groups. Notice that "digits" is already sorted
            if list(digits) == sorted(sumdigits):
            found.append(s)

            # the sum-first-method doesn't find Armstrong numbers in order, so
            # an additional sorting is thrown in here.
            armstrongs.extend(sorted(found))

            print(armstrongs)


            This could be optimized further, by for example checking if sumdigits has the right length before sorting. You could also check the digit d as it's chopped off from the sum and make sure it exists at all within digits. If not, the two digit groups are clearly different and you can move to the next iteration.




            Now, this example doesn't limit the results to a range. But it can quite easily be modified to do so: Check the lengths of the boundary numbers, then use those in the for length in range(1, 11): line to only generate Armstrong numbers of relevant length. So modify the top to:



            lower = 400
            upper = 15000

            lowerlength = len(str(lower))
            upperlength = len(str(upper))
            armstrongs = []
            for length in range(lowerlength, upperlength + 1):


            Then generate the numbers as before, and once you have them, filter down:



            armstrongs = [n for n in armstrongs if lower <= n <= upper]





            share|improve this answer











            $endgroup$



            It would save a lot of time to start from the other end, by making the sums first. Notice that a digit combination like (1, 2, 3) is shared by six numbers: 123, 132, 213, 231, 312, 321. They all have the same digit cube sum 1^3 + 2^3 + 3^3 = 36. Your code will iterate over those numbers and recalculate the same sum 6 times.



            Instead, you could use the digit combination (1, 2, 3) and calculate the digit cube sum 36 once. Then check if the digits in that sum is a permutation of those in the digit combination - if so, it's an Armstrong number. Then move to the next digit combination (1, 2, 4) to check another six numbers in one fell swoop.



            The digit combinations can be iterated over using itertools.combinations_with_replacement. Here's an example that generates all Armstrong numbers of length 1 to 10 (so under 10 billion), running in under 3 seconds on my machine:



            from itertools import combinations_with_replacement

            armstrongs = []
            for length in range(1, 11):
            found = []
            for digits in combinations_with_replacement(range(10), length):

            # make the digit power sum
            s = sum(pow(d, length) for d in digits)

            # collect the digits of the sum
            temp = s
            sumdigits = []
            while temp:
            temp, d = divmod(temp, 10)
            sumdigits.append(d)

            # compare the two digit groups. Notice that "digits" is already sorted
            if list(digits) == sorted(sumdigits):
            found.append(s)

            # the sum-first-method doesn't find Armstrong numbers in order, so
            # an additional sorting is thrown in here.
            armstrongs.extend(sorted(found))

            print(armstrongs)


            This could be optimized further, by for example checking if sumdigits has the right length before sorting. You could also check the digit d as it's chopped off from the sum and make sure it exists at all within digits. If not, the two digit groups are clearly different and you can move to the next iteration.




            Now, this example doesn't limit the results to a range. But it can quite easily be modified to do so: Check the lengths of the boundary numbers, then use those in the for length in range(1, 11): line to only generate Armstrong numbers of relevant length. So modify the top to:



            lower = 400
            upper = 15000

            lowerlength = len(str(lower))
            upperlength = len(str(upper))
            armstrongs = []
            for length in range(lowerlength, upperlength + 1):


            Then generate the numbers as before, and once you have them, filter down:



            armstrongs = [n for n in armstrongs if lower <= n <= upper]






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited May 23 at 12:37

























            answered May 23 at 7:01









            SedsarqSedsarq

            2646




            2646











            • $begingroup$
              Upvoted! Thanks for the answer. It helped me a lot.
              $endgroup$
              – Justin
              May 23 at 7:08
















            • $begingroup$
              Upvoted! Thanks for the answer. It helped me a lot.
              $endgroup$
              – Justin
              May 23 at 7:08















            $begingroup$
            Upvoted! Thanks for the answer. It helped me a lot.
            $endgroup$
            – Justin
            May 23 at 7:08




            $begingroup$
            Upvoted! Thanks for the answer. It helped me a lot.
            $endgroup$
            – Justin
            May 23 at 7:08











            4












            $begingroup$

            I'm going to focus on the performance aspect, as I believe other parts already have good answers. While the program may be simple and performant, it is still fun to go in and micro-optimize a little. In general I would recommend against it.



            Here is your code put into a function to make it clear what changes in the future. I've made the function return the numbers since that was easier for me to work with. I don't have anything new that I would suggest changing.



            def armstrong(lower, upper):
            armstrong_numbers = []
            for num in range(lower, upper + 1):
            order = len(str(num))
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += digit ** order
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Precompute



            A google search says another name for these numbers are narcissistic numbers and that there are only a finite number of them (88 to be exact). We could make a list of the numbers, loop over the list and return the numbers between lower and upper. This only works if someone else has done the work and generated all the numbers already.



            Precompute a little



            The above point could be useful though, the first 9 armstrong numbers are the numbers 1 through 9. Let's "precompute" as many of those as we need. We should really add a check to make sure lower <= upper, but alas...



            def armstrong(lower, upper):
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            for num in range(lower, upper + 1):
            order = len(str(num))
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += digit ** order
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Optimize the powers



            Another good point that showed up when googling armstrong numbers is that no number bigger than 1060 can be an armstrong number. This means we can work out every possible answer for digitorder ahead of time and reuse it each loop. This should be useful as I think computing arbitrary powers is not as fast as looking up the answer in a table.



            As plainly as I can state it, there are only 10 digits, and order is at most 60, so we can make a table 10 * 60 big that stores all the answers to digitorder and use that instead.



            def armstrong(lower, upper):
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            powers_table = [[d ** n for d in range(10)] for n in range(60)]

            for num in range(lower, upper + 1):
            order = len(str(num))
            row = powers_table[order] # We only care about one row of the table at a time.
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += row[digit]
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Check less numbers



            The last idea that I found online (see section 5) is to skip numbers with certain prefixes. We can guarantee a number will never work if it the sum of all of its digits except the last one is odd.



            The reason for this is as follows. Raising a number to a power won't change its parity. In other words if a number x is even, xn is also even. If x is odd xn is also odd. The sum of the digits raised to a power n will have the same parity as the sum of the digits. For example if we have the 3 digit number 18X. The sum of it's digits cubed is 1**3 (odd) + 83 (even) + X3 which is the same as 1 (odd) + 8 (even) + X.



            Assume the sum of all the digits of an armstrong number excluding the last digit is odd then we have either



            (A**n + B**n + C**n + ...W**n) + X**n == odd + X**n == odd if X is even or
            (A**n + B**n + C**n + ...W**n) + X**n == odd + X**n == even if X is odd


            But if the last digit (X) is even, the sum has to be even it to which it isn't.
            If the last digit is odd, the sum has to be odd, but it isn't. Either way, we get a contradiction, so our assumption must be wrong.



            The code is a bit messy, but it gives the idea. It agrees with the other snippets above for the query (1, 100000)



            def armstrong3(lower, upper): 
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            powers_table = [[d ** n for d in range(10)] for n in range(60)]

            start, end = lower // 10, upper // 10
            for leading_digits in range(start, end + 1):
            if sum(c in "13579" for c in str(leading_digits)) % 2 == 1:
            # Skip numbers with an odd sum parity
            continue

            order = len(str(leading_digits)) + 1 # We will add a last digit later
            row = powers_table[order] # We only care about one row of the table at a time.
            sum = 0

            temp = leading_digits
            while temp > 0:
            digit = temp % 10
            sum += row[digit]
            temp //= 10

            for last_digit in range(10):
            final_total = sum + row[last_digit]
            if 10 * leading_digits + last_digit == final_total and final_total <= upper:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Micro-benchmarked locally I get the following



            %timeit armstrong(1, 100000)
            143 ms ± 104 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong2(1, 100000)
            69.4 ms ± 2.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong3(1, 100000)
            14.9 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


            So the extra tuning was worth it.



            Other work



            I didn't get around to implementing the ideas at this github project. The code is in java and run with N < 10 at the smallest (above you can see my benchmarks were only for N < 5), so I don't think the performance is anywhere near as good as that code. It would be the next place to go if you are interested in pushing things further.



            I looked at using divmod instead of modding and dividing by 10. The performance was worse for me, so I chose not to use it.



            %timeit armstrong(1, 100000)
            143 ms ± 104 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong_divmod(1, 100000)
            173 ms ± 5.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)





            share|improve this answer











            $endgroup$












            • $begingroup$
              Upvoted! Thanks for the amazing response. It helped me a lot.
              $endgroup$
              – Justin
              May 23 at 2:56















            4












            $begingroup$

            I'm going to focus on the performance aspect, as I believe other parts already have good answers. While the program may be simple and performant, it is still fun to go in and micro-optimize a little. In general I would recommend against it.



            Here is your code put into a function to make it clear what changes in the future. I've made the function return the numbers since that was easier for me to work with. I don't have anything new that I would suggest changing.



            def armstrong(lower, upper):
            armstrong_numbers = []
            for num in range(lower, upper + 1):
            order = len(str(num))
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += digit ** order
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Precompute



            A google search says another name for these numbers are narcissistic numbers and that there are only a finite number of them (88 to be exact). We could make a list of the numbers, loop over the list and return the numbers between lower and upper. This only works if someone else has done the work and generated all the numbers already.



            Precompute a little



            The above point could be useful though, the first 9 armstrong numbers are the numbers 1 through 9. Let's "precompute" as many of those as we need. We should really add a check to make sure lower <= upper, but alas...



            def armstrong(lower, upper):
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            for num in range(lower, upper + 1):
            order = len(str(num))
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += digit ** order
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Optimize the powers



            Another good point that showed up when googling armstrong numbers is that no number bigger than 1060 can be an armstrong number. This means we can work out every possible answer for digitorder ahead of time and reuse it each loop. This should be useful as I think computing arbitrary powers is not as fast as looking up the answer in a table.



            As plainly as I can state it, there are only 10 digits, and order is at most 60, so we can make a table 10 * 60 big that stores all the answers to digitorder and use that instead.



            def armstrong(lower, upper):
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            powers_table = [[d ** n for d in range(10)] for n in range(60)]

            for num in range(lower, upper + 1):
            order = len(str(num))
            row = powers_table[order] # We only care about one row of the table at a time.
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += row[digit]
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Check less numbers



            The last idea that I found online (see section 5) is to skip numbers with certain prefixes. We can guarantee a number will never work if it the sum of all of its digits except the last one is odd.



            The reason for this is as follows. Raising a number to a power won't change its parity. In other words if a number x is even, xn is also even. If x is odd xn is also odd. The sum of the digits raised to a power n will have the same parity as the sum of the digits. For example if we have the 3 digit number 18X. The sum of it's digits cubed is 1**3 (odd) + 83 (even) + X3 which is the same as 1 (odd) + 8 (even) + X.



            Assume the sum of all the digits of an armstrong number excluding the last digit is odd then we have either



            (A**n + B**n + C**n + ...W**n) + X**n == odd + X**n == odd if X is even or
            (A**n + B**n + C**n + ...W**n) + X**n == odd + X**n == even if X is odd


            But if the last digit (X) is even, the sum has to be even it to which it isn't.
            If the last digit is odd, the sum has to be odd, but it isn't. Either way, we get a contradiction, so our assumption must be wrong.



            The code is a bit messy, but it gives the idea. It agrees with the other snippets above for the query (1, 100000)



            def armstrong3(lower, upper): 
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            powers_table = [[d ** n for d in range(10)] for n in range(60)]

            start, end = lower // 10, upper // 10
            for leading_digits in range(start, end + 1):
            if sum(c in "13579" for c in str(leading_digits)) % 2 == 1:
            # Skip numbers with an odd sum parity
            continue

            order = len(str(leading_digits)) + 1 # We will add a last digit later
            row = powers_table[order] # We only care about one row of the table at a time.
            sum = 0

            temp = leading_digits
            while temp > 0:
            digit = temp % 10
            sum += row[digit]
            temp //= 10

            for last_digit in range(10):
            final_total = sum + row[last_digit]
            if 10 * leading_digits + last_digit == final_total and final_total <= upper:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Micro-benchmarked locally I get the following



            %timeit armstrong(1, 100000)
            143 ms ± 104 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong2(1, 100000)
            69.4 ms ± 2.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong3(1, 100000)
            14.9 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


            So the extra tuning was worth it.



            Other work



            I didn't get around to implementing the ideas at this github project. The code is in java and run with N < 10 at the smallest (above you can see my benchmarks were only for N < 5), so I don't think the performance is anywhere near as good as that code. It would be the next place to go if you are interested in pushing things further.



            I looked at using divmod instead of modding and dividing by 10. The performance was worse for me, so I chose not to use it.



            %timeit armstrong(1, 100000)
            143 ms ± 104 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong_divmod(1, 100000)
            173 ms ± 5.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)





            share|improve this answer











            $endgroup$












            • $begingroup$
              Upvoted! Thanks for the amazing response. It helped me a lot.
              $endgroup$
              – Justin
              May 23 at 2:56













            4












            4








            4





            $begingroup$

            I'm going to focus on the performance aspect, as I believe other parts already have good answers. While the program may be simple and performant, it is still fun to go in and micro-optimize a little. In general I would recommend against it.



            Here is your code put into a function to make it clear what changes in the future. I've made the function return the numbers since that was easier for me to work with. I don't have anything new that I would suggest changing.



            def armstrong(lower, upper):
            armstrong_numbers = []
            for num in range(lower, upper + 1):
            order = len(str(num))
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += digit ** order
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Precompute



            A google search says another name for these numbers are narcissistic numbers and that there are only a finite number of them (88 to be exact). We could make a list of the numbers, loop over the list and return the numbers between lower and upper. This only works if someone else has done the work and generated all the numbers already.



            Precompute a little



            The above point could be useful though, the first 9 armstrong numbers are the numbers 1 through 9. Let's "precompute" as many of those as we need. We should really add a check to make sure lower <= upper, but alas...



            def armstrong(lower, upper):
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            for num in range(lower, upper + 1):
            order = len(str(num))
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += digit ** order
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Optimize the powers



            Another good point that showed up when googling armstrong numbers is that no number bigger than 1060 can be an armstrong number. This means we can work out every possible answer for digitorder ahead of time and reuse it each loop. This should be useful as I think computing arbitrary powers is not as fast as looking up the answer in a table.



            As plainly as I can state it, there are only 10 digits, and order is at most 60, so we can make a table 10 * 60 big that stores all the answers to digitorder and use that instead.



            def armstrong(lower, upper):
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            powers_table = [[d ** n for d in range(10)] for n in range(60)]

            for num in range(lower, upper + 1):
            order = len(str(num))
            row = powers_table[order] # We only care about one row of the table at a time.
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += row[digit]
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Check less numbers



            The last idea that I found online (see section 5) is to skip numbers with certain prefixes. We can guarantee a number will never work if it the sum of all of its digits except the last one is odd.



            The reason for this is as follows. Raising a number to a power won't change its parity. In other words if a number x is even, xn is also even. If x is odd xn is also odd. The sum of the digits raised to a power n will have the same parity as the sum of the digits. For example if we have the 3 digit number 18X. The sum of it's digits cubed is 1**3 (odd) + 83 (even) + X3 which is the same as 1 (odd) + 8 (even) + X.



            Assume the sum of all the digits of an armstrong number excluding the last digit is odd then we have either



            (A**n + B**n + C**n + ...W**n) + X**n == odd + X**n == odd if X is even or
            (A**n + B**n + C**n + ...W**n) + X**n == odd + X**n == even if X is odd


            But if the last digit (X) is even, the sum has to be even it to which it isn't.
            If the last digit is odd, the sum has to be odd, but it isn't. Either way, we get a contradiction, so our assumption must be wrong.



            The code is a bit messy, but it gives the idea. It agrees with the other snippets above for the query (1, 100000)



            def armstrong3(lower, upper): 
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            powers_table = [[d ** n for d in range(10)] for n in range(60)]

            start, end = lower // 10, upper // 10
            for leading_digits in range(start, end + 1):
            if sum(c in "13579" for c in str(leading_digits)) % 2 == 1:
            # Skip numbers with an odd sum parity
            continue

            order = len(str(leading_digits)) + 1 # We will add a last digit later
            row = powers_table[order] # We only care about one row of the table at a time.
            sum = 0

            temp = leading_digits
            while temp > 0:
            digit = temp % 10
            sum += row[digit]
            temp //= 10

            for last_digit in range(10):
            final_total = sum + row[last_digit]
            if 10 * leading_digits + last_digit == final_total and final_total <= upper:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Micro-benchmarked locally I get the following



            %timeit armstrong(1, 100000)
            143 ms ± 104 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong2(1, 100000)
            69.4 ms ± 2.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong3(1, 100000)
            14.9 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


            So the extra tuning was worth it.



            Other work



            I didn't get around to implementing the ideas at this github project. The code is in java and run with N < 10 at the smallest (above you can see my benchmarks were only for N < 5), so I don't think the performance is anywhere near as good as that code. It would be the next place to go if you are interested in pushing things further.



            I looked at using divmod instead of modding and dividing by 10. The performance was worse for me, so I chose not to use it.



            %timeit armstrong(1, 100000)
            143 ms ± 104 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong_divmod(1, 100000)
            173 ms ± 5.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)





            share|improve this answer











            $endgroup$



            I'm going to focus on the performance aspect, as I believe other parts already have good answers. While the program may be simple and performant, it is still fun to go in and micro-optimize a little. In general I would recommend against it.



            Here is your code put into a function to make it clear what changes in the future. I've made the function return the numbers since that was easier for me to work with. I don't have anything new that I would suggest changing.



            def armstrong(lower, upper):
            armstrong_numbers = []
            for num in range(lower, upper + 1):
            order = len(str(num))
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += digit ** order
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Precompute



            A google search says another name for these numbers are narcissistic numbers and that there are only a finite number of them (88 to be exact). We could make a list of the numbers, loop over the list and return the numbers between lower and upper. This only works if someone else has done the work and generated all the numbers already.



            Precompute a little



            The above point could be useful though, the first 9 armstrong numbers are the numbers 1 through 9. Let's "precompute" as many of those as we need. We should really add a check to make sure lower <= upper, but alas...



            def armstrong(lower, upper):
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            for num in range(lower, upper + 1):
            order = len(str(num))
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += digit ** order
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Optimize the powers



            Another good point that showed up when googling armstrong numbers is that no number bigger than 1060 can be an armstrong number. This means we can work out every possible answer for digitorder ahead of time and reuse it each loop. This should be useful as I think computing arbitrary powers is not as fast as looking up the answer in a table.



            As plainly as I can state it, there are only 10 digits, and order is at most 60, so we can make a table 10 * 60 big that stores all the answers to digitorder and use that instead.



            def armstrong(lower, upper):
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            powers_table = [[d ** n for d in range(10)] for n in range(60)]

            for num in range(lower, upper + 1):
            order = len(str(num))
            row = powers_table[order] # We only care about one row of the table at a time.
            sum = 0

            temp = num
            while temp > 0:
            digit = temp % 10
            sum += row[digit]
            temp //= 10

            if num == sum:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Check less numbers



            The last idea that I found online (see section 5) is to skip numbers with certain prefixes. We can guarantee a number will never work if it the sum of all of its digits except the last one is odd.



            The reason for this is as follows. Raising a number to a power won't change its parity. In other words if a number x is even, xn is also even. If x is odd xn is also odd. The sum of the digits raised to a power n will have the same parity as the sum of the digits. For example if we have the 3 digit number 18X. The sum of it's digits cubed is 1**3 (odd) + 83 (even) + X3 which is the same as 1 (odd) + 8 (even) + X.



            Assume the sum of all the digits of an armstrong number excluding the last digit is odd then we have either



            (A**n + B**n + C**n + ...W**n) + X**n == odd + X**n == odd if X is even or
            (A**n + B**n + C**n + ...W**n) + X**n == odd + X**n == even if X is odd


            But if the last digit (X) is even, the sum has to be even it to which it isn't.
            If the last digit is odd, the sum has to be odd, but it isn't. Either way, we get a contradiction, so our assumption must be wrong.



            The code is a bit messy, but it gives the idea. It agrees with the other snippets above for the query (1, 100000)



            def armstrong3(lower, upper): 
            cutoff = min(10, upper + 1)
            armstrong_numbers = list(range(lower, cutoff))
            if lower < 10:
            lower = 10

            powers_table = [[d ** n for d in range(10)] for n in range(60)]

            start, end = lower // 10, upper // 10
            for leading_digits in range(start, end + 1):
            if sum(c in "13579" for c in str(leading_digits)) % 2 == 1:
            # Skip numbers with an odd sum parity
            continue

            order = len(str(leading_digits)) + 1 # We will add a last digit later
            row = powers_table[order] # We only care about one row of the table at a time.
            sum = 0

            temp = leading_digits
            while temp > 0:
            digit = temp % 10
            sum += row[digit]
            temp //= 10

            for last_digit in range(10):
            final_total = sum + row[last_digit]
            if 10 * leading_digits + last_digit == final_total and final_total <= upper:
            armstrong_numbers.append(num)

            return armstrong_numbers


            Micro-benchmarked locally I get the following



            %timeit armstrong(1, 100000)
            143 ms ± 104 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong2(1, 100000)
            69.4 ms ± 2.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong3(1, 100000)
            14.9 ms ± 31.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


            So the extra tuning was worth it.



            Other work



            I didn't get around to implementing the ideas at this github project. The code is in java and run with N < 10 at the smallest (above you can see my benchmarks were only for N < 5), so I don't think the performance is anywhere near as good as that code. It would be the next place to go if you are interested in pushing things further.



            I looked at using divmod instead of modding and dividing by 10. The performance was worse for me, so I chose not to use it.



            %timeit armstrong(1, 100000)
            143 ms ± 104 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
            %timeit armstrong_divmod(1, 100000)
            173 ms ± 5.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited May 23 at 9:17









            Community

            1




            1










            answered May 22 at 21:02









            spyr03spyr03

            1,436520




            1,436520











            • $begingroup$
              Upvoted! Thanks for the amazing response. It helped me a lot.
              $endgroup$
              – Justin
              May 23 at 2:56
















            • $begingroup$
              Upvoted! Thanks for the amazing response. It helped me a lot.
              $endgroup$
              – Justin
              May 23 at 2:56















            $begingroup$
            Upvoted! Thanks for the amazing response. It helped me a lot.
            $endgroup$
            – Justin
            May 23 at 2:56




            $begingroup$
            Upvoted! Thanks for the amazing response. It helped me a lot.
            $endgroup$
            – Justin
            May 23 at 2:56

















            draft saved

            draft discarded
















































            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f220738%2fpython-program-to-find-armstrong-numbers-in-a-certain-range%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Club Baloncesto Breogán Índice Historia | Pavillón | Nome | O Breogán na cultura popular | Xogadores | Adestradores | Presidentes | Palmarés | Historial | Líderes | Notas | Véxase tamén | Menú de navegacióncbbreogan.galCadroGuía oficial da ACB 2009-10, páxina 201Guía oficial ACB 1992, páxina 183. Editorial DB.É de 6.500 espectadores sentados axeitándose á última normativa"Estudiantes Junior, entre as mellores canteiras"o orixinalHemeroteca El Mundo Deportivo, 16 setembro de 1970, páxina 12Historia do BreogánAlfredo Pérez, o último canoneiroHistoria C.B. BreogánHemeroteca de El Mundo DeportivoJimmy Wright, norteamericano do Breogán deixará Lugo por ameazas de morteResultados de Breogán en 1986-87Resultados de Breogán en 1990-91Ficha de Velimir Perasović en acb.comResultados de Breogán en 1994-95Breogán arrasa al Barça. "El Mundo Deportivo", 27 de setembro de 1999, páxina 58CB Breogán - FC BarcelonaA FEB invita a participar nunha nova Liga EuropeaCharlie Bell na prensa estatalMáximos anotadores 2005Tempada 2005-06 : Tódolos Xogadores da Xornada""Non quero pensar nunha man negra, mais pregúntome que está a pasar""o orixinalRaúl López, orgulloso dos xogadores, presume da boa saúde económica do BreogánJulio González confirma que cesa como presidente del BreogánHomenaxe a Lisardo GómezA tempada do rexurdimento celesteEntrevista a Lisardo GómezEl COB dinamita el Pazo para forzar el quinto (69-73)Cafés Candelas, patrocinador del CB Breogán"Suso Lázare, novo presidente do Breogán"o orixinalCafés Candelas Breogán firma el mayor triunfo de la historiaEl Breogán realizará 17 homenajes por su cincuenta aniversario"O Breogán honra ao seu fundador e primeiro presidente"o orixinalMiguel Giao recibiu a homenaxe do PazoHomenaxe aos primeiros gladiadores celestesO home que nos amosa como ver o Breo co corazónTita Franco será homenaxeada polos #50anosdeBreoJulio Vila recibirá unha homenaxe in memoriam polos #50anosdeBreo"O Breogán homenaxeará aos seus aboados máis veteráns"Pechada ovación a «Capi» Sanmartín e Ricardo «Corazón de González»Homenaxe por décadas de informaciónPaco García volve ao Pazo con motivo do 50 aniversario"Resultados y clasificaciones""O Cafés Candelas Breogán, campión da Copa Princesa""O Cafés Candelas Breogán, equipo ACB"C.B. Breogán"Proxecto social"o orixinal"Centros asociados"o orixinalFicha en imdb.comMario Camus trata la recuperación del amor en 'La vieja música', su última película"Páxina web oficial""Club Baloncesto Breogán""C. B. Breogán S.A.D."eehttp://www.fegaba.com

            Vilaño, A Laracha Índice Patrimonio | Lugares e parroquias | Véxase tamén | Menú de navegación43°14′52″N 8°36′03″O / 43.24775, -8.60070

            Cegueira Índice Epidemioloxía | Deficiencia visual | Tipos de cegueira | Principais causas de cegueira | Tratamento | Técnicas de adaptación e axudas | Vida dos cegos | Primeiros auxilios | Crenzas respecto das persoas cegas | Crenzas das persoas cegas | O neno deficiente visual | Aspectos psicolóxicos da cegueira | Notas | Véxase tamén | Menú de navegación54.054.154.436928256blindnessDicionario da Real Academia GalegaPortal das Palabras"International Standards: Visual Standards — Aspects and Ranges of Vision Loss with Emphasis on Population Surveys.""Visual impairment and blindness""Presentan un plan para previr a cegueira"o orixinalACCDV Associació Catalana de Cecs i Disminuïts Visuals - PMFTrachoma"Effect of gene therapy on visual function in Leber's congenital amaurosis"1844137110.1056/NEJMoa0802268Cans guía - os mellores amigos dos cegosArquivadoEscola de cans guía para cegos en Mortágua, PortugalArquivado"Tecnología para ciegos y deficientes visuales. Recopilación de recursos gratuitos en la Red""Colorino""‘COL.diesis’, escuchar los sonidos del color""COL.diesis: Transforming Colour into Melody and Implementing the Result in a Colour Sensor Device"o orixinal"Sistema de desarrollo de sinestesia color-sonido para invidentes utilizando un protocolo de audio""Enseñanza táctil - geometría y color. Juegos didácticos para niños ciegos y videntes""Sistema Constanz"L'ocupació laboral dels cecs a l'Estat espanyol està pràcticament equiparada a la de les persones amb visió, entrevista amb Pedro ZuritaONCE (Organización Nacional de Cegos de España)Prevención da cegueiraDescrición de deficiencias visuais (Disc@pnet)Braillín, un boneco atractivo para calquera neno, con ou sen discapacidade, que permite familiarizarse co sistema de escritura e lectura brailleAxudas Técnicas36838ID00897494007150-90057129528256DOID:1432HP:0000618D001766C10.597.751.941.162C97109C0155020