I solved the below problem using nested while loops. Is there a way to solve the problem in a simple, Pythonic way?
Question:
Define a procedure that takes in a string of numbers from 1-9 and outputs a list with the following parameters:
Every number in the string should be inserted into the list.
If a number x in the string is less than or equal to the preceding number y, the number x should be inserted into a sublist.
Continue adding the following numbers to the sublist until reaching a number z that is greater than the number y.
Then add this number z to the normal list and continue.
They are comparing the subsequent against the previous digit.
For example:
string = '543987'
result = [5,[4,3],9,[8,7]]
string= '987654321'
result = [9,[8,7,6,5,4,3,2,1]]
string = '455532123266'
result = [4, 5, [5, 5, 3, 2, 1, 2, 3, 2], 6, [6]]
My code:
def numbers_in_lists(string):
array = []
for i in string:
array.append(int(i))
temp_list = []
final_list = [array[0]]
i = 0
while i+1 < len(array):
if array[i] >= array[i+1]:
j = 0
while j+1 < len(array[i:]) and array[i] >= array[i:][j+1]:
temp_list.append(array[i:][j+1])
j += 1
final_list.append(temp_list)
i += len(temp_list)
temp_list = []
else:
final_list.append(array[i+1])
i += 1
#print final_list
return final_list
string = '543987'
print numbers_in_lists(string)
[5, [4, 3], 9, [8, 7]]
You can certainly simplify the approach; I would do it as follows:
def numbers_in_lists(string):
"""Describe what it does here!"""
output = []
sublist = []
for num in map(int, string):
if not output or num > output[-1]:
if sublist:
output.append(sublist)
sublist = []
output.append(num)
else:
sublist.append(num)
if sublist:
output.append(sublist)
return output
In use:
>>> numbers_in_lists('543987')
[5, [4, 3], 9, [8, 7]]
>>> numbers_in_lists('987654321')
[9, [8, 7, 6, 5, 4, 3, 2, 1]]
>>> numbers_in_lists('455532123266')
[4, 5, [5, 5, 3, 2, 1, 2, 3, 2], 6, [6]]
This is the solution, you should consider.
num = '455532123266'
final_list = [];
prev = 0
sublist = []
for n in num:
n = int(n)
if (n > prev):
if (sublist != []):
final_list.append(sublist)
sublist = []
final_list.append(n)
else:
sublist.append(n)
prev = n
if sublist != []:
final_list.append(sublist)
print final_list
[4, 5, [5, 5, 3, 2, 1], 2, 3, [2], 6, [6]]
well... Adding my 2 cents :-)
def custom_func(input_string):
if not input_string:
return []
value = input_string[0]
output_list = [int(value)]
for char in input_string[1:]:
if char > value:
# Encountered a value higher than the past maximum
output_list.append(int(char))
else:
if value == str(output_list[-1]):
# First value that is lesser than the past maximum
output_list.append([int(char)])
else:
# nth value which is lesser than the past maximum
output_list[-1].append(int(char))
value = char # updating the past maximum
return output_list
I would consider doing it in more functional way.
def solve(input):
pairs = zip(input, input[1:])
sublist = [False] + map(lambda x: x[0] > x[1], pairs)
result = []
for (val, to_sublist) in zip(input, sublist):
if not to_sublist:
result.append(val)
else:
if isinstance(result[-1], list):
result[-1].append(val)
else:
result.append([val])
return result
This is allows for separation of checking to_sublist predicate and actual building of resulting data structure. To my understanding first number never goes to sublist so it always starts with False.
The problem is not very well-defined, in that the example input does not exactly clarify 2 issues:
What should happen with '567' - should there be empty list after each number
The example cannot tell if you are comparing the subsequent against the previous digit or the first digit of the decreasing subsequence!
Thus here is my take, it is not necessarily the most pythonic, but I couldn't come up with more pythonic solution.
def numbers_in_lists(string):
current_min = -1 # larger than any digit
rv = []
current_list = []
def push_sublist():
if current_list:
largest = current_list.pop(0)
rv.extend([largest, current_list])
for digit in map(int, string):
if digit > current_min:
push_sublist()
current_list = []
current_min = digit
current_list.append(digit)
push_sublist() # if remaining
return rv
As I suspected, the question was not at all clear. Considering how the example code worked, I devised a new algorithm
def numbers_in_lists(string):
current_min = -1 # larger than any digit
rv = []
current_list = []
def push_sublist():
if current_list:
largest = current_list.pop(0)
rv.append(largest)
if current_list:
rv.append(current_list)
for digit in map(int, string):
if digit > current_min:
push_sublist()
current_list = []
current_min = digit
current_list.append(digit)
push_sublist() # if remaining
return rv
The simplest solution I could think of that follows the actual problem statement is:
def numbers_in_lists(string):
output = []
sublist = []
for number in [int(c) for c in string]: # Every number in the string should be inserted into the list.
if output and number <= output[-1]:
"""
- If a number x in the string is less than or equal
to the preceding number y, the number x should be
inserted into a sublist.
- Continue adding the following numbers to the sublist
until reaching a number z that is greater than the number y.
"""
sublist.append(number)
else:
"""
- Then add this number z to the normal list and continue.
"""
if sublist:
output.append(sublist)
sublist = []
output.append(number)
if sublist:
output.append(sublist)
return output
One other way of doing it... Might be confusing though
_val = int(string[0])
my_lis = [_val]
temp_li = []
for i in string[1:]:
_cval = int(i)
if _cval > _val:
if temp_li:
my_lis.append(temp_li[:])
temp_li = []
my_lis.append(_cval)
_val = _cval
else:
temp_li.append(_cval)
_val = temp_li[0]
if temp_li: my_lis.append(temp_li[:])# last value
print string
print my_lis
Mine is the lazy one:
def num_and_sublist_generator(iterable):
start = -1
sublist = []
for n in iterable:
if n > start:
if sublist:
yield sublist
sublist = []
start = n
yield n
else:
sublist.append(n)
if sublist:
yield sublist
def numbers_in_lists(string):
return list(num_and_sublist_generator(map(int, string)))
Related
Here's the problem. The input is a list of integers. If more than three adjacent numbers appear next to each other they should be dropped and the operation goes again. Kind of similar to the Iphone game, where player needs to pop lines of three or more balls of the same colors. The output should be the count of the balls that will be removed.
The algorithm is as follows. Starting with a sample list of say [3,3,4,4,4,4,3,2].
First iteration should remove the 4,4,4,4 - so the list would become [3,3,3,2], and the intermediary output of removed numbers will be 4.
Second iteration should remove 3,3,3 - so the final list would be [2] and final count of removed numbers - 7.
The first implementation for three consecutive items came from another stackoverflow thread - Remove triplets of adjacent numbers from the list
Here's the working function implementation for exactly 3 consecutive numbers:
def balls(l):
values = l
while len(values) >= 3:
print(values) #for demonstrative purposes of this question
for x in range(0,len(values)-2):
if values[x] == values[x+1] and values[x] == values[x+2]:
values = values[:x] + values[x+3:]
break
else:
break
print(values) #for demonstrative purposes of this question
return len(l) - len(values)
balls([3, 3, 4, 4, 4, 3, 4])
Output:
[3, 3, 4, 4, 4, 3, 4]
[3, 3, 3, 4]
[4]
6
How could I update the implementation to include the more general solution of removing 3+ consecutive numbers. I am thinking about tracking the start and end index of the consecutive duplicates, then subsetting the list. However, not sure how to implement that. Here are the tests that should work.
if __name__ == "__main__":
test1 = [3, 3, 4, 4, 4, 3, 4]
print(balls(test1))
#Output should be 6
test2 = [5, 5, 5, 5, 5, 5, 5, 5]
print(balls(test2))
#Output should be 8
test3 = [5, 7, 8, 3]
print(balls(test3))
#Output should be 0
def remove_consecutive(l, length):
amount = len(l)
count = 1
start = 0
current = l[0]
i = 1
while i < len(l):
if l[i] == current:
count += 1
else:
if count >= length:
for i in range(count):
l.pop(start)
start = 0
i = 0
current = l[0]
else:
start = i
current = l[i]
count = 1
i+=1
if count >= length:
for i in range(count):
l.pop(start)
return amount - len(l)
Wuff, i got it. My brain is kinda stinky lately so it took so long.
Here is my code, it works well. But I think there may be better ways to achieve higher efficiency.
def remove_consecutive(lst):
len_init = len(lst)
contain_tuplets = True
while contain_tuplets:
for i in range(len(lst)-2):
indices_to_pop = []
if lst[i]==lst[i+1]==lst[i+2]:
indices_to_pop.extend([i, i+1, i+2])
for j in range(i+3,len(lst)):
if lst[j] == lst[i]:
indices_to_pop.append(j)
else:
break
[lst.pop(i) for _ in indices_to_pop]
contain_tuplets = True
break
else:
contain_tuplets = False
count_removed_numbers = len_init - len(lst)
return count_removed_numbers, lst
test case1:
lst = [3,3,4,4,4,4,3,2]
remove_consecutive(lst)
output
(7, [2])
test case 2:
lst = [2, 2, 1, 1, 1, 2, 1]
remove_consecutive(lst)
output:
(6, [1])
def remove_consecutive(l, length):
amount = 0
count = 1
current = l[0]
for i in range(1, len(l)):
if l[i] == current:
count += 1
if count > length:
amount += 1
elif count == length:
amount += length
else:
current = l[i]
count = 1
return amount
This question already has answers here:
Recursive method to find the minimum number in a list of numbers
(6 answers)
Closed 2 years ago.
Here is a recursion program
numbers = [1, 2, 3, -3, 5]
numbers0 = [1, 2, 3, 4, 5]
numbers1 = []
def smallest(myList):
if len(myList) == 0:
return -1
else:
return min(myList[0], smallest(myList[1:]))
print(smallest(numbers))
print(smallest(numbers0))
print(smallest(numbers1))
If you just want return the smallest number from the list, you don't need to use recursion
numbers = [1,2,3,-3,5]
numbers0 = [1,2,3,4,5]
numbers1 = []
def smallest(myList):
if len(myList)==0:
return -1
else:
return min(myList)
print(smallest(numbers))
print(smallest(numbers0))
print(smallest(numbers1))
Output:
-3
1
-1
Hey this question was pretty easy:
#--Lists--
numbers = [1,2,3,-3,5]
numbers0 = [1,2,3,4,5]
numbers1 = []
#--Store all list in a list--
myList = [numbers, numbers0, numbers1]
#--List to store the minimum values--
a = []
#--For loop on each list--
for i in myList:
if len(i)==0: #--If it is empty print -1
print(-1)
else: #--If it is not empty, find the minimum--
b = min(i)
a.append(b) #--And then append in a--
print(a) #--Show the values of a--
This should do it and is pretty self explanatory
You can try and do something like this.
numbers = [1,2,3,-3,5]
numbers0 = [1,2,3,4,5]
numbers1 = []
#make a list of all the lists but in sorted order
n = [sorted(numbers),sorted(numbers0),sorted(numbers1)]
#then take the first element of each list. If list is empty, use -1
x = [-1 if not i else i[0] for i in n]
#x will now contain the smallest values from each list
print (x)
Output
[-3, 1, -1]
Another way to do this would be:
#create a list with all the lists
n = [numbers, numbers0, numbers1]
#run thru them and identify the minimum value of each list
#if list is empty, then -1
x = [-1 if not i else min(i) for i in n]
#print the final result
print(x)
Output:
[-3, 1, -1]
I've tried to find the sub-array(s) from a given which contain elements of maximum sum than any other sub array.
Below function has parameter as input a and the output needs to be returned. There can be more than one subarray as their maximum sum can be equal. The code did not seem to be working as expected.
def max_sum_subarray(a):
N, sub_sum, max_sum, subArrays = len(a), 0, 0, {}
p,q=0,0 #starting and ending indices of a max sub arr
for i in range(N):
q=i
sub_sum+=a[i]
if(a[i]<0):
q-=1
if(sub_sum>=max_sum):
if(sub_sum>max_sum):
subArrays.clear()
subArrays[sub_sum]=[(p,q)]
else:
subArrays[sub_sum].append((p,q))
sub_sum=0
p=i+1
if(sub_sum>=max_sum):
if(sub_sum>max_sum):
subArrays.clear()
subArrays[sub_sum]=[(p,q)]
else:
subArrays[sub_sum].append((p,q))
return(subArrays[p:q+1])
When I tried to run for input
a=[ 1, 2, 5, -7, 2, 5 ]
Expected output is [1, 2, 5] but it gave [2, 5] instead. Can anyone please post the solution in python?
It seems like you making this harder than necessary. You can just keep track of max array seen to far and the current one you're pushing into -- you don't really need to care about anything else. When you hit a negative (or the end of the array) decide if the current should be the new max:
def maxSub(a):
max_so_far = []
max_sum = 0
cur = []
for n in a:
if n >= 0:
cur.append(n)
else:
cur_sum = sum(cur)
if cur_sum > max_sum:
max_sum = cur_sum
max_so_far = cur
cur = []
return max([max_so_far, cur], key = sum)
a=[ 1, 2, 5, -7, 2, 5 ]
maxSub(a)
# [1, 2, 5]
Of course itertools.groupby makes this a one-liner:
from itertools import groupby
a=[ 1, 2, 5, -7, 2, 5 ]
max([list(g) for k,g in groupby(a, key=lambda x: x>0) if k == True], key=sum)
For the following conditions:
NOTE 1: If there is a tie, then compare with segment’s length and
return segment which has maximum length
NOTE 2: If there is still a tie, then return the segment with minimum
starting index
Here is my working code in python:
def check(max_arr,curr):
if sum(curr) > sum(max_arr):
max_arr = curr
elif sum(curr) == sum(max_arr):
if len(curr) > len(max_arr):
max_arr = curr
elif len(curr) == len(max_arr):
if max_arr and (curr[0] > max_arr[0]):
max_arr = curr
return max_arr
def maxset(A):
curr = []
max_arr = []
for i in A:
if i >= 0:
curr.append(i)
else:
max_arr = check(max_arr,curr)
curr = []
max_arr = check(max_arr,curr)
return max_arr
Let's say I have a array like l = [1, 3, 4, 5, 6, 8]
where the nth element represents the distance between the nth and n+1th object.
I want to find the distance between any two objects, and I used this code for this:
def dis(l_list, index1, index2, mylist):
m = mylist.index(index1)
n = mylist.index(index2)
i=0
j=0
if n > m:
while n >= m:
i = i + mylist[m]
m = m + 1
elif n < m:
while n <= m:
i = i + mylist[n]
n = n + 1
else:
return(0)
j = mylist[n] % l_mylist
print(abs(i - j))
l_mylist = input()
l_mylist = int(l_mylist)
mylist = []
mylist = list(map(int, input().split()))
i,j = input().split()
i, j=int(i), int(j)
dis(l_mylist, i, j, mylist)
but I am still getting the wrong output. Can anyone please point out where I am wrong?
If you want to sum around a potentially circular list. You can use a collections.deque() to rotate the list, e.g.:
from collections import deque
def dist(l, i1, i2):
d = deque(l)
d.rotate(-i1)
return sum(list(d)[:i2-i1]))
In []:
l = [1,2,3,4,5,6,7,8]
dist(l, 3-1, 6-1) # 3, 4, 5
Out[]:
12
In []:
dist(l, 6-1, 3-1) # 6, 7, 8, 1, 2
Out[]:
24
def distance(first_index, second_index, my_list):
temp_list = my_list + my_list
if (first_index > second_index):
first_index += len(my_list)
requested_sum = sum(my_list[second_index-1:first_index-1])
else:
requested_sum = sum(my_list[first_index-1:second_index-1])
return requested_sum
If I understood you correctly, then this should do the trick.
There are much more compact and efficient ways to do this, but this is the simplest and easiest to understand in my opinion.
This program is designed to take a string consisting of numbers (any length) and outputting the contents of the string into a list, one digit at a time. Should a number, x, be less than or equal to the preceding number, y, the number x is to be inserted into a sublist. Until a number, z, is greater than y, everything between x and z will also be added to the sublist. Here is the code
def numbers_in_lists(string):
final = []
temp = []
prev = 0
for i in range(len(string)):
value = int(string[i])
if value<=prev:
temp.append(value)
else:
if temp != []:
final.append(temp)
temp = []
final.append(value)
prev = int(string[i])
print final
return final
To test this function, add the following to the remainder of the code:
string = '543987'
result = [5,[4,3],9,[8,7]]
print repr(string), numbers_in_lists(string) == result
string= '987654321'
result = [9,[8,7,6,5,4,3,2,1]]
print repr(string), numbers_in_lists(string) == result
string = '455532123266'
result = [4, 5, [5, 5, 3, 2, 1, 2, 3, 2], 6, [6]]
print repr(string), numbers_in_lists(string) == result
string = '123456789'
result = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print repr(string), numbers_in_lists(string) == result
After the code creates and returns a sublist, it finds a new maximum value and doesn't add anything else to the list, thus leaving the final list incomplete.
If I test with the string '543987' the prefered result is [5,[4,3],9,[8,7]] whereas my result is [5,[4,3],9]
You need to check temp after the for loop ends, it might still contain something:
def numbers_in_lists(string):
final = []
temp = []
prev = 0
for digit in string:
value = int(digit)
if value<=prev:
temp.append(value)
else:
if temp:
final.append(temp)
temp = []
final.append(value)
prev = int(digit)
if temp:
final.append(temp)
print final
return final
I also slightly re-worked your for loop (no need to use indexed access) and replaced temp != [] with temp (see here).