Code simplification. Between positive and negative conditions - python

I have a question, how to simplify this code? I have the impression that it can be done in 3 conditional instructions and not in 6 ...
if (PID > 10 and self.last_pid > 0):
if (PID >= self.last_pid):
self.setKp(self.Kp+self.increase_val)
self.increase_val = self.increase_val*2
else:
percent_last = PID/self.last_pid*100
self.increase_val + (percent_last/100*self.increase_val)
self.setKp(self.Kp+self.increase_val)
if (PID < -10 and self.last_pid < 0):
if (PID <= self.last_pid):
self.setKp(self.Kp+self.increase_val)
self.increase_val = self.increase_val*2
else:
percent_last = PID/self.last_pid*100
self.increase_val + (percent_last/100*self.increase_val)
self.setKp(self.Kp+self.increase_val)
(Which might be simplified to:)
if A > 10 and B > 0:
if A >= B:
# do block A
else:
# do block B
if A < -10 and B < 0:
if A <= B):
# do block A
else:
# do block B

This should be equivalent to your two cases for positive and negative values:
if abs(A) > 10 and A * B > 0:
if abs(A) >= abs(B):
# do block A
else:
# do block B
Explanation:
abs(A) corresponds to A > 10 and A < -10 respectively
A * B > 0 means that both have the same sign and B != 0
abs(A) >= abs(B) means A <= B if both are < 0 and A => B if both are > 0
Now that's shorter and less repetitive, but whether it's easier to understand is for you to decide. In any case, you should add a comment explaining the code and that it is supposed to do.
With your original variables and procedures, this would be:
if abs(PID) > 10 and PID * self.last_pid > 0:
if abs(PID) >= abs(self.last_pid):
self.setKp(self.Kp+self.increase_val)
self.increase_val = self.increase_val*2
else:
percent_last = PID/self.last_pid*100
self.increase_val + (percent_last/100*self.increase_val)
self.setKp(self.Kp+self.increase_val)
Some more points that I just noticed:
your line self.increase_val + (percent_last / 100 * self.increase_val) does not do anything. I guess the + should be = or +=?
it is kind of pointless to first * 100 to get percent just to then / 100 again
it's odd how in one case you add increase_val to KP before increasing it, but after increasing it in the other case; is this intentional?
In fact, I think that this could be further simplified to this, provided that the inner if is used to cap the increase to the increase_val; not sure whether it should be added to Kp before or after being increased itself, though, or if that should actually depend on the case.
if abs(PID) > 10 and PID * self.last_pid > 0:
self.setKp(self.Kp + self.increase_val)
self.increase_val *= 1 + min(PID/self.last_pid, 1)

It's a bit long, but it takes less lines:
if (A > 10 and B > 10 and A>=B) or (A < -10 and B < 0 and A<= B):
#do block a
else:
#do block b
If you don't like it being so long, I would recommend turning each side of the or on the first line into a boolean variable and then using said variable in the if statement. Like so:
condA = A > 10 and B > 10 and A>=B
condB = A < -10 and B < 0 and A<= B
if condA or condB:
#do block a
else:
#do block b

Related

How do I go about coding a custom function (without using any library) which returns the log of a number in python [duplicate]

I need to generate the result of the log.
I know that:
Then I made my code:
def log(x, base):
log_b = 2
while x != int(round(base ** log_b)):
log_b += 0.01
print(log_b)
return int(round(log_b))
But it works very slowly. Can I use other method?
One other thing you might want to consider is using the Taylor series of the natural logarithm:
Once you've approximated the natural log using a number of terms from this series, it is easy to change base:
EDIT: Here's another useful identity:
Using this, we could write something along the lines of
def ln(x):
n = 1000.0
return n * ((x ** (1/n)) - 1)
Testing it out, we have:
print ln(math.e), math.log(math.e)
print ln(0.5), math.log(0.5)
print ln(100.0), math.log(100.0)
Output:
1.00050016671 1.0
-0.692907009547 -0.69314718056
4.6157902784 4.60517018599
This shows our value compared to the math.log value (separated by a space) and, as you can see, we're pretty accurate. You'll probably start to lose some accuracy as you get very large (e.g. ln(10000) will be about 0.4 greater than it should), but you can always increase n if you need to.
I used recursion:
def myLog(x, b):
if x < b:
return 0
return 1 + myLog(x/b, b)
You can use binary search for that.
You can get more information on binary search on Wikipedia:
Binary search;
Doubling search.
# search for log_base(x) in range [mn, mx] using binary search
def log_in_range(x, base, mn, mx):
if (mn <= mx):
med = (mn + mx) / 2.0
y = base ** med
if abs(y - x) < 0.00001: # or if math.isclose(x, y): https://docs.python.org/3/library/math.html#math.isclose
return med
elif x > y:
return log_in_range(x, base, med, mx)
elif x < y:
return log_in_range(x, base, mn, med)
return 0
# determine range using doubling search, then call log_in_range
def log(x, base):
if base <= 0 or base == 1 or x <= 0:
raise ValueError('math domain error')
elif 0 < base < 1:
return -log(x, 1/base)
elif 1 <= x and 1 < base:
mx = 1
y = base
while y < x:
y *= y
mx *= 2
return log_in_range(x, base, 0, mx)
elif 0 <= x < 1 and 1 < base:
mn = -1
y = 1/base
while y > x:
y = y ** 0.5
mn *= 2
return log_in_range(x, base, mn, 0)
import math
try :
number_and_base = input() ##input the values for number and base
##assigning those values for the variables
number = int(number_and_base.split()[0])
base = int(number_and_base.split()[1])
##exception handling
except ValueError :
print ("Invalid input...!")
##program
else:
n1 = 1 ##taking an initial value to iterate
while(number >= int(round(base**(n1),0))) : ##finding the most similer value to the number given, varying the vlaue of the power
n1 += 0.000001 ##increasing the initial value bit by bit
n2 = n1-0.0001
if abs(number-base**(n2)) < abs(base**(n1)-number) :
n = n2
else :
n = n1
print(math.floor(n)) ##output value
Comparison:-
This is how your log works:-
def your_log(x, base):
log_b = 2
while x != int(round(base ** log_b)):
log_b += 0.01
#print log_b
return int(round(log_b))
print your_log(16, 2)
# %timeit %run your_log.py
# 1000 loops, best of 3: 579 us per loop
This is my proposed improvement:-
def my_log(x, base):
count = -1
while x > 0:
x /= base
count += 1
if x == 0:
return count
print my_log(16, 2)
# %timeit %run my_log.py
# 1000 loops, best of 3: 321 us per loop
which is faster, using the %timeit magic function in iPython to time the execution for comparison.
It will be long process since it goes in a loop. Therefore,
def log(x,base):
result = ln(x)/ln(base)
return result
def ln(x):
val = x
return 99999999*(x**(1/99999999)-1)
log(8,3)
Values are nearly equal but not exact.

Arranging Integers using Conditional statements

I was wondering if someone would be able to help me regarding my coding problem. I'm still a beginner and I may have missed something here.
Basically, the instruction says that I need to arrange 3 integers in ascending order.
e.g Input : 5 2 4 ------> Output : 2 4 5
I need to utilize the conditional statements for this. Here is what I've done so far.
a, b, c = input().split()
a = int (a)
b = int (b)
c = int (c)
if a < b and a < c:
smallest = a
elif b < a and b < c:
smallest = b
else:
smallest = c
if a > b and a < c:
middle = a
elif a < b and a > c:
middle = a
elif b > a and b < c:
middle = b
elif b < a and b > c:
middle = b
else:
middle = c
if a > b and a > c:
largest = a
elif b > a and b > c:
largest = b
else:
largest = c
print(smallest, middle, largest)
This is on CodeChum btw, I'm stuck in this because I can't figure out why it doesn't accept the code I did. Basically there are 5 tests that the code needs in order to submit the whole activity. I managed to do test 1-4 but for some reason it fails on test 5.
The problem is that your code does not react well if two numbers are identical. You should replace your strict inequalities with non-strict ones.
a = 3
b = 3
c = 1
if a > b and a < c:
middle = a
elif a < b and a > c:
middle = a
elif b > a and b < c:
middle = b
elif b < a and b > c:
middle = b
else:
middle = c
print(middle) # 1 when it should be 3
As a == b, none of the 4 conditions can be True, and therefore the middle number will always be c.
In order to support the case where a == b, we can add another condition:
elif a == b:
middle = a
else:
middle = c
we can simplify this code by replacing the strict inequalities with non-strict ones (⩽ , <= in Python):
if b <= a < c:
middle = a
elif b >= a > c:
middle = a
elif a < b < c:
middle = b
elif a > b > c:
middle = b
else:
middle = c
We simply said that if a == b, the middle is always a. I also simplified the structure for better readability.
Why don't you simply use sorted function?
This one results in the exact thing you are looking for.
smallest, middle, largest = sorted(map(int, input().split()))
print(smallest, middle, largest)
Note that you don't need the map to int function for this. But you if don't do that, the smallest, middle, largest variables will remain str objects.
As the input is guaranteed to contain exactly 3 numbers (separated by whitespace) you can just do this:
if (inval := input()):
if len(nums := inval.split()) == 3:
try:
lo, mid, hi = sorted(map(int, nums))
print(lo, mid, hi)
except ValueError:
pass

python increment operator has weird behavior in one line conditional statement

Why does idcter not reset to 0 when it exceeds maxid?
maxid=9999
idcter=9999
idcter += 1 if(idcter <= maxid) else 0
print('this is good: ' + str(idcter))
idcter += 1 if(idcter <= maxid) else 0
print('now this is weird: ' + str(idcter))
idcter=10000
idcter = idcter + 1 if(idcter <= maxid) else 0
print("that's better: " + str(idcter))
Output:
this is good: 10000
now this is weird: 10000
that's better: 0
So it's a simple fix, but why would idcter not reset after it exceeds maxid?
The operator precedence of
idcter += 1 if (idcter <= maxid) else 0
is visualized by the following grouping
idcter += (1 if (idcter <= maxid) else 0)
which means you increment by 0 if the condition doesn't hold.
Compare that to
idcter = idcter + 1 if (idcter <= maxid) else 0
# ==
idcter = (idcter + 1) if (idcter <= maxid) else 0
Where you assign 0 to the result in the same case.
BTW, 10000 already exceeds your maxid of 9999. One typical way to implement such a resetting increment uses the modulo operator. In your case:
idcter = (idcter+1) % (maxid+1) # 9997 -> 9998 -> 9999 -> 0 -> 1

Python-Why aren't my elif statements not getting evaluated in this recursive multiplication which takes into account negative numbers

Hi I have looked up a few recursive multiplication of 2 numbers in python, but none of them take into account the possibility of negative numbers or if they do they don't explain the entire steps.
I have coded the following the program but I am getting an error, can someone please let me know what is wrong with it?
def mult(a,b):
if a==0 | b==0:
return 0
elif a==1:
return b
elif a > 0 & b >0:
return b + mult(a-1,b)
elif a < 0 & b > 0:
return - b + mult(a+1,b))
elif a > 0 & b < 0:
return - b + mult(a-1, b))
else:
return -b + mult(a+1, b)
print(mult(-4,5))
| and & are bitwise operators, not logical operators, and their (relatively) high precedence means that a > 0 & b >0 is parsed as a > (0 & b) > 0, which is not what you want. Use or and and instead.
You have some python syntax errors and some sign problems. This works for mult(-4,5) and mult(5,-4).
def mult(a,b):
if a == 0 or b == 0:
return 0
elif a == 1:
return b
elif b == 1:
return a
elif a >0 and b > 0:
return b + mult(a-1,b)
elif a < 0 and b > 0:
return -b+mult(a+1,b)
elif a > 0 and b < 0:
return b+mult(a-1,b)
else:
return b + mult(a+1,b)
In your elif statement, you're using a bitwise "and" operator rather than the logical and operator. Everywhere that you have "&" replace it with "and"

Division using recursion

I am pretty sure that this must be some glaringly stupid mistake by me. But can anyone explain what is wrong in this division code using recursion. I know there are a lot of alternatives, but I need to know what is wrong with this
def division(a, b):
x = 0
if a < b:
return x
else:
x += 1
return division(a - b, b)
return x
When I do division(10, 2), it gives me 0 as output
You always set your local variable x to 0.
Then if the dividend is smaller than the divisor you return that x which is of course 0.
On the other hand when the dividend is greater or equal to the divisor you increment x by 1 and do a recursive call with a decremented dividend which will of course lead to the first case at the end and still you return an x which holds the value of 0.
Note: Nonetheless your final return is not reachable since both your if and else branch contains a return.
So please try considering this solution:
def division(a, b):
if a < b:
return 0
else:
return 1 + division(a-b, b)
Update:
A possible solution for working with negative integers following python's round towards negative infinity division:
def division_with_negatives(a, b):
a_abs, b_abs = abs(a), abs(b)
if (a >= 0 and b >= 0) or (a < 0 and b < 0):
# branch for positive results
if a_abs < b_abs:
return 0
else:
return 1 + division_with_negatives(a_abs - b_abs, b_abs)
else:
# branch for negative results
if b_abs > a_abs or a_abs == b_abs:
return -1
else:
return -1 + division_with_negatives(a_abs - b_abs, -b_abs)
assert division_with_negatives(-4, 1) == -4 // 1
assert division_with_negatives(10, 2) == 10 // 2
assert division_with_negatives(1, -5) == 1 // -5
assert division_with_negatives(-3, -2) == -3 // -2
This might save you from RecursionError:
def div(x, y):
if y == 0:
return 0
elif x == y:
return 1
elif x < y:
if y * -1 == x:
return -1
elif x % y == 0:
return 1 + div(x - y, y)
else:
return 0
else:
if y < 0:
return 1 - div(x - y, -y)
else:
return 1 + div(x - y, y)
#Application
A = div(1, 2)
B = div(-9, 9)
C = div(3, 2)
D = div(-9, -3)
E = div(100, -2)
print(A) # 0
print(B) # -1
print(C) # 1
print(D) # 3
print(E) # -50
Your escape condition is if a < b. That means that for this function to terminate, this must be fulfilled to leave the recursion. However, because x is declared at the top of the function, only redefined inside the body of the else statement but never returned, the function will always terminate with a value of x = 0.
You should either set x = 1 + division(a-b,b) or return division(a-b,b) + 1 and remove the unreachable returnat the end.
def div(a, b, x):
if a < b:
return x
else:
x +=1
return div(a - b, b, x)
print(div(130, 10, 0))
# 13
This might work a little better for you:
def division(a,b,x=0):
if a < b:
return x
else:
x += 1
return division(a-b,b,x)
return x
Every time your function ran through a new recusion, you were setting x to 0. This way, it defaults to 0 if x is not specified, and should work like I think you want.
Also note that this will not work for negative numbers, but you probably knew that :)
With global you get:
def division(a,b):
global x
if a < b: return x
else:
x += 1
return division(a-b,b)
x=0
print (division(10,2))
But you have to set x to zero every time before call the division
i think this is best way, this way u can get floating answer also
def division(a,b):
if a == 0 or a == 1 or a == 2:
return b
else:
return a / division(a % b,b)
print(division(9,2))

Categories

Resources