Cómo alternar un valor en Python

128

¿Cuál es la forma más eficiente de alternar entre 0y 1?

codeforester
fuente
Si bien esta pregunta pregunta cómo alternar los valores de manera binaria de manera más eficiente, algunas respuestas explican el ciclo a través de valores (arbitrarios), por ejemplo stackoverflow.com/a/61041907/537865
mad

Respuestas:

273

Solución usando NOT

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

Solución usando resta

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

Solución usando XOR

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).

Solución usando un diccionario

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'

Solución usando una expresión condicional

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]

Solución usando itertools

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__

Raymond Hettinger
fuente
El último ejemplo parece tan ingenioso e intuitivo, pero no funciona en Python 3+ con la eliminación de .next (). ¿Hay alguna manera de hacer que funcione de manera similar en una versión posterior de python?
labarna
2
@labarna En Python 3, el .next()ha sido reemplazado por una next()función global . El ejemplo anterior sería:toggle = itertools.cycle(...); next(toggle)
elpres
2
toggle = itertools.cycle(['red', 'green', 'blue']) next(toggle)
Maximiliano
77
El ejemplo XOR se puede generalizar para alternar entre valores ay busar x = x ^ (a ^ b).
zxxc
int(not 0)y int(not 1)... hrmmm
jhrr
33

Yo siempre uso:

p^=True

Si p es un booleano, esto cambia entre verdadero y falso.

renger
fuente
1
¡Perfecto! 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.
ThorSummoner
1
¿Cómo se llama este operador?
mix3d
44
Este es el operador XOR.
bastelflp
1
@ mix3d Precisamente es "exclusivo bit a bit o" (en oposición a "exclusivo lógico o") - wiki.python.org/moin/BitwiseOperators . XOR lógico no tiene un operador específico en Python en general, pero puede encontrarlo implementado en algunos casos especiales, como en el módulo decimal.
Taylor Edmiston
@ mix3d ^=es asignación
bitor
23

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
Abhijit
fuente
1
Sí, eso es una locura. gracias a todos, es divertido ver cómo diferentes personas abordan el problema (e informativo)
Bien, es una máquina de estados en miniatura.
poco
bueno, el tuyo es el más interesante, pero no es lo que personalmente necesito para lo que estaba preguntando, así que está bien, creo que la matemática simple es probablemente la mejor para mí, ¿no debería ser 1-x allí?
Sí, pero eso no debería hacer que la velocidad sea diferente.
Blender
ai, pero lo haría mal, ¿no? algunas respuestas geniales aquí, ¡SO rocas!
19

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'
Licuadora
fuente
1
+1 pero toggle = 0 if toggle else 1es más corto y más general
luc
Lo siento, intercambiaré variables para hacerlo más claro. Estaba usando la línea ifpara alternar entre dos variables arbitrarias , no solo 1y 0.
Blender
14

Solo entre 1 y 0, haz esto

1-x 

x puede tomar 1 o 0

em
fuente
Dado que (en Python 2.x, de todos modos) Truey en Falserealidad son enteros, aunque con un __str__()método sorprendentemente detallado , xtambién pueden ser Trueo Falseaquí. Sin embargo, obtendrá 1 o 0 de vuelta.
poco
12

Aproximación trigonométrica , sólo porque siny cosfunciones son frescas.

ingrese la descripción de la imagen aquí

>>> 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
dani herrera
fuente
¡Oh Dios mío! Yo <3 tu.
Rishabh Agrahari
2
@RishabhAgrahari, sí hombre, fui el ganador del desafío de Raymond Hettinger ;)
dani herrera
7

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
Yauhen Yakimovich
fuente
No estoy seguro de cuán "pitónico" es esta aritmética de módulo (similar a C) (i, e, si se aplica "pitónico"). Supongo que es solo aritmética, funciona en cualquier otro lugar donde tenga binario.
Yauhen Yakimovich
Obviamente, la máquina de estado finito con tuplas como x = (1,2,3,0); token = 0; token = x [token] es extremadamente emocionante, ya que puede ser incluso más general que solo la operación grupal.
Yauhen Yakimovich
7

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
hugo24
fuente
4

La forma más fácil de alternar entre 1 y 0 es restar de 1.

def toggle(value):
    return 1 - value
Conejo conejo
fuente
4

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:

ingrese la descripción de la imagen aquí

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)
dani herrera
fuente
3

¿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!
Rick apoya a Monica
fuente
2

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
usuario2948775
fuente
1

Yo uso la función abs, muy útil en bucles

x = 1
for y in range(0, 3):
    x = abs(x - 1)

x será 0.

Proteo5
fuente
0

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
Brantley Harris
fuente
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
Italo Nesi
fuente
0

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)
John Bograd
fuente
0

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"

enojado
fuente