0
> num = 0
total = 0

while(True):
    num += 1
    total += num
    if total < 100:
        break
     
print('The last number is %d and total is %d' % (num, total))

The answer is supposed to be "The last number is 13 and total is 91" but I keep getting 1 for the answers. I know there's something wrong with the sixth line but I can't figure out how to make it right.

2 Answers2

2

Even with the fix of if total >= 100: break you'll always get one higher number (and total) than the limit. That's because total is first assigned and then checked. So it's assigned 105 with number being 14 and then checked to see if it exceeds 100.

Two changes:

  1. check what total would be before assigning it.
  2. check this potential value of total at the start of the loop
num = 0
total = 0
while True:
    if total + num + 1 > 100:
        break
    num += 1
    total += num

print(f'The last number is {num} and total is {total}')
# The last number is 13 and total is 91

And using an Assignment Expression (aka Walrus operator) you can reduce the re-calculation by assigning checked value to the next possible total:

num = 0
total = 0
while True:
    if (next_total := total + num + 1) > 100:
        break
    num += 1
    total = next_total  # don't add, just assign

print(num, total)  # the values are more important than printing
# 13 91

Edit: More advanced ways to do it using functional programming:

from itertools import accumulate, count, takewhile

result = list(takewhile(lambda t: t < 100, accumulate(count(1))))
print(result)  # output for sanity
# [1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91]
print(len(result), result[-1])  # num and total
# 13 91

or

from itertools import accumulate, count, takewhile

result = list(enumerate(takewhile(lambda t: t < 100, accumulate(count(1))), start=1))
print(result)  # output for sanity
# [(1, 1), (2, 3), (3, 6), (4, 10), (5, 15), (6, 21), (7, 28), (8, 36), (9, 45), (10, 55), (11, 66), (12, 78), (13, 91)]
print(result[-1])  # each element is tuple of num and total
# (13, 91)

This second functional option appears longer, so why use it? It can be combined with a deque of size 1 so that the result list never needs to be stored, only calculated. And when the list isn't stored, we wouldn't know it's length len(). So that index comes from enumerate(..., start=1). Without needing to store the list, it can be used for huge numbers without running out of RAM.

from collections import deque
from itertools import accumulate, count, takewhile

# items below is an iterable, not the actual list
items = enumerate(takewhile(lambda t: t < 100, accumulate(count(1))), start=1)
print(items)
# <enumerate object at 0x0000024F9CCBB5C0>
dd = deque(items, maxlen=1)
last_element = dd.pop()
print(last_element)  # tuple of num & total
# (13, 91)

And with a huge limit:

def largest_until_total(limit):
    items = enumerate(takewhile(lambda t: t < limit, accumulate(count(1))), start=1)
    return deque(items, maxlen=1).pop()

print(largest_until_total(1_000_000_000_000))  # 1 trillion
# (1414213, 999999911791)
print(largest_until_total(1_000_000_000_000_000))  # 1 quadrillion
# (44721359, 999999997764120)

However, if you really wanted to use this for very large limits, it would be better to reverse engineer it with the "sum of first N natural numbers" formula N * (N+1) / 2

aneroid
  • 12,983
  • 3
  • 36
  • 66
  • 2
    Better readable: `if total + num + 1 > 100:` could be written as `if total + num >= 100:`. – Matthias Apr 12 '22 at 06:14
  • @Matthias I agree with that but it _hides_ the off-by-one problem for newer users. The `+ 1` makes the intent clearer. Also, that the `+ 1` is actually for `num`, ie `num + 1` which is the next number being added. – aneroid Apr 12 '22 at 06:17
  • Great answer @aneroid, I'm just curious, is the any reason to modify the OP's `print` statement? And did you use the *python* interpreter syntax because OP used it as well? Personally I prefer it when it's written in a simple format that can be copy pasted in an online compiler to test it out quickly. – Liam Apr 12 '22 at 06:21
  • 1
    @Liam The answer to the first two question is related: I did it in Python REPL (if that's what you meant by _"python interpreter syntax"_) so `print`'ing is not required to see the result. I'll edit the question to remove the REPL bits. – aneroid Apr 12 '22 at 06:27
  • 1
    @newbiepythoner You're welcome. And welcome to StackOverflow! If that helped, read: [What should I do when someone answers my question?](https://stackoverflow.com/help/someone-answers), and about [accepting](https://meta.stackexchange.com/a/5235/193893) and [voting](https://stackoverflow.com/privileges/vote-up). – aneroid Apr 12 '22 at 06:57
  • @aneroid Lovely answer, although one suggestion (edit queue is full). I'd change `print(num, total)` to `print('The last number is %d and total is %d' % (num, total))` to keep the print statement closer to the OP's code. – Liam Apr 12 '22 at 07:27
  • 1
    @Liam Ok, I've edited only the first print to show the same output as the OP. I generally don't bother with such `print`s unless it's important to the answer, such as formatting or alignment. What's more important is the actual values of `num` and `total`. Once they have the result, they can do what they want with it - print or log, etc. is unrelated. And having excessive prints can sometimes make the important details harder to see. Like in `print(f'The last number is {result[-1][0]} and total is {result[-1][1]}')`. And print's hide the actual type of the data; like in the tuples above. – aneroid Apr 12 '22 at 08:07
  • 1
    I totally agree @aneroid, I appreciate you taking the time to edit your answer, I've deleted mine and upvoted yours, as a result since yours is better anyways. Thank you for being so kind and patient – Liam Apr 12 '22 at 08:13
0

Comments should be description enough.

def max_sum(m):
    t, n = 0, 2
    while True:
        #linear sum
        t_ = int((n+1)*(n/2))
        #adjust num based on current condition
        n += (t_<m)
        n -= (t_>m)
        #store or print respectively
        if t_<m: t = t_
        else:
            print(f'num is {n} total is {t}')
            break

max_sum(100)

OneMadGypsy
  • 4,640
  • 3
  • 10
  • 26