adding fractions class python - python

I have this portion of a class that takes a whole number and a fraction and add them together.
def __add__(self, g):
whole_add=self.whole_number + g.whole_number
numerator = (self.fraction.numerator * g.fraction.denominator ) + (g.fraction.numerator * self.fraction.denominator)
denominator = self.fraction.denominator * g.fraction.denominator
f=Fraction(numerator,denominator)
return '{} and {}'.format(whole_add,f)
fraction_1 = Fraction(3, 4)
fraction_2 = Fraction(2, 3)
mixed_num_1 = MixedNumber(2, fraction_1)
mixed_num_2 = MixedNumber(1, fraction_2)
print(mixed_num_1 + mixed_num_2)
The outcome of this is 3 and 17/12, when it should be 4 and 5/12, I am not sure how to do this, I assume with an if the fraction is >= 1. Any help would be much appreciated

If you are using the fractions library, ou can just sum everything, then take the int() portion of the fraction:
def __add__(self, g):
summed = sum((self.whole_number, g.whole_number, self.fraction, g.fraction))
whole = int(summed)
remainder = summed - whole
return '{} and {}'.format(whole, remainder)
The Fraction() class implements __add__ for you, you can just sum up integers and Fraction() objects and it all works as it should.
Demo using constants:
>>> from fractions import Fraction
>>> summed = sum((2, 1, Fraction(3, 4), Fraction(2, 3)))
>>> whole = int(summed)
>>> remainder = summed - whole
>>> '{} and {}'.format(whole, remainder)
'4 and 5/12'
One little-known but handy factoid is that Python the int() type has both .numerator and .denominator attributes, that the fractions.Fraction() class makes use of. If you are not using the fractions library, you can make use of that yourself:
def __add__(self, g):
summed = 0
for v in (self.whole_number, g.whole_number, self.fraction, g.fraction):
summed = Fraction(summed.numerator * v.denominator +
v.numerator * summed.denominator,
summed.denominator * v.denominator)
whole = summed._numerator // summed._denominator
remainder = Fraction(summed.numerator * whole.denominator -
whole.numerator * summed.denominator,
summed.denominator * whole.denominator)
return '{} and {}'.format(whole, remainder)

One way to "fix" your version would be to deal with the improper fraction directly:
whole_add=self.whole_number + g.whole_number
numerator = (self.fraction.numerator * g.fraction.denominator ) + (g.fraction.numerator * self.fraction.denominator)
denominator = self.fraction.denominator * g.fraction.denominator
whole_add += numerator // denominator
numerator -= numerator % denominator
f=Fraction(numerator,denominator)
return '{} and {}'.format(whole_add,f)
Although there are more efficient ways of doing this addition more directly.

Related

Python Rounding Down to Custom Step

We have a partially working code and 2 examples with different types of custom steps. The example 2 (Int) is working, while the example 1 is not, as it is rounding up instead of down.
import math
def step_size_to_precision(ss):
return ss.find('1') - 1
def format_value(val, step_size_str):
precision = step_size_to_precision(step_size_str)
if precision > 0:
return "{:0.0{}f}".format(val, precision)
return math.floor(int(val))
###########################
# # example 1
step_size = "0.00000100"
quantity = 0.00725562
print(quantity)
print(format_value(quantity, step_size))
# 0.00725562
# 0.007256 <= Is rounding up instead of down. Should be 0.007255
###########################
# # example 2
# step_size = "1"
# quantity = 3.00725562
# print(quantity)
# print(format_value(quantity, step_size))
# returns 3 <= This is correct
###########################
How do we fix it?
You'll want to use Decimal objects to for precise decimal numbers to begin with.
Then, use Decimal.quantize() in the ROUND_DOWN mode.
from decimal import Decimal, ROUND_DOWN
quantity = 0.00725562
step_size = Decimal("0.000001")
print(Decimal(quantity).quantize(step_size, ROUND_DOWN))
prints out
0.007255
Another approach is outlined in this SO answer:
If you want to round down always (instead of rounding to the nearest
precision), then do so, explicitly, with the math.floor()
function:
from math import floor
def floored_percentage(val, digits):
val *= 10 ** (digits + 2)
return '{1:.{0}f}%'.format(digits, floor(val) / 10 ** digits)
print floored_percentage(0.995, 1)
Demo:
>>> from math import floor
>>> def floored_percentage(val, digits):
... val *= 10 ** (digits + 2)
... return '{1:.{0}f}%'.format(digits, floor(val) / 10 ** digits)
...
>>> floored_percentage(0.995, 1)
'99.5%'
>>> floored_percentage(0.995, 2)
'99.50%'
>>> floored_percentage(0.99987, 2)
'99.98%'
For your example:
import math
def step_size_to_precision(ss):
return max(ss.find('1'), 1) - 1
def format_value(val, step_size):
digits = step_size_to_precision(step_size)
val *= 10 ** digits
return '{1:.{0}f}'.format(digits, math.floor(val) / 10 ** digits)
step_size = "0.00000100"
quantity = 0.00725562
print(quantity)
print(format_value(quantity, step_size))
# prints out: 0.007255
A more general approach which allows to round down for step_size which is not only power of 10:
from decimal import Decimal
def floor_step_size(quantity, step_size):
step_size_dec = Decimal(str(step_size))
return float(int(Decimal(str(quantity)) / step_size_dec) * step_size_dec)
Usage:
>>> floor_step_size(0.00725562, "0.00000100")
0.007255
>>> floor_step_size(3.00725562, "1")
3.0
>>> floor_step_size(2.6, "0.25")
2.5
>>> floor_step_size(0.9, "0.2")
0.8

Combining numbers together to form multiple digit number

I'm trying to combine multiple numbers together in python 3.7 but I'm having no luck.
I want it to be like such:
1 + 4 + 5 = 145
I know this is simple but I'm getting nowhere!
You can use reduce to do this in a mathematical way
>>> l = [1, 4, 5]
>>>
>>> from functools import reduce
>>> reduce(lambda x,y: 10*x+y, l)
145
Alternatively, you can use string concat
>>> int(''.join(map(str, l)))
145
If you want to do this numerically, consider what base-10 numerals means:
145 = 1 * 10**2 + 4 * 10**1 + 5 * 10**0
So, you need to get N numbers that range from N-1 to 0, in lockstep with the digits. One way to do this is with enumerate plus a bit of extra arithmetic:
def add_digits(*digits):
total = 0
for i, digit in enumerate(digits):
total += digit * 10**(len(digits)-i-1)
return total
Now:
>>> add_digits(1, 4, 5)
145
Of course this only works with sequences of digits—where you know how many digits you have in advance. What if you wanted to work with any iterable of digits, even an iterator coming for a generator expression or something? Then you can rethink the problem:
1456 = ((1 * 10 + 4) * 10 + 5) * 10 + 6
So:
def add_digits(digits):
total = 0
for digit in digits:
total = total * 10 + digit
return total
>>> add_digits((1, 3, 5, 6))
1356
>>> add_digits(n for n in range(10) if n%2)
13579
Notice that you can easily extend either version to other bases:
def add_digits(*digits, base=10):
total = 0
for i, digit in enumerate(digits):
total += digit * base**(len(digits)-i-1)
return total
>>> hex(add_digits(1, 0xF, 2, 0xA, base=16))
'0x1f2a'
… which isn't quite as easy to do with the stringy version; you can't just do int(''.join(map(str, digits)), base), but instead need to replace that str with a function that converts to a string in a given base. Which there are plenty of solutions for, but no obvious and readable one-liner.
You should try casting the numbers as strings! When you do something like this
str(1)+str(4)+str(5)
You will get 145, but it will be a string. If you want it to be a number afterwards, then you can cast the whole thing as an integer.
int(str(1)+str(4)+str(5))
or just set the answer to a new variable and cast that as an integer.
You could just write a function that concatenates numbers or any other object/datatype as a string
concatenate = lambda *args : ''.join([str(arg) for arg in args])
a = 1
print(concatenate(4, 5, 6))
print(concatenate(a, MagicNumber(1), "3"))
But also in python you can make a class and write magic functions that control the way that objects of your class are added, subtracted etc. You could make a class to store a number and add it like you want to. You could save this code in a file and import it or paste it into your own script.
class MagicNumber():
value = 0
def __init__(self, value):
self.value = int(value)
def __str__(self):
return str(self.value)
def __int__(self):
return self.value
def __repr__(self):
return self.value
def __add__(self, b):
return MagicNumber(str(self)+str(b))
if __name__ == "__main__":
a = MagicNumber(4)
b = MagicNumber(5)
c = MagicNumber(6)
print(a+b+c)
#You could even do this but I strongly advise against it
print(a+5+6)
And heres a link to the documentation about these "magic methods"
https://docs.python.org/3/reference/datamodel.html
The easiest way to do this is to concat them as strings, and then parse it back into a number.
x = str(1) + str(4) + str(5)
print(int(x))
or
int(str(1) + str(4) + str(5))

Imprecise results of logarithm and power functions in Python

I am trying to complete the following exercise:
https://www.codewars.com/kata/whats-a-perfect-power-anyway/train/python
I tried multiple variations, but my code breaks down when big numbers are involved (I tried multiple variations with solutions involving log and power functions):
Exercise:
Your task is to check wheter a given integer is a perfect power. If it is a perfect power, return a pair m and k with m^k = n as a proof. Otherwise return Nothing, Nil, null, None or your language's equivalent.
Note: For a perfect power, there might be several pairs. For example 81 = 3^4 = 9^2, so (3,4) and (9,2) are valid solutions. However, the tests take care of this, so if a number is a perfect power, return any pair that proves it.
The exercise uses Python 3.4.3
My code:
import math
def isPP(n):
for i in range(2 +n%2,n,2):
a = math.log(n,i)
if int(a) == round(a, 1):
if pow(i, int(a)) == n:
return [i, int(a)]
return None
Question:
How is it possible that I keep getting incorrect answers for bigger numbers? I read that in Python 3, all ints are treated as "long" from Python 2, i.e. they can be very large and still represented accurately. Thus, since i and int(a) are both ints, shouldn't the pow(i, int(a)) == n be assessed correctly? I'm actually baffled.
(edit note: also added integer nth root bellow)
you are in the right track with logarithm but you are doing the math wrong, also you are skipping number you should not and only testing all the even number or all the odd number without considering that a number can be even with a odd power or vice-versa
check this
>>> math.log(170**3,3)
14.02441559235585
>>>
not even close, the correct method is described here Nth root
which is:
let x be the number to calculate the Nth root, n said root and r the result, then we get
rn = x
take the log in any base from both sides, and solve for r
logb( rn ) = logb( x )
n * logb( r ) = logb( x )
logb( r ) = logb( x ) / n
blogb( r ) = blogb( x ) / n
r = blogb( x ) / n
so for instance with log in base 10 we get
>>> pow(10, math.log10(170**3)/3 )
169.9999999999999
>>>
that is much more closer, and with just rounding it we get the answer
>>> round(169.9999999999999)
170
>>>
therefore the function should be something like this
import math
def isPP(x):
for n in range(2, 1+round(math.log2(x)) ):
root = pow( 10, math.log10(x)/n )
result = round(root)
if result**n == x:
return result,n
the upper limit in range is to avoid testing numbers that will certainly fail
test
>>> isPP(170**3)
(170, 3)
>>> isPP(6434856)
(186, 3)
>>> isPP(9**2)
(9, 2)
>>> isPP(23**8)
(279841, 2)
>>> isPP(279841)
(529, 2)
>>> isPP(529)
(23, 2)
>>>
EDIT
or as Tin Peters point out you can use pow(x,1./n) as the nth root of a number is also expressed as x1/n
for example
>>> pow(170**3, 1./3)
169.99999999999994
>>> round(_)
170
>>>
but keep in mind that that will fail for extremely large numbers like for example
>>> pow(8191**107,1./107)
Traceback (most recent call last):
File "<pyshell#90>", line 1, in <module>
pow(8191**107,1./107)
OverflowError: int too large to convert to float
>>>
while the logarithmic approach will success
>>> pow(10, math.log10(8191**107)/107)
8190.999999999999
>>>
the reason is that 8191107 is simple too big, it have 419 digits which is greater that the maximum float representable, but reducing it with a log produce a more reasonable number
EDIT 2
now if you want to work with numbers ridiculously big, or just plain don't want to use floating point arithmetic altogether and use only integer arithmetic, then the best course of action is to use the method of Newton, that the helpful link provided by Tin Peters for the particular case for cube root, show us the way to do it in general alongside the wikipedia article
def inthroot(A,n):
if A<0:
if n%2 == 0:
raise ValueError
return - inthroot(-A,n)
if A==0:
return 0
n1 = n-1
if A.bit_length() < 1024: # float(n) safe from overflow
xk = int( round( pow(A,1/n) ) )
xk = ( n1*xk + A//pow(xk,n1) )//n # Ensure xk >= floor(nthroot(A)).
else:
xk = 1 << -(-A.bit_length()//n) # power of 2 closer but greater than the nth root of A
while True:
sig = A // pow(xk,n1)
if xk <= sig:
return xk
xk = ( n1*xk + sig )//n
check the explanation by Mark Dickinson to understand the working of the algorithm for the case of cube root, which is basically the same for this
now lets compare this with the other one
>>> def nthroot(x,n):
return pow(10, math.log10(x)/n )
>>> n = 2**(2**12) + 1 # a ridiculously big number
>>> r = nthroot(n**2,2)
Traceback (most recent call last):
File "<pyshell#48>", line 1, in <module>
nthroot(n**2,2)
File "<pyshell#47>", line 2, in nthroot
return pow(10, math.log10(x)/n )
OverflowError: (34, 'Result too large')
>>> r = inthroot(n**2,2)
>>> r == n
True
>>>
then the function is now
import math
def isPPv2(x):
for n in range(2,1+round(math.log2(x))):
root = inthroot(x,n)
if root**n == x:
return root,n
test
>>> n = 2**(2**12) + 1 # a ridiculously big number
>>> r,p = isPPv2(n**23)
>>> p
23
>>> r == n
True
>>> isPPv2(170**3)
(170, 3)
>>> isPPv2(8191**107)
(8191, 107)
>>> isPPv2(6434856)
(186, 3)
>>>
now lets check isPP vs isPPv2
>>> x = (1 << 53) + 1
>>> x
9007199254740993
>>> isPP(x**2)
>>> isPPv2(x**2)
(9007199254740993, 2)
>>>
clearly, avoiding floating point is the best choice

rounding in python not working

I've been having trouble trying to round my answer for a problem where I find the area of a regular polygon and then square the perimeter. My final answer should be the area plus the perimeter(squared), rounded to 4 decimal places. My math seems to be correct, however, no matter what numbers I use for inputs, there are only zeros after the decimal. I have included a screen shot of my code and incorrect message from the checker that I use.
import math
def polysum(n, s):
a = ((0.25 * n * s ** 2) / (math.tan(math.pi / 2)))
p = ((n * s) ** 2)
total = a + p
return '%.4f' % round(total)
print polysum(8, 8)
Of course you're only getting zeroes after the decimal point, because you are using the round() function to chop off all digits after the decimal point. If that's not what you want, don't do it. Just do:
return "%.4f" % total
Or possibly:
return round(total, 4)
There are two issues:
Change return '%.4f' % round(total) to return round(total,4) or else you are returning a str round to the nearest integer. It looks like the expected output is a float.
The factor of math.tan(math.pi / 2) is incorrect. This should evaluate to infinity (if not for floating point approximations) and is clearly not what you want. It should be math.tan(math.pi / 2 / n).
import math
def polysum(n, s):
a = (0.25 * n * s ** 2) / (math.tan(math.pi / n))
p = ((n * s) ** 2)
total = a + p
ans = round(total, 4)
return ans
print polysum(8,8)
print polysum(4, 89)
from math import *
def polysum(n, s):
lst = [(0.25 * n * s **2) / tan(pi / n), ((n * s) ** 2)]
return round(sum(lst), 4)
I have tried both the test cases. The output is matching.

Weird (incorrect) rounding in Python [duplicate]

>>> a = 0.3135
>>> print("%.3f" % a)
0.314
>>> a = 0.3125
>>> print("%.3f" % a)
0.312
>>>
I am expecting 0.313 instead of 0.312
Any thought on why is this, and is there alternative way I can use to get 0.313?
Thanks
Python 3 rounds according to the IEEE 754 standard, using a round-to-even approach.
If you want to round in a different way then simply implement it by hand:
import math
def my_round(n, ndigits):
part = n * 10 ** ndigits
delta = part - int(part)
# always round "away from 0"
if delta >= 0.5 or -0.5 < delta <= 0:
part = math.ceil(part)
else:
part = math.floor(part)
return part / (10 ** ndigits) if ndigits >= 0 else part * 10 ** abs(ndigits)
Example usage:
In [12]: my_round(0.3125, 3)
Out[12]: 0.313
Note: in python2 rounding is always away from zero, while in python3 it rounds to even. (see, for example, the difference in the documentation for the round function between 2.7 and 3.3).
If you need accuracy don't use float, use Decimal
>>> from decimal import *
>>> d = Decimal(0.3125)
>>> getcontext().rounding = ROUND_UP
>>> round(d, 3)
Decimal('0.313')
or even Fraction
try
print '%.3f' % round(.3125,3)
I had the same incorrect rounding
round(0.573175, 5) = 0.57317
My solution
def to_round(val, precision=5):
prec = 10 ** precision
return str(round(val * prec) / prec)
to_round(0.573175) = '0.57318'

Categories

Resources