Stopping summation loop as soon as digits after decimal points stop changing - python

I have a loop that calculates a approximation. The loop should stop as soon as 10 digits after the decimalpoint stop changing. But I just can't get it to stop.
This is what I tried:
summe = 0
i = 0
l = []
while True:
i = i + 1
summe = summe + 1/(i**3)
l.append(format(summe, '.20f'))
if l[i][:14] == l[i+1][:14]:
break
Here is a example of what the first elements of the list looks like:
['1.00000000000000000000',
'1.12500000000000000000',
'1.16203703703703697947',
'1.17766203703703697947',
'1.18566203703703698658',
'1.19029166666666652574']
So it should continue until the first 10 digits after the decimal point stop changing. Also tried it with i-1 instead of i+1 in the if statement. So shouldn't it compare the element and the element before with each other? Like l[3] == l[2].
Edit: I just saw it should be [:12] instead of [:14]. But same problem is still there

Solution:
summe = 0
summe_10dps = f'{summe:.10f}'
prev_summe_10dps = '' # anything as long as it's != summe_10dps
i = 0
while summe_10dps != prev_summe_10dps:
i = i + 1
prev_summe_10dps = summe_10dps # remember previous sum
print(f'i={i} {summe_10dps} {prev_summe_10dps}') # check
summe = summe + 1/(i**3)
summe_10dps = f'{summe:.10f}'
Notes:
since you only want to inspect the first 10 dps, only output 10dps! Instead of outputting unlimited precision then having to string-slice it.
Using f-strings f'{summe:.10f}' is more compact than the old str.format.
Inside your loop, keep the previous 10dps string value prev_summe_10dps. Then the loop termination condition is simpler and more efficient, just checking if summe_10dps == prev_summe_10dps
There's no reason at all to keep a list, you can just directly compare and termination condition: while summe_10dps != prev_summe_10dps: in a while-loop. Or I had done a while True: ... with an if summe_10dps == prev_summe_10dps: break termination. The former seems slightly better style.
One slight caveat about implementing the "first 10 decimal places stop changing": beware that ':.f' format rounds the unwanted dp's (rather than truncate), and '{:.10f}'.format(1.2000000991500) rounds down to '1.2000000991'
(FYI by the way, the well-known way to get more accuracy, instead sum the series from smallest absolute value to largest, i.e. in reverse order for i = range(large_value_of_i, 0, step=-1). But you're not being asked to do that here.)
Result:
...
i=2190 1.2020567989 1.2020567989
i=2191 1.2020567990 1.2020567990
i=2192 1.2020567991 1.2020567991

The if statement requires the list to contain at least two elements. During the first iteration it will only contain one.

Seems a shame to use strings for this when you can check it mathematically:
summe = 0
i = 1
add = 1.0
while add > 1e-11:
summe += add
i += 1
add = 1/(i**3)
print(summe)

Related

How to count number of times we can halve a number until it reaches 1?

I want to have a number that halves until it reaches 1, then it should return a count of how many times it halved.
example:
halve(4)
2
halve(11)
3
since 4/2 = 2 and 2/2= 1, hence it halved twice before reaching 1, and this is what I want it to return but my code isn't working, why? Can a modification be made ?
Here's my code
Python
def halve(n):
i = 0
for i in range(n,1):
if float(i/2) >=1:
i+=1
return i
Thanks,
It seems to me you can employ some math and just write:
import math
def halve(n):
return math.floor(math.log(n, 2))
You attempt is wrong for three reasons.
You are returning from within the loop. Thus it will never execute more than once.
Your loop will never execute unless n is 0 because range needs a third parameter as a negative number to increment "backwards."
The i assigned by your loop is shadowing the i you have previously assigned. Let's just use a while loop.
def halve(n):
i = 0
while n/2 >= 1:
i += 1
n /= 2
return i
Since you wanted to know what is wrong with your code:
First, immediate problem is that you return on first loop iteration. You need to return only when the number is smaller than 1 (so if condition is not met):
else:
return i
Now, there is another problem - you are iterating over range(n,1) which... doesn't really make sense. You need way less than n divisions to reach number smaller than 1. Use a while loop instead - this way you loop as long as you need. You're also using i as an iterator, but also seem to be dividing it to see if it's more than one - shouldn't you use n there? You're also not reducing n, so you'd actually never reach n < 1.
Taking all of those your code might look like this:
while True:
if float(n/2) >=1:
i+=1
n /= 2
else:
return i
We can improve it even further - as you want your code to end if the condition is not met, we can simply move that to while condition:
def halve(n):
i = 0
while float(n/2) >=1:
i+=1
n /= 2
return i
As #Sembei Norimaki commented, a while loop is a more natural fit for this use case.
def halve(n):
halves = 0
while n > 1:
n /= 2
halves += 1
return halves
This loop can be summarized "As long as n is greater than one, cut it in half and add one to the number of halves we have performed."

Breaking an iterative function in Python before a condition turns False

This is for a school assignment.
I have been tasked to define a function determining the largest square pyramidal number up to a given integer(argument). For some background, these are square pyramidal numbers:
1 = 1^2
5 = 1^2+2^2
14 = 1^2+2^2+3^2
So for a function and parameter largest_square_pyramidal_num(15), the function should return 14, because that's the largest number within the domain of the argument.
I get the idea. And here's my code:
def largest_square_pyramidal_num(n):
sum = 0
i = 0
while sum < n:
sum += i**2
i += 1
return sum
Logically to me, it seemed nice and rosy until I realised it doesn't stop when it's supposed to. When n = 15, sum = 14, sum < n, so the code adds one more round of i**2, and n is exceeded. I've been cracking my head over how to stop the iteration before the condition sum < n turns false, including an attempt at break and continue:
def largest_square_pyramidal_num(n):
sum = 0
for i in range(n+1):
sum += i**2
if sum >= n:
break
else:
continue
return sum
Only to realise it doesn't make any difference.
Can someone give me any advice? Where is my logical lapse? Greatly appreciated!
You can do the following:
def largest_pyr(x):
pyr=[sum([i**2 for i in range(1,k+1)]) for k in range(int(x**0.5)+1)]
pyr=[i for i in pyr if i<=x]
return pyr[-1]
>>>largest_pyr(15)
14
>>> largest_pyr(150)
140
>>> largest_pyr(1500)
1496
>>> largest_pyr(15000)
14910
>>> largest_pyr(150000)
149226
Let me start by saying that continue in the second code piece is redundant. This instruction is used for scenario when you don't want the code in for loop to continue but rather to start a new iteration (in your case there are not more instructions in the loop body).
For example, let's print every number from 1 to 100, but skip those ending with 0:
for i in range(1, 100 + 1):
if i % 10 != 0:
print(i)
for i in range(1, 100 + 1):
if i % 10 == 0:
# i don't want to continue executing the body of for loop,
# get me to the next iteration
continue
print(i)
The first example is to accept all "good" numbers while the second is rather to exclude the "bad" numbers. IMHO, continue is a good way to get rid of some "unnecessary" elements in the container rather than writing an if (your code inside if becomes extra-indented, which worsens readability for bigger functions).
As for your first piece, let's think about it for a while. You while loop terminates when the piramid number is greater or equal than n. And that is not what you really want (yes, you may end up with a piramid number which is equal to n, but it is not always the case).
What I like to suggest is to generate a pyramid number until in exceedes n and then take a step back by removing an extra term:
def largest_square_pyramidal_num(n):
result = 0
i = 0
while result <= n:
i += 1
result += i**2
result -= i ** 2
return result
2 things to note:
don't use sum as a name for the variable (it might confuse people with built-in sum() function)
I swapped increment and result updating in the loop body (such that i is up-to-date when the while loop terminates)
So the function reads like this: keep adding terms until we take too much and go 1 step back.
Hope that makes some sense.
Cheers :)

How to find the indexs of a sequence of values in a list?

I'm trying to find sequences of 200 labels and return their indexes. Here is my first attempt but I'm sure there must be a better way(faster)...
Sequences must be multiples of 200 and next sequence is less than 200 then reject it.
This image shows what my code does at the moment with labels at the top and indexes on the bottom
For a signal processing problem. New to python.
indexs = []
zeros = [0]*200
ones = [1]*200
twos = [2]*200
threes = [3]*200
fours = [4]*200
fives = [5]*200
i=0
while i < range(len(labels)):
if i+200 > len(labels):
break
if labels[i:i+len(zeros)] == zeros:
indexs.extend((range(i,i+len(zeros))))
i+=200
continue
if labels[i:i+len(ones)] == ones:
indexs.extend((range(i,i+len(zeros))))
i+=200
continue
if labels[i:i+len(twos)] == twos:
indexs.extend((range(i,i+len(zeros))))
i+=200
continue
if labels[i:i+len(threes)] == threes:
indexs.extend((range(i,i+len(zeros))))
i+=200
continue
if labels[i:i+len(fours)] == fours:
indexs.extend((range(i,i+len(zeros))))
i+=200
continue
if labels[i:i+len(fives)] == fives:
indexs.extend((range(i,i+len(zeros))))
i+=200
continue
i+=1
EDIT:
Thanks for all the feedback people it is much appreciated. I think I should mention that I'm working with arrays that are 1M+ in size and are dense in that they are mostly blocks of 200. Therefore, I thought a while loop would allow me to skip the majority of loops. Part of my plan is to reduce the dimensionality by taking averages of these 200 blocks but also create a number of features for a classifier. I'm following a similar method in the paper http://www.cis.fordham.edu/wisdm/includes/files/sensorKDD-2010.pdf.
Here's similar (but smaller) generated data
labels = [0]*1250+[1]*15400+[0]*12245+[5]*1204*[4]*20045
Edit2: heres some neater code taking on some of your advice
def tensequencer(df):
labels = df.activity.as_matrix().tolist()
#find and store all indexs
indexs = []
zeros = [0]*200
ones = [1]*200
twos = [2]*200
threes = [3]*200
fours = [4]*200
fives = [5]*200
numbers = [zeros, ones, twos, threes, fours, fives]
i=0
while i < range(len(labels)):
if i+200 > len(labels):
break
if labels[i:i+200] in numbers :
indexs.extend((range(i,i+len(zeros))))
i+=200
continue
i+=1
#index dataframe
df = df.iloc[indexs,:]
df.index=range(df.shape[0])
return df
Scan through the labels, keeping track of how long the sequence of identical numbers is that ends with the current value. Whenever that sequence meets your criteria (i.e. is long enough), add it to indexes.
One wrinkle: say there is a run of 400 3's. It isn't clear from your description if this should be counted as 2 sequences of 200 or 1 of 400 or both. If just the longest, then don't decide about adding to indices until a run ends.
You know ahead of time how many iterations you need: use a for loop:
for i in range(len(labels) - 200):
That eliminates your break statement.
Next, you have six nearly identical sets of check sequences. Drop all six into a single list (of lists), and you can run a loop range(6) for your label checks.
Even better, are you familiar with map and reduce? You could do a nice one-line check, asking whether each element in labels[i+1:i+200] is equal to labels[0]. That would eliminate the six-fold check.
Does that get you moving?
I won't discuss the algorithm, just will make adjustments to the code.
You can use sets to check if an list of 200 elements is made of a single repeated element.
for i in range(len(labels) - 200):
interval = labels[i:i+200]
reduced = set(interval)
if len(reduced) == 1:
repeated_number = reduced.pop()
# You have founded the number repeated 200 times consecutively.
# Now do whatever you wish with it.
Not sure if that will be faster in terms of execution, but that'll definitely be
better to maintain :
indexs = []
for i in range(len(labels)):
if i+200 > len(labels):
break
for j in range(0,6):
cset = [j] * 200
if labels[i:i+len(cset)] == cset:
indexs.extend((range(i,i+len(cset))))
i += 200
break

Is there a better way to write the following method in python?

I am writing a small program, in python, which will find a lone missing element from an arithmetic progression (where the starting element could be both positive and negative and the series could be ascending or descending).
so for example: if the input is 1 3 5 9 11, then the function should return 7 as this is the lone missing element in the above AP series.
The input format: the input elements are separated by 1 white space and not commas as is commonly done.
Here is the code:
def find_missing_elm_ap_series(n, series):
ap = series
ap = ap.split(' ')
ap = [int(i) for i in ap]
cd = []
for i in range(n-1):
cd.append(ap[i+1]-ap[i])
common_diff = 0
if len(set(cd)) == 1:
print 'The series is complete'
return series
else:
cd = [abs(i) for i in cd]
common_diff = min(cd)
if ap[0] > ap[1]:
common_diff = (-1)*common_diff
new_ap = []
for i in range(n+1):
new_ap.append(ap[0] + i*common_diff)
missing_element = set(new_ap).difference(set(ap))
return missing_element
where n is the length of the series provided (the series with the missing element:5 in the above example).
I am sure there are other shorter and more elegant way of writing this code in python. Can anybody help ?
Thanks
BTW: i am learning python by myself and hence the question.
Based on the fact that if an element is missing it is exactly expected-sum(series) - actual-sum(series). The expected sum for a series with n elements starting at a and ending at b is (a+b)*n/2. The rest is Python:
def find_missing(series):
A = map(int, series.split(' '))
a, b, n, sumA = A[0], A[-1], len(A), sum(A)
if (a+b)*n/2 == sumA:
return None #no element missing
return (a+b)*(n+1)/2-sumA
print find_missing("1 3 5 9") #7
print find_missing("-1 1 3 5 9") #7
print find_missing("9 6 0") #3
print find_missing("1 2 3") #None
print find_missing("-3 1 3 5") #-1
Well... You can do simpler, but it would completely change your algorithm.
First, you can prove that the step for the arithmetic progression is ap[1] - ap[0], unless ap[2] - ap[1] is lower in magnitude than it, in which case the missing element is between terms 0 and 1. (This is true as there is a single missing element.)
Then you can just take ap[0] + n * step and print the first one that doesn't match.
Here is the source code (also implementing some minor shortcuts, such as grouping your first three lines into one):
def find_missing_elm_ap_series(n, series):
ap = [int(i) for i in series.split(' ')]
step = ap[1] - ap[0]
if (abs(ap[2] - ap[1]) <= abs(step)): # Check missing elt is not between 0 and 1
return ap[0] + ap[2] - ap[1]
for (i, val) in zip(range(len(ap)), ap): # And check position of missing element
if ap[0] + i * step != val:
return ap[0] + i * step
return series # missing element not found
The code appears to be working. There is perhaps a slightly easier way to get it done. This is due to the fact that you don't have to attempt to look through all of the values to get the common difference. The following code simply looks at the difference between the 1st and 2nd as well as the last and second last.
This works in the event that only a single value is missing (and the length of the list is at least 3). As the min difference between the values will provide you the common difference.
def find_missing(prog):
# First we cast them to numbers.
items = [int(x) for x in prog.split()]
#Then we compare the first and second
first_to_second = items[1] - items[0]
#then we compare the last to second last
last_to_second_last = items[-1] - items[-2]
#Now we have to care about which one is closes
# to zero
if abs(first_to_second) < abs(last_to_second_last):
change = first_to_second
else:
change = last_to_second_last
#Iterate through the list. As soon as we find a gap
#that is larger than change, we fill in and return
for i in range(1, len(items)):
comp = items[i] - items[i-1]
if comp != change:
return items[i-1] + change
#There was no gap
return None
print(find_missing("1 3 5 9")) #7
print(find_missing("-1 1 3 5 9")) #7
print(find_missing("9 6 0")) #3
print(find_missing("1 2 3")) #None
The previous code shows this example. First of all attempting to find change between each of the values of the list. Then iterating till the change is missed, and returning the value that has been expected.
Here's the way I thought about it: find the position of the maximum difference between the elements of the array; then regenerate the expected number in the sequence from the other differences (which should be all the same and the minimum number in the differences list):
def find_missing(a):
d = [a[i+1] - a[i] for i in range(len(a)-1)]
i = d.index(max(d))
x = min(d)
return a[0] + (i+1)*x
print find_missing([1,3,5,9,11])
7
print find_missing([1,5,7,9,11])
3
Here are some ideas:
Passing the length of the series seems like a bad idea. The function can more easily calculate the length
There is no reason to assign series to ap, just do a function using series and assign the result to ap
When splitting the string, don't give the sep argument. If you don't give the argument, then consecutive white space will also be removed and leading and trailing white space will also be ignored. This is more friendly on the format of the data.
I've combined a few operations. For example the split and the list comprehension converting to integer make sense to group together. There is also no need to create cd as a list and then convert that to a set. Just build it as a set to start with.
I don't like that the function returns the original series in the case of no missing element. The value None would be more in keeping with the name of the function.
Your original function returned a one item set as the result. That seems odd, so I've used pop() to extract that item and return just the missing element.
The last item was more of an experiment with combining all of the code at the bottom into a single statement. Don't know if it is better, but it's something to think about. I built a set with all the correct numbers and a set with the given numbers and then subtracted them and returned the number that was missing.
Here's the code that I came up with:
def find_missing_elm_ap_series(series):
ap = [int(i) for i in series.split()]
n = len(ap)
cd = {ap[i+1]-ap[i] for i in range(n-1)}
if len(cd) == 1:
print 'The series is complete'
return None
else:
common_diff = min([abs(i) for i in cd])
if ap[0] > ap[1]:
common_diff = (-1)*common_diff
return set(range(ap[0],ap[0]+common_diff*n,common_diff)).difference(set(ap)).pop()
Assuming the first & last items are not missing, we can also make use of range() or xrange() with the step of the common difference, getting rid of the n altogether, it can also return more than 1 missing item (although not reliably depending on number of items missing):
In [13]: def find_missing_elm(series):
ap = map(int, series.split())
cd = map(lambda x: x[1]-x[0], zip(ap[:-1], ap[1:]))
if len(set(cd)) == 1:
print 'complete series'
return ap
mcd = min(cd) if ap[0] < ap[1] else max(cd)
sap = set(ap)
return filter(lambda x: x not in sap, xrange(ap[0], ap[-1], mcd))
....:
In [14]: find_missing_elm('1 3 5 9 11 15')
Out[14]: [7, 13]
In [15]: find_missing_elm('15 11 9 5 3 1')
Out[15]: [13, 7]

Add commas into number string [duplicate]

This question already has answers here:
How to print a number using commas as thousands separators
(30 answers)
Closed 10 months ago.
I have a value running through my program that puts out a number rounded to 2 decimal places at the end, like this:
print ("Total cost is: ${:0.2f}".format(TotalAmount))
Is there a way to insert a comma value every 3 digits left of the decimal point?
e.g. 10000.00 becomes 10,000.00 or 1000000.00 becomes 1,000,000.00.
In Python 2.7 and 3.x, you can use the format syntax :,
>>> total_amount = 10000
>>> print("{:,}".format(total_amount))
10,000
>>> print("Total cost is: ${:,.2f}".format(total_amount))
Total cost is: $10,000.00
This is documented in PEP 378 -- Format Specifier for Thousands Separator and has an example in the Official Docs "Using the comma as a thousands separator"
if you are using Python 3 or above, here is an easier way to insert a comma:
First way
value = -12345672
print (format (value, ',d'))
or another way
value = -12345672
print ('{:,}'.format(value))
You could use locale.currency if TotalAmount represents money. It works on Python <2.7 too:
>>> locale.setlocale(locale.LC_ALL, '')
'en_US.utf8'
>>> locale.currency(123456.789, symbol=False, grouping=True)
'123,456.79'
Note: it doesn't work with the C locale so you should set some other locale before calling it.
another way very short is
value = -122212123.12
print(f"{value:,}")
'{:20,.2f}'.format(TotalAmount)
This is not particularly elegant but should work too :
a = "1000000.00"
e = list(a.split(".")[0])
for i in range(len(e))[::-3][1:]:
e.insert(i+1,",")
result = "".join(e)+"."+a.split(".")[1]
A function that works in python2.7+ or python3.1+
def comma(num):
'''Add comma to every 3rd digit. Takes int or float and
returns string.'''
if type(num) == int:
return '{:,}'.format(num)
elif type(num) == float:
return '{:,.2f}'.format(num) # Rounds to 2 decimal places
else:
print("Need int or float as input to function comma()!")
Latest versions of python use f-strings. So you can do this:
print("Total cost: {total_amount:,}
As long as total_amount is a not a string. Otherwise you'd need to cast it to a number type first like so:
print("Total cost: {Decimal(total_amount):,}
The above answers are so much nicer than the code I was using in my (not-homework) project:
def commaize(number):
text = str(number)
parts = text.split(".")
ret = ""
if len(parts) > 1:
ret = "."
ret += parts[1] # Apparently commas aren't used to the right of the decimal point
# The -1 offsets to len() and 0 are because len() is 1 based but text[] is 0 based
for i in range(len(parts[0]) - 1,-1,-1):
# We can't just check (i % 3) because we're counting from right to left
# and i is counting from left to right. We can overcome this by checking
# len() - i, although it needs to be adjusted for the off-by-one with a -1
# We also make sure we aren't at the far-right (len() - 1) so we don't end
# with a comma
if (len(parts[0]) - i - 1) % 3 == 0 and i != len(parts[0]) - 1:
ret = "," + ret
ret = parts[0][i] + ret
return ret
Started learning Python about 5 hours ago, but I think I came up with something for integers (sorry, couldn't figure out floats). I'm in high school, so big chance the code could be way more efficient; I just made something from scratch that made sense to me. If anyone has any ideas on how to improve with ample explanation of how it works, let me know!
# Inserts comma separators
def place_value(num):
perm_num = num # Stores "num" to ensure it cannot be modified
lis_num = list(str(num)) # Makes "num" into a list of single-character strings since lists are easier to manipulate
if len(str(perm_num)) > 3:
index_offset = 0 # Every time a comma is added, the numbers are all shifted over one
for index in range(len(str(perm_num))): # Converts "perm_num" to string so len() can count the length, then uses that for a range
mod_index = (index + 1) % 3 # Locates every 3 index
neg_index = -1 * (index + 1 + index_offset) # Calculates the index that the comma will be inserted at
if mod_index == 0: # If "index" is evenly divisible by 3
lis_num.insert(neg_index, ",") # Adds comma place of negative index
index_offset += 1 # Every time a comma is added, the index of all items in list are increased by 1 from the back
str_num = "".join(lis_num) # Joins list back together into string
else: # If the number is less than or equal to 3 digits long, don't separate with commas
str_num = str(num)
return str_num
I feel comfortable using like this in python:
input_value=float(input())
print("{:,}".format(input_value))

Categories

Resources