Boolean Statements For Elements In Lists - python

Suppose I have a list of coefficients of a polynomial in descending order of exponents (if len(list) == x then the exponents would range from integers x-1 to 0). I want to delete the "small" elements of said list, meaning abs(element) > 0 and abs(element) < .000001 but keep the exponents of the polynomial that are not "small."
How do I exactly do this in Python 3.0?
Here is an example of what I want in context:
my_list = [3.000000000000000e-12, 4.99999999999948, 4.00000000000002, -0.000042535500000e-15, -0.200000000000000]
exponents = [4,3,2,1,0] #As stated previously'
``>>> newlist = [4.99999999999948, 4.00000000000002, -0.200000000000000]
``>>> nexexp = [3,2,0]
Hence, the polynomial would be in the form
4.999999999999948*x^3 + 4.000000000000002*x^2 -0.200000000000000
Any suggestions would be very helpful!

Instead of deleting the small elements, keep the large ones:
newlist,newexp = zip(*[(x,e) for x,e in zip(my_list,exponents) if abs(x) > 1e-6])
You can use a filter, too:
newlist,newexp = zip(*filter(lambda x: abs(x[0]) > 1e-6, zip(my_list,exponents))))

Related

python getting the largest even sum of an array with k elements

I've been studying python algorithm and would like to solve a problem that is:
A positive integer array A and an integer K are given.
Find the largest even sum of the array A with K elements.
If not possible, return -1.
For example, if there is an array A= [1,2,3,4,4,5] and K= 3,
the answer is 12 (5+4+3),
which is the largest even sum with K (3) elements.
However, if A= [3, 3, 3] and K= 1,
the answer is -1 because it cannot make an even sum with one element.
I tried to exclude every minimum odd from the array, but it failed when K=n in the while loop.
Is there any simple way to solve this problem? I would sincerely appreciate if you could give some advice.
Sort the array and "take" the biggest K elements.
If it's already even sum - you are done.
Otherwise, you need to replace exactly one element, replace an even element you have chosen with an odd one you have not, or the other way around. You need the difference between the two elements to be minimized.
A naive solution will check all possible ways to do that, but that's O(n^2). You can do better, by checking the actual two viable candidates:
The maximal odd element you did not choose, and the minimal
even element you have chosen
The maximal even element you did not choose and the minimal odd element you have chosen.
Choose the one that the difference between the two elements is minimal. If no such two elements exist (i.e. your k=3, [3,3,3] example) - there is no viable solution.
Time complexity is O(nlogn) for sorting.
In my (very rusty) python, it should be something like:
def FindMaximalEvenArray(a, k):
a = sorted(a)
chosen = a[len(a)-k:]
not_chosen = a[0:len(a)-k]
if sum(chosen) % 2 == 0:
return sum(chosen)
smallest_chosen_even = next((x for x in chosen if x % 2 == 0), None)
biggest_not_chosen_odd = next((x for x in not_chosen[::-1] if x % 2 != 0), None)
candidiate1 = smallest_chosen_even - biggest_not_chosen_odd if smallest_chosen_even and biggest_not_chosen_odd else float("inf")
smallest_chosen_odd = next((x for x in chosen if x % 2 != 0), None)
biggest_not_chosen_even = next((x for x in not_chosen[::-1] if x % 2 == 0), None)
candidiate2 = smallest_chosen_odd - biggest_not_chosen_even if smallest_chosen_odd and biggest_not_chosen_even else float("inf")
if candidiate1 == float("inf") and candidiate2 == float("inf"):
return -1
return sum(chosen) - min(candidiate1, candidiate2)
Note: This can be done even better (in terms of time complexity), because you don't actually care for the order of all elements, only finding the "candidates" and the top K elements. So you could use Selection Algorithm instead of sorting, which will make this run in O(n) time.

Guidance on removing a nested for loop from function

I'm trying to write the fastest algorithm possible to return the number of "magic triples" (i.e. x, y, z where z is a multiple of y and y is a multiple of x) in a list of 3-2000 integers.
(Note: I believe the list was expected to be sorted and unique but one of the test examples given was [1,1,1] with the expected result of 1 - that is a mistake in the challenge itself though because the definition of a magic triple was explicitly noted as x < y < z, which [1,1,1] isn't. In any case, I was trying to optimise an algorithm for sorted lists of unique integers.)
I haven't been able to work out a solution that doesn't include having three consecutive loops and therefore being O(n^3). I've seen one online that is O(n^2) but I can't get my head around what it's doing, so it doesn't feel right to submit it.
My code is:
def solution(l):
if len(l) < 3:
return 0
elif l == [1,1,1]:
return 1
else:
halfway = int(l[-1]/2)
quarterway = int(halfway/2)
quarterIndex = 0
halfIndex = 0
for i in range(len(l)):
if l[i] >= quarterway:
quarterIndex = i
break
for i in range(len(l)):
if l[i] >= halfway:
halfIndex = i
break
triples = 0
for i in l[:quarterIndex+1]:
for j in l[:halfIndex+1]:
if j != i and j % i == 0:
multiple = 2
while (j * multiple) <= l[-1]:
if j * multiple in l:
triples += 1
multiple += 1
return triples
I've spent quite a lot of time going through examples manually and removing loops through unnecessary sections of the lists but this still completes a list of 2,000 integers in about a second where the O(n^2) solution I found completes the same list in 0.6 seconds - it seems like such a small difference but obviously it means mine takes 60% longer.
Am I missing a really obvious way of removing one of the loops?
Also, I saw mention of making a directed graph and I see the promise in that. I can make the list of first nodes from the original list with a built-in function, so in principle I presume that means I can make the overall graph with two for loops and then return the length of the third node list, but I hit a wall with that too. I just can't seem to make progress without that third loop!!
from array import array
def num_triples(l):
n = len(l)
pairs = set()
lower_counts = array("I", (0 for _ in range(n)))
upper_counts = lower_counts[:]
for i in range(n - 1):
lower = l[i]
for j in range(i + 1, n):
upper = l[j]
if upper % lower == 0:
lower_counts[i] += 1
upper_counts[j] += 1
return sum(nx * nz for nz, nx in zip(lower_counts, upper_counts))
Here, lower_counts[i] is the number of pairs of which the ith number is the y, and z is the other number in the pair (i.e. the number of different z values for this y).
Similarly, upper_counts[i] is the number of pairs of which the ith number is the y, and x is the other number in the pair (i.e. the number of different x values for this y).
So the number of triples in which the ith number is the y value is just the product of those two numbers.
The use of an array here for storing the counts is for scalability of access time. Tests show that up to n=2000 it makes negligible difference in practice, and even up to n=20000 it only made about a 1% difference to the run time (compared to using a list), but it could in principle be the fastest growing term for very large n.
How about using itertools.combinations instead of nested for loops? Combined with list comprehension, it's cleaner and much faster. Let's say l = [your list of integers] and let's assume it's already sorted.
from itertools import combinations
def div(i,j,k): # this function has the logic
return l[k]%l[j]==l[j]%l[i]==0
r = sum([div(i,j,k) for i,j,k in combinations(range(len(l)),3) if i<j<k])
#alaniwi provided a very smart iterative solution.
Here is a recursive solution.
def find_magicals(lst, nplet):
"""Find the number of magical n-plets in a given lst"""
res = 0
for i, base in enumerate(lst):
# find all the multiples of current base
multiples = [num for num in lst[i + 1:] if not num % base]
res += len(multiples) if nplet <= 2 else find_magicals(multiples, nplet - 1)
return res
def solution(lst):
return find_magicals(lst, 3)
The problem can be divided into selecting any number in the original list as the base (i.e x), how many du-plets we can find among the numbers bigger than the base. Since the method to find all du-plets is the same as finding tri-plets, we can solve the problem recursively.
From my testing, this recursive solution is comparable to, if not more performant than, the iterative solution.
This answer was the first suggestion by #alaniwi and is the one I've found to be the fastest (at 0.59 seconds for a 2,000 integer list).
def solution(l):
n = len(l)
lower_counts = dict((val, 0) for val in l)
upper_counts = lower_counts.copy()
for i in range(n - 1):
lower = l[i]
for j in range(i + 1, n):
upper = l[j]
if upper % lower == 0:
lower_counts[lower] += 1
upper_counts[upper] += 1
return sum((lower_counts[y] * upper_counts[y] for y in l))
I think I've managed to get my head around it. What it is essentially doing is comparing each number in the list with every other number to see if the smaller is divisible by the larger and makes two dictionaries:
One with the number of times a number is divisible by a larger
number,
One with the number of times it has a smaller number divisible by
it.
You compare the two dictionaries and multiply the values for each key because the key having a 0 in either essentially means it is not the second number in a triple.
Example:
l = [1,2,3,4,5,6]
lower_counts = {1:5, 2:2, 3:1, 4:0, 5:0, 6:0}
upper_counts = {1:0, 2:1, 3:1, 4:2, 5:1, 6:3}
triple_tuple = ([1,2,4], [1,2,6], [1,3,6])

Find elements in a list of which all elements in another list are factors, using a list comprehension

I have a list of numbers from which I have extracted common factors of all these numbers. For example, from list b = [16, 32, 96], I have produced list_of_common_factors = [1, 8, 16, 2, 4].
I have another list of integers, a and I wish to extract the numbers from list_of_common_factors of which all elements of a are factors. So if a = [2, 4], then I should end up with [4, 8, 16], as these are the numbers in list_of_common_factors of which 2 and 4 are factors.
However, I am struggling to figure out how to implement this step in a list comprehension, even in pseudocode. It should look something like this: [x for x in list_of_common_factors if all elements of a are factors of x]. It's the if statement that I'm having trouble with because I believe it should contain a for loop, but I can't think of a concise way to write it.
I have managed to do it the long way, using a nested for loop and it looks like this:
between_two_lists = []
# Determine the factors in list_of_common_factors of which all elements of a are factors.
for factor in list_of_common_factors:
# Check that all a[i] are factors of factor.
""" Create a counter.
For each factor, find whether a[i] is a factor of factor.
Do this with a for loop up to len(a).
If a[i] is a factor of factor, then increment the counter by 1.
At the end of this for loop, check if the counter is equal to len(a).
If they are equal to each other, then factor satisfies the problem requirements.
Add factor to between_two_lists. """
counter = 0
for element in a:
if factor % element == 0:
counter += 1
if counter == len(a):
between_two_lists.append(factor)
between_two_lists is the list I am trying to produce by converting the above code into a list comprehension. How can I do that, if it is even possible?
It is what you are looking for:
[x for x in list_of_common_factors if all(x % i==0 for i in a)]
So basically, you need to have a function returning the factors from a list of numbers. This function would return a list. And then you simply need to find the intersection of both list. Since each factor is unique, I suggest to use a set implementation which will be more efficient. To resume, the code would look like:
A = set(factors(#Input 1))
B = set(factors(#Input 2))
N = A.intersection(B)
It might be more efficient to calculate the least common multiple of the elements of a first, especially if a has more than 2 elements:
from functools import reduce
def gcd(x, y): # greatest common divisor
while y:
x, y = y, x % y
return x
def lcm(x, y): # least common multiple
return (x*y)//gcd(x,y)
lcm_of_a = reduce(lcm, a)
result = [x for x in list_of_common_factors if (x % lcm_of_a == 0)]

Python - If array is in range of other array

Meaning "if each item is within range of other item with the same index".
price = [1, 2]
budget = [5, 7]
This works:
if price[0] in range(budget[0]) and price[1] in range(budget[1]):
affordable = True
I figure there's some way to just reference the whole array though. Like so: if price in budget:
You could use:
if all(x in range(y) for x,y in zip(price,budget)):
affordable = True
This will create tuples of price[i],budget[i] and then for each of these tuples we check that price[i] is in range(budget[i]). Nevertheless, you can optimize this further to:
if all(0 <= x < y for x,y in zip(price,budget)):
affordable = True
Note that this makes the assumption that prices are all integers. If you however use x in range(y) it will fail if x is not an integer. So 0.7 in range(10) would fail whereas our second approach will succeed (but it depends of course on what you want).
Assuming that both prices and budgets must be non-negative, using in range seems to be over-complicating things. Instead, you could just use the < operator.
Regardless of whether you use < or in range, it seems like the easiest approach would be to zip both lists and apply the condition on the pairs:
if (all([x[0] >= x[1] for x in zip(budget, price)])):
affordable = True

Python Radix Sort

I'm trying to implement Radix sort in python.
My current program is not working correctly in that a list like [41,51,2,3,123] will be sorted correctly to [2,3,41,51,123], but something like [52,41,51,42,23] will become [23,41,42,52,51] (52 and 51 are in the wrong place).
I think I know why this is happening, because when I compare the digits in the tens place, I don't compare units as well (same for higher powers of 10).
How do I fix this issue so that my program runs in the fastest way possible? Thanks!
def radixsort(aList):
BASEMOD = 10
terminateLoop = False
temp = 0
power = 0
newList = []
while not terminateLoop:
terminateLoop = True
tempnums = [[] for x in range(BASEMOD)]
for x in aList:
temp = int(x / (BASEMOD ** power))
tempnums[temp % BASEMOD].append(x)
if terminateLoop:
terminateLoop = False
for y in tempnums:
for x in range(len(y)):
if int(y[x] / (BASEMOD ** (power+1))) == 0:
newList.append(y[x])
aList.remove(y[x])
power += 1
return newList
print(radixsort([1,4,1,5,5,6,12,52,1,5,51,2,21,415,12,51,2,51,2]))
Currently, your sort does nothing to reorder values based on anything but their highest digit. You get 41 and 42 right only by chance (since they are in the correct relative order in the initial list).
You should be always build a new list based on each cycle of the sort.
def radix_sort(nums, base=10):
result_list = []
power = 0
while nums:
bins = [[] for _ in range(base)]
for x in nums:
bins[x // base**power % base].append(x)
nums = []
for bin in bins:
for x in bin:
if x < base**(power+1):
result_list.append(x)
else:
nums.append(x)
power += 1
return result_list
Note that radix sort is not necessarily faster than a comparison-based sort. It only has a lower complexity if the number of items to be sorted is larger than the range of the item's values. Its complexity is O(len(nums) * log(max(nums))) rather than O(len(nums) * log(len(nums))).
Radix sort sorts the elements by first grouping the individual digits of the same place value. [2,3,41,51,123] first we group them based on first digits.
[[],[41,51],[2],[3,123],[],[],[],[],[],[]]
Then, sort the elements according to their increasing/decreasing order. new array will be
[41,51,2,3,123]
then we will be sorting based on tenth digit. in this case [2,3]=[02,03]
[[2,3],[],[123],[],[41],[51],[],[],[],[]]
now new array will be
[2,3,123,41,51]
lastly based on 100th digits. this time [2,3,41,51]=[002,003,041,051]
[[2,3,41,51],[123],[],[],[],[],[],[],[],[]]
finally we end up having [2,3,41,51,123]
def radixsort(A):
if not isinstance(A,list):
raise TypeError('')
n=len(A)
maxelement=max(A)
digits=len(str(maxelement)) # how many digits in the maxelement
l=[]
bins=[l]*10 # [[],[],.........[]] 10 bins
for i in range(digits):
for j in range(n): #withing this we traverse unsorted array
e=int((A[j]/pow(10,i))%10)
if len(bins[e])>0:
bins[e].append(A[j]) #adds item to the end
else:
bins[e]=[A[j]]
k=0 # used for the index of resorted arrayA
for x in range(10):#we traverse the bins and sort the array
if len(bins[x])>0:
for y in range(len(bins[x])):
A[k]=bins[x].pop(0) #remove element from the beginning
k=k+1

Categories

Resources