I have a problem that asks me to write a program that has a defined list (as the argument) and will then find all runs of Y consecutive numbers that increase or decrease by 1. It will then return the list of indices of the first element of each of these runs.
For example, if Y = 3, then l1 = [1,2,3,5,10,9,8,9,10,11,7,8,7] returns [0,4,6,7]. I got the code working somewhat, but it only works if I 'hard code' how many numbers I am checking. For example, if Y = 3, then my IF statement is checking l1[i], l1[i] + 1 and l1[i] + 2). I need it to work dynamically no matter what I set Y to.
Below is my code with my 'base logic':
A = [1,2,3,5,10,9,8,9,10,11,7,8,7]
new_indices = []
for index, number in enumerate(A[:-2]):
urgent_number = abs(A[index + 1])
next_number = abs(A[index + 2])
if (number + 1 == urgent_number and number + 2 == next_number) or (number - 1 == urgent_number and number - 2 == next_number):
new_indices.append(index)
print(new_indices)
Below is my attempt to try and incorporate a while loop to do this dynamically. I'm close, but I just can't seem to figure it out:
A = [1,2,3,5,10,9,8,9,10,11,7,8,7]
Y = 3 #length of list
new_indices = []
for index, number in enumerate(A[:-2]):
urgent_number = abs(A[index + 1])
next_number = abs(A[index + 2])
x = 0
while x < Y:
if (number + 1 == urgent_number and number + 2 == next_number) or (number - 1 == urgent_number and number - 2 == next_number):
new_indices.append(index)
x += 1
print(new_indices)
prints out [0,0,0,4,4,4,6,6,6,7,7,7]. However, I'm looking for [0,4,6,7].
Since you've gotten all the start indices and just need to remove the duplicates, you can do:
from itertools import groupby
original_result = [0,0,0,4,4,4,6,6,6,7,7,7]
final_result = [key for key, _ in groupby(original_result)]
[key for key, _ in groupby(original_result)]
to get the desired output.
This outputs:
[0, 4, 6, 7]
If you need to deal with variable run length specifically, you can find all the valid list slices that could make up a run, and then validate whether or not that slices makes up a run:
A = [1,2,3,5,10,9,8,9,10,11,7,8,7]
RUN_LENGTH = 3
indices = []
for s in range(len(A) - RUN_LENGTH):
is_incrementally_increasing = \
all(i2 - i1 == 1 for i1, i2 in zip(A[s:s+RUN_LENGTH], A[s+1:s+RUN_LENGTH]))
is_incrementally_decreasing = \
all(i1 - i2 == 1 for i1, i2 in zip(A[s:s+RUN_LENGTH], A[s+1:s+RUN_LENGTH]))
if is_incrementally_increasing or is_incrementally_decreasing:
indices.append(s)
print(indices)
This also outputs:
[0, 4, 6, 7]
This also works:
def dynamical(A):
new_indices = []
for index, number in enumerate(A[:-2]):
urgent_number = abs(A[index + 1])
next_number = abs(A[index + 2])
if (number + 1 == urgent_number and number + 2 == next_number) or (number - 1 == urgent_number and number - 2 == next_number):
new_indices.append(index)
return new_indices
Related
I am trying to solve problem 4.1 on Codility.com. I need a solution which runs in O(N) time. My code solves the problem, but it runs in O(N^2) time, according to Codility's performance tests. Unfortunately I can't see why. There is only one for loop, which should scale linearly with n.
The challenge is to write a solution which tests whether an array ('A') contains all integers between 1 and X. It should return the index of the element which is the last element in 1 to X that appears in the array, or it should return -1 if not every integer between 1 and X is an element of the array. For example, the solution to X = 4 and A = [1, 3, 4, 2, 2] is 3 since 2 is the last element in 1 to 4 which appears in the array and it first appears in position 3. The solution to X = 5 and A = [1, 2, 4, 2, 3] is -1 because 5 never appears. My solution is below.
def Solution(X, A):
N = len(A)
count = [0] * (X + 1)
# Solution for single-element arrays
if N == 1:
if A[0] == 1:
return 0
elif A[0] != 1:
return - 1
# Solution for multi-element arrays
elif N != 1:
for i in range(0, N + 1, 1):
if count[A[i]] == 0:
count[A[i]] = count[A[i]] + 1
else:
pass
if count == [0] + [1] * (X):
return i
elif count != [0] + [1] * (X) and i == N - 1:
return -1
Would anyone know why it runs in O(N^2) time? Codility's performance tests confirm this, but as far as I can see this should run in O(kN) time since there is only one for loop. Any help is appreciated.
Something like this would work in O(n), you would have to adjust it to the exact question given:
def solution(x, a):
b = []
for v in a:
# the conditions
if (type(v) == int) and (v < x) and (v>1): b.append(v)
else: return -1
return b
# some examples
# this returns -1
a = [1,2,3,4,5.5,6]
x = solution(5,a)
print(x)
# this returns the the list [2, 3, 4]
a = [2,3,4]
x = solution(5,a)
print(x)
the reason why is because you can exit the list at the first fail with the return -1 statement.
This should be O(N): a single instantiation of a list of length X, one for-loop over A which does O(1) retrieval of single list items, and one single final search of the list for None values.
def solution(X, A):
pos = [None] * X
for i, n in enumerate(A):
if 1 <= n <= X:
if pos[n-1] is None:
pos[n-1] = i
return pos[n-1] if None not in pos else -1
print(solution(4, [1, 3, 4, 2, 2]))
print(solution(5, [1, 2, 4, 2, 3]))
print(solution(1000, reversed(range(1, 1001))))
prints:
3
-1
999
# the main
def main():
# calling list and variables
golfScores = []
index = 0
SCORES = 10
# enter scores 10 times
while (index <= SCORES - 1):
scoreInput = int(input('Enter score: '))
golfScores.append(scoreInput)
index = index + 1
# calling module to sort with 2 parameters passed
bubbleSort(golfScores, SCORES)
# print the final result
print('The sorted order is:', golfScores)
# going through the array to sort
def bubbleSort(golfScores, SCORES):
maxElement = SCORES - 1
while (maxElement >= 1):
index = 0
while (index <= maxElement - 1):
while (golfScores[index] > golfScores[index + 1]):
swap(golfScores[index], golfScores[index + 1])
index = index + 1
maxElement = maxElement - 1
return golfScores
# a & b passed as ref values to swap
def swap(a,b):
temp = 0
a = b
b = temp
return a,b
# call the main to run
main()
Whenever I would run the whole thing, it takes inputs 1-10 and displays fine, but upon any other one, it seems to get stuck and I have to keyboardinterupt to get out of it. It also seems to print the stuff in the array, but not in the order
So I feel like its the bubbleSort that's not working.. I've tried thinking about the swap layout for this and was wondering if it had to do with this.
Trying to accomplish
User input: 3, 2, 1, 4, 6, 5, 8, 7, 10, 9
Output: The sorted order is: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
The problem is actually in your swap function. In Python, ints are pass-by-value, so your swap function does not affect the values of a & b in your bubbleSort function.
Try deleting the swap function, and replace your line:
swap(golfScores[index], golfScores[index + 1])
With:
golfScores[index], golfScores[index + 1] = golfScores[index + 1], golfScores[index]
while (golfScores[index] > golfScores[index + 1]):
swap(golfScores[index], golfScores[index + 1])
This should be:
if (golfScores[index] > golfScores[index + 1]):
swap(golfScores[index], golfScores[index + 1])
Python does not have pass by reference. To swap two variables, use var1, var2 = var2, var1. You can improve the input statement by using map and split like so golfScores = list(map(int, input().split(", "))), as you are currently reading the entire line as one element of the list.
Revised code:
def main():
# calling list and variables
SCORES = 10
golfScores = list(map(int, input().split(", ")))
if(len(golfScores) != SCORES):
return
# calling module to sort with 2 parameters passed
bubbleSort(golfScores, SCORES)
# print the final result
print('The sorted order is:', golfScores)
# going through the array to sort
def bubbleSort(golfScores, SCORES):
maxElement = SCORES - 1
while (maxElement >= 1):
index = 0
while (index <= maxElement - 1):
while (golfScores[index] > golfScores[index + 1]):
golfScores[index], golfScores[index + 1] = golfScores[index + 1], golfScores[index]
index = index + 1
maxElement = maxElement - 1
return golfScores
Demo!
I believe the problem you were having was created due to your condition resulted in an infinite loop at some point. I changed a code a bit, do check it out and if any confusions do ask. As with this, you wouldn't need to use swap function.
def main():
# calling list and variables
golfScores = []
index = 0
SCORES = 10
# enter scores 10 times
while (index <= SCORES - 1):
scoreInput = int(input('Enter score: '))
golfScores.append(scoreInput)
index = index + 1
# calling module to sort with 2 parameters passed
golfScores = bubbleSort(golfScores, SCORES)
# print the final result
print('The sorted order is:', golfScores)
# going through the array to sort
def bubbleSort(golfScores, SCORES):
maxElement = SCORES
while (maxElement >= 1):
index = 0
while (index < maxElement - 1):
if golfScores[index] > golfScores[index + 1] :
temp = golfScores[index]
golfScores[index] = golfScores[index + 1]
golfScores[index + 1] = temp
index += 1
maxElement = maxElement - 1
return golfScores
# a & b passed as ref values to swap
def swap(a,b):
temp = 0
a = b
b = temp
return a,b
# call the main to run
main()
import random
def lottery(lucky_numbers, run):
i = 0
while i < run:
x = random.uniform(0, 1) #prints out a random number between 0 and 1
numbers = lucky_numbers
NewNumbers = numbers[-1:] + numbers[:-1] #shifts each element in the to the right
lucky_numbers = NewNumbers
print(lucky_numbers, x)
i += 1
lottery([1, 2, 0], 3)
This code prints out something like:
>>>>>>>>>>
[0, 1, 2] 0.33016179294984127
[2, 0, 1] 0.7797639530009745
[1, 2, 0] 0.6292245916315391
>>>>>>>>>>
The x values will always be different because they are random numbers between 0 and 1.
I am trying to add a function that says if x is the lowest value(min) in the loop then the programme should print the list of that iteration, for example in this case the lowest value of x in this loop is 0.33016179.. , the program should therefore print the list [0, 1, 2]
I would just save the info in a variable and print it after the loop ends:
import random
def lottery(lucky_numbers, run):
i = 0
min_x = 1
while i < run:
x = random.uniform(0, 1) #prints out a random number between 0 and 1
numbers = lucky_numbers
NewNumbers = numbers[-1:] + numbers[:-1] #shifts each element in the to the right
lucky_numbers = NewNumbers
if x < min_x:
min_x = x
min_lucky_numbers = lucky_numbers
i += 1
print(min_lucky_numbers, min_x)
lottery([1, 2, 0], 3)
You can create a "cache" that stores all the x values then call the lowest value.
cache = []
for _ in range(3):
x = random.uniform(0, 1)
cache.append(x)
print min(cache)
to do what you want, you have just to store your items in two different lists, sort them and display the firt elements of each one :
import random
luckiest_num = list()
luckiest_list = list()
def lottery(lucky_numbers, run):
i = 0
while i < run:
x = random.uniform(0, 1)
numbers = lucky_numbers
NewNumbers = numbers[-1:] + numbers[:-1]
print(NewNumbers, x)
i += 1
luckiest_num.append(x)
luckiest_list.append(NewNumbers)
lottery([1, 2, 0], 3)
luckiest_num.sort()
luckiest_list.sort()
print ("The luckiest item is : ")
print(luckiest_num[0],luckiest_list[0])
Let A be a non-empty set of integers. Write a function find that outputs a non-empty subset of A that has the maximum product. For example, find([-1, -2, -3, 0, 2]) = 12 = (-2)*(-3)*2
Here's what I think: divide the list into a list of positive integers and a list of negative integers:
If we have an even number of negative integers, multiply everything in both list and we have the answer.
If we have an odd number of negative integers, find the largest and remove it from the list. Then multiply everything in both lists.
If the list has only one element, return this element.
Here's my code in Python:
def find(xs):
neg_int = []
pos_int = []
if len(xs) == 1:
return str(xs[0])
for i in xs:
if i < 0:
neg_int.append(i)
elif i > 0:
pos_int.append(i)
if len(neg_int) == 1 and len(pos_int) == 0 and 0 in xs:
return str(0)
if len(neg_int) == len(pos_int) == 0:
return str(0)
max = 1
if len(pos_int) > 0:
for x in pos_int:
max=x*max
if len(neg_int) % 2 == 1:
max_neg = neg_int[0]
for j in neg_int:
if j > max_neg:
max_neg = j
neg_int.remove(max_neg)
for k in neg_int:
max = k*max
return str(max)
Am I missing anything? P.S. This is a problem from Google's foobar challenge, I am apparently missing one case but I don't know which.
Now here's actual problem:
from functools import reduce
from operator import mul
def find(array):
negative = []
positive = []
zero = None
removed = None
def string_product(iterable):
return str(reduce(mul, iterable, 1))
for number in array:
if number < 0:
negative.append(number)
elif number > 0:
positive.append(number)
else:
zero = str(number)
if negative:
if len(negative) % 2 == 0:
return string_product(negative + positive)
removed = max(negative)
negative.remove(removed)
if negative:
return string_product(negative + positive)
if positive:
return string_product(positive)
return zero or str(removed)
You can simplify this problem with reduce (in functools in Py3)
import functools as ft
from operator import mul
def find(ns):
if len(ns) == 1 or len(ns) == 2 and 0 in ns:
return str(max(ns))
pos = filter(lambda x: x > 0, ns)
negs = sorted(filter(lambda x: x < 0, ns))
return str(ft.reduce(mul, negs[:-1 if len(negs)%2 else None], 1) * ft.reduce(mul, pos, 1))
>>> find([-1, -2, -3, 0, 2])
'12'
>>> find([-3, 0])
'0'
>>> find([-1])
'-1'
>>> find([])
'1'
Here's a solution in one loop:
def max_product(A):
"""Calculate maximal product of elements of A"""
product = 1
greatest_negative = float("-inf") # greatest negative multiplicand so far
for x in A:
product = max(product, product*x, key=abs)
if x <= -1:
greatest_negative = max(x, greatest_negative)
return max(product, product // greatest_negative)
assert max_product([2,3]) == 6
assert max_product([-2,-3]) == 6
assert max_product([-1, -2, -3, 0, 2]) == 12
assert max_product([]) == 1
assert max_product([-5]) == 1
Extra credit: what if the integer constraint were relaxed? What extra information do you need to collect during the loop?
Here is another solution that doesn't require libraries :
def find(l):
if len(l) <= 2 and 0 in l: # This is the missing case, try [-3,0], it should return 0
return max(l)
l = [e for e in l if e != 0] # remove 0s
r = 1
for e in l: # multiply all
r *= e
if r < 0: # if the result is negative, remove biggest negative number and retry
l.remove(max([e for e in l if e < 0]))
r = find(l)
return r
print(find([-1, -2, -3, 0, 2])) # 12
print(find([-3, 0])) # 0
EDIT :
I think I've found the missing case which is when there are only two elements in the list, and the highest is 0.
This is a typical interview question. Given an array that contains both positive and negative elements without 0, find the largest subarray whose sum equals 0. I tried to solve this. This is what I came up with.
def sub_array_sum(array,k=0):
start_index = -1
hash_sum = {}
current_sum = 0
keys = set()
best_index_hash = {}
for i in array:
start_index += 1
current_sum += i
if current_sum in hash_sum:
hash_sum[current_sum].append(start_index)
keys.add(current_sum)
else:
if current_sum == 0:
best_index_hash[start_index] = [(0,start_index)]
else:
hash_sum[current_sum] = [start_index]
if keys:
for k_1 in keys:
best_start = hash_sum.get(k_1)[0]
best_end_list = hash_sum.get(k_1)[1:]
for best_end in best_end_list:
if abs(best_start-best_end) in best_index_hash:
best_index_hash[abs(best_start-best_end)].append((best_start+1,best_end))
else:
best_index_hash[abs(best_start-best_end)] = [(best_start+1,best_end)]
if best_index_hash:
(bs,be) = best_index_hash[max(best_index_hash.keys(),key=int)].pop()
return array[bs:be+1]
else:
print "No sub array with sum equal to 0"
def Main():
a = [6,-2,8,5,4,-9,8,-2,1,2]
b = [-8,8]
c = [-7,8,-1]
d = [2200,300,-6,6,5,-9]
e = [-9,9,-6,-3]
print sub_array_sum(a)
print sub_array_sum(b)
print sub_array_sum(c)
print sub_array_sum(d)
print sub_array_sum(e)
if __name__ == '__main__':
Main()
I am not sure if this will satisfy all the edge case. if someone can comment on that, it would be excellent Also i want to extend this to sum equalling to any K not just 0. How should i go about it. And any pointers to optimize this further is also helpful.
You have given a nice, linear-time solution (better than the two other answers at this time, which are quadratic-time), based on the idea that whenever sum(i .. j) = 0, it must be that sum(0 .. i-1) = sum(0 .. j) and vice versa. Essentially you compute the prefix sums sum(0 .. i) for all i, building up a hashtable hash_sum in which hash_sum[x] is a list of all positions i having sum(0 .. i) = x. Then you go through this hashtable, one sum at a time, looking for any sum that was made by more than one prefix. Among all such made-more-than-once sums, you choose the one that was made by a pair of prefixes that are furthest apart -- this is the longest.
Since you already noticed the key insight needed to make this algorithm linear-time, I'm a bit puzzled as to why you build up so much unnecessary stuff in best_index_hash in your second loop. For a given sum x, the furthest-apart pair of prefixes that make that sum will always be the smallest and largest entries in hash_sum[x], which will necessarily be the first and last entries (because that's the order they were appended), so there's no need to loop over the elements in between. In fact you don't even need a second loop at all: you can keep a running maximum during your first loop, by treating start_index as the rightmost endpoint.
To handle an arbitrary difference k: Instead of finding the leftmost occurrence of a prefix summing to current_sum, we need to find the leftmost occurrence of a prefix summing to current_sum - k. But that's just first_with_sum{current_sum - k}.
The following code isn't tested, but should work:
def sub_array_sum(array,k=0):
start_index = -1
first_with_sum = {}
first_with_sum{0} = -1
best_start = -1
best_len = 0
current_sum = 0
for i in array:
start_index += 1
current_sum += i
if current_sum - k in first_with_sum:
if start_index - first_with_sum{current_sum - k} > best_len:
best_start = first_with_sum{current_sum - k} + 1
best_len = start_index - first_with_sum{current_sum - k}
else:
first_with_sum{current_sum} = start_index
if best_len > 0:
return array[best_start:best_start+best_len-1]
else:
print "No subarray found"
Setting first_with_sum{0} = -1 at the start means that we don't have to treat a range beginning at index 0 as a special case. Note that this algorithm doesn't improve on the asymptotic time or space complexity of your original one, but it's simpler to implement and will use a small amount less space on any input that contains a zero-sum subarray.
Here's my own answer, just for fun.
The number of subsequences is quadratic, and the time to sum a subsequence is linear, so the most naive solution would be cubic.
This approach is just an exhaustive search over the subsequences, but a little trickery avoids the linear summing factor, so it's only quadratic.
from collections import namedtuple
from itertools import chain
class Element(namedtuple('Element', ('index', 'value'))):
"""
An element in the input sequence. ``index`` is the position
of the element, and ``value`` is the element itself.
"""
pass
class Node(namedtuple('Node', ('a', 'b', 'sum'))):
"""
A node in the search graph, which looks like this:
0 1 2 3
\ / \ / \ /
0-1 1-2 2-3
\ / \ /
0-2 1-3
\ /
0-3
``a`` is the start Element, ``b`` is the end Element, and
``sum`` is the sum of elements ``a`` through ``b``.
"""
#classmethod
def from_element(cls, e):
"""Construct a Node from a single Element."""
return Node(a=e, b=e, sum=e.value)
def __add__(self, other):
"""The combining operation depicted by the graph above."""
assert self.a.index == other.a.index - 1
assert self.b.index == other.b.index - 1
return Node(a=self.a, b=other.b, sum=(self.sum + other.b.value))
def __len__(self):
"""The number of elements represented by this node."""
return self.b.index - self.a.index + 1
def get_longest_k_sum_subsequence(ints, k):
"""The longest subsequence of ``ints`` that sums to ``k``."""
n = get_longest_node(n for n in generate_nodes(ints) if n.sum == k)
if n:
return ints[n.a.index:(n.b.index + 1)]
if k == 0:
return []
def get_longest_zero_sum_subsequence(ints):
"""The longest subsequence of ``ints`` that sums to zero."""
return get_longest_k_sum_subsequence(ints, k=0)
def generate_nodes(ints):
"""Generates all Nodes in the graph."""
nodes = [Node.from_element(Element(i, v)) for i, v in enumerate(ints)]
while len(nodes) > 0:
for n in nodes:
yield n
nodes = [x + y for x, y in zip(nodes, nodes[1:])]
def get_longest_node(nodes):
"""The longest Node in ``nodes``, or None if there are no Nodes."""
return max(chain([()], nodes), key=len) or None
if __name__ == '__main__':
def f(*ints):
return get_longest_zero_sum_subsequence(list(ints))
assert f() == []
assert f(1) == []
assert f(0) == [0]
assert f(0, 0) == [0, 0]
assert f(-1, 1) == [-1, 1]
assert f(-1, 2, 1) == []
assert f(1, -1, 1, -1) == [1, -1, 1, -1]
assert f(1, -1, 8) == [1, -1]
assert f(0, 1, -1, 8) == [0, 1, -1]
assert f(5, 6, -2, 1, 1, 7, -2, 2, 8) == [-2, 1, 1]
assert f(5, 6, -2, 2, 7, -2, 1, 1, 8) == [-2, 1, 1]
I agree with sundar nataraj when he says that this must be posted to the code review forum.
For fun though I looked at your code. Though I am able to understand your approach, I fail to understand the need to use Counter.
best_index_hash[start_index] = [(0,start_index)] - Here best_index_hash is of the type Counter. Why are you assigning a list to it?
for key_1, value_1 in best_index_hash.most_common(1) - You trying to get largest subsequence and for that you are using most_common as the answer. This is not intuitive semantically.
I am tempted to post a solution but I will wait for you to edit the code snippet and improve it.
Addendum
For fun, I had a go at this puzzle and I present my effort below. I make no guarantees of correctness/completeness.
from collections import defaultdict
def max_sub_array_sum(a, s):
if a:
span = defaultdict(lambda : (0,0))
current_total = 0
for i in xrange(len(a)):
current_total = a[i]
for j in xrange (i + 1, len(a)):
current_total += a[j]
x,y = span[current_total]
if j - i > y - x:
span[current_total] = i,j
if s in span:
i, j = span[s]
print "sum=%d,span_length=%d,indices=(%d,%d),sequence=%s" %\
(s, j-i + 1, i, j, str(a[i:j + 1]))
return
print "Could not find a subsequence of sum %d in sequence %s" % \
(s, str(a))
max_sub_array_sum(range(-6, -1), 0)
max_sub_array_sum(None, 0)
max_sub_array_sum([], 0)
max_sub_array_sum(range(6), 15)
max_sub_array_sum(range(6), 14)
max_sub_array_sum(range(6), 13)
max_sub_array_sum(range(6), 0)
Here's the solution taken from LeetCode :
def sub_array_sum(nums, k=0):
count, sum = 0, 0
map = dict()
map[0] = 1
for i in range(len(nums)):
sum += nums[i]
if map.__contains__(sum - k):
count += map[sum - k]
map[sum] = map.get(sum, 0) + 1
return count