Python Multiple Assignment Is a Puzzle
jw2013.github.io
jw2013.github.io
>>> i = 0
>>> a = [0, 0]
>>> i, a[i] = 1, 10
>>> a
[0, 10]
versus >>> i = 0
>>> a = [0, 0]
>>> a[i], i = 10, 1
>>> a
[10, 0]To me, it smells like the result of a sloppy specification.
expr3, expr4 = expr1, expr2
Although evaluation order is handled differently by different programming languages, it seems that Python is behaving logically here.[1] https://docs.python.org/3.4/reference/expressions.html#evalu...
Does it do tmp3 = expr1 tmp4 = expr2
expr3 = tmp3
expr4 = tmp4
or expr3 = expr1
expr4 = expr2
? I guess it is the latter, but the text does not make that clear.i.e. from perl's POV, python's behaviour is an optimisation, which if over-applied introduces a bug ... but, y'know, DWIM vs. regularity etc., I'm just surprised that python's behaviour is sloppier than perl's here :)
def missPos(a):
b={x for x in a}
for x in range(1,len(b)+2):
if not x in b:
return xIs there an advantage in using a set comprehension over set(a) or is it just a stylistic choice?
The set function is somewhat faster.
a=[]
for x in range(10000):
a.append(randint(1,2000))
%timeit b=set(a)
1000 loops, best of 3: 373 µs per loop
%timeit b={x for x in a}
1000 loops, best of 3: 542 µs per loopI do like your solution better though, but mostly because it doesn't mutate the array passed to the function. A function called "firstMissingPositive" shouldn't modify state.
Actual times definitely more than O(N) growth.
a10k = []
for x in range(10000):
a10k.append(randint(1,20000))
%timeit b10k = set(a10k)
10k elements = 364 microseconds/loop
100k elements = 5 milliseconds/loop
1mm elements = 170 milliseconds/loop
10mm elements = 2.4 seconds/loop.
100mm elements = 34.5 seconds/loop
Presumably the jump from 100k elements to 1mm elements hit that "cache locality" boundary you were referring to.Edit - nevermind. It's a hash table of course. So I'm wrong.
Also, the average case for set membership testing is O(1), so the average case runtime would actually be O(N).
def miss_pos(a):
b = range(1, len(a)+2)
return min(set(b).difference(a))
It's probably not as efficient, but remember the first rule of optimization: http://c2.com/cgi/wiki?FirstRuleOfOptimizationBack then when I was still looking for job, I was asked this question by a startup. I gave out this hashset solution and was quickly asked if O(1) space solution was available and then asked to implemented it.
If the space restriction is not an issue, I would definitely go with the method you suggested. Way more succinct and easier to follow.
(let ((A (vector 2 1)))
(rotatef (elt A
0)
(elt A
(1- (elt A 0))))
A)
It returns a changed vector as if indexes were saved.I am not sure which should be considered the right behavior.
There is also setf and psetf which in the examples I'm giving evaluate from lowest suffix to highest (same as todd8).
(setf expr2 expr1
expr4 expr3)
(psetf expr3 expr1
expr4 expr2)
[0] http://clhs.lisp.se/Body/m_rotate.htm A = [1, 2, 4, 5, 7]
A_s = sorted(A)
b = A_s[0]
for a in A_s[1:]:
if a - b > 1:
print b + 1
break
else:
b = a
else:
print 'No missing element'
Trying to be more Pythonic: def test(l):
l_s = sorted(l)
print [l0 + 1 for l1, l0 in zip(l_s[1:], l_s[:-1]) if l1 - l0 > 1] a) takes O(n*lgn) time; the method in post uses O(n) time
b) use extra memory; the method in post uses O(1) extra memory
c) have logical error: the problem asks to find the first missing positive (in range [1, infinity)).
So firstMissingPositive([4,100]) should return 1, instead of 5. But the problem is not stated in the post, so let's assume you are implementing the first missing positive in range(A[0], A[-1] + 1) for sorted(A), your code does not handle corner case well.For example:
a) your firstMissingPositive([100]) gives ValueError: min() arg is an empty sequence
b) your firstMissingPositive([]) gives IndexError: list index out of range
It is attempting to write three-liners that seems to solve the problem, but it is far more important to solve the problem in time and space efficient way. At least, it is important to handle the corner cases well. def firstMissingPositive(A):
try:
for x in range(1, max(A)+1):
if x not in A: return x
except: return A
def firstMissingPositive(A):
try: return next(x for x in range(1, max(A)+1) if x not in A)
except: return A
Two above return: print(firstMissingPositive([4,2,5,7,1])) # 3
print(firstMissingPositive([4,100])) # 1
print(firstMissingPositive([])) # []
print(firstMissingPositive([5])) # 1
I wasn't sure what [5] or [] were supposed to return so maybe I'm still wrong? Had never heard of this question before, thought I'd try it out.Thanks for the reply, very informative.
EAFP: Easier to ask for forgiveness than permission
That being said, just a blanket except is a bad idea.
Also, I suggest first converting A to a set. Just "A = set(A)" would work.