|
|
To quickly find the index of the first maximum item of a sequence.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 | def _posmax_psy(seq, key=None):
"""posmax(seq, key=None): return the position of the first maximum
item of a sequence. Accepts the usual key parameter too.
>>> posmax([])
Traceback (most recent call last):
...
ValueError: maxpos() arg is an empty sequence
>>> posmax([1])
0
>>> posmax(xrange(100))
99
>>> posmax(xrange(100, 0, -1))
0
>>> posmax([1,5,0,4,3])
1
>>> posmax([1,5,0,4,5,3])
1
>>> l = ['medium', 'longest', 'short']
>>> posmax(l)
2
>>> posmax(l, key=len)
1
>>> posmax(xrange(10**4))
9999
>>> posmax([2,4,-2,[4],21]) # silly comparison
3
>>> posmax([2,4,-2+3J,[4],21])
Traceback (most recent call last):
...
TypeError: no ordering relation is defined for complex numbers
"""
first = True
max_pos = 0
pos = 0
if key is None:
for el in seq:
if first:
max_el = el
first = False
elif el > max_el:
max_el = el
max_pos = pos
pos += 1
else:
for el in seq:
key_el = key(el)
if first:
max_key_el = key_el
first = False
elif key_el > max_key_el:
max_key_el = key_el
max_pos = pos
pos += 1
if first:
raise ValueError("maxpos() arg is an empty sequence")
else:
return max_pos
def _posmax_nopsy(seq, key=None):
"""posmax(seq, key=None): return the position of the first maximum
item of a sequence. Accepts the usual key parameter too.
>>> posmax([])
Traceback (most recent call last):
...
ValueError: maxpos() arg is an empty sequence
>>> posmax([1])
0
>>> posmax(xrange(100))
99
>>> posmax(xrange(100, 0, -1))
0
>>> posmax([1,5,0,4,3])
1
>>> posmax([1,5,0,4,5,3])
1
>>> l = ['medium', 'longest', 'short']
>>> posmax(l)
2
>>> posmax(l, key=len)
1
>>> posmax(xrange(10**4))
9999
>>> posmax([2,4,-2,[4],21]) # silly comparison
3
>>> posmax([2,4,-2+3J,[4],21])
Traceback (most recent call last):
...
TypeError: no ordering relation is defined for complex numbers
"""
first = True
max_pos = 0
if key is None:
for pos, el in enumerate(seq):
if first:
max_el = el
first = False
elif el > max_el:
max_el = el
max_pos = pos
else:
for pos, el in enumerate(seq):
key_el = key(el)
if first:
max_key_el = key_el
first = False
elif key_el > max_key_el:
max_key_el = key_el
max_pos = pos
if first:
raise ValueError("maxpos() arg is an empty sequence")
else:
return max_pos
def _posmax_benchmark1():
from time import clock
alist = [3]*1000 + [5] + [3]*1000
t = clock()
for _ in xrange(60000):
r = posmax(alist)
print round(clock() - t, 2), r
try:
import psyco
psyco.full()
posmax = _posmax_psy
except ImportError:
posmax = _posmax_nopsy
if __name__ == "__main__":
import doctest
doctest.testmod()
print "Doctests finished.\n"
_posmax_benchmark1()
|
Timings (2_001 items, 60_000 loops):
C: 1.27 ( 1 X)
D: 1.52 ( 1.2 X)
Psyco: 4.3 ( 3.38 X) ( 1 X)
Python 167.7 (132 X) (39 X)
Notes:
- Compiled with MinGW 3.4.2 (-O3 -s), DMD 1.026 (-O -release -inline), Psyco 1.5.2, Python 2.5.1.1, on a PIII.
- The C version uses a malloc. And it works on an array of ints only.
- The D version works with any kind of array, dynamical vectors and any kind of iterable object, just like the Python version. The D version is slower than the C one just because the DMD compiler isn't much optimized yet.
- With a tiny adaptation of the code (for el in seq: instead of for pos, el in enumerate(seq):) the Psyco version is almost 40 times faster than the Python version, and just 2.8 times slower than the D version.
- A Python/Psyco posmin function can be written easily from this ones.
- The Python/Psyco versions I have shown here are probably close to the faster ones. They work with empty arrays, any iterable objects, etc.
- The Python version compiled with Psyco is just about 10-20% slower than the Psyco version compiled with Psyco.
- A version on array.array("l") is about twice slower than this one.
- Probably a Cython/Pyrex version isn't much faster than this Psyco one.
|