Short-circuiting a condition statement - python

I wrote a function:
# given a n x m grid return how many different ways there are to move from top left to
# bottom right by only being able to move right or down
def grid(n, m, memo = {}):
if f'{n},{m}' in memo:
return memo[f'{n},{m}']
if n == 1 and m == 1:
return 1
if n == 0 or m == 0:
return 0
memo[f'{n},{m}'] = grid(n,m-1,) + grid(n-1,m)
return grid(n,m-1,) + grid(n-1,m)
Recently I read a bit about short-circuiting in Python and I am trying to understand it further.
As I understand it does not provide any boost in runtime, just sort of syntax sugar.
For example:
1 < 2 < 3 # is True
1 < 2 and 2 < 3 # is also True
# hence
(1 < 2 < 3) == 1 < 2 and 2 < 3 # is True
I was wondering can I write my function with this kind of short-circuiting in my if statements?
I came up with this:
def grid(n, m, memo = {}):
if f'{n},{m}' in memo:
return memo[f'{n},{m}']
if (n or m) == 1:
return 1
if (n and m) == 0:
return 0
memo[f'{n},{m}'] = grid(n,m-1,) + grid(n-1,m)
return grid(n,m-1,) + grid(n-1,m)
Is there any smarter way of using the short-circuit here?

(1 < 2 < 3) is not short-circuiting - I think you misunderstood the meaning of the term. You are correct that it is merely syntax sugar - although it can produce some very weird results in obscure cases. (1 < 2 < 3) expands to (1 < 2) and (2 < 3) - the middle operand is copied to both and and is used for the joining operator.
Short circuiting occurs when python already knows the answer to a boolean expression, even before calculating both the inputs. For example
def false():
print("false")
return False
def true():
print("true")
return True
print(false() and true())
The output would be
false
False
Because when python sees False and, it already knows that the result is False (because and requires both operands to be True), so it doesn't bother running the second half of the line.
This real short circuiting does result in a performance boost, but since you can't turn off short circuiting, it doesn't really matter ¯\_(ツ)_/¯

if (n or m) == 1 is definitely not the same thing as if n == 1 or m == 1. The first if statement is equivalent to:
value = n
if not value:
value = m
if value == 1:
# do something:
Or expressed more succinctly:
if (n if n else m) == 1:
# do something
In other words, n or m only evaluates m if n is False (or 0 if n is an integer), otherwise the result of the expression is n.
What you want to avoid redundancy is:
if 1 in (n, m): # equivalent to: if 1 is either n or m:
Update: Demo
n = 4
m = 1
if (n or m) == 1:
print('if branch taken')
else:
print('else branch taken')
Prints:
else branch taken

if (n or m) == 1
evaluates to
if (<bool>) == 1 # e.g. if {True,False} == 1
which is probably not what you want, since it is essentially evaluating the truthiness of n or m.
Your existing code already captures the nature of short-circuiting;
if n == 1 and m == 1
will only evaluate the second argument m == 1 iff n == 1, or will otherwise short-circuit.
To your comment
As I understand it does not provide any boost in runtime, just sort of syntax suggar.
Well, actually it does provide a runtime boost if Python is able to skip evaluating what would otherwise be "expensive" conditions to evaluate because it is able to short-circuit early.

Related

Happy Number String Python function

I am pretty new to Python and just got started in Leet and I am doing the Happy Number question, only half of the test cases have been passed. I would appreciate any help. Thanks
A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.
My test cases were 19, 100 where it output True correctly but when I do 7, it is wrong
def isHappy(self, n: int) -> bool:
if (n == 1):
return True
sum = 0
flag = False
for j in range(1,100):
x = str(n)
a = list(x)
for i in range(0,len(a)):
sum += int(a[i])*int(a[i])
if sum == 1:
return True
break
else:
x = sum
return False
Here is an implementation using a set to keep track of numbers that we have already seen. (I've removed the self argument here for sake of something that can be run outside of your test class.)
def isHappy(n: int) -> bool:
seen = set()
while True:
if n == 1:
return True
if n in seen:
return False
seen.add(n)
n = sum(int(c) ** 2 for c in str(n))
Your code has various issues aside from the fact that the number 100 is arbitrary.
Mainly that you never update n in your loop. Also you do not wait for the completion of the for loop before testing sum (in fact your break is never reached), you initialise sum only once, and you return False prematurely. Here is a minimally corrected version of your code, although still subject to the fact that there is no rule about 100 maximum iterations.
def isHappy(n: int) -> bool:
if (n == 1):
return True
for j in range(1,100):
x = str(n)
a = list(x)
sum = 0
for i in range(0,len(a)):
sum += int(a[i])*int(a[i])
if sum == 1:
return True
else:
n = sum
return False

How to write 2**n - 1 as a recursive function?

I need a function that takes n and returns 2n - 1 . It sounds simple enough, but the function has to be recursive. So far I have just 2n:
def required_steps(n):
if n == 0:
return 1
return 2 * req_steps(n-1)
The exercise states: "You can assume that the parameter n is always a positive integer and greater than 0"
2**n -1 is also 1+2+4+...+2n-1 which can made into a single recursive function (without the second one to subtract 1 from the power of 2).
Hint: 1+2*(1+2*(...))
Solution below, don't look if you want to try the hint first.
This works if n is guaranteed to be greater than zero (as was actually promised in the problem statement):
def required_steps(n):
if n == 1: # changed because we need one less going down
return 1
return 1 + 2 * required_steps(n-1)
A more robust version would handle zero and negative values too:
def required_steps(n):
if n < 0:
raise ValueError("n must be non-negative")
if n == 0:
return 0
return 1 + 2 * required_steps(n-1)
(Adding a check for non-integers is left as an exercise.)
To solve a problem with a recursive approach you would have to find out how you can define the function with a given input in terms of the same function with a different input. In this case, since f(n) = 2 * f(n - 1) + 1, you can do:
def required_steps(n):
return n and 2 * required_steps(n - 1) + 1
so that:
for i in range(5):
print(required_steps(i))
outputs:
0
1
3
7
15
You can extract the really recursive part to another function
def f(n):
return required_steps(n) - 1
Or you can set a flag and define just when to subtract
def required_steps(n, sub=True):
if n == 0: return 1
return 2 * required_steps(n-1, False) - sub
>>> print(required_steps(10))
1023
Using an additional parameter for the result, r -
def required_steps (n = 0, r = 1):
if n == 0:
return r - 1
else:
return required_steps(n - 1, r * 2)
for x in range(6):
print(f"f({x}) = {required_steps(x)}")
# f(0) = 0
# f(1) = 1
# f(2) = 3
# f(3) = 7
# f(4) = 15
# f(5) = 31
You can also write it using bitwise left shift, << -
def required_steps (n = 0, r = 1):
if n == 0:
return r - 1
else:
return required_steps(n - 1, r << 1)
The output is the same
Have a placeholder to remember original value of n and then for the very first step i.e. n == N, return 2^n-1
n = 10
# constant to hold initial value of n
N = n
def required_steps(n, N):
if n == 0:
return 1
elif n == N:
return 2 * required_steps(n-1, N) - 1
return 2 * required_steps(n-1, N)
required_steps(n, N)
One way to get the offset of "-1" is to apply it in the return from the first function call using an argument with a default value, then explicitly set the offset argument to zero during the recursive calls.
def required_steps(n, offset = -1):
if n == 0:
return 1
return offset + 2 * required_steps(n-1,0)
On top of all the awesome answers given earlier, below will show its implementation with inner functions.
def outer(n):
k=n
def p(n):
if n==1:
return 2
if n==k:
return 2*p(n-1)-1
return 2*p(n-1)
return p(n)
n=5
print(outer(n))
Basically, it is assigning a global value of n to k and recursing through it with appropriate comparisons.

Optimizing if-elif expressions in Python

I'm trying to optimize my code by using dictionaries instead of if-elif statements.
I've read that you can optimize code by using dictionaries instead of if-elif statements, but I don't know how to do that. I'd like to use the logical expressions below somehow in the dictionary. (The code iterates through a and b)
def e_ha(n, t, a, b, E):
if a == b:
return 6
elif (a%n == 0, a != n**2, b == a + 1) == (True, True, True):
return 0
elif ((a-1)%n == 0, (a-1) != n**2, b == a - 1) == (True, True, True):
return 0
elif (a%n == 0, b == a-(n-1)) == (True, True):
return 1
elif (b%n == 0, a == b-(n-1)) == (True, True):
return 1
elif abs(a-b) == 1:
return 1
elif abs(a-b) == n:
return 1
else:
return 0
One naive approach to achieve the best performance is to build a big table storing the results for all possible (a, b) pairs. However, this could consume lots of memory and becomes inpractical for large ns.
Here is how the code can be optimized using a normal approach, as explained in the following step-by-step.
1. Using Explicit and for Logical Expressions
As suggested in the comments, this is much more readable and also more efficient because of the short circuiting behavior of and. This change alone reduces the runtime by 60% in my tests.
2. Remove Redundant Conditions
Since both a and b range from 1 to n**2, if a == n**2, then b == a + 1 can never be fulfilled. Therefore the a != n**2 check in the condition a%n == 0 and a != n**2 and b == a + 1 is redundant. The same applies to the third condition. Eliminating them simplifies these conditions to:
...
elif a % n == 0 and b == a + 1:
elif (a - 1) % n == 0 and b == a - 1:
...
3. Avoid Repeated Computations in Conditions
Note that the above-improved conditions
a % n == 0 and b == a + 1 and (a - 1) % n == 0 and b == a - 1 are special cases of abs(a - b) == 1. Therefore these conditions can be rewritten using nested if-else as follows.
if abs(a - b) == 1:
if a % n == 0 and b > a: return 0
elif b % n == 0 and a > b: return 0 # a - 1 equals to b here so it is replaced to save one computation
else return 1
Also note that the value abs(a - b) is related to all the conditions. Therefore it can be computed before all conditions are checked. With this change, the code becomes
d = abs(a - b)
if d == 0: return 6
elif d == 1:
if a % n == 0 and b > a: return 0
elif b % n == 0 and a > b: return 0
else return 1
elif d == n - 1:
if a % n == 0 and a > b: return 1
elif b % n == 0 and b > a: return 1
else return 0
elif d == n: return 1
else: return 0
4. Simplify Logic
For example, the first nested if-else above can be simplified to
if min(a, b) % n == 0: return 0
else return 1
A more compact syntax is:
return 1 if min(a, b) % n == 0 else 0
5. Apply Python-specific Optimizations
In Python, the number 0 is regarded as having a falsy value. So for numbers if d != 0: and if d == 0: are equivalent to if d: and if not d: respectively. The latter is a bit faster. Applying this change results in the following optimized code (here a more compact syntax is used to shorten the answer).
d = abs(b - a)
if not d: return 6
elif d == 1: return 1 if min(a, b) % n else 0
elif d == n - 1: return 0 if max(a, b) % n else 1
else: return 1 if d == n else 0
Applying steps 2 to 5 above reduces the runtime by another 50%.
6. Adjust Order of Conditions based on Input Distribution
This change relies on the knowledge of the actual input distribution in the application. The target is to make the more frequently seen inputs return faster. In this example, assume the inputs a and b are uniformly distributed within [1, n**2] and n >= 10. In this case, the most frequent scenario is that the value d does not match any of the if conditions and 0 is returned at the end after all conditions are checked. In order to speedup, we can make it fail faster by first checking whether d can possibly lead to a non-zero return value.
d = abs(a - b)
if 1 < d < n - 1 or d > n: return 0 # Return 0 if d is not in [0, 1, n - 1, n]
elif d == 1: return 1 if min(a, b) % n else 0
elif d == n - 1: return 0 if max(a, b) % n else 1
else: return 1 if d == n else 6 # d == 0 case is moved to the last "else" since it is least frequently seen
7. Using Lookup Tables
Further speedup can be achieved by using lookup tables. Here, the values [0, 1, n - 1, n] for the first conditional check can be stored to speedup the check. In this case, there are two primary options for this: a set or a length-n+1 list of boolean values. The former uses less memory while the latter has better performance. Note that the lookup table should be constructed once outside the function and passed into it. The code using a boolean list as a lookup is as follows:
def func(n, a, b, lookup):
d = abs(a - b)
if not (d <= n and lookup[d]): return 0
...
Applying steps 6 and 7 (with boolean list lookup) reduces the runtime by another 15%.
Note that in this example a 2D lookup table (implemented as nested lists or dictionaries) can also be applied using (min(a, b) % n, d) as indices. However, under the same assumption of input distribution in step 6, this is slightly slower than a 1D lookup because of the overhead of one extra level of indexing.
The runtime above is the total time of applying the function to all possible (a, b) values within [1, n**2] for n=20.
Using a dictionary, where the keys are boolean expressions is not going to work the way you hope it does. There is no such thing as a boolean-expression-object that could take the place of the key, only booleans. In the end, boolean expressions evaluate to either True or False, so at most you could only have two key-value pairs.
I would suggest, however, you make things a bit more readable/pythonic:
if a%n == 0 and a != n**2 and b == a + 1:
or
if all((a%n == 0, a != n**2, b == a + 1)):
You can just use a list of tuples and loop through it:
def e_ha(n, t, a, b, E):
checks = [
(a == b, 6),
(all(a%n == 0, a != n**2, b == a + 1), 0 ),
(all((a-1)%n == 0, (a-1) != n**2, b == a - 1), 0),
(all(a%n == 0, b == a-(n-1)), 1),
(all(b%n == 0, a == b-(n-1)), 1 ),
(abs(a-b) == 1, 1),
(abs(a-b) == n, 1),
(true, 0)
]
for valid, return_value in checks:
if valid:
return return_value
Caveat:
This is most certainly not faster in any way. Timed it multiple times and it was always slower.
It is less readable than the alternative

Goldbach Conjecture Verify up to N in Python

I seem to have created an infinite loop in my python code. My aim was to create a function 'check' which uses my previous 'goldbach' function to confirm that every even number greater than 4 and up to the inputted N comply with the Goldbach Conjecture (a pretty pointless procedure, I know, but it's for my assignment). I know that my 'goldbach' function is working well and produces a pair of primes that sum to N for 'good' inputs and (0,0) for 'bad' inputs. I want my check function to return True for all even inputs greater than 4 (as these comply with the conjecture), and False for any odd inputs. However, my code won't run when I try my check function in the console so something has gone wrong - any ideas what it is?
def goldbach(N):
x, y = 0, 0
result = 0
if N % 2 == 0:
prime = odd_primes(N)
while result != N:
for i in range(len(prime)):
if result == N: break
x = prime[i]
for j in range(len(prime)):
y = prime[j]
result = x + y
if result == N: break
return x, y
def check(N):
for n in range(4, N+1):
if n % 2 ==0:
g = goldbach(n)
if g == (0,0):
return False
else:
return True
You're returning immediately after checking the first item in the range. You need to return False as soon as you encounter an item that doesn't match expectations, and return True at the end if all of them match expectations.
If you only want to look at even numbers, use a stride of 2 in the range() function rather than testing each number to see if it's even or odd.
def check(N):
for n in range(4, N+1, 2):
if goldbach(n) == (0, 0):
return False
return True
You don't need the while loop in goldbach(). The two for loops test all combinations of primes. If they don't find a matching pair, there's no reason to restart them.
You can also simplify and optimize your loops. The inner loop only needs to test primes starting from x, because pairs of primes where y < x would have already been tested in an earlier iteration of x.
def goldbach(N):
if N % 2 == 0:
prime = odd_primes(N)
for i, x in enumerate(prime):
for y in prime[i:]:
if x + y == N:
return x, y
return 0, 0
However, I think your code should still work, which suggests that the problem is actually that odd_primes() isn't returning all primes up to N.
I just created a replacement for your function odd_primes(N), to return a list of all the primes less than or equal to N (having 2 in the there doesn't seem to make a difference). Your check() function seems to check all integers between 4 and N inclusive, and return False if it finds something for which no Goldbach sum was found. However, as somebody else pointed out, it also immediately returns True once it finds a pair. So what happens when you run check() is that starts with the number 4, finds that its Goldbach pair is (2,2), then immediately quits the function by returning True, ignoring any other values between 4 and N.
When I replace the return True with a print statement and just add a `return True after the entire loop:
def check(N):
for n in range(4, N+1):
if n % 2 ==0:
g = goldbach(n)
if g == (0,0):
print("No sum found for %d !" % n)
return False
else:
print("%d is equal to %d + %d" % (n, g[0], g[1]))
return True
and then run check(20) for example, I get:
4 is equal to 2 + 2
6 is equal to 3 + 3
8 is equal to 3 + 5
10 is equal to 3 + 7
12 is equal to 5 + 7
14 is equal to 3 + 11
16 is equal to 3 + 13
18 is equal to 5 + 13
20 is equal to 3 + 17
By the way, if you just want to know whether a given even number can be written as the sum of two primes but you don't care what the actual pair of primes is, you could do something like this:
def goldbach(N):
if N % 2 == 0:
primes = odd_primes(N)
# Generate a list of all the i+j sums where j >= i (skipping
# most duplicates this way), and see if N is among the sums.
sums = [i + j for i in primes for j in primes[primes.index(i):]]
return(N in sums)
else:
print("N must be even.")
return(False)

Checking Powers of Four - Edge Case

Disclosure: This question is from codewars.
Write a method that returns true if a given parameter is a power of 4, and false if it's not. If parameter is not an Integer (eg String, Array) method should return false as well.
I cannot for the life of me figure out what edge case I'm missing. Both code samples produce the same error that 'True should equal False' for some test. (I tried it two ways when the first didn't work, since I was positive the second would.)
def powerof4(n):
if n <= 0 or not isinstance(n, int): return False
else:
while (n != 1):
if (n%4 != 0): return False
else: n = n/4
return True
and
import math
def powerof4(n):
if ((not isinstance(n, int)) or n&(n-1) != 0 or n == 0):
return False
#Now, I cheat, since 4^n = (2^n)^2
reduce_to_2 = math.sqrt(n)
if math.floor(reduce_to_2) != reduce_to_2:
return False
else:
reduce_to_2 = int(reduce_to_2)
return reduce_to_2&(reduce_to_2 - 1) == 0
Your first problem is that you are checking if the type of the argument is int but starting with numbers larger than 2^32 thats not true for Python 2 which codewars is using.
The next error is if you print the value at codewars at which the test fails you see it's for the call powerof4(True). isinstance(True, (int,long)) is True because bool is a subclass of int.
In your first code change your typecheck to
if n <= 0 or type(n) not in (int,long): return False
To add another variation on the problem. When I solved this problem initially some time ago I did it with some bit fiddling :)
def powerof4(n):
if type(n) not in (int, long):
return False
bin_repr = bin(n)[2:]
return bin_repr[0]=="1" and bin_repr.count("0")%2==0 and bin_repr.count("1")==1
There's an easier way.
import math
def powerof4(n):
return math.log(n, 4) % 1 == 0
There is one less obvious gotcha. In Python 2 the number could be too big to fit in an int and it would be a long instead:
>>> isinstance(1000000000000000000000000000000, int)
False
whereas Python 3 would return
>>> isinstance(1000000000000000000000000000000, int)
True
The easiest algorithm on any CPython (*except for the isinstance check) could be
def powerof4(n):
if n <= 0 or not isinstance(n, (int, long)):
return False
return hex(n).rstrip('0') in ('0x1', '0x4')
This converts the number into hex, and removes trailing zeroes; a number is a power of 4 if and only if the hex representation is 1 or 4 followed by any number of zeroes. (hint: it would be a power of 16 if it were 1 followed by zeroes).
With bitwise logic:
test = 1
while n < test:
test <<= 2
return n == test
In Python 2.7 and 3.3+ this is even easier, no loop needed:
b = n.bit_length()
return bool(b & 1) and 1 << (b - 1) == n
Like this?
def isPowerOf4(n):
try:
while n:
if n==1:
return True
if ((n>>2)<<2) != n:
return False
n >>= 2
except:
return False
return False

Categories

Resources