Related
Sinopsis: my code runs well with simple lists, but when I attempt, after the 4 basic test its execution time gets timed out.
Since I don't want to look for others solution, I'm asking for help and someone can show me which part of the code its messing with the time execution in order to focus only into modify that part.
Note: I don't want a finally solution, just know which part of the code I have to change please
Exercise:
Given an array of positive or negative integers
I= [i1,..,in]
you have to produce a sorted array P of the form
[ [p, sum of all ij of I for which p is a prime factor (p positive) of ij] ...]
P will be sorted by increasing order of the prime numbers. The final result has to be given as a string in Java, C# or C++ and as an array of arrays in other languages.
Example:
I = [12, 15] # result = [[2, 12], [3, 27], [5, 15]]
[2, 3, 5] is the list of all prime factors of the elements of I, hence the result.
Notes: It can happen that a sum is 0 if some numbers are negative!
Example: I = [15, 30, -45] 5 divides 15, 30 and (-45) so 5 appears in the result, the sum of the numbers for which 5 is a factor is 0 so we have [5, 0] in the result amongst others.
`
def sum_for_list(lst):
if len(lst) == 0:
return []
max = sorted(list(map(lambda x: abs(x), lst)), reverse = True)[0]
#create the list with the primes, already filtered
primes = []
for i in range (2, max + 1):
for j in range (2, i):
if i % j == 0:
break
else:
for x in lst:
if x % i == 0:
primes.append([i])
break
#i add the sums to the primes
for i in primes:
sum = 0
for j in lst:
if j % i[0] == 0:
sum += j
i.append(sum)
return primes
`
Image
I tried to simplyfy the code as much as I could but same result.
I also tried other ways to iterate in the first step:
# Find the maximum value in the list
from functools import reduce
max = reduce(lambda x,y: abs(x) if abs(x)>abs(y) else abs(y), lst)
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]))
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.
What I'm looking to do:
I need to make a function that, given a list of positive integers (there can be duplicate integers), counts all triples (in the list) in which the third number is a multiple of the second and the second is a multiple of the first:
(The same number cannot be used twice in one triple, but can be used by all other triples)
For example, [3, 6, 18] is one because 18 goes evenly into 6 which goes evenly into 3.
So given [1, 2, 3, 4, 5, 6] it should find:
[1, 2, 4] [1, 2, 6] [1, 3, 6]
and return 3 (the number of triples it found)
What I've tried:
I made a couple of functions that work but are not efficient enough. Is there some math concept I don't know about that would help me find these triples faster? A module with a function that does better? I don't know what to search for...
def foo(q):
l = sorted(q)
ln = range(len(l))
for x in ln:
if len(l[x:]) > 1:
for y in ln[x + 1:]:
if (len(l[y:]) > 0) and (l[y] % l[x] == 0):
for z in ln[y + 1:]:
if l[z] % l[y] == 0:
ans += 1
return ans
This one is a bit faster:
def bar(q):
l = sorted(q)
ans = 0
for x2, x in enumerate(l):
pool = l[x2 + 1:]
if len(pool) > 1:
for y2, y in enumerate(pool):
pool2 = pool[y2 + 1:]
if pool2 and (y % x == 0):
for z in pool2:
if z % y == 0:
ans += 1
return ans
Here's what I've come up with with help from y'all but I must be doing something wrong because it get's the wrong answer (it's really fast though):
def function4(numbers):
ans = 0
num_dict = {}
index = 0
for x in numbers:
index += 1
num_dict[x] = [y for y in numbers[index:] if y % x == 0]
for x in numbers:
for y in num_dict[x]:
for z in num_dict[y]:
print(x, y, z)
ans += 1
return ans
(39889 instead of 40888) - oh, I accidentally made the index var start at 1 instead of 0. It works now.
Final Edit
I've found the best way to find the number of triples by reevaluating what I needed it to do. This method doesn't actually find the triples, it just counts them.
def foo(l):
llen = len(l)
total = 0
cache = {}
for i in range(llen):
cache[i] = 0
for x in range(llen):
for y in range(x + 1, llen):
if l[y] % l[x] == 0:
cache[y] += 1
total += cache[x]
return total
And here's a version of the function that explains the thought process as it goes (not good for huge lists though because of spam prints):
def bar(l):
list_length = len(l)
total_triples = 0
cache = {}
for i in range(list_length):
cache[i] = 0
for x in range(list_length):
print("\n\nfor index[{}]: {}".format(x, l[x]))
for y in range(x + 1, list_length):
print("\n\ttry index[{}]: {}".format(y, l[y]))
if l[y] % l[x] == 0:
print("\n\t\t{} can be evenly diveded by {}".format(l[y], l[x]))
cache[y] += 1
total_triples += cache[x]
print("\t\tcache[{0}] is now {1}".format(y, cache[y]))
print("\t\tcount is now {}".format(total_triples))
print("\t\t(+{} from cache[{}])".format(cache[x], x))
else:
print("\n\t\tfalse")
print("\ntotal number of triples:", total_triples)
Right now your algorithm has O(N^3) running time, meaning that every time you double the length of the initial list the running time goes up by 8 times.
In the worst case, you cannot improve this. For example, if your numbers are all successive powers of 2, meaning that every number divides every number grater than it, then every triple of numbers is a valid solution so just to print out all the solutions is going to be just as slow as what you are doing now.
If you have a lower "density" of numbers that divide other numbers, one thing you can do to speed things up is to search for pairs of numbers instead of triples. This will take time that is only O(N^2), meaning the running time goes up by 4 times when you double the length of the input list. Once you have a list of pairs of numbers you can use it to build a list of triples.
# For simplicity, I assume that a number can't occur more than once in the list.
# You will need to tweak this algorithm to be able to deal with duplicates.
# this dictionary will map each number `n` to the list of other numbers
# that appear on the list that are multiples of `n`.
multiples = {}
for n in numbers:
multiples[n] = []
# Going through each combination takes time O(N^2)
for x in numbers:
for y in numbers:
if x != y and y % x == 0:
multiples[x].append(y)
# The speed on this last step will depend on how many numbers
# are multiples of other numbers. In the worst case this will
# be just as slow as your current algoritm. In the fastest case
# (when no numbers divide other numbers) then it will be just a
# O(N) scan for the outermost loop.
for x in numbers:
for y in multiples[x]:
for z in multiples[y]:
print(x,y,z)
There might be even faster algorithms, that also take advantage of algebraic properties of division but in your case I think a O(N^2) is probably going to be fast enough.
the key insight is:
if a divides b, it means a "fits into b".
if a doesn't divide c, then it means "a doesn't fit into c".
And if a can't fit into c, then b cannot fit into c (imagine if b fitted into c, since a fits into b, then a would fit into all the b's that fit into c and so a would have to fit into c too.. (think of prime factorisation etc))
this means that we can optimise. If we sort the numbers smallest to largest and start with the smaller numbers first. First iteration, start with the smallest number as a
If we partition the numbers into two groups, group 1, the numbers which a divides, and group 2 the group which a doesn't divide, then we know that no numbers in group 1 can divide numbers in group 2 because no numbers in group 2 have a as a factor.
so if we had [2,3,4,5,6,7], we would start with 2 and get:
[2,4,6] and [3,5,7]
we can repeat the process on each group, splitting into smaller groups. This suggests an algorithm that could count the triples more efficiently. The groups will get really small really quickly, which means its efficiency should be fairly close to the size of the output.
This is the best answer that I was able to come up with so far. It's fast, but not quite fast enough. I'm still posting it because I'm probably going to abandon this question and don't want to leave out any progress I've made.
def answer(l):
num_dict = {}
ans_set = set()
for a2, a in enumerate(l):
num_dict[(a, a2)] = []
for x2, x in enumerate(l):
for y2, y in enumerate(l):
if (y, y2) != (x, x2) and y % x == 0:
pair = (y, y2)
num_dict[(x, x2)].append(pair)
for x in num_dict:
for y in num_dict[x]:
for z in num_dict[y]:
ans_set.add((x[0], y[0], z[0]))
return len(ans_set)
I want to test if a list contains consecutive integers and no repetition of numbers.
For example, if I have
l = [1, 3, 5, 2, 4, 6]
It should return True.
How should I check if the list contains up to n consecutive numbers without modifying the original list?
I thought about copying the list and removing each number that appears in the original list and if the list is empty then it will return True.
Is there a better way to do this?
For the whole list, it should just be as simple as
sorted(l) == list(range(min(l), max(l)+1))
This preserves the original list, but making a copy (and then sorting) may be expensive if your list is particularly long.
Note that in Python 2 you could simply use the below because range returned a list object. In 3.x and higher the function has been changed to return a range object, so an explicit conversion to list is needed before comparing to sorted(l)
sorted(l) == range(min(l), max(l)+1))
To check if n entries are consecutive and non-repeating, it gets a little more complicated:
def check(n, l):
subs = [l[i:i+n] for i in range(len(l)) if len(l[i:i+n]) == n]
return any([(sorted(sub) in range(min(l), max(l)+1)) for sub in subs])
The first code removes duplicates but keeps order:
from itertools import groupby, count
l = [1,2,4,5,2,1,5,6,5,3,5,5]
def remove_duplicates(values):
output = []
seen = set()
for value in values:
if value not in seen:
output.append(value)
seen.add(value)
return output
l = remove_duplicates(l) # output = [1, 2, 4, 5, 6, 3]
The next set is to identify which ones are in order, taken from here:
def as_range(iterable):
l = list(iterable)
if len(l) > 1:
return '{0}-{1}'.format(l[0], l[-1])
else:
return '{0}'.format(l[0])
l = ','.join(as_range(g) for _, g in groupby(l, key=lambda n, c=count(): n-next(c)))
l outputs as: 1-2,4-6,3
You can customize the functions depending on your output.
We can use known mathematics formula for checking consecutiveness,
Assuming min number always start from 1
sum of consecutive n numbers 1...n = n * (n+1) /2
def check_is_consecutive(l):
maximum = max(l)
if sum(l) == maximum * (maximum+1) /2 :
return True
return False
Once you verify that the list has no duplicates, just compute the sum of the integers between min(l) and max(l):
def check(l):
total = 0
minimum = float('+inf')
maximum = float('-inf')
seen = set()
for n in l:
if n in seen:
return False
seen.add(n)
if n < minimum:
minimum = n
if n > maximum:
maximum = n
total += n
if 2 * total != maximum * (maximum + 1) - minimum * (minimum - 1):
return False
return True
import numpy as np
import pandas as pd
(sum(np.diff(sorted(l)) == 1) >= n) & (all(pd.Series(l).value_counts() == 1))
We test both conditions, first by finding the iterative difference of the sorted list np.diff(sorted(l)) we can test if there are n consecutive integers. Lastly, we test if the value_counts() are all 1, indicating no repeats.
I split your query into two parts part A "list contains up to n consecutive numbers" this is the first line if len(l) != len(set(l)):
And part b, splits the list into possible shorter lists and checks if they are consecutive.
def example (l, n):
if len(l) != len(set(l)): # part a
return False
for i in range(0, len(l)-n+1): # part b
if l[i:i+3] == sorted(l[i:i+3]):
return True
return False
l = [1, 3, 5, 2, 4, 6]
print example(l, 3)
def solution(A):
counter = [0]*len(A)
limit = len(A)
for element in A:
if not 1 <= element <= limit:
return False
else:
if counter[element-1] != 0:
return False
else:
counter[element-1] = 1
return True
The input to this function is your list.This function returns False if the numbers are repeated.
The below code works even if the list does not start with 1.
def check_is_consecutive(l):
"""
sorts the list and
checks if the elements in the list are consecutive
This function does not handle any exceptions.
returns true if the list contains consecutive numbers, else False
"""
l = list(filter(None,l))
l = sorted(l)
if len(l) > 1:
maximum = l[-1]
minimum = l[0] - 1
if minimum == 0:
if sum(l) == (maximum * (maximum+1) /2):
return True
else:
return False
else:
if sum(l) == (maximum * (maximum+1) /2) - (minimum * (minimum+1) /2) :
return True
else:
return False
else:
return True
1.
l.sort()
2.
for i in range(0,len(l)-1)))
print(all((l[i+1]-l[i]==1)
list must be sorted!
lst = [9,10,11,12,13,14,15,16]
final = True if len( [ True for x in lst[:-1] for y in lst[1:] if x + 1 == y ] ) == len(lst[1:]) else False
i don't know how efficient this is but it should do the trick.
With sorting
In Python 3, I use this simple solution:
def check(lst):
lst = sorted(lst)
if lst:
return lst == list(range(lst[0], lst[-1] + 1))
else:
return True
Note that, after sorting the list, its minimum and maximum come for free as the first (lst[0]) and the last (lst[-1]) elements.
I'm returning True in case the argument is empty, but this decision is arbitrary. Choose whatever fits best your use case.
In this solution, we first sort the argument and then compare it with another list that we know that is consecutive and has no repetitions.
Without sorting
In one of the answers, the OP commented asking if it would be possible to do the same without sorting the list. This is interesting, and this is my solution:
def check(lst):
if lst:
r = range(min(lst), max(lst) + 1) # *r* is our reference
return (
len(lst) == len(r)
and all(map(lst.__contains__, r))
# alternative: all(x in lst for x in r)
# test if every element of the reference *r* is in *lst*
)
else:
return True
In this solution, we build a reference range r that is a consecutive (and thus non-repeating) sequence of ints. With this, our test is simple: first we check that lst has the correct number of elements (not more, which would indicate repetitions, nor less, which indicates gaps) by comparing it with the reference. Then we check that every element in our reference is also in lst (this is what all(map(lst.__contains__, r)) is doing: it iterates over r and tests if all of its elements are in lts).
l = [1, 3, 5, 2, 4, 6]
from itertools import chain
def check_if_consecutive_and_no_duplicates(my_list=None):
return all(
list(
chain.from_iterable(
[
[a + 1 in sorted(my_list) for a in sorted(my_list)[:-1]],
[sorted(my_list)[-2] + 1 in my_list],
[len(my_list) == len(set(my_list))],
]
)
)
)
Add 1 to any number in the list except for the last number(6) and check if the result is in the list. For the last number (6) which is the greatest one, pick the number before it(5) and add 1 and check if the result(6) is in the list.
Here is a really short easy solution without having to use any imports:
range = range(10)
L = [1,3,5,2,4,6]
L = sorted(L, key = lambda L:L)
range[(L[0]):(len(L)+L[0])] == L
>>True
This works for numerical lists of any length and detects duplicates.
Basically, you are creating a range your list could potentially be in, editing that range to match your list's criteria (length, starting value) and making a snapshot comparison. I came up with this for a card game I am coding where I need to detect straights/runs in a hand and it seems to work pretty well.