Figure out where decimal point goes when dividing floats as integers - python

I'm wanting to make a script that can perform maths on large numbers while keeping the precision as high as required. I attempted ones ages ago and division was the worst part by far, so I thought I'd try start with it this time.
Basically, I'm using str inputs, as they obviously remain perfectly accurate, and I'd like to perform calculations on them as integers so that nothing is lost to floating point numbers. I'm doing that currently by removing the decimal point before the calculation, then trying to re-add it, but I can't figure out how.
With multiplication, you can figure out the decimal by adding how many numbers are left of the decimal point in both numbers. Unfortunately that method doesn't seem to work with division. I previously tried coding long division, but again it's an awkward one if there's floats involved.
Here's the current code I've got, with some of the non working parts removed:
from __future__ import division
def _divide(a, b, precision):
count = 0
while a and count < precision:
for num in str(int(a // b)):
yield int(num)
count += 1
a = a % b * 10
def divide(a, b, precision=100, round_int=True):
result = []
a = str(a)
b = str(b)
#Check for negative numbers
a_neg = a[0] == '-'
b_neg = b[0] == '-'
if a_neg and not b_neg or b_neg and not a_neg:
result.append('-')
#Process number strings
a_split = a.split('.')
b_split = b.split('.')
try:
a = abs(int(a_split[0] + a_split[1]))
except IndexError:
a = abs(int(a_split[0]))
try:
b = abs(int(b_split[0] + b_split[1]))
except IndexError:
b = abs(int(b_split[0]))
#Calculate number
result += list(_divide(a, b, precision=precision+round_int))
if not round_int:
return ''.join(map(str, result))
#Round last integer
last_int = int(result.pop(-1))
index = -1
if last_int >= 5:
try:
result[index] += 1
except TypeError:
index -= 1
result[index] += 1
#Lower any integers above 10
while True:
if result[index] == '.':
index -= 1
if result[index] > 9:
result[index] -= 10
if result[index-1] == '.':
index -= 1
result[index-1] += 1
else:
return ''.join(map(str, result)).rstrip('.')
index -= 1
a = '365.72'
b = '2247.7'
print divide(a, b, 9, False)
print float(a) / float(b)
I just drew up a quick hacky way which uses the inbuilt float division to figure out where the decimal is, but obviously it's not ideal as it'll fail after 1e300:
cheat_div = str(abs(float(a) / float(b)))
if 'e' in cheat_div:
num_decimals = int(cheat_div.split('e')[1])
if num_decimals > 1:
num_decimals += 1
else:
if cheat_div.startswith('0.'):
num_decimals = 2 + len(cheat_div[2:].lstrip('0')) - len(cheat_div)
else:
try:
num_decimals = cheat_div.index('.')
except ValueError:
num_decimals = 0
while not result[is_negative]:
del result[is_negative]
if num_decimals:
if num_decimals > 0:
result = result[:is_negative+num_decimals] + ['.'] + result[is_negative+num_decimals:]
else:
result = result[:is_negative] + ['0', '.'] + ['0'] * -num_decimals + result[is_negative:]

Related

Asserting that a number is in binary

Im working on my homework and encountered a problem. I had to string numbers in binary back to back.
Lets say we have joined(1, 3) - 1 is the starting number and 3 says that Im gonna work with 2 more numbers (if i had joined(5, 4), id work with 5, 6, 7 and 8)
1 1
2 10
3 11
result 11011
def num_to_2(number):
num2 = 0
exp = 0
while number >= 1:
num2 += (number % 2) * (10 ** exp)
number = number // 2
exp += 1
return num2
def num_lenght(number):
if number == 0:
return 0
lenght = 0
while number >= 1:
number /= 10
lenght += 1
return lenght
def joined(start, count):
result = 0
end = start + count - 1
for i in range(end, start - 1, -1):
number = num_to_2(i)
res_lenght = num_lenght(result)
result += number * (10 ** res_lenght)
return result
def main():
assert joined(1, 3) == 0b11011
assert joined(10, 4) == 0b1010101111001101
assert joined(8, 5) == 0b10001001101010111100
assert joined(99, 2) == 0b11000111100100
assert joined(999, 3) == 0b111110011111111010001111101001
assert joined(1111, 1) == 0b10001010111
The function works properly but it gives me results without the 0b prefix, which i need in order to pass the asserts. How do i add it there? and also: i cant use strings.
Thanks in advance!
Edit: i added the rest of my code so it makes more sense
I already solved my problem, what i had to do was "manually" convert the pseudo-binary number into base 10 number. this is my code now:
def convert_to_2(number):
num2 = 0
exp = 0
while number >= 1:
num2 += (number % 2) * (10 ** exp)
number = number // 2
exp += 1
return num2
def num_lenght(number):
if number == 0:
return 0
lenght = 0
while number >= 1:
number /= 10
lenght += 1
return lenght
def joined(start, count):
result = 0
end = start + count - 1
for i in range(end, start - 1, -1):
number = convert_to_2(i)
res_lenght = num_lenght(result)
result += number * (10 ** res_lenght)
result = convert_to_10(result)
return result
def convert_to_10(number):
num10 = 0
exp = 0
while number >= 1:
num10 += (number % 10) * (2 ** exp)
number = number // 10
exp += 1
return num10
def main():
assert joined(1, 3) == 0b11011
assert joined(10, 4) == 0b1010101111001101
assert joined(8, 5) == 0b10001001101010111100
assert joined(99, 2) == 0b11000111100100
assert joined(999, 3) == 0b111110011111111010001111101001
assert joined(1111, 1) == 0b10001010111
if __name__ == "__main__":
main()
thank you so much for your help(:
Here is a bit-shifting version which passes all assertions:
from math import ceil, log2
from functools import reduce
def join2(a,b):
n = ceil(log2(b+1))
return (a<<n) | b
def joined(start, count):
return reduce(join2,range(start,start+count))
def main():
assert joined(1, 3) == 0b11011
assert joined(10, 4) == 0b1010101111001101
assert joined(8, 5) == 0b10001001101010111100
assert joined(99, 2) == 0b11000111100100
assert joined(999, 3) == 0b111110011111111010001111101001
assert joined(1111, 1) == 0b10001010111
Well, it's unclear what your teachers mean with "don't use strings". I found this a very strange homework, so I did it, without strings.
I'm not sure what you are supposed to learn from it, but here it is:
def to_binary(number):
b = 0
while number >= 2**(b+1):
b += 1
while True:
if number >= 2**b:
yield 1
number -= 2**b
else:
yield 0
b -= 1
if b < 0:
return
def joined(start, count):
result = 0
for i in range(start, start + count):
for digit in to_binary(i):
result = (result << 1) + digit
return result
def main():
assert joined(1, 3) == 0b11011
assert joined(10, 4) == 0b1010101111001101
assert joined(8, 5) == 0b10001001101010111100
assert joined(99, 2) == 0b11000111100100
assert joined(999, 3) == 0b111110011111111010001111101001
assert joined(1111, 1) == 0b10001010111
It implements a to_binary() function that will yield a sequence of integers. Then the join function will binary shift the result to the left, and add that one or zero.
No strings attached...
You don't need to worry about the 0b prefix, it is merely a way to express numeric literals in Python source code. They could have written 27 or 0x1b instead of 0b11011 but the binary form makes it clearer what bit pattern is expected in the integer value returned by joined().
You can use the integer's .bit_length() method to know how many bits are used to represent a number. This should make the function a bit simpler.
def joined(start,count):
result = 0
for n in range(start,start+count):
result <<= n.bit_length() # offset by the number of bits
result |= n # add the number's bits
return result
if you're not allowed to use that method, you can write your own function to do it:
def bitLength(N): return 0 if not N else bitLength(N//2)+1
If you're also not allowed to use bit-wise operators, you can do it with multiplications and additions:
def joined(start,count):
result = 0
for n in range(start,start+count):
result *= 2**bitLength(n) # offset by the number of bits
result += n # add the number's bits
return result

sum digit in string but how to do it if it start with negative sign?

I have this problem I did the positive but what about if it is a negative number and need to sum? Please help.
Question: Write a function called sum_digits.
Given a number, sum_digits returns the sum of all its digits.
output = sum_digits(1148)
print(output) # --> 14
If the number is negative, the first digit should count as negative.
output = sum_digits(-316)
print(output) # --> 4
Notes:
In order to use some of the methods that will be most helpful to you, you will most likely want to do some string to number conversion and vice versa.
Below is my function:
def sum_digits(num):
num_str = str(num)
num_sum = 0
for i in num_str:
dig = int(i)
num_sum += dig
return num_sum
If parsing number to string is okay with you solution this simple should be okay.
In [1]: def sum_digits(x):
...: x = str(x)
...: if x[0] == '-':
...: return sum(map(int, [x[:2], *x[2:]]))
...: return sum(map(int, x))
In [2]: sum_digits(1148)
Out[2]: 14
In [3]: sum_digits(-316)
Out[3]: 4
One-liner just because:
sum(map(int, num[1:])) + (int(num[0]) if num[0] != '-' else int(num[0:2]) * 2)
This assumes num is a string, otherwise convert it to a string before using the above.
num = str(num)
No need for string conversion. This works for positive and negative integers:
def sum_digits(num):
if num < 0:
return -sum_digits(-num)
elif not num:
return 0
else:
return num % 10 + sum_digits(num // 10)
If recursion isn't on the menu:
def sum_digits(num):
factor, num, total = -1 if num < 0 else 1, abs(num), 0
while num:
total += num % 10
num = num // 10
return total * factor
Edit: someone pointed out you may only want the first digit subtracted, instead of having the result be negative, i.e. -316 as -3 + 1 + 6 instead of -(3 + 1 + 6). If you do indeed want to subtract the first digit if the number is negative:
def sum_digits(num):
factor, num, total = -1 if num < 0 else 1, abs(num), 0
while num:
total += num % 10 if num > 10 else factor * num
num = num // 10
return total
For the first 2 examples, the result for -316 would be -10, for the last, the result would be 4.
>>> def sum_digits(num):
... negative = num < 0 # returns Boolean of if number is negative or not
... if negative:
... value_1 = int(str(num)[1]) # get the first numerical digit i.e. in -35, 3
... num = str(abs(num)) # convert to modulus of number and to string
... num_sum = sum(int(i) for i in num) # sum of all the digits i.e. -35 = 8
... if negative:
... num_sum = num_sum - 2*value_1 # subtract 2*the first numerical digits i.e 8-(2*3) = 2
...
... return num_sum
...
>>> """test cases"""
>>> print(sum_digits(316))
10
>>> print(sum_digits(-316))
4
A simple and logical solution
Just add an if-statement to deal with the negative case.
def sum_digits(num):
num_str = str(num)
num_sum = 0
# Check if number is negative (first char is negative sign)
if num_str[0] == "-":
# If so, subtract first digit then remove the first two chars
num_sum -= int(num_str[1])
num_str = num_str[2:]
# Number is now positive, so can deal with it accordingly
for i in num_str:
dig = int(i)
num_sum += dig
return num_sum
output = sum_digits(1148)
print(output) # --> 14
output = sum_digits(-316)
print(output) # --> 4
I think such a simple problem should not be solved using str/int conversions, slices and other complex instruments. So, that's my solution:
def sum_digits(num: int) -> int:
is_negative = num < 0
num = abs(num)
num_sum = 0
while num > 10:
num_sum += num % 10
num = num // 10
if is_negative:
num_sum -= num % 10
else:
num_sum += num % 10
return num_sum

Fibonacci series in bit string

I am working on Fibonacci series but in bit string which can be represented as:
f(0)=0;
f(1)=1;
f(2)=10;
f(3)=101;
f(4)=10110;
f(5)=10110101;
Secondly, I have a pattern for example '10' and want to count how many times this occurs in particular series, for example, the Fibonacci series for 5 is '101101101' so '10' occur 3 times.
my code is running correctly without error but the problem is that it cannot run for more than the value of n=45 I want to run n=100
can anyone help? I only want to calculate the count of occurrence
n=5
fibonacci_numbers = ['0', '1']
for i in range(1,n):
fibonacci_numbers.append(fibonacci_numbers[i]+fibonacci_numbers[i-1])
#print(fibonacci_numbers[-1])
print(fibonacci_numbers[-1])
nStr = str (fibonacci_numbers[-1])
pattern = '10'
count = 0
flag = True
start = 0
while flag:
a = nStr.find(pattern, start)
if a == -1:
flag = False
else:
count += 1
start = a + 1
print(count)
This is a fun one! The trick is that you don't actually need that giant bit string, just the number of 10s it contains and the edges. This solution runs in O(n) time and O(1) space.
from typing import NamedTuple
class FibString(NamedTuple):
"""First digit, last digit, and the number of 10s in between."""
first: int
tens: int
last: int
def count_fib_string_tens(n: int) -> int:
"""Count the number of 10s in a n-'Fibonacci bitstring'."""
def combine(b: FibString, a: FibString) -> FibString:
"""Combine two FibStrings."""
tens = b.tens + a.tens
# mind the edges!
if b.last == 1 and a.first == 0:
tens += 1
return FibString(b.first, tens, a.last)
# First two values are 0 and 1 (tens=0 for both)
a, b = FibString(0, 0, 0), FibString(1, 0, 1)
for _ in range(1, n):
a, b = b, combine(b, a)
return b.tens # tada!
I tested this against your original implementation and sure enough it produces the same answers for all values that the original function is able to calculate (but it's about eight orders of magnitude faster by the time you get up to n=40). The answer for n=100 is 218922995834555169026 and it took 0.1ms to calculate using this method.
The nice thing about the Fibonacci sequence that will solve your issue is that you only need the last two values of the sequence. 10110 is made by combining 101 and 10. After that 10 is no longer needed. So instead of appending, you can just keep the two values. Here is what I've done:
n=45
fibonacci_numbers = ['0', '1']
for i in range(1,n):
temp = fibonacci_numbers[1]
fibonacci_numbers[1] = fibonacci_numbers[1] + fibonacci_numbers[0]
fibonacci_numbers[0] = temp
Note that it still uses a decent amount of memory, but it didn't give me a memory error (it does take a bit of time to run though).
I also wasn't able to print the full string as I got an OSError [Errno 5] Input/Output error but it can still count and print that output.
For larger numbers, storing as a string is going to quickly cause a memory issue. In that case, I'd suggest doing the fibonacci sequence with plain integers and then converting to bits. See here for tips on binary conversion.
While the regular fibonacci sequence doesn't work in a direct sense, consider that 10 is 2 and 101 is 5. 5+2 doesn't work - you want 10110 or an or operation 10100 | 10 yielding 22; so if you shift one by the length of the other, you can get the result. See for example
x = 5
y = 2
(x << 2) | y
>> 22
Shifting x by the number of bits representing y and then doing a bitwise or with | solves the issue. Python summarizes these bitwise operations well here. All that's left for you to do is determine how many bits to shift and implement this into your for loop!
For really large n you will still have a memory issue shown in the plot:
'
Finally i got the answer but can someone explain it briefly why it is working
def count(p, n):
count = 0
i = n.find(p)
while i != -1:
n = n[i + 1:]
i = n.find(p)
count += 1
return count
def occurence(p, n):
a1 = "1"
a0 = "0"
lp = len(p)
i = 1
if n <= 5:
return count(p, atring(n))
while lp > len(a1):
temp = a1
a1 += a0
a0 = temp
i += 1
if i >= n:
return count(p, a1)
fn = a1[:lp - 1]
if -lp + 1 < 0:
ln = a1[-lp + 1:]
else:
ln = ""
countn = count(p, a1)
a1 = a1 + a0
i += 1
if -lp + 1 < 0:
lnp1 = a1[-lp + 1:]
else:
lnp1 = ""
k = 0
countn1 = count(p, a1)
for j in range(i + 1, n + 1):
temp = countn1
countn1 += countn
countn = temp
if k % 2 == 0:
string = lnp1 + fn
else:
string = ln + fn
k += 1
countn1 += count(p, string)
return countn1
def atring(n):
a0 = "0"
a1 = "1"
if n == 0 or n == 1:
return str(n)
for i in range(2, n + 1):
temp = a1
a1 += a0
a0 = temp
return a1
def fn():
a = 100
p = '10'
print( occurence(p, a))
if __name__ == "__main__":
fn()

Evenly intermix two lists of elements (load balance)

Let's say I have two strings made with only 1 character:
'aaaaaaa'
'bbb'
I'd like to find an algorithm to produce a combined string of:
'aabaabaaba'
The two are merged so that there is the fewest # of consecutive characters from either list (in this case that # is 2). The length of each string is arbitrary, and I'd like for it to be symmetrical. Bonus points for extending it to more than just 2 strings.
I am doing this in python, but the language doesn't matter. This is for a load balancing problem I'm working on.
You can use the elements alternatively and use a letter of the longer string if necessary. You can determine whether an additional letter is possible with integer arithmetics: A fraction tells you how many letters come between each letter pair. You accumulate this fraction and use letters from the longer array as long as that accumulated fraction is larger than ½:
def intertwine(a, b):
""" Return a combination of string with fewest number of
consecutive elements from one string
"""
if len(b) > len(a):
return intertwine(b, a)
if not b:
return a
a = list(a)
b = list(b)
num = len(a) - len(b)
denom = len(b)
acc = 0
res = []
while a or b:
acc += num
while acc >= denom / 2:
if a: res += a.pop(0)
acc -= num
if a: res += a.pop(0)
if b: res += b.pop(0)
return "".join(res)
print intertwine("aaabaaa", "bbb") # "aababbaaba"
print intertwine("aaaaaaa", "b") # "aaabaaaa"
print intertwine("aaaaaa", "b") # "aaabaaa"
print intertwine("aa", "bbbbbb") # "bbabbabb"
print intertwine("", "bbbbbb") # "bbbbbb"
print intertwine("", "") # ""
import itertools
def intermix(*containers):
mix = []
for c in sorted(containers, key=lambda c: len(c)):
if len(c) >= len(mix):
bigger, smaller = c, mix
else:
bigger, smaller = mix, c
ratio, remainder = divmod(len(bigger), len(smaller) + 1)
chunk_sizes = (ratio + (1 if i < remainder else 0) for i in range(len(smaller) + 1))
chunk_offsets = itertools.accumulate(chunk_sizes)
off_start = 0
new_mix = []
for i, off in enumerate(chunk_offsets):
new_mix.extend(bigger[off_start:off])
if i == len(smaller):
break
new_mix.append(smaller[i])
off_start = off
mix = new_mix
return mix

Convert decimal to ternary(base3) in python

I am trying to make a decimal number ternary in a python function. My idea was to keep dividing until the quotient and remainder were equal, but I can't seem to get that to work. Here's my code:
l = 1
#problem code
def ternary(n):
e = n/3
q = n%3
e= n/3
q= e%3
print q
r = input("What number should I convert?: ")
k = bin(r)
v = hex(r)
i = oct(r)
print k+"(Binary)"
print v+"(Hex)"
print i+"(Octals)"
ternary(r)
l+=1
# Variables:
#l,r,k,v,i
#n,q,e
My idea was to keep dividing until the quotient and remainder were equal, but I can't seem to get that to work.
Yeah, something like that. Essentially, you want to keep dividing by 3, and collect the remainders. The remainders then make up the final number. In Python, you can use divmod to divide and collect the remainder.
def ternary (n):
if n == 0:
return '0'
nums = []
while n:
n, r = divmod(n, 3)
nums.append(str(r))
return ''.join(reversed(nums))
Examples:
>>> ternary(0)
'0'
>>> ternary(1)
'1'
>>> ternary(2)
'2'
>>> ternary(3)
'10'
>>> ternary(12)
'110'
>>> ternary(22)
'211'
You can also use the implementation of NumPy:
https://numpy.org/doc/stable/reference/generated/numpy.base_repr.html?highlight=base_repr#numpy.base_repr
Though, I agree that a function for ternary exclusively is faster.
import numpy as np
number=100 # decimal
ternary=np.base_repr(number,base=3)
print(ternary)
#10201
This can also be done with recursion.
def ternary(n):
e = n//3
q = n%3
if n == 0:
return '0'
elif e == 0:
return str(q)
else:
return ternary(e) + str(q)
More generally, you can convert to any base b (where 2<=b<=10) with the following recursive function.
def baseb(n, b):
e = n//b
q = n%b
if n == 0:
return '0'
elif e == 0:
return str(q)
else:
return baseb(e, b) + str(q)

Categories

Resources