i would like to find an algorithm to find the max disjoint subarray with alternated signs (not necessarily contiguos), for example in (2, -3, 4., 5, -4, 3, -3, 5, -2, 1) it returns 7 that is the greatest sum, and the subarray is (5,-3,5)
I tried something like this using dp:
A=[2,-3,4,5,-4,3,-3,5,-2,1]
m = A[0]
flag = A[0] #flag has the same sign of the previously taken element
maximum = m #temporary maxsum
for i in range(1,10):
if A[i]>0 and flag<0 or A[i]<0 and flag>0: #i look only for alternate signs
m = max(m ,A[i]+m)
if m > maximum:
flag = -flag
maximum = m
else:
if A[i]>maximum:
maximum=A[i]
flag=-flag
print(maximum)
it gives 7 but it's only a coincidence
Should i have to use another For nested in order to properly compare every possible subarray?
A=[2,-3,4,5,-4,3,-3,5,-2,1]
dp = [[([], 0), ([], 0)]]
for i, x in enumerate(A):
curr = list(dp[-1])
if x < 0 and curr[0][1] + x > curr[1][1]:
curr[1] = (curr[0][0] + [i], curr[0][1] + x)
elif x > 0 and curr[1][1] + x > curr[0][1]:
curr[0] = (curr[1][0] + [i], curr[1][1] + x)
dp.append(curr)
print dp[-1][0]
This will print a tuple with the indices of the elements to take, and the maximum sum.
Each element in dp represents the best solution up to that point in the list for the case where the next number needs to be positive (first element) and where the next number needs to be negative (second element). Within each of those best solutions, we a list of indices to get to that point, and the maximum sum. The answer is always going to be from the last element in dp where the most recently taken number was positive.
If you don't need to track the elements used:
A=[2,-3,4,5,-4,3,-3,5,-2,1]
dp = [[0, 0]]
for i, x in enumerate(A):
curr = list(dp[-1])
if x < 0 and curr[0] + x > curr[1]:
curr[1] = curr[0] + x
elif x > 0 and curr[1] + x > curr[0]:
curr[0] = curr[1] + x
dp.append(curr)
print dp[-1][0]
Related
I have a porblem that I almost solved it but I'm facing problem with one test case,
giving an array of numbers, it is allowed to choose any number and swap it digits so the array become strictly increasing, for example: [2, 4, 800, 12], we can choose 800 and convert it to 008 then array become [2,4,008,12], so it should return true, and it is allowed to it once, so the task is to check if it possible to convert an array to strictly increasing with at most one swap, here is my code
def solution(numbers):
swapped = 0
for i in range(len(numbers)-1):
if numbers[i] > numbers[i+1]:
if swapped >= 1:
return False
s1= int(''.join(sorted(str(numbers[i]))))
if s1 < numbers[i+1]:
if i > 0 and s1 >= numbers[i-1]:
numbers[i] = s1
swapped += 1
continue
s2 = int(''.join(sorted(str(numbers[i+1]), reverse=True)))
if s2 >= numbers[i]:
numbers[i+1] = s2
swapped += 1
continue
return True
the only test case the code doesn't pass is [13,31,30] (returning True instead of False) and I don't know how to solve this problem.
(apologies for any an un-elegant parts in my question, I'm new to this world and I'm trying my best)
Your logic is largely right, except that I would put the check for the swap at the end of the loop, and I would check all the permutations instead of just the smallest and largest integer created from the digits.
from itertools import permutations, pairwise
def soln(numbers):
swapped = 0
for i in range(len(numbers)):
perms = list(int(''.join(shuffled_digits)) for shuffled_digits in permutations(str(numbers[i])))
if i == 0:
candidate_nums = [shuffled_number for shuffled_number in perms if shuffled_number < numbers[i+1]]
elif i < len(numbers) - 1:
candidate_nums = [shuffled_number for shuffled_number in perms if numbers[i-1] < shuffled_number < numbers[i+1]]
else:
candidate_nums = [shuffled_number for shuffled_number in perms if shuffled_number > numbers[i-1]]
if candidate_nums:
swapped_num = candidate_nums.pop()
numbers[i] = swapped_num
swapped +=1
if all(x < y for x, y in pairwise(numbers)):
return True
return False
Output
In [80]: inlist
Out[80]: [2, 4, 8, 12]
In [81]: soln(inlist)
Out[81]: True
In [82]: inlist = [2, 4, 800, 12]
In [83]: soln(inlist)
Out[83]: True
In [84]: inlist = [21, 21]
In [85]: soln(inlist)
Out[85]: True
In [86]: inlist = [2, 1, 9, 7]
In [87]: soln(inlist)
Out[87]: False
In [88]: inlist = [2, 1, 1]
In [89]: soln(inlist)
Out[89]: False
In [90]: inlist = [100, 2, 3]
In [91]: soln(inlist)
Out[91]: True
In [92]: inlist = [52, 53, 36]
In [93]: soln(inlist)
Out[93]: True
In [94]: inlist = [44,37,74]
In [95]: soln(inlist)
Out[95]: True
When you have a candidate number that needs to change, I would suggest making a directed search in the (virtual) list of permutations to find the permutation whose value is as close as possible to the value you compared with in order to restore the strictly ascending order.
It is not necessary to generate all permutations for this, which would consume too much time and space when the number of digits in the input numbers exceeds 10.
The idea is to count the number of digits you have of each digit, in the number that needs a permutation, and then take the next digit for building a permutation in such a way that you stay as close as possible to the target value. Some backtracking is needed, but for each digit position at most 2 attempts are needed: one for a digit that exactly matches the digit in the target (the value to compare with), and potentially a second attempt with another digit.
Here is an implementation of that idea.
First a helper function to find a permutation of a given value that is as near as possible to a target value (but not equal to it), and must be either less or greater (last argument controls this):
def permute_near(value, target, side=1):
# If side is 1, we will look for the minimum permutation of value that is
# greater than the target.
# If side is -1, we will look for the maximum permutation of value that is
# less than the target.
freedigit = 0 if side == 1 else 9
end = freedigit + 10*side
target += side
# Get a count for each digit of value (could also use Counter module for this)
s = str(value)
n = len(s)
digits = [0]*10
for digit in s:
digits[int(digit)] += 1
# Pad the target value to have the same number of digits
limit = [int(digit) for digit in str(target).rjust(n, "0")]
# Use recursion to find the permutation of value that is nearest to the target
def recur(k, isfree, accumulator):
if k >= n: # All digits were processed, we have the result
return accumulator
limitdigit = limit[k]
for i in range(freedigit if isfree else limitdigit, end, side):
if digits[i]:
digits[i] -= 1 # Select this digit for the permutation
permutation = recur(k + 1, isfree or i != limitdigit, accumulator * 10 + i)
digits[i] += 1 # Backtrack
if permutation > -1 or i != limitdigit:
return permutation
# If it failed for the case where the chosen digit was the same
# as in the target, try with one alternative digit in a
# second and also last iteration of this loop.
return -1
return recur(0, False, 0)
Here is the main function:
def solution(numbers):
try:
# Find the first index where the next value is not greater
i = next(i for i, val in enumerate(numbers) if val >= numbers[i+1])
except:
return numbers # The input is already completely ascending
# Permute numbers[i], so it becomes less than its successor
result = permute_near(numbers[i], numbers[i+1], -1)
# Check that such permutation exists, and is still greater than its predecessor
if result >= 0 and (i == 0 or numbers[i - 1] < result):
numbers[i] = result # OK, we have success
else:
# ... or permute numbers[i+1], so it becomes greater than its predecessor
result = permute_near(numbers[i+1], numbers[i], 1)
# Check that such permutation exists, and is still less than its successor
if result >= 0 and (i + 2 >= len(numbers) or result < numbers[i + 2]):
numbers[i + 1] = result # OK, we have success
if all(a < b for a, b in zip(numbers, numbers[1:])):
return numbers
Some runs:
# A few test cases
print(solution([2,4,800,12])) # [2, 4, 8, 12]
print(solution([44,37,74])) # [44, 73, 74]
print(solution([52,53,36])) # [52, 53, 63]
print(solution([64,52,53])) # [46, 52, 53]
print(solution([89221173000,89221173456,13437127829,89221173477]))
# [89221173000, 89221173456, 89221173473, 89221173477]
The function returns the adjusted list if there is a solution, None otherwise.
The code mentioned in the question also fails for this testcase: [52,31,30]
Here's the solution that returns True if there's atleast one increasing array / list by swapping only one number.
def solution(numbers):
# Converts to str
numbers = [str(i) for i in numbers]
n = len(numbers)
for j in range(n + 1):
if j != n:
# Reverse number
numbers[j] = str(numbers[j])[::-1]
# Checks if the array is in increasing order
if all([int(numbers[i + 1]) > int(numbers[i]) for i in range(n - 1)]):
return True
if j != n:
numbers[j] = str(numbers[j])[::-1]
return False
Is there a short way to detect the longest sublist with alternative signs within a list?
For instance:
my_list = [-1, -0.5, 1, -3, 4, 5, 5, -1]
returning 4 starting from -0.5 to 4?
This is what I have written so far but I feel there is room for something much shorter.
import numpy
my_list = [-1, -0.5, 1, -3, 4, 5, 5, -1]
# function that detects whether a list has alternate signs
# https://stackoverflow.com/questions/6451514/detect-alternating-signs
def is_alternating_signs(a):
return numpy.all(numpy.abs(numpy.diff(numpy.sign(a))) == 2)
# getting all sublists from the main list
sublists = []
for i in range(len(my_list) + 1):
for j in range(i + 1, len(my_list) + 1):
sublists.append(my_list[i:j])
# detecting the longest sublist with alternate signs
max_list = 0
for sublist in sublists:
if is_alternating_signs(sublist) and len(sublist) > max_list:
max_list = len(sublist)
print(max_list)
Use zip to compare the current element with the next one:
maxlen = 1
curlen = 1
for i, j in zip(l, l[1:]):
# if one conditions match
# increment curlen by 1
if (i < 0 and j > 0) or (i > 0 and j < 0):
curlen += 1
# break the alternative sign
# keep the highest value between maxlen and curlen
# reset curlen to 1
else:
maxlen = max(maxlen, curlen)
curlen = 1
maxlen = max(maxlen, curlen)
Output:
>>> maxlen
4
You can use zip to detect the positions of 'breaks' in the alternance. Then combine these breaks into ranges to find the longest streak of alternating values:
L = [-1, -0.5, 1, -3, 4, 5, 5, -1]
breaks = [i for i,(a,b) in enumerate(zip(L,L[1:]),1) if (a<0)==(b<0)]
longest = max((L[s:e] for s,e in zip([0]+breaks,breaks+[None])),key=len)
print(longest)
[-0.5, 1, -3, 4]
If you're only looking for the length of the streak, you could convert the zip result to a string of 1s and 0s, then split on 0s and measure the longest substring:
max(map(len,"".join("01"[a*b<0] for a,b in zip(L,L[1:])).split('0')))+1
4
What about a single loop?
def max_alt_subseq_size(seq):
last_x = seq[0]
size = max_size = 1
for x in seq[1:]:
# use the fact that x * y < 0 iff x > 0 and y < 0 or x < 0 and y > 0
if last_x * x < 0:
size += 1
else:
# once the size of the alternating subsequence is found, we need to check if it is the largest
if size > max_size:
max_size = size
size = 1
last_x = x
# check on the final subsequence to see if it is the largest
if size > max_size:
max_size = size
return max_size
my_list = [-1, -0.5, 1, -3, 4, 5, 5, -1]
max_alt_subseq_size(my_list)
# 4
One could have a (number of) fully vectorized approach.
The code below assumes a NumPy 1D array as input.
For example, if one computes the run-length encoding (RLE) in a vectorized fashion, it would be simple to use RLE information on some array that represents where the signs change to compute the desired value
import numpy as np
def rle(arr):
n = len(arr)
if n == 0:
values = np.empty(0, dtype=arr.dtype)
lengths = np.empty(0, dtype=np.int_)
else:
positions = np.concatenate(
[[-1], np.nonzero(arr[1:] != arr[:-1])[0], [n - 1]])
lengths = positions[1:] - positions[:-1]
values = arr[positions[1:]]
return values, lengths
def max_alt_rle(arr):
values, lengths = rle(arr[1:] * arr[:-1] < 0)
subs_lengths = lengths[values]
return (1 if len(arr) > 0 else 0) + \
(np.max(subs_lengths) if len(subs_lengths) > 0 else 0)
Alternatively, one could make good use of the richer functionalities available to Strings/Bytes, notably str.split() to craft a very short, vectorized, but not very efficient solution:
def max_alt_np(arr):
return (1 if len(arr) > 0 else 0) + \
len(max((arr[1:] * arr[:-1] < 0).tobytes().split(b'\x00')))
If one is after raw speed, accelerating with Numba the single loop solution would be most efficient and fast solution:
import numba as nb
#nb.jit
def max_alt_nb(arr):
if len(arr):
last_x = arr[0]
size = max_size = 1
for x in arr[1:]:
if last_x * x < 0:
size += 1
else:
if size > max_size:
max_size = size
size = 1
last_x = x
if size > max_size:
max_size = size
return max_size
else:
return 0
Finally, here is reported an adaptation of the currently accepted answer, which is neither efficient nor fast, but it is relatively compact (but not as compact as max_alt_np and considerably slower) and can use lists without prior conversion to a NumPy array:
def max_alt_str(arr):
return (1 if len(arr) > 0 else 0) + len(max(
("".join(
"01"[1 if a * b < 0 else 0]
for a, b in zip(arr[:-1], arr[1:])))
.split("0")))
Here some benchmarks on random integer arrays of varying size:
(Full analysis here).
How to check whether a list and an element of a list with such an index exist in the list itself?
I have a list [[10,10,9], [10,10,10], [10,10,10]]
Then I enter the number of coordinates (k) and the coordinates themselves. At these coordinates, I have to subtract 8 from the cell and 4 with each cell standing next to it. But what if there are no cells nearby?
When checking if field [r + f] [c + s] in field: it always gives a negative answer. How to make a check?
for i in range(k):
for j in range(1):
f = drops[i][j]
s = drops[i][j + 1]
field[f][s] -= 8
for r in range(-1, 1):
for c in range(-1, 1):
if not (r == c == 1):
if field[r + f][c + s] in field:
field[r + f][c + s] -= 4
You just have to check whether the index isn't at the start or the end of the list.
n = 2
mylist = [4, 5, 8, 9, 12]
if len(mylist) > n+1:
mylist[n+1] -= 1
if n > 0:
mylist[n-1] -= 1
Slice assignment might help. You have to avoid letting an index go negative, but something like
s = slice(max(n-1,0), n+2)
x[s] = [v-1 for v in x[s]]
isn't too repetitive, while handling the edge cases n == 0 and n == len(s) - 1. (It won't work ifn` is explicitly set to a negative index, though.)
The idea is for each number to add the next sequential numbers to try to reach the target value. If for each starting value (for i...) if adding the next sequential numbers exceeds the target then i has failed and move onto the next.
I'm getting some values slipping through and some duplicating.
If the targets intentionally match the lists it works fine; I've noticed 13 throws up some strange behaviour.
def addToTarget (mylist, target):
solutions_list = []
for i in range(0,len(mylist)):
#set base values
total = mylist[i]
counter = i
solutions = []
solutions.append(total)
if total == target:
solutions_list.append(solutions) # first value matches immediately
elif total > target:
solutions_list.append([counter-1, "first value already too high"])
elif counter == (len(mylist)):
solutions_list.append("caught as final value ")
while total < target and counter < (len(mylist)-1):
counter +=1
value = mylist[counter]
total += value
solutions.append(value)
if total == target:
solutions_list.append([counter, solutions])
elif total > target:
solutions_list.append([counter-1, "total > target during"])
elif counter == (len(mylist)-1):
solutions_list.append([counter-1, "total < target - came to end of list "])
else : solutions_list.append([counter-1, "not sure but certian values seem to slip through"])
return solutions_list
mylist = [5, 5, 3, 10, 2, 8, 10]
solutions_list = []
test = answer(mylist, 13)
for i in test : print(i)
You could use two markers that move through the list and keep track of sum of values between them. While current sum is less than target (13) and first marker is not at the end of the list move it forward and add value to current sum. Once first marker has been moved check if the current sum matches the target and update result accordingly. In final step move second marker forward one step and subtract item that it pointed to from current sum.
l = [5, 5, 3, 10, 2, 8, 10]
TARGET = 13
res = []
end = 0
current = 0
for start in range(len(l)):
while current < TARGET and end < len(l):
current += l[end]
end += 1
res.append(l[start:end] if current == TARGET else 'fail')
current -= l[start]
print(res)
Output:
[[5, 5, 3], 'fail', [3, 10], 'fail', 'fail', 'fail', 'fail']
Your code and your problem statement would benefit from rewriting as a sequence of small, self-contained, painfully obvious steps.
As far as I could understand, you take a list of numbers xs and look for values of i such that xs[i] + xs[i + 1] == target.
So, let's first generate a list of triples (i, x[i], x[i + 1]), then scan it for solutions.
An explicit way:
def pairs(xs):
for i in range(len(xs) - 1):
yield (i, xs[i], xs[i + 1])
A one-line way, fine for reasonably short lists:
def pairs(xs):
return zip(range(len(xs)), xs, xs[1:])
Now find the matches:
matches = [(i, x0, x1) for (i, x0, x1) if x0 + x1 == target]
We don't mark the various conditions of mismatches, though. Tese can be added if the list comprehension above is converted into an explicit loop.
Hope this helps.
Given a list consisting of 1 to 50 integers in the range of -1000 to 1000, calculate the maximum product of one or any number of integers within the list given.
My approach:
import itertools
def answer(xs):
cur = 1
dal = []
for i in range(len(xs), 0, -1):
for j in itertools.combinations(xs, i):
for x in j:
cur *= x
dal.append(cur)
cur = 1
dal.sort(reverse=True)
return str(dal[0])
The results timed out. I want to optimize the structure of the procedure to be as efficient as possible.
Going through all the combinations is a bad idea unless you have months for the calculation. If all numbers were positive, You would just multiply them all. If all were negative You would take even number of them. If You have to skip one, skip the biggest (-2 is bigger than -5). Adding zero to the mix returns always zero, which is worse than any of the previous cases. If there is no positive number and there are zero or one negative numbers, just take the biggest number You have. It can be zero or the only negative number You have.
def answer(xs):
mult = 1
valid = 0
for i in xs:
if i > 0:
mult *= i
valid = 1
negative = [i for i in xs if i<0]
negative.sort()
if(len(negative) & 1):
del negative[-1]
for i in negative:
mult *= i
valid = 1
if valid==0:
return max(xs)
return mult
and here are some test cases:
xs = [0]
print(xs,"->",answer(xs)) #[0] -> 0
xs = [-1]
print(xs,"->",answer(xs)) #[-1] -> -1
xs = [0,-1]
print(xs,"->",answer(xs)) #[0, -1] -> 0
xs = [-2,-3]
print(xs,"->",answer(xs)) #[-2, -3] -> 6
xs = [-2,-3,-4]
print(xs,"->",answer(xs)) #[-2, -3, -4] -> 12
xs = [-2,-3,0]
print(xs,"->",answer(xs)) #[-2, -3, 0] -> 6
xs = [-2,3]
print(xs,"->",answer(xs)) #[-2, 3] -> 3
maximum product can be achieved by multiplying all integers if count of negative is even else maximum product will be leaving the negative (closest to zero) and multiply all others.
for n=1 print the number as it is.
EDITED :
if len(mylist)==1:
print mylist[0]
else:
count=0
for i in mylist:
if i<0:
count+=1
if count>0:
mylist.sort()
if mylist[-1]==0:
print "0"
else:
ans=1
flag=1
for i in xrange(len(mylist)):
if mylist[i]>0 and flag==1:
ans/=mylist[i-1]
else:
ans*=mylist[i]
if flag==1:
ans/=mylist[-1]
print ans
else:
ans=1
for i in mylist:
if i>0:
ans*=i
print ans
and then return ans from your function.
this is a O(n) solution.
You could use a two-phase algorithm for O(n) time complexity. First multiply all the positive numbers with each other and in case there are no positive numbers pick the largest one. With reduce this can be easily done with one line.
On the following step filter out all negative numbers. If there's more than one multiply them all together. In case the multiplication results to negative number (= there's odd amount of negative numbers) divide the result with maximum of the negative numbers. Then multiply the product you got in step one with product of step 2 for the final result. In case product of step 1 was non-positive number then product of step 2 is the result.
from functools import reduce
nums = [3, -4, 5, -2, -3, 0, 1]
res = reduce(lambda x,y: x * y if x > 0 and y > 0 else max(x, y), nums)
negatives = [x for x in nums if x < 0]
if len(negatives) > 1:
neg = reduce(lambda x,y: x * y, negatives)
if neg < 0:
neg //= max(negatives)
res = max(res, 1) * neg
print(res)
Output:
180
If you're using Python 2 there's no need to import reduce since it's a built-in and instead of floordiv just use regular one.
This can be optimized in a few ways. First, instead of hosting everything in an array, have a variable maximum which is initialized to xs[0] and each product is checked against. Additionally, instead of doing the multiplication yourself, you can use mul from the operator module with reduce. Finally, I would use xrange as in Python 2 it does not create an array making it more efficient than range This would make your code look like this
from itertools import combinations
from operator import mul
def answer(xs):
maximum = xs[0]
one = 1 in xs
filter(lambda a: a != 0 and a != 1, xs)
if len(xs) == 0:
if one:
return 1
else:
return 0
for i in xrange(len(xs), 0, -1):
for j in combinations(xs, i):
prod = reduce(mul, j, 1)
if prod > maximum:
maximum = prod
return str(maximum)
I left the return as str(maximum), but you can return it as maximum which is an integer if you want.