Clear mathematical way to cast int value to 0 or 1 - python

Maybe it's too trivial question but I can't find answer if there is a some more elegant way to cast int value to 0 or 1 without using condition or type casting?
Now I have only following variants and both are ugly:
x = 5
a = some_int_value * (1 if x > 0 else 0)
b = some_int_value * int(bool(x))
ADDED
x is a non-negative value.

To paraphrase your condition, x can be 0 or a value larger than 0. If x > 0, you want to use the value some_int_value, otherwise you want 0 (which is identical to x). Then do:
c = x and some_int_value
If x is 0, i.e. falsey, the and expression returns x. If x is truthy (non-zero), it returns some_int_value.
Arguably even more comprehensible would be:
d = some_int_value if x > 0 else 0
Or:
e = 0
if x > 0:
e = some_int_value

Another way without functions, casting, or conditionals, but with comparisons:
d = some_int_value * (x > 0)
However, in terms of readability, a ternary ... if ... else ... should probably be preferred.
If you want a purely mathematical way, you can use exponentiation with 1 - 0x, since 0**x is 1 only if x == 0 and 0 otherwise. But whether that is in any way clear is another question. (Also note that this gives a division-by-zero for x < 0, but you said that you don't have those.)
>>> x = 5
>>> 1 - 0**x
1
>>> x = 0
>>> 1 - 0**x
0

Related

What does (x < y) | (x > z) do?

def my_function(x,y,z):
out = True
if(x < y) | (x > z):
out = False
return out
Can you help me understand what this is doing? Is it, "out is True, but if x is less than y or greater than z: out is False"? I am unsure about the | operator.
TL;DR, someone thought they were clever.
This code takes advantage of some implementation details about booleans and then does some binary integer operations.
To understand it we need to cover a few things:
the > and < operators in a comparison will give back boolean True or False.
Boolean True and False are actually subclasses of int and function as 1 and 0 respectively in operations that expect an integer (try running True + 1 in your REPL).
The | operator is bitwise OR (as opposed to logical OR which is just or) which works on integers.
And now we can understand the code: the <> comparisons gives us a bool that gets used as an integer 1 or 0 and ORed bitwise with the result of the other comparison.
An even cleverer person would note that bitwise OR with 1's and 0's would only be 0 if both were 0, and just check for that condition:
1 | 1 # 1
1 | 0 # 1
0 | 1 # 1
0 | 0 # 0
This code is needlessly obfuscated: there's no reason to treat the True/False as 1/0 just to do bit manipulation when regular boolean logic would work. This will work identically:
def my_function(x, y, z):
return y <= x <= z
Best way to wrap your head around code you are not sure about is to test it:
testValues = [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
for x, y, z in testValues:
print("The values are: x = %d, y = %d, z = %d" % (x, y, z))
print("out is:", my_function(x, y, z))
def my_function(x,y,z):
global out
out = True
if(x < y) | (x > z):
out = False
return out
x = 6
y = 2
z = 5
my_function(x,y,z)
The above code outputs True/False based on the values of x,y,z. If x is smaller than y OR if x is greater than z it will change the value of 'out' to False.You can further customize the code to do stuff with the output. Like so:
if out == True:
//some stuff to execute when true
else:
//some stuff to execute when false

Python IF statement to a single line

Is it possible to put this code into a single line?
if x == 0:
a += j["sum"]
elif x == 1:
b += j["sum"]
e.e. :D
This is not a working example just for demonstration purposes
a += j["sum"] if x == 0 else b += j["sum"]
You can do it this way if you have Python 3.8 or later for the assignment expression operator :=:
(a := a + j["sum"]) if x == 0 else (b := b + j["sum"]) if x == 1 else None
but really the original is best. It's preferable that code is clear and straightforward.
I may suggest you this one-line dual assignment:
a, b = a + (j["sum"] if x == 0 else 0), b + (j["sum"] if x == 1 else 0)
But you can also use the old good semi-colon to perform two instructions on one line:
a += (j["sum"] if x == 0 else 0); b += (j["sum"] if x == 1 else 0)
I would strongly discourage it, but I guess one could use boolean true as the multiplication identity to do:
a, b = a + (x==0) * j["sum"], b + (x==1) * j["sum"]
This seems to work as I expect in a little loop
a=100
b=200
jsum=10
for x in range(3):
a, b = a + (x==0) * jsum, b + (x==1) * jsum
print(a, b)
Giving:
110 200
110 210
110 210
While I would not code like this myself, one might throw caution to the wind and leverage exec():
exec(f"{['a','b'][x]} += j['sum']" if x in [0,1] else "pass")
or potentially even better (credit to #wjandrea):
if x in {0, 1}: exec(f"{['a','b'][x]} += j['sum']")
Note, if you know x is always either 0 or 1 this would simplify to:
exec(f"{['a','b'][x]} += j['sum']")
Your pseudo-code solution suggests that might be so but your questions suggests it is not.
example:
a=100
b=200
jsum=10
for x in range(3):
exec(f"{['a','b'][x]} += jsum" if x in [0,1] else "pass")
print(a, b)
giving:
110 200
110 210
110 210
#wjandrea has suggested a nice improvement that produces the same results and is likely faster given the ability to bypass exec() if x is out of range.
a=100
b=200
jsum=10
for x in range(3):
if x in {0, 1}: exec(f"{['a','b'][x]} += jsum")
print(a, b)

Python: shortcut conditional expression

the shortcut conditional expression :
expression1 if condition else expression2
x=1 if a>3 else 2
But: can I have 2 expressions at the start ?
x=1,b=3 if a>3 else 2
Thanks to > idontknow, solution is >
previousTime,BS_Count=(db_row_to_list[0][14],BS_Count+1) if db_row_to_list[0][14] is not None else (db_row_to_list[0][3],BS_Count)
Not quite. For something this, it would be possible to use an if statement.
if a > 3:
x = 1
b = 3
else:
x = 2
b = None
If you want everything to become a oneliner, you can use tuple unpacking in Python. What tuple unpacking does is basically take the elements from a tuple and store them as variables, instead of elements of a tuple.
An application of this concept would be something like this:
x, b = (1, 3) if a > 3 else (2, None)
Note that it is a oneliner! 🤗
EDIT: To answer your question in the updated context:
You can use the following, shorter code. I think the effect will be the same.
a = 3
b = 7
c = 6
a, b = (8, b+1) if c > 3 else (5, b)
print(a, b)
In your example:
x=1 if a>3 else 2
The conditional expression part is:
1 if a>3 else 2
So, you're assigning the result of that conditional expression to x.
In your second example, this is invalid syntax:
x=1,b=3 if a>3 else 2
That's because it's equivalent to:
(1,b)=3 if a>3 else 2
Or, as a simpler example:
(1,b)=3
This is invalid syntax, because you can't do something like 1 = 3 in Python, which is what that code is trying to do. Although if both elements of the tuple were not literals, you'd get a different error:
(b,a)=3
TypeError: cannot unpack non-iterable int object
So, if you want to do multiple assignments with a conditional expression, you can have the conditional expression return a tuple, and have your left-hand side be a tuple of the two variables you want to assign values to:
x,b=(1, 3) if a>3 else (2, 2)
This is equivalent to:
if a > 3:
x = 1
b = 3
else:
x = 2
b = 2
Which is what I assume you intended with your original code.
You can use tuples:
>>> a = 4
>>> (x, b) = (1, 3) if a > 3 else (2, 2)
>>> x
1
>>> b
3
>>>
The statement x = 1, b = 3 if a > 3 else 2 is not valid. You can still have a one-liner though:
x, b = 1 if a > 3 else 2, 3 if a > 3 else 2
This sets the value of x to 1 if a > 3 else 2 and b to 3 if a > 3 else 2
For context : This is the actual code >
if db_row_to_list[0][14] is not None:
previousTime=db_row_to_list[0][14]
BS_Count+=1
else:
db_row_to_list[0][3]
I can do this though : ie. use a function > but is it any better??
num=3
num1=7
def doIt():
print("doIt()")
global num,num1
num=8
num1+=1
return num
a=6
num=doIt() if a>3 else 5
print(num," ",num1)

lambda with nested if's weird syntax

I like using lambdas more then I should so more than once I wanted to solve nested if statement with it and failed using standard syntax.
But then I tried something like this:
lambda x: 1 if x > 100 else 2 if x > 50 else 3
Surprisingly it works, although proper function is supposed to have elif:
def foo(x):
if x > 100:
return 1
elif x > 50:
return 2
else:
return 3
Any ideas why?
You don't have to use elif. You can use if inside another else which is the same as what lambda function is doing:
def foo(x):
if x > 100:
return 1
else:
if x > 50:
return 2
else:
return 3
I'd recommend
lambda x: 1 if x > 100 else (2 if x > 50 else 3)
for readability because explicit is better than implicit.
Indeed, due to evaluation from left to right, the ternary operator is right associative as the following code demonstrates
def f(a, b):
return 1 if a else 2 if b else 3
def left(a, b):
return (1 if a else 2) if b else 3
def right(a, b):
return 1 if a else (2 if b else 3)
tpl = "{:^8}{:^8}{:^6}{:^6}{:^6}"
print(tpl.format('a', 'b', 'f', 'left', 'right'))
for (a, b) in [(False, False),(False, True),(True, False),(True, True)]:
print(tpl.format(repr(a), repr(b), f(a,b), left(a,b), right(a,b)))
""" my output ->
a b f left right
False False 3 3 3
False True 2 2 2
True False 1 3 1
True True 1 1 1
"""
The ternary expression always gives the same result as the expression where everything after the first else is parenthesized.
When the conditions are (x>100) and (x>50), the case True, False can never happen, so the three expressions give the same result. Nevertheless, explicit is better than implicit!
You are actually using something like elif by writing
else 2 if x > 50
in
lambda x: 1 if x > 100 else 2 if x > 50 else 3
Its just a different way of writing something like elif in lambda
lambda x: 1 if x > 100 else 2 if x > 50 else 3
Means :
# 1 if x > 100
if x > 100:
return 1
# else 2 if x > 50 else 3
else:
# 2 if x > 50 else 3
if x > 50:
return 2
else:
return 3
Which does the same thing that your second snippet with elif.
You cannot use elif in a lambda expression. As far as I know, it should raise a SyntaxError.
You have concatenated two conditional expressions.
One
2 if x > 50 else 3
provides the value for the else clause of the other.
1 if x > 100 else ...
You have the conditional expression
x if C else y
where
x = 1
C = 100
and
y = 2 if x > 50 else 3
From the docs, y can be an expression so y can be a conditional expression.

Compute fast log base 2 ceiling in python

for given x < 10^15, quickly and accurately determine the maximum integer p such that 2^p <= x
Here are some things I've tried:
First I tried this but it's not accurate for large numbers:
>>> from math import log
>>> x = 2**3
>>> x
8
>>> p = int(log(x, 2))
>>> 2**p == x
True
>>> x = 2**50
>>> p = int(log(x, 2))
>>> 2**p == x #not accurate for large numbers?
False
I could try something like:
p = 1
i = 1
while True:
if i * 2 > n:
break
i *= 2
p += 1
not_p = n - p
Which would take up to 50 operations if p was 50
I could pre-compute all the powers of 2 up until 2^50, and use binary search to find p. This would take around log(50) operations but seems a bit excessive and ugly?
I found this thread for C based solutions: Compute fast log base 2 ceiling
However It seems a bit ugly and I wasn't exactly sure how to convert it to python.
In Python >= 2.7, you can use the .bit_length() method of integers:
def brute(x):
# determine max p such that 2^p <= x
p = 0
while 2**p <= x:
p += 1
return p-1
def easy(x):
return x.bit_length() - 1
which gives
>>> brute(0), brute(2**3-1), brute(2**3)
(-1, 2, 3)
>>> easy(0), easy(2**3-1), easy(2**3)
(-1, 2, 3)
>>> brute(2**50-1), brute(2**50), brute(2**50+1)
(49, 50, 50)
>>> easy(2**50-1), easy(2**50), easy(2**50+1)
(49, 50, 50)
>>>
>>> all(brute(n) == easy(n) for n in range(10**6))
True
>>> nums = (max(2**x+d, 0) for x in range(200) for d in range(-50, 50))
>>> all(brute(n) == easy(n) for n in nums)
True
You specify in comments your x is an integer, but for anyone coming here where their x is already a float, then math.frexp() would be pretty fast at extracting log base 2:
log2_slow = int(floor(log(x, 2)))
log2_fast = frexp(x)[1]-1
The C function that frexp() calls just grabs and tweaks the exponent. Some more 'splainin:
The subscript[1] is because frexp() returns a tuple (significand, exponent).
The subtract-1 accounts for the significand being in the range [0.5,1.0). For example 250 is stored as 0.5x251.
The floor() is because you specified 2^p <= x, so p == floor(log(x,2)).
(Derived from another answer.)
Be careful! The accepted answer returns floor(log(n, 2)), NOT ceil(log(n, 2)) like the title of the question implies!
If you came here for a clog2 implementation, do this:
def clog2(x):
"""Ceiling of log2"""
if x <= 0:
raise ValueError("domain error")
return (x-1).bit_length()
And for completeness:
def flog2(x):
"""Floor of log2"""
if x <= 0:
raise ValueError("domain error")
return x.bit_length() - 1
You could try the log2 function from numpy, which appears to work for powers up to 2^62:
>>> 2**np.log2(2**50) == 2**50
True
>>> 2**np.log2(2**62) == 2**62
True
Above that (at least for me) it fails due to the limtiations of numpy's internal number types, but that will handle data in the range you say you're dealing with.
Works for me, Python 2.6.5 (CPython) on OSX 10.7:
>>> x = 2**50
>>> x
1125899906842624L
>>> p = int(log(x,2))
>>> p
50
>>> 2**p == x
True
It continues to work at least for exponents up to 1e9, by which time it starts to take quite a while to do the math. What are you actually getting for x and p in your test? What version of Python, on what OS, are you running?
With respect to "not accurate for large numbers" your challenge here is that the floating point representation is indeed not as precise as you need it to be (49.999999999993 != 50.0). A great reference is "What Every Computer Scientist Should Know About Floating-Point Arithmetic."
The good news is that the transformation of the C routine is very straightforward:
def getpos(value):
if (value == 0):
return -1
pos = 0
if (value & (value - 1)):
pos = 1
if (value & 0xFFFFFFFF00000000):
pos += 32
value = value >> 32
if (value & 0x00000000FFFF0000):
pos += 16
value = value >> 16
if (value & 0x000000000000FF00):
pos += 8
value = value >> 8
if (value & 0x00000000000000F0):
pos += 4
value = value >> 4
if (value & 0x000000000000000C):
pos += 2
value = value >> 2
if (value & 0x0000000000000002):
pos += 1
value = value >> 1
return pos
Another alternative is that you could round to the nearest integer, instead of truncating:
log(x,2)
=> 49.999999999999993
round(log(x,2),1)
=> 50.0
I needed to calculate the upper bound power of two (to figure out how many bytes of entropy was needed to generate a random number in a given range using the modulus operator).
From a rough experiment I think the calculation below gives the minimum integer p such that val < 2^p
It's probably about as fast as you can get, and uses exclusively bitwise integer arithmetic.
def log2_approx(val):
from math import floor
val = floor(val)
approx = 0
while val != 0:
val &= ~ (1<<approx)
approx += 1
return approx
Your slightly different value would be calculated for a given n by
log2_approx(n) - 1
...maybe. But in any case, the bitwise arithmetic could give you a clue how to do this fast.

Categories