Can i know why my while loop is not working? - python-3.x

This code should find the minimal number of moves required to obtain a strictly increasing sequence from the input, but it doesn't.
def arrayChange(a):
count = 0
for i in range(len(a)):
if a[i]<a[i-1] and i>0:
while a[i]>a[i-1]:
a[i]+=1
count+=1
return count
print(arrayChange([-1000, 0, -2, 0]))

Related

Sum of consecutive data in List - Python

I have a long list of number of which a sample look something like shown below:
L = [0, 1, 2, 0, 0, 2, 3, 1, 0, 10, 0, 2, 4, 5, 0, 0, 7, 8, 9, 0, ...]
singleData = []
sumofTwo = []
sumofThree = []
sumofFour = []
.
.
What I want to be able to do is categorize a data OR sum of two or more consecutive data into the respective lists based on the COUNT of numbers involved in the sum operation. So, if there is an occurrence of zero between two numbers, then their sum would not be considered.
For example, if I take the list above, the sum of 1 and 2 is to be added to the list sumofTwo. Similarly, if it is the sum of three consecutive numbers without the occurrence of 0's then the sum is expected to be in the sumofThree list (sum of 2,3,1; 2,4,5 and 7, 8, 9). If a number occurs between two or more 0's then it is to be appended to the singleData list (eg. 10).
How can i achieve this considering that in the list(L) there can be a sum of random consecutive numbers? Example, sum of 6 or 7 or 8 or any consecutive numbers?
I was able to segregate only a single number between 0's and the sum of two numbers. Following is the code:
for i in range(len(l)):
try:
if i == 0:
if l[i] == 0:
continue
elif l[i] != 0 and l[i+1] == 0:
singleData.append(l[i])
elif l[i] != 0 and l[i+1] != 0:
sumofTwo.append(l[i]+l[i+1])
elif i == len(l)-1:
if l[i] != 0 and l[i-1] == 0:
singleData.append(l[i])
else:
if l[i] != 0:
if l[i+1] == 0 and l[i-1] == 0:
singleData.append(l[i])
elif l[i+1] != 0:
sumofTwo.append(l[i]+l[i+1])
except IndexError:
print("Index out of range")
I realized that my code will only get messier with more cases of the sum of consecutive numbers and ultimately end up with error.
Can anybody help me out? Thanks in advance :))))
I would recommend using a dictionary to store the results:
L = [0, 1, 2, 0, 0, 2, 3, 1, 0, 10, 0, 2, 4, 5, 0, 0, 7, 8, 9, 0]
consecutive_sums = {} # Create an empty dictionary
I would also recommend directly iterating through the list, rather than using range(len(L). e.g.
for number in L:
print(number)
Then, you can just create a counter variable to check how long the current sequence is, and reset the counter when you get to a zero.
L = [0, 1, 2, 0, 0, 2, 3, 1, 0, 10, 0, 2, 4, 5, 0, 0, 7, 8, 9, 0]
consecutive_sums = {} # Create an empty dictionary
counter = 0
sum = 0
for number in L:
if number == 0: # Save the current sum and reset counter
# Check if we've already had a sequence of this length.
# If so, we have already added a list, so can append to it.
if counter in consecutive_sums:
consecutive_sums[counter].append(sum)
else: # If this is the first time we've had this length sequence
# we need to create a new key value pair in the dictionary.
# Note that the value is a list containing `sum`.
# Make sure to use a list so that you can append to it later.
consecutive_sums[counter] = [sum]
# Reset counter and sum
counter = 0
sum = 0
else: # Increment counter
counter += 1
sum += number
print(consecutive_sums)
Note that this code will not sort the dictionary keys, so the sums of sequences of length 1 may not appear at the beginning. But you can access it using consecutive_sums[1].
Also note that this version of the code also counts sequences of length 0. I suspect this is not what you want, but I'll let you figure out how to fix it!
Output:
{0: [0, 0, 0], 2: [3], 3: [6, 11, 24], 1: [10]}
EDIT:
I've intentionally tried to solve this using just builtin functions and datatypes. But if you really want to be fancy, you can use collections.defaultdict.
Below is an alternative version which uses defaultdict:
from collections import defaultdict
L = [0, 1, 2, 0, 0, 2, 3, 1, 0, 10, 0, 2, 4, 5, 0, 0, 7, 8, 9, 0]
consecutive_sums = defaultdict(list) # Create an empty defaultdict of type list
counter = 0
sum = 0
for number in L:
if number == 0: # Save the current sum and reset counter
# The magic of defaultdict:
# We don't need to check if the key exists.
# If it doesn't exist yet, defaultdict will automatically make
# an empty list for us to append to!
consecutive_sums[counter].append(sum)
# Reset counter and sum
counter = 0
sum = 0
else: # Increment counter
counter += 1
sum += number
print(consecutive_sums)
The first thing I would do is to organize a bit better our output lists, so that each can be accessed in the same way using the number of consecutive numbers. As #daviewales suggested, you could do this with a dictionary with lists as values, something like sums = {}, so that sums[1] would be the same as singleData, sums[2] the same as sumofTwo, and so on. That way, you will avoid a lot of if to know in what list you should put your data, you'll just need to use stuff like sums[nbOfValuesInSum].
Second thing, you could write a function that detects your sequences of non-zero values. A possibility would be a function that takes a list and a start index, and returns the start and end indexes of the next "interesting" sequence. It would look like this :
def findNextSequence(l, start):
while l[start] == 0:
if start == len(l)-1:
return None # there is no non-zero value left
start+=1
# when we exit the loop, start is the index of the first non-zero value
end = start + 1
while l[end] != 0:
if end == len(l)-1:
break
end+=1
# and now end is the index of the first zero value after the sequence
return (start, end)
Then you can call it in a loop, like this:
i = 0
while True:
bounds = findNextSequence(l, i)
if bounds is None:
break # there is no non-zero value left
seq = l[bounds[0]:bounds[1]] # get the start and end index of the sequence
if len(seq) not in sums:
sums[len(seq)] = []
sums[len(seq)].append(sum(seq)) # see? No need to explicitly check len(seq) to know what list I want
i = bounds[1] # get ready for the next iteration
if i == len(l):
break
NB : no need to pass l as a parameter of findNextSequence if it's a global variable

Simple Python program to convert roman numerals to the international system

I've tried to put together a basic program to convert roman numbers to conventional numbers and I seem to have made some mistake and right now it's a hit or miss.Do take a look at the code.I've described it better in the code as comments.Thanks!
def roman(roman_num):
inter_dic = {
"I":1,"V":5,"X":10,"L":50,"C":100,"D":500,"M":1000
}
output = 0
num = roman_num.upper()
#this is to ensure that the number appears as uppercase alphabets
for numeral in num:
numeral_index = num.index(numeral)
#I've squeezed an exception here to handle the last digit in the number
try:
#for the additive part
if inter_dic[numeral] > inter_dic[num[numeral_index + 1]]:
output += inter_dic[numeral]
#for the subtraction part
elif inter_dic[numeral] < inter_dic[num[numeral_index + 1]]:
output -= inter_dic[numeral]
elif inter_dic[numeral] == inter_dic[num[numeral_index + 1]]:
output += inter_dic[numeral]
#the following line is actually dead code,but I've added it just in case.
else:
print("There was an error.")
#the IndexError will be called when the last digit is reached and so the last digit
#will be added
except IndexError:
output += inter_dic[numeral]
return output
assert roman("cxcix") == 199
#this returns an assertion error
#when the function is called,the output is 179
This should do what you want:
def roman(roman_num):
inter_dic = {
"I":1,"V":5,"X":10,"L":50,"C":100,"D":500,"M":1000
}
x = list(map(lambda x: inter_dic[x], roman_num.upper()))
for idx in range(len(x)-1):
if x[idx+1] > x[idx]:
x[idx] *= -1
return x
decimal = roman("cxcix")
print(decimal) # Output: [100, -10, 100, -1, 10]
print(sum(decimal)) # Output: 199
This works on the assumption that the numbers are properly structured. As in, the numbers represented should be in order of the biggest to the smallest.
The above code will work even if you just give it one character, because the loop is based on the range(len() - 1) of the given list that is created when we translate the letters into their integers.
If x == [100] then len(x)-1 == 0 which will just terminate the for-loop immediately, so we will not encounter any IndexError inside of the loop.
To explain what is happening in your code that differs from my version we can create a simple example here:
lst = list("abcabc")
for idx, letter in enumerate(lst):
print(f"Letter: {letter}, list.index: {lst.index(letter)}, Actual index: {idx}")
Output:
Letter: a, list.index: 0, Actual index: 0
Letter: b, list.index: 1, Actual index: 1
Letter: c, list.index: 2, Actual index: 2
Letter: a, list.index: 0, Actual index: 3
Letter: b, list.index: 1, Actual index: 4
Letter: c, list.index: 2, Actual index: 5
If we look at the documentation for list.index, we can see this description:
index(self, value, start=0, stop=2147483647, /)
Return first index of value.
Raises ValueError if the value is not present.
So because there are repeated values inside of lst when we call to check for it's index, it just returns the first value that matches the variable we give it.

Function that fills in missing numbers to create complete sequence

I'm trying to create a function that will fill in any missing numbers in between two numbers in a list. The original list must be altered and cannot be new.
For example: [13,15,20] would return [13,14,15,16,17,18,19,20].
Note that I am not allowed to use a range function.
Here's my code:
def complete(list1):
i= 0
if len(list1) > 1:
for number in list1:
if number - list1[i+1] != -1:
number += 1
list1.insert(i + 1, number)
i += 1
return list1
else:
return list1
I got a "list index out of range" error.
Here is the source of your error:
...
for number in list1:
if number - list1[i+1] != -1:
...
i += 1
Basically, there comes a point (that point being the last number in list1) when i+1 gets you out of bounds and you are not doing anything to prevent that from happening. Indexing is tricky like that, so I would like to offer an indexing-free (well, almost) approach. By the way, from your comment to Bonfire's answer, I see that the task is to change original lists in-place. While mutating arguments is considered a very poor coding practice these days, here is a relatively efficient way of doing that:
import typing as t
def complete_sequence(partial: t.List[int]) -> t.List[int]:
# edge case
if len(partial) < 2:
return partial
# a lookup table for numbers we already have
observed = set(partial)
# append numbers we don't have
start = partial[0]
stop = partial[-1]
num = start + 1
while num < stop:
if not num in observed:
partial.append(num)
num += 1
# in-place sort
partial.sort()
return partial
As you see, instead of inserting values between existing numbers (paying O(n) time for each insertion), we can simply append everything (O(1) per insertion) and sort. This not only simplifies the logic (we no longer have to track those pesky indices), but also reduces computational time-complexity from O(n^2) to O(n*log(n)).
To achieve what you want to do I have made some changes to the logic:
def complete(list1):
if len(list1) < 2 : return list1
num = list1[0]
i = -1
while num < list1[-1]:
num += 1
i += 1
if num in list1: continue
if i < len(list1) - 1:
list1.insert(i + 1, num)
else:
list1.append(num)
return list1
print(complete([13, 14, 20]))
# [13, 14, 15, 16, 17, 18, 19, 20]
print(complete([13, 14, 15]))
# [13, 14, 15]

How do I replace an element in a list?

I'm not really sure how to properly word my question but I will try my best.
I'm working on a class project and one part is using lists.
So first off I set random values in a list. a = [1,1,1,0,0,0,0]
To make this clearer let's say I'm making a program to manage the seating arrangement in a cinema. 1 is for taken and 0 is for vacant. So this means that there are only 4 vacant seats remaining. A customer uses the program and the program chooses the seat for the customer, so the first zero is chosen and now there are only three vacant seats left. Another customer uses the program and the same thing happens until all the seats are taken which will then print a statement that says "All seats are taken"
How do I pull this off?
I hope I was able to word this question properly
Thanks in advance!
Here's what I have tried. (and absolutely failed lol):
a = [1, 1, 1, 0, 0, 0, 0]
for i in a:
if i ==0:
a[i] = 1
Edit:
while True:
seats = [1,1,1,0,0,0]
for i, seat in enumerate(seats):
if seat == 0:
seats[i] = 1
print("Your seat is at", i)
break
else:
print("No more seats!")
user = int(input("Press 1 to reserve another seat, 2 to quit the program"))
if user == 2:
break
Guessing from the type of question, some of this might be new to you. However, I believe it's always nice to see different ways to do things, and you can always look stuff up and learn something new!
You'll need to check the list for zeros, and if one is found, switch it with a 1. Importantly, you should then stop (break) looking, as you don't want to fill all seats at once.
Something like this is a possible way to write that up (again, many ways to do this, this is just one). You would still need to repeat this process however many times you want to seat someone.
for i, seat in enumerate(seats):
if seat == 0:
seats[i] = 1
print("Your seat is at", i)
break # Stop looking for seats.
else: # If no break happened.
print("No more seats!")
a[a.index(0)] = 1 will replace the first zero found with a one, and throw an exception if there are no more zeros. You can catch the exception:
try:
a[a.index(0)] = 1
except ValueError:
print('no more seats')
Your code snippet will set the first element to 1 if it is 0. You need a way to find the index of the first element that is 0 and just set that to 1.
Try the index function to get the index for one element and use that...or just save the total and currently occupied seat counts as numbers.
Why are you comparing i with 0, when you should be checking the array content:
a = [1, 1, 1, 0, 0, 0, 0]
for i, val in enumerate(a):
if val == 0:
a[i] = 1
a =[1,1,1,0,0,0]
for x in sorted(a):
if len(a) == sum(a):
print('seats are full ')
else:
a[x].pop
a.append(1)
print ('your seat is at a[x]')
Okay, so I have given two fixes for your problem, using both for and while loops. Personally, I believe the while loop is much more preferable in this scenario, to avoid using the break command.
def original_main():
a = [1, 1, 1, 0, 0, 0, 0]
for i in a:
# i here will be equal to the values in the list, what happens here is that the value is being copied to a new paramater.
if i == 0:
a[i] = 1
print('original_main:', a)
def fixed_main():
a = [1, 1, 1, 0, 0, 0, 0]
for index, value in enumerate(a):
# What happens here is that enumerate takes both the index and value of the element and uses them.
# You check whether the value of the seat is 0, AKA free.
if value == 0:
# You update the location of the same element/ seat to being occupied.
a[index] = 1
# To stop the loop, often preferable not to need to use breaks.
break
print('fixed_main:', a)
def alternative_main():
a = [1, 1, 1, 0, 0, 0, 0]
index = 0
# Cycle through the list until reaching the end of the list or the seat is free.
while index < len(a) and a[index] != 0:
index += 1
# Check whether there were any empty seats as if there weren't index will be higher or equal to the amount of seats.
# Thechnically it can only be equal due to the while condition but it is a better practice to cover all scenarios.
if index >= len(a):
print('No empty seats.')
else:
a[index] = 1
print('alternative_main:', a)
if __name__ == '__main__':
original_main()
print()
fixed_main()
alternative_main()
Printed result:
original_main: [1, 1, 1, 0, 0, 0, 0]
fixed_main: [1, 1, 1, 1, 0, 0, 0]
alternative_main: [1, 1, 1, 1, 0, 0, 0]

Maximum Number in Mountain Sequence

Given a mountain sequence of n integers which increase firstly and then decrease, find the mountain top.
Example
Given nums = [1, 2, 4, 8, 6, 3] return 8
Given nums = [10, 9, 8, 7], return 10
class Solution:
"""
#param nums: a mountain sequence which increase firstly and then decrease
#return: then mountain top
"""
def mountainSequence(self, nums):
# write your code here
if nums == []:
return None
if len(nums) <= 1:
return nums[0]
elif len(nums) <= 2:
return max(nums[0], nums[1])
for i in range(len(nums) -2):
if nums[i] >= nums[i + 1]:
return nums[i]
return nums[-1]
it stuck at [3,5,3]. Based on my analysis, it went wrong after running the for loop. But I cannot figure it out why the for loop failed.
this should be more efficient than your approach. it is a binary search customized for your use-case:
def top(lst):
low = 0
high = len(lst)
while low != high:
i = (high+low)//2
if lst[i] < lst[i+1]:
low = i+1
else:
high = i
return low
it starts in the middle of the list and checks if the series is still increasing there. if it is it sets low and will ignore all indices below low for the rest of the algorithm. if the series decreases already, high is set to the current index and all the elements above are ignored. and so on... when high == low the algorithm terminates.
if you have two or more of the same elements at the maximum of your list (a plateau) the algorithm will not even terminate.
and i skipped the tests for empty lists or lists of length 1.
This will get all triplets from your input, isolate all that are higher in the middle then left or right and return the one that is highest overall:
def get_mountain_top(seq):
triplets = zip(seq, seq[1:], seq[2:])
tops = list(filter(lambda x: x[0] < x[1] > x[2], triplets))
if tops:
# max not allowed, leverage sorted
return sorted(tops, key = lambda x:x[1])[-1]
# return max(tops,key = lambda x:x[1])
return None
print(get_mountain_top([1,2,3,4,3,2,3,4,5,6,7,6,5]))
print(get_mountain_top([1,1,1]))
Output:
(6,7,6)
None
It does not handle plateaus.
Doku:
zip(), filter() and max()

Resources