¿Cuál es la forma más eficiente de alternar entre 0y 1?
¿Cuál es la forma más eficiente de alternar entre 0y 1?
Respuestas:
Si los valores son booleanos, el enfoque más rápido es usar el operador not :
>>> x = True
>>> x = not x # toggle
>>> x
False
>>> x = not x # toggle
>>> x
True
>>> x = not x # toggle
>>> x
False
Si los valores son numéricos, entonces la resta del total es una forma simple y rápida de alternar valores:
>>> A = 5
>>> B = 3
>>> total = A + B
>>> x = A
>>> x = total - x # toggle
>>> x
3
>>> x = total - x # toggle
>>> x
5
>>> x = total - x # toggle
>>> x
3
Si el valor alterna entre 0 y 1 , puede usar un exclusivo bit a bit o :
>>> x = 1
>>> x ^= 1
>>> x
0
>>> x ^= 1
>>> x
1
La técnica se generaliza a cualquier par de enteros. El paso xor por uno se reemplaza con una constante xor por precomputado:
>>> A = 205
>>> B = -117
>>> t = A ^ B # precomputed toggle constant
>>> x = A
>>> x ^= t # toggle
>>> x
-117
>>> x ^= t # toggle
>>> x
205
>>> x ^= t # toggle
>>> x
-117
(Esta idea fue presentada por Nick Coghlan y luego generalizada por @zxxc).
Si los valores son hashable, puede usar un diccionario:
>>> A = 'xyz'
>>> B = 'pdq'
>>> d = {A:B, B:A}
>>> x = A
>>> x = d[x] # toggle
>>> x
'pdq'
>>> x = d[x] # toggle
>>> x
'xyz'
>>> x = d[x] # toggle
>>> x
'pdq'
La forma más lenta es usar una expresión condicional :
>>> A = [1,2,3]
>>> B = [4,5,6]
>>> x = A
>>> x = B if x == A else A
>>> x
[4, 5, 6]
>>> x = B if x == A else A
>>> x
[1, 2, 3]
>>> x = B if x == A else A
>>> x
[4, 5, 6]
Si tiene más de dos valores, la función itertools.cycle () proporciona una forma rápida genérica para alternar entre valores sucesivos:
>>> import itertools
>>> toggle = itertools.cycle(['red', 'green', 'blue']).next
>>> toggle()
'red'
>>> toggle()
'green'
>>> toggle()
'blue'
>>> toggle()
'red'
>>> toggle()
'green'
>>> toggle()
'blue'
Tenga en cuenta que en Python 3 el next()método se cambió a __next__(), por lo que la primera línea ahora se escribiría comotoggle = itertools.cycle(['red', 'green', 'blue']).__next__
.next()ha sido reemplazado por una next()función global . El ejemplo anterior sería:toggle = itertools.cycle(...); next(toggle)
toggle = itertools.cycle(['red', 'green', 'blue']) next(toggle)
ay busar x = x ^ (a ^ b).
int(not 0)y int(not 1)... hrmmm
Yo siempre uso:
p^=True
Si p es un booleano, esto cambia entre verdadero y falso.
p¡no es necesario hacer referencia dos veces para que este método funcione! Idea si está alternando un valor con una referencia larga y larga.
^=es asignación
Aquí hay otra forma no intuitiva. La belleza es que puedes recorrer varios valores y no solo dos [0,1]
Para dos valores (alternar)
>>> x=[1,0]
>>> toggle=x[toggle]
Para valores múltiples (digamos 4)
>>> x=[1,2,3,0]
>>> toggle=x[toggle]
No esperaba que esta solución fuera casi la más rápida también
>>> stmt1="""
toggle=0
for i in xrange(0,100):
toggle = 1 if toggle == 0 else 0
"""
>>> stmt2="""
x=[1,0]
toggle=0
for i in xrange(0,100):
toggle=x[toggle]
"""
>>> t1=timeit.Timer(stmt=stmt1)
>>> t2=timeit.Timer(stmt=stmt2)
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=100000)/100000)
7.07 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000)
6.19 usec/pass
stmt3="""
toggle = False
for i in xrange(0,100):
toggle = (not toggle) & 1
"""
>>> t3=timeit.Timer(stmt=stmt3)
>>> print "%.2f usec/pass" % (1000000 * t3.timeit(number=100000)/100000)
9.84 usec/pass
>>> stmt4="""
x=0
for i in xrange(0,100):
x=x-1
"""
>>> t4=timeit.Timer(stmt=stmt4)
>>> print "%.2f usec/pass" % (1000000 * t4.timeit(number=100000)/100000)
6.32 usec/pass
El notoperador niega su variable (convirtiéndola en booleana si aún no es una). Puede probablemente utilizar 1y 0de manera intercambiable con Truey False, por lo que sólo negarlo:
toggle = not toggle
Pero si está utilizando dos valores arbitrarios, use una línea if:
toggle = 'a' if toggle == 'b' else 'b'
toggle = 0 if toggle else 1es más corto y más general
ifpara alternar entre dos variables arbitrarias , no solo 1y 0.
Aproximación trigonométrica , sólo porque siny cosfunciones son frescas.
>>> import math
>>> def generator01():
... n=0
... while True:
... yield abs( int( math.cos( n * 0.5 * math.pi ) ) )
... n+=1
...
>>> g=generator01()
>>> g.next()
1
>>> g.next()
0
>>> g.next()
1
>>> g.next()
0
Sorprendentemente, nadie menciona la buena división anterior módulo 2:
In : x = (x + 1) % 2 ; x
Out: 1
In : x = (x + 1) % 2 ; x
Out: 0
In : x = (x + 1) % 2 ; x
Out: 1
In : x = (x + 1) % 2 ; x
Out: 0
Tenga en cuenta que es equivalente a x = x - 1, pero la ventaja de la técnica de módulo es que el tamaño del grupo o la longitud del intervalo puede ser mayor que solo 2 elementos, lo que le brinda un esquema de entrelazado de operación por turnos similar para realizar un bucle.
Ahora solo para 2, alternar puede ser un poco más corto (usando el operador de bits):
x = x ^ 1
una forma de alternar es mediante la asignación múltiple
>>> a = 5
>>> b = 3
>>> t = a, b = b, a
>>> t[0]
3
>>> t = a, b = b, a
>>> t[0]
5
Usando itertools:
In [12]: foo = itertools.cycle([1, 2, 3])
In [13]: next(foo)
Out[13]: 1
In [14]: next(foo)
Out[14]: 2
In [15]: next(foo)
Out[15]: 3
In [16]: next(foo)
Out[16]: 1
In [17]: next(foo)
Out[17]: 2
La forma más fácil de alternar entre 1 y 0 es restar de 1.
def toggle(value):
return 1 - value
Usando el manejador de excepciones
>>> def toogle(x):
... try:
... return x/x-x/x
... except ZeroDivisionError:
... return 1
...
>>> x=0
>>> x=toogle(x)
>>> x
1
>>> x=toogle(x)
>>> x
0
>>> x=toogle(x)
>>> x
1
>>> x=toogle(x)
>>> x
0
Ok, soy lo peor:
import math
import sys
d={1:0,0:1}
l=[1,0]
def exception_approach(x):
try:
return x/x-x/x
except ZeroDivisionError:
return 1
def cosinus_approach(x):
return abs( int( math.cos( x * 0.5 * math.pi ) ) )
def module_approach(x):
return (x + 1) % 2
def subs_approach(x):
return x - 1
def if_approach(x):
return 0 if x == 1 else 1
def list_approach(x):
global l
return l[x]
def dict_approach(x):
global d
return d[x]
def xor_approach(x):
return x^1
def not_approach(x):
b=bool(x)
p=not b
return int(p)
funcs=[ exception_approach, cosinus_approach, dict_approach, module_approach, subs_approach, if_approach, list_approach, xor_approach, not_approach ]
f=funcs[int(sys.argv[1])]
print "\n\n\n", f.func_name
x=0
for _ in range(0,100000000):
x=f(x)
¿Qué tal un conmutador imaginario que almacena no solo el conmutador actual, sino también un par de otros valores asociados con él?
toggle = complex.conjugate
Almacene cualquier valor + o - a la izquierda y cualquier valor sin signo a la derecha:
>>> x = 2 - 3j
>>> toggle(x)
(2+3j)
Zero también funciona:
>>> y = -2 - 0j
>>> toggle(y)
(-2+0j)
Recupere fácilmente el valor de alternancia actual ( Truey Falserepresente + y -), el valor LHS (real) o el valor RHS (imaginario):
>>> import math
>>> curr = lambda i: math.atan2(i.imag, -abs(i.imag)) > 0
>>> lhs = lambda i: i.real
>>> rhs = lambda i: abs(i.imag)
>>> x = toggle(x)
>>> curr(x)
True
>>> lhs(x)
2.0
>>> rhs(x)
3.0
Cambie fácilmente LHS y RHS (pero tenga en cuenta que el signo de ambos valores no debe ser importante):
>>> swap = lambda i: i/-1j
>>> swap(2+0j)
2j
>>> swap(3+2j)
(2+3j)
Cambia fácilmente LHS y RHS y también alterna al mismo tiempo:
>>> swaggle = lambda i: i/1j
>>> swaggle(2+0j)
-2j
>>> swaggle(3+2j)
(2-3j)
Protecciones contra errores:
>>> toggle(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor 'conjugate' requires a 'complex' object but received a 'int'
Realizar cambios en LHS y RHS:
>>> x += 1+2j
>>> x
(3+5j)
... pero tenga cuidado al manipular el RHS:
>>> z = 1-1j
>>> z += 2j
>>> z
(1+1j) # whoops! toggled it!
Las variables ayb pueden ser CUALQUIER dos valores, como 0 y 1, o 117 y 711, o "caras" y "colas". No se utilizan matemáticas, solo un intercambio rápido de los valores cada vez que se desea alternar.
a = True
b = False
a,b = b,a # a is now False
a,b = b,a # a is now True
Hagamos un poco de pirateo de cuadros. Alternar una variable por nombre. Nota: Esto puede no funcionar con todos los tiempos de ejecución de Python.
Digamos que tienes una variable "x"
>>> import inspect
>>> def toggle(var_name):
>>> frame = inspect.currentframe().f_back
>>> vars = frame.f_locals
>>> vars[var_name] = 0 if vars[var_name] == 1 else 1
>>> x = 0
>>> toggle('x')
>>> x
1
>>> toggle('x')
>>> x
0
Si está tratando con una variable entera, puede incrementar 1 y limitar su conjunto a 0 y 1 (mod)
X = 0 # or X = 1
X = (X + 1)%2
El cambio entre -1 y +1 se puede obtener por multiplicación en línea; utilizado para calcular pi de la manera 'Leibniz' (o similar):
sign = 1
result = 0
for i in range(100000):
result += 1 / (2*i + 1) * sign
sign *= -1
print("pi (estimate): ", result*4)
Puede hacer uso de la indexdel lists.
def toggleValues(values, currentValue):
return values[(values.index(currentValue) + 1) % len(values)]
> toggleValues( [0,1] , 1 )
> 0
> toggleValues( ["one","two","three"] , "one" )
> "two"
> toggleValues( ["one","two","three"] , "three")
> "one"
Pros : No hay bibliotecas adicionales, código autoexplicativo y trabajo con tipos de datos arbitrarios.
Contras : no duplicar-guardar.
toggleValues(["one","two","duped", "three", "duped", "four"], "duped")
siempre regresará"three"