Find a maximum possible value of an array - python

I have an array of integers and I need to get their maximum possible value. If I have negative numbers and their total amount is uneven I have to exclude one of them to make an array positive.
If I have 0 inside and it can affect on result of my multiplication, I have to exclude it also.
For example: for [2, 5, -2] result should be 10, for [-2,-5,-1, 0, 2] result should be 20.
I implemented the task, but the system doesn't accept my solution, could you please take a look at my solution where I could make a mistake? I tried different edge cases like [-1], [1], [0], [0,0,0]
def answer(n):
arr = 0
res = 1
for number in n:
if number < 0:
arr += 1
n.sort()
while 0 in n: n.remove(0)
if not n:
return '0'
if len(n) == 1:
if n[0] < 0:
return '0'
elif arr % 2 != 0:
n.pop(arr - 1)
for x in n:
res *= x
return str(res)

It appears you are looking to multiply all numbers in a list, except for any zeroes and if there's an odd number of negative numbers, you are excluding the smallest negative number?
A simple solution:
from functools import reduce
def answer(numbers):
selection = [n for n in numbers if n != 0]
negative = [n for n in selection if n < 0]
if len(negative) % 2 == 1:
selection.remove(max(negative))
if not selection:
return 0
else:
return reduce(lambda x, y: x * y, selection)
print(answer([-2, -5, -1, 0, 2]))

Related

finding if an array can be converted to strictly increasing order

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

Size of the largest subsequence of values with consecutive alternative sign?

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).

Find array subset with the maximum product

I received interview question to find the non-empty subset of an array with the maximum product. I solved the task but one test out of five doesn't pass. I don't understand what I may have missed in my solution. (The task should be done in python 2.7)
Task is here:
I have an array of integers and I need to return the non-empty subset of elements with the maximum product of values. If I have an odd number of negative numbers in the array, I have to exclude one of them to make the selected product be positive.
If I have 0 inside, in general I'll want to exclude it, also. For example: for [2, 5, -2] result should be 10, for [-2,-5,-1, 0, 2] result should be 20.
I tried different edge cases like [-1], [1], [0], [0,0,0]
Solution is here:
from functools import reduce
def answer(arr):
selection = [n for n in arr if n !=0 and -1000<=n<=1000]
negative = [n for n in selection if n<0]
if len(negative) % 2 == 1:
selection.remove(max(negative))
if not selection:
return '0'
else:
return str(reduce(lambda x, y: x * y, selection))
A careful reading of the question indicates that answer([-5]) should yield solution -5, since it needs to select a non-empty subset of the input array. However, your code returns 0 for answer([-5]). So perhaps something like:
from functools import reduce
def answer(arr):
hasZero = any([n == 0 for n in arr])
selection = [n for n in arr if n !=0 and -1000<=n<=1000]
negative = [n for n in selection if n<0]
if len(negative) % 2 == 1 and (len(selection) > 1 or hasZero):
selection.remove(max(negative))
if not selection:
return '0'
else:
return str(reduce(lambda x, y: x * y, selection))
answer([-5])
# '-5'
answer([0, -5])
# '0'

Find whether the largest element in the array is at least twice as much as every other number in the array?

I'm trying to run a program that finds the index of the number that is at least two times larger than all other number in the array.
Here's my code
def dominantIndex(self, nums):
max_num = max(nums)
max_i =nums.index(max_num)
if len(nums) == 1:
return nums.index(max_num)
for num in nums:
if max_num >= 2*num:
return num.index(max_num)
return -1
However , it doesn't work perfectly for all inputs. Could someone please fix it and check for inputs like :
[1,0]
[0,3,4,8]
[0,3,5,2]
This checks for many possible input problems.
Then it sorts the list to get the answer you are looking for. I decided to sort, for simplicity, but you could use other methods as well. I added comments so everything is clear, especially about the input tests, as asked.
def dominantIndex(nums):
# If the array is empty or None or not a list, return -1
if not nums or type(nums) != list:
return -1
# If the array is of length 1, return the only index, 0
elif len(nums) == 1:
return 0
sorted_numbers = sorted(nums)
# If the highest number is twice the second largest, return it's index
if sorted_numbers[-2] * 2 <= sorted_numbers[-1]:
return nums.index(sorted_numbers[-1])
else:
return -1
There actually is a library function nlargest in the heapq module.
>>> L1 = [1, 0]
>>> L2 = [0, 3, 4, 8]
>>> L3 = [0, 4, 5, 2]
>>>
>>> from heapq import nlargest
>>> def dom_ind(nums):
... a, b = nlargest(2, range(len(nums)), key=nums.__getitem__)
... return a if nums[a] >= 2 * nums[b] else -1
...
>>>
>>> dom_ind(L1)
0
>>> dom_ind(L2)
3
>>> dom_ind(L3)
-1
Use below code
def check(nums):
if len(nums) == 1:
return 0
max_num = max(nums)
ind = nums.index(max_num)
updated_array = map(lambda x: x if x != max_num else -1, nums)
if max_num >= 2*max(updated_array):
return ind
return -1
Output:
check([1,0])
0
>>> check([0,3,4,8])
3
>>> check([0,3,5,2])
-1
You can get the second highest as well;:
def dominantIndex(nums):
max_num = max(nums)
secondHighest = max( n for n in nums if n != max_num)
if max_num > 2 * secondHighest:
return nums.index(max_num)
return -1
print(dominantIndex( [1,2,39,7]))
print(dominantIndex( [1,2,3,5]))
Output:
2
-1
I'd iterate over the list and find the two largest elements. Then, if the largest element is at least twice as large as the second largest, you can return its index.
def dominantIndex(nums):
# Assumption - nums has a length of at least 2.
# Bootsrap the rist two indexes
if nums[0] > nums[1]:
max_ind = 0
next_ind = 1
else:
max_ind = 1
next_ind = 0
# Go over the rest
for i in range(2, len(nums)):
if nums[i] > nums[max_ind]:
next_ind = max_ind
max_ind = i
elif nums[i] > nums[next_ind]:
next_ind = i
if nums[max_ind] >= nums[next_ind] * 2:
retrun max_ind
return -1
I think none of the other suggested solutions works if the largest number appears multiple times in nums. This does:
def dominantIndex(self, nums):
s = sorted(nums)
if s[-1] >= 2 * s[-2]:
return nums.index(s[-1])
else:
return -1
In addition, you might want to check if nums has more than one element.

Efficiency optimization

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.

Categories