index value of the tuples - python

write a function named sums that takes 3 arguments: 2 lists of integers nums1 and nums2, and an integer target; then return a tuple containing indices i and j of the two numbers in nums1 and nums2, respectively, where the numbers at that indices add up to target (i.e, nums1[i] + num2[j] = target). If there is no solution, return “Not found”. The function sums must contain exactly only one loop (either a for-loop or a while-loop), otherwise you will get no point. A nested-loop is considered as more than one loop.
def sums(nums1, nums2, target):
nums1 = [11, 2, 15, 7, 8]
nums2 = [2, 3, 4, 5]
target_number = 9
for i in range(len(nums1)):
for j in range(len(nums2)):
total = nums1[i] + nums2[j]
if total == target:
a = (nums1[i], nums2[j])
print(a)
This is the code that i have written and i am struggling to get the index values can anyone please help!

You can solve it in O(n) with a dictionary and a single for loop;
nums1 = [11, 2, 15, 7, 8]
nums2 = [2, 3, 4, 5]
target_number = 9
def sums(nums1, nums2, target):
d = dict(zip(nums2, range(len(nums2))))
for i,n in enumerate(nums1):
remainder = target-n
if remainder in d:
return (i, d[remainder])
return 'Not found'
sums(nums1, nums2, target_number)
Output: (3, 0)
Be aware that while there is a single for loop, the code is looping to construct the dictionary, it's just not visible directly.

Here's an alternative solution using basic index incrementing. It's not as efficient as using a dictionary and finding the difference, as this naively iterates over b as many times as there are items in a. In the worst case, with two equal-length lists, this solution is O(n^2).
Although it may actually be faster in smaller cases, if there happens to be a bottlneck around the creation of any of the intermediate data structures that #mozway uses. I'm only speculating on this, I don't actually know.
def target_sum_idxs(a, b, target):
i = j = 0
while i < len(a):
if a[i] + b[j] == target:
return i, j
j += 1
if j == len(b):
i += 1
j = 0
return "Not found"
Demo:
>>> target_sum_idxs(nums1, nums2, target=9)
(3, 0)
>>> target_sum_idxs(nums2, nums1, target=9)
(0, 3)
>>> target_sum_idxs(nums1, nums2, target=999)
'Not found'

just one little thing.
Check that the variable in the if statement is really your parameter, not the "target_number" variable.
Also, in the if statement, you need to stop execution, maybe you can use a break or return the tuple.
I would use something like this:
def sums(nums1, nums2, target):
for i in range(len(nums1)):
for j in range(len(nums2)):
total = nums1[i] + nums2[j]
if total == target:
a = (i, j)
return a
return "Not found"

Related

Sum by Factors From Codewars.com

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)

'in' for two sorted lists with the lowest complexity

I have two sorted lists, e.g.
a = [1, 4, 7, 8]
b = [1, 2, 3, 4, 5, 6]
I want to know for each item in a if it is in b. For the above example, I want to find
a_in_b = [True, True, False, False]
(or having the indices where a_in_b is True would be fine too).
Now, both a and b are very large, so complexity is an issue. If M = len(a) and N = len(b). How can I do this with a complexity lower than M * O(N) by making use of the fact that both lists are sorted?
You can iterate over your b list manually within a loop over a. You'll want to advance the b iteration when the latest value you've seen from it is less than the current value from a.
from math import inf
result = []
b_iter = iter(b) # create an iterator over b
b_val = -inf
for a_val in a:
while b_val < a_val:
b_val = next(b_iter, inf) # manually iterate on it
result.append(a_val == b_val)
This should have a running time of O(M+N), since each list item gets iterated over at most once (b may not even need to be fully iterated).
You could avoid using floating point infinities if you want to, but you'd need to do a bit of extra work to handle some edge cases (e.g. if b is empty).
Exploiting sorted'ness is a red-herring for time complexity: The ideal case is to iterate both in lockstep for O(n+m) complexity. This is the same as converting b to a set for O(m), then searching the elements of a in the set for O(n).
>>> a = [1, 4, 7, 8]
>>> b = [1, 2, 3, 4, 5, 6]
>>> bs = set(b) # create set for O(len(b))
>>> [item in bs for item in a] # check O(len(a)) items "in set of b" for O(1) each
[True, True, False, False]
Since most of these operations are builtin, the only costly operation is the iteration over a which is needed in all solutions.
However, this will duplicate the references to the items in b. If b is treated as external to the algorithm, the space complexity is O(m+n) instead of the ideal case O(n) for just the answer.
Late answer, but a different approach to the problem using set() uniqueness and O(1) speed of len(), i. e. :
a_in_b = []
a = [1,4,7,8]
b = [1,2,3,4,5,6]
b_set = set(b)
for v in a:
l1 = len(b_set)
b_set.add(v)
a_in_b.append(l1 == len(b_set))
Unfortunately, my approach isn't the fastest:
mistermiyagi: 0.387 ms
tomerikoo: 0.442 ms
blckknght: 0.729 ms
lobito: 1.043 ms
semisecure: 1.87 ms
notnotparas: too long
lucky6qi: too long
Benchmark
Use Binary Search here:
def bs(b,aele,start,end):
if start > end:
return False
mid = (start + end) // 2
if ale == b[mid]:
return True
if ale < b[mid]:
return bs(b, aele, start, mid-1)
else:
return bs(b, aele, mid+1, end)
For each element in a check if it exists in b using this method.
Time Complexity: O(m*log(n))
Using sets the order doesn't even matter.
Turn b to a set (O(N)). Then iterate a (O(M)), and for each element check if it's in set_b (O(1)). This will give a time complexity of O(max(M, N)):
a = [1, 4, 7, 8]
b = [1, 2, 3, 4, 5, 6]
set_b = set(b)
res = []
for elem in a:
res.append(elem in set_b)
This can of-course be shortened to a nifty list-comp:
res = [elem in set_b for elem in a]
Both give:
[True, True, False, False]
For your parenthesized request, simply iterate with enumerate instead:
for i, elem in enumerate(a):
if elem in set_b:
res.append(i)
Which will give [0, 1].
You should use binary search algorithm(read about it if you don't know what it is).
The modified bin_search function has to return position right for which b[right] >= elem - the first element in b that is greater or equal than searched element from a. This position will be used as the left position for next bin_search call. Also bin_search returns True as a second argument if it have found elem in b
def bin_search(arr, elem, left):
right = len(arr)
while left < right:
mid = (left+right)//2
if arr[mid] == elem:
return (mid, True)
if arr[mid] < elem:
left = mid + 1
else:
right = mid
return (right, False)
def find_a_in_b(a, b):
new_left = 0
a_in_b = [False] * len(a)
# we could have used enumerate but size of a is too large
index = 0
for i in a:
new_left, a_in_b[index] = bin_search(b, i, new_left)
index += 1
return a_in_b
It's probably the best time
P.S. Forget it, i'm stupid and forgot about linear algorithm used in merge sort, so it's not the best
Go through a and b once:
a_in_b = []
bstart = 0
for ai in a:
print (ai,bstart)
if bstart == len(b):
a_in_b.append(False)
else:
for bi in b[bstart:]:
print (ai, bi, bstart)
if ai == bi:
a_in_b.append(True)
break
elif ai > bi:
if bstart < len(b):
bstart+=1
if bstart == len(b):
a_in_b.append(False)
continue
The obvious solution is actually O(M + N):
a = [1, 1, 4, 7, 8]
b = [1, 2, 3, 4, 5, 6]
c = [0] * len(a) # Or use a dict to stash hits ..
j = 0
for i in range(0, len(a)):
while j < len(b) - 1 and b[j] < a[i]:
j += 1
if b[j] == a[i]:
c[i] = 1
print(c)
For each i in 0 ... N where N is length of a, only a unique partition / sub-sequence of b plus one border number is checked, making it O(M + N) all together.
for el in a:
try:
b = b[b.index(el):]
a_in_b.append("True")
except:
a_in_b.append("False")
A simple solution is to convert the lists to a data frame and do an inner merge
The inner join matches like values on a specific column

Why control is going inside if condition while the condition is false

Function to find minimum number of eliminations such that sum of all adjacent elements is even:
def min_elimination(n, arr):
countOdd = 0
# Stores the new value
for i in range(n):
# Count odd numbers
***if (arr[i] % 2):
countOdd += 1***
# Return the minimum of even and
# odd count
return min(countOdd, n - countOdd)
# Driver code
if __name__ == '__main__':
arr = [1, 2, 3, 7, 9]
n = len(arr)
print(min_elimination(n, arr))
Please help me with the if condition. When the code does if(number%2) then control is going inside the if since the first element of list is an odd number. Is there any difference between if(number%2) and if(number%2==0). Because when I tried if(number%2==0) control didn't go inside the if as the number was odd (check first element of the list).
This is a simple version of the above.
def min_elimination(arr):
lst1 = [n for n in arr if n%2] # List of all odd numbers
lst2 = [n for n in arr if not n%2] # List of all even numbers
lst = max(lst1, lst2, key=lambda x: len(x))
return lst
print(min_elimination([1, 2, 3, 7, 9]))
I believe your code works fine as-is. It correctly returns the minimum number of eliminations necessary to achieve the result, not the result itself.
is there any difference between if(number%2) and if(number%2==0)
Yes, if number % 2 is the same as saying if number % 2 == 1, which is the opposite of saying if number % 2 == 0. So switching one for the other will break your program's logic.
I might simplify your code as follows:
def min_elimination(array):
odd = 0
# Stores the new value
for number in array:
# Count odd numbers
odd += number % 2
return min(odd, len(array) - odd)
Where min_elimination([1, 2, 3, 7, 9]) returns 1, there is one (1) elimination necessary to make the sum of all adjacent elements even.
The only way two integers can add up to be even is that both integers should be odd, or both integers should be even. So your question basically wants to find whether there are more odd number in the array, or even numbers:
def min_elimination(arr):
return len(min([n for n in arr if n%2],[n for n in arr if not n%2],key=len))
print(min_elimination([1, 2, 3, 7, 9]))
Output:
1
You can use numpy to compare the number of odd numbers in the array to the number of even numbers in the array:
import numpy as np
def min_elimination(arr):
return len(min(arr[arr%2],arr[any(arr%2)],key=len))
print(min_elimination(np.array([1, 2, 3, 7, 9])))
Output:
1

Trying to optimize this code: iterating over a list to replace its values

I am trying to do a challenge in Python, the challenge consists of :
Given an array X of positive integers, its elements are to be transformed by running the following operation on them as many times as required:
if X[i] > X[j] then X[i] = X[i] - X[j]
When no more transformations are possible, return its sum ("smallest possible sum").
Basically you pick two non-equal numbers from the array, and replace the largest of them with their subtraction. You repeat this till all numbers in array are same.
I tried a basic approach by using min and max but there is another constraint which is time. I always get timeout because my code is not optimized and takes too much time to execute. Can you please suggest some solutions to make it run faster.
def solution(array):
while len(set(array)) != 1:
array[array.index(max(array))] = max(array) - min(array)
return sum(array)
Thank you so much !
EDIT
I will avoid to spoil the challenge... because I didn't find the solution in Python. But here's the general design of an algorithm that works in Kotlin (in 538 ms). In Python I'm stuck at the middle of the performance tests.
Some thoughts:
First, the idea to remove the minimum from the other elements is good: the modulo (we remove the minimum as long as it is possible) will be small.
Second, if this minimum is 1, the array will be soon full of 1s and the result is N (the len of the array).
Third, if all elements are equal, the result is N times the value of one element.
The algorithm
The idea is to keep two indices: i is the current index that cycles on 0..N and k is the index of the current minimum.
At the beginning, k = i = 0 and the minimum is m = arr[0]. We advance i until one of the following happen:
i == k => we made a full cycle without updating k, return N*m;
arr[i] == 1 => return N;
arr[i] < m => update k and m;
arr[i] > m => compute the new value of arr[i] (that is arr[i] % m or m if arr[i] is a multiple of m). If thats not m, thats arr[i] % m < m: update k and m;
arr[i] == m => pass.
Bascially, we use a rolling minimum and compute the modulos on the fly until all element are the same. That spares the computation of a min of the array periodically.
PREVIOUS ANSWER
As #BallpointBen wrote, you'll get the n times the GCD of all numbers. But that's cheating ;)! If you want to find a solution by hand, you can optimize your code.
While you don't find N identical numbers, you use the set, max (twice!), min and index functions on array. Those functions are pretty expensive. The number of iterations depend on the array.
Imagine the array is sorted in reverse order: [22, 14, 6, 2]. You can replace 22 by 22-14, 14 by 14-6, ... and get: [8, 12, 4, 2]. Sort again: [12, 8, 4, 2], replace again: [4, 4, 4, 2]. Sort again, replace again (if different): [4, 4, 2, 2], [4, 2, 2, 2], [2, 2, 2, 2]. Actually, in the first pass 14 could be replaced by 14-2*6 = 2 (as in the classic GCD computation), giving the following sequence:
[22, 14, 6, 2]
[8, 2, 2, 2]
[2, 2, 2, 2]
The convergence is fast.
def solution2(arr):
N = len(arr)
end = False
while not end:
arr = sorted(arr, reverse=True)
end = True
for i in range(1, N):
while arr[i-1] > arr[i]:
arr[i-1] -= arr[i]
end = False
return sum(arr)
A benchmark:
import random
import timeit
arr = [4*random.randint(1, 100000) for _ in range(100)] # GCD will be 4 or a multiple of 4
assert solution(list(arr)) == solution2(list(arr))
print(timeit.timeit(lambda: solution(list(arr)), number=100))
print(timeit.timeit(lambda: solution2(list(arr)), number=100))
Output:
2.5396839629975148
0.029025810996245127
def solution(a):
N = len(a)
end = False
while not end:
a = sorted(a, reverse=True)
small = min(a)
end = True
for i in range(1, N):
if a[i-1] > small:
a[i-1] = a[i-1]%small if a[i-1]%small !=0 else small
end = False
return sum(a)
made it faster with a slight change
This solution worked for me. I iterated on the list only once. initially I find the minimum and iterating over the list I replace the element with the rest of the division. If I find a rest equal to 1 the result will be trivially 1 multiplied by the length of the list otherwise if it is less than the minimum, i will replace the variable m with the minimum found and continue. Once the iteration is finished, the result will be the minimum for the length of the list.
Here the code:
def solution(a):
L = len(a)
if L == 1:
return a[0]
m=min(a)
for i in range(L):
if a[i] != m:
if a[i] % m != 0:
a[i] = a[i]%m
if a[i]<m:
m=a[i]
elif a[i] % m == 0:
a[i] -= m * (a[i] // m - 1)
if a[i]==1:
return 1*L
return m*L

Best way to replace values in a list based on many indexes

I have list like this:
l = [1,2,3,4,5,6,7,8,9,10]
idx = [2,5,7]
I want to replace values in l with 0, using indexes from idx. For now I do:
for i in idx:
l[i] = 0
This give: l = [1, 2, 0, 4, 5, 0, 7, 0, 9, 10]
Is there better, faster, more pythonic way. This is only small example, but what if I have huge lists?
If you're talking about huge lists, you should really try not to create a new list, as the new list will require space in memory in addition to your input lists.
Now, let's consider the indices that you want to set to 0. These indices are contained in a list (idx), which itself could be just as long as the list with numbers (l). So, if you were to do something like this:
for i in range(len(l)):
if i in idx:
l[i] = 0
it would take O(mn) time, where m is the number of elements in idx and n is the number of elements in l. This is a really slow algorithm.
Now, you really can't do much faster than O(m), seeing as you have to consider every element in idx. But since m is strictly bounded from above by n, it's definitely a better strategy to loop over idx instead:
for i in idx:
l[i] = 0
But let's consider that idx might contain elements that are not valid indices of l (i.e. there is at least one element in idx whose value is greater than the largest index in l). Then, you could do this:
for i in idx:
if i<len(l):
l[i] = 0
or:
for ind in (i for i in idx if i<len(L)):
l[ind] = 0
Now, this makes O(m) comparisons, which could potentially be improved upon. For example, if idx were sorted, then a modified binary search could provide the appropriate slice of idx that has valid indices:
def binSearch(L, idx, i=0, j=None): # note that the list is not sliced, unlike some common binary search implementations. This saves on additional space
if not idx:
return pad
if j==None:
j = len(idx)-1
mid = (i+j)//2
if idx[mid] == len(L)-1:
return mid
elif idx[mid] > len(L)-1:
return binSearch(L, idx, i, mid-1)
else:
return binSearch(L, idx, mid+1, j)
So now, you could replace only the valid indices without any comparisons at all:
for ind in range(binSearch(L, idx)):
l[idx[ind]] = 0
Note that this approach takes O(log m) time to apply binSearch on idx in the first place
This would work if idx were already sorted. However, if that is an invalid assumption, then you might want to sort it yourself, which would cost O(m log m) time, which would be slower than the aforementioned O(m) implementation.
Yet, if idx were sufficiently large, you could try a distributed approach, with multiprocessing:
import multiprocessing as mp
def replace(l, idx):
numWorkers = mp.cpu_count()*2 -1
qIn = mp.Queue(maxsize=len(idx))
qOut = mp.Queue()
procs = [mp.Process(target=slave, args=(L, qIn, qOut)) for _ in range(numWorkers)]
for p in procs:
p.start()
for i in idx:
qIn.put(i)
numFinished = 0
while numFinished != numWorkers:
i = qOut.get()
if i is None:
numFinished += 1
continue
l[i] = 0
def slave(L, qIn, qOut):
for i in iter(qIn.get, None):
if i< len(L):
qOut.put(i)
qOut.put(None)
Of course, you could further improve this by adding the binSearch to the distributed solution as well, but I'll leave that to you.
Don't create another list for index. Instead:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
index = 1
while index < len(l):
if index == 2:
l[index] = 0
elif index == 5:
l[index] = 0
elif index == 7:
l[index] = 0
index += 1
print(l)
You do not have to use "elif" statements if you combine them all on one line with an "or" statement. For example:
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
index = 1
while index < len(l):
if (index == 2) or (index == 5) or (index == 7):
l[index] = 0
index += 1
print(l)
I think this is perfectly fine. You could write a list comprehension, like this:
[v if i not in idx else 0 for i, v in enumerate(l)]
Or change it in place by iterating over l
for i, v in enumerate(l):
if i in idx:
l[i] = 0
But I find that harder to read, and very likely slower. I don't think any other solution will beat yours by a significant margin, ignoring CPU caching.

Categories