Related
I am trying to write a function called find_it(seq) that, given list of numbers, returns the number that appears an odd amount of times.
I have tried rearranging the return and for loop.
and tried without the else clause.
can someone point out how to format it?
thanks
def find_it(seq):
#return i for i in seq if seq.count(i) % 2 == 1 else 0
for i in seq: return i if seq.count(i) % 2 == 1 else: pass
#this is my solution without the one line and without using count()
def find_it(seq):
dic = {}
for i in seq:
if i not in dic:
dic.update({i:1})
else:
dic[i] += 1
print(dic)
for item,num in dic.items():
if num % 2 == 1:
return item
If you insist on making one-liner loop I suggest you use generator with next, this will make the code more readable
def find_it(seq):
return next((i for i in seq if seq.count(i) % 2 == 1), None)
However the more efficient way will be a simple loop
def find_it(seq):
for i in seq:
if seq.count(i) % 2 == 1:
return i
def find_it(seq):
return set([el for el in seq if seq.count(el) % 2 == 1])
> print(find_it([1, 2, 3, 1, 1, 2, 3]))
{1}
This snippet returns a set of elements present an odd number of times in a list.
It's not as efficient as can be, as checked elements present multiple times are still counted. For example, count(2) returns an int of how many 2s are in the list, but because of how the loop is, the next time the program sees a 2, it stills calculates the .count of 2 even though it's done it before.
This can be rectified by removing all the occurrences of an element from the list after it's been checked, or ignore checked elements. I was unable to find a way to do this in one line as you requested.
I digress only because OP seems intent on efficiency.
For this problem, the technique used can be influenced by the data being processed. This is best explained by example. Here are six different ways to achieve the same objective.
from collections import Counter
from timeit import timeit
# even number of 1s, odd number of 2s
list_ = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2]
def find_it_1(seq):
for k, v in Counter(seq).items():
if v % 2:
return k
def find_it_2(seq):
for i in seq:
if seq.count(i) % 2 :
return i
def find_it_3(seq):
s = set()
for e in seq:
if e not in s:
if seq.count(e) % 2:
return e
s.add(e)
def find_it_4(seq):
return next((i for i in seq if seq.count(i) % 2), None)
def find_it_5(seq):
for e in set(seq):
if seq.count(e) % 2:
return e
def find_it_6(seq):
d = {}
for e in seq:
d[e] = d.get(e, 0) + 1
for k, v in d.items():
if v % 2:
return k
for func in find_it_1, find_it_2, find_it_3, find_it_4, find_it_5, find_it_6:
print(func.__name__, timeit(lambda: func(list_)))
Output:
find_it_1 1.627880711999751
find_it_2 2.23142556699986
find_it_3 0.9605982989996846
find_it_4 2.4646536830000514
find_it_5 0.6783656980001069
find_it_6 1.9190425920000962
Now, let's change the data as follows:
list_ = [2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
Note that there are 3 occurrences of 2 and that they're at the start of the list. This results in:
find_it_1 1.574513012999887
find_it_2 0.3627374699999564
find_it_3 0.4003442379998887
find_it_4 0.5936855530007961
find_it_5 0.674294768999971
find_it_6 1.8698847380001098
Quod Erat Demonstrandum
I am trying to reverse the array in groups but I am getting this error:
:---- for i in arr: TypeError: 'NoneType' object is not iterable.
What's wrong with my code?
def reverseSubarray(arr,n,k):
if k == 1:
return
i = 0
while i < n:
l = i
r = min(i+k-1, n-1)
while l < r:
temp = arr[l]
arr[l] = arr[r]
arr[r] = temp
l += 1
r -= 1
i += k
return arr
def main():
n = int(input().strip())
string = input().strip().split()
arr=[]
for j in string:
arr.append(int(j.strip()))
k=int(input().strip())
arr = reverseSubarray(arr,n,k)
for i in arr:
print(i,end=' ')
if __name__ == "__main__":
main()
So the problem is that you're actually returning None. This happens because most likely you're giving k=1 so it will go to that line where you return nothing, which will return this error when trying to iterate.
You can treat the problem with a try-catch block on arr=reverseSubarray(arr,n,k) that will return a message like k cannot be 1
You can reverse an array in groups in python as given below,
def reverseSubarray(arr, N, K):
for i in range(0, len(arr),K):
l=arr[i:i+K]
l.reverse()
arr[i:i+K] =l
return arr
While your error was indeed coming from the fact that your function is returning None as other answers have pointed out, you have also written the function in a very non-pythonic style. Here is an example of how you could rewrite it more succintly:
def reverseInGroups(self, arr, N, K):
for i in range(0, N, K):
arr[i:i+K] = reversed(arr[i:i+K])
return arr
range(0, N, K) will return an iterator that goes from 0 to N-1 in steps of K. In other word, i will successively have value: 0, K, 2K, 3K, 4K, etc. until the last multiple of K that is less than N. Here is the documentation for more details.
arr[i:i+K] will refer to the slice of arr between indices i and i+K-1 or, put another way, [arr[i], arr[i+1], arr[i+2], ..., arr[i+K-1]]. It stops at i+K-1 so that you can naturally use arr[i:i+K] and arr[i+K:] without counting arr[i+K] twice.
reversed... reverses an iterator. Here's the doc.
I made a bubble sort code. It works fine but I want to reduce the number of variables. I would like to use only i in the code. How can I remove j?
def bubble(v):
l = len(v)
for i in range(l-1):
for j in range(l-i-1):
if v[j]>v[j+1]:
v[j+1],v[j] = v[j],v[j+1]
return v
One of the possible ways is to use a list of length two, one for storing i and the other for storing j, in this fashion:
def bubble(v):
l = len(v)
i = [0, 0] # use like [i, j]
while i[0] < (l-1):
i[1] = 0
while i[1] < (l-i[0]-1):
if v[i[1]]>v[i[1]+1]:
v[i[1]+1],v[i[1]] = v[i[1]],v[i[1]+1]
i[1] += 1
return v
i[0] += 1
Note that, here, we have to use while loops instead of for loops.
I want to iterate through a list and sum all the elements. Except, if the number is a 5, I want to skip the number following that 5. So for example:
x=[1,2,3,4,5,6,7,5,4,3] #should results in 30.
I'm just not sure how I can access the index of a tuple, when I use enumerate. What I want to do, is use an if statement, that if the number at the previous index == 5, continue the loop.
Thanks you
The itertools documentation has a recipe for this called pairwise. You can either copy-paste the function or import it from more_itertools (which needs to be installed).
Demo:
>>> from more_itertools import pairwise
>>>
>>> x = [1,2,3,4,5,6,7,5,4,3]
>>> x[0] + sum(m for n, m in pairwise(x) if n != 5)
30
edit:
But what if my datastructure is iterable, but does not support indexing?
In this case, the above solution needs a minor modification.
>>> from itertools import tee
>>> from more_itertools import pairwise
>>>
>>> x = (n for n in [1,2,3,4,5,6,7,5,4,3]) # generator, no indices!
>>> it1, it2 = tee(x)
>>> next(it1, 0) + sum(m for n, m in pairwise(it2) if n != 5)
30
Not a fan of bug-ridden one-liners that get upvoted.
So here's the answer with a for-loop.
x=[1,2,3,4,5,6,7,5,4,3, 5] #should results in 35.
s = 0
for i, v in enumerate(x):
if i != 0 and x[i-1] == 5:
continue
s += v
print(s)
Using sum with enumerate
Ex:
x=[1,2,3,4,5,6,7,5,4,3]
print(sum(v for i, v in enumerate(x) if (i == 0) or (x[i-1] != 5)))
Output:
30
Simple, verbose way:
SKIP_PREV = 5
x = [1,2,3,4,5,6,7,5,4,3]
prev = -1
s = 0
for num in x:
if prev != SKIP_PREV:
s += num
prev = num
print(s)
# 30
Compact, maybe less clear way:
SKIP_PREV = 5
x = [1,2,3,4,5,6,7,5,4,3]
s = sum(num for i, num in enumerate(x) if i == 0 or x[i - 1] != SKIP_PREV)
print(s)
# 30
If you are happy to use a 3rd party library, you can use NumPy with integer indexing:
import numpy as np
x = np.array([1,2,3,4,5,6,7,5,4,3])
res = x.sum() - x[np.where(x == 5)[0]+1].sum() # 30
See also What are the advantages of NumPy over regular Python lists?
You can pair the list with a shifted version of itself. This should work:
sum(val for (prev, val)
in zip(itertools.chain((None,), x), x)
if prev != 5 )
The longest code so far. Anyway, it does not need enumerate, it is a simple FSM.
x = [1,2,3,4,5,6,7,5,4,3]
skip = False
s = 0
for v in x:
if skip:
skip = False
else:
s += v
skip = v == 5
print(s)
I'm trying to solve this problem on the easy section of coderbyte and the prompt is:
Have the function ArrayAdditionI(arr) take the array of numbers stored in arr and return the string true if any combination of numbers in the array can be added up to equal the largest number in the array, otherwise return the string false. For example: if arr contains [4, 6, 23, 10, 1, 3] the output should return true because 4 + 6 + 10 + 3 = 23. The array will not be empty, will not contain all the same elements, and may contain negative numbers.
Here's my solution.
def ArrayAddition(arr):
arr = sorted(arr, reverse=True)
large = arr.pop(0)
storage = 0
placeholder = 0
for r in range(len(arr)):
for n in arr:
if n + storage == large: return True
elif n + storage < large: storage += n
else: continue
storage = 0
if placeholder == 0: placeholder = arr.pop(0)
else: arr.append(placeholder); placeholder = arr.pop(0)
return False
print ArrayAddition([2,95,96,97,98,99,100])
I'm not even sure if this is correct, but it seems to cover all the numbers I plug in. I'm wondering if there is a better way to solve this through algorithm which I know nothing of. I'm thinking a for within a for within a for, etc loop would do the trick, but I don't know how to do that.
What I have in mind is accomplishing this with A+B, A+C, A+D ... A+B+C ... A+B+C+D+E
e.g)
for i in range(len(arr):
print "III: III{}III".format(i)
storage = []
for j in range(len(arr):
print "JJ: II({}),JJ({})".format(i,j)
for k in range(len(arr):
print "K: I{}, J{}, K{}".format(i,j,k)
I've searched all over and found the suggestion of itertool, but I'm wondering if there is a way to write this code up more raw.
Thanks.
A recursive solution:
def GetSum(n, arr):
if len(arr) == 0 and n != 0:
return False
return (n == 0 or
GetSum(n, arr[1:]) or
GetSum(n-arr[0], arr[1:]))
def ArrayAddition(arr):
arrs = sorted(arr)
return GetSum(arrs[-1], arrs[:-1])
print ArrayAddition([2,95,96,97,98,99,100])
The GetSum function returns False when the required sum is non-zero and there are no items in the array. Then it checks for 3 cases:
If the required sum, n, is zero then the goal is achieved.
If we can get the sum with the remaining items after the first item is removed, then the goal is achieved.
If we can get the required sum minus the first element of the list on the rest of the list the goal is achieved.
Your solution doesn't work.
>>> ArrayAddition([10, 11, 20, 21, 30, 31, 60])
False
The simple solution is to use itertools to iterate over all subsets of the input (that don't contain the largest number):
def subsetsum(l):
l = list(l)
target = max(l)
l.remove(l)
for subset_size in xrange(1+len(l)):
for subset in itertools.combinations(l, subset_size):
if sum(subset) == target:
return True
return False
If you want to avoid itertools, you'll need to generate subsets directly. That can be accomplished by counting in binary and using the set bits to determine which elements to pick:
def subsetsum(l):
l = list(l)
target = max(l)
l.remove(l)
for subset_index in xrange(2**len(l)):
subtotal = 0
for i, num in enumerate(l):
# If bit i is set in subset_index
if subset_index & (1 << i):
subtotal += num
if subtotal == target:
return True
return False
Update: I forgot that you want to check all possible combinations. Use this instead:
def ArrayAddition(l):
for length in range(2, len(l)):
for lst in itertools.combinations(l, length):
if sum(lst) in l:
print(lst, sum(lst))
return True
return False
One-liner solution:
>>> any(any(sum(lst) in l for lst in itertools.combinations(l, length)) for length in range(2, len(l)))
Hope this helps!
Generate all the sums of the powerset and test them against the max
def ArrayAddition(L):
return any(sum(k for j,k in enumerate(L) if 1<<j&i)==max(L) for i in range(1<<len(L)))
You could improve this by doing some preprocessing - find the max first and remove it from L
One more way to do it...
Code:
import itertools
def func(l):
m = max(l)
rem = [itertools.combinations([x for x in l if not x == m],i) for i in range(2,len(l)-1)]
print [item for i in rem for item in i if sum(item)==m ]
if __name__=='__main__':
func([1,2,3,4,5])
Output:
[(1, 4), (2, 3)]
Hope this helps.. :)
If I understood the question correctly, simply this should return what you want:
2*max(a)<=sum(a)