Home > other >  Python Loop While cumulative sum less than 100
Python Loop While cumulative sum less than 100

Time:04-13

> 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.

CodePudding user response:

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

CodePudding user response:

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)

  • Related