Stably computing large quantites through recursion - python

I have two quantities a & b that are defined by recursion and through reference to another list of values x = [ x_1, x_2, ... x_N ], which will be an input to the program. The program will iterate over all the values in x and update a & b according to:
for n in range(1,N)
a[n] = a[n-1] * exp(+x[n]) + b[n-1] * exp(-x[n])
b[n] = b[n-1] * exp(+x[n]) + a[n-1] * exp(-x[n])
and starting values
a[0] = exp(+x[0])
b[0] = exp(-x[0])
The values in x are not big numbers (always <10) but N will be in the hundreds, and because of all the exponentials the final values of a & b will be very large. I'm worried that because of the form of the recursion where we are constantly multiplying exponentially large numbers with exponentially small ones and adding them this scheme will become quite numerically unstable.
Ideally I would calculate log(a) and log(b) instead to stop the values becoming too large. But because of the recursion scheme that's not possible, unless I compute something much messier like
log_a[n] = x[n] + log_a[n-1] + log( 1 + exp(-2*x[n] + log_b[n-1]-log_a[n-1] ) )
Is numerical stability something I am right to be concerned about here? And if so would something like the log based scheme help to stabilise it?

We can rewrite that first as:
for n in range(1,N)
a[n] = exp(log(a[n-1]) + x[n]) + exp(log(b[n-1]) - x[n])
b[n] = exp(log(b[n-1]) + x[n]) + exp(log(a[n-1]) - x[n]))
Then change our iteration variables:
for n in range(1,N)
log_a[n] = log(exp(log_a[n-1] + x[n]) + exp(log_b[n-1] - x[n]))
log_b[n] = log(exp(log_b[n-1] + x[n]) + exp(log_a[n-1] - x[n]))
Which can be computed more stably using np.logaddexp:
for n in range(1,N)
log_a[n] = np.logaddexp(log_a[n-1] + x[n], log_b[n-1] - x[n])
log_b[n] = np.logaddexp(log_b[n-1] + x[n], log_a[n-1] - x[n])
The implementation of logaddexp can be seen here

As far as I'm aware, all(?) recursive problems can be solved through dynamic programming. For example, the Fibonacci sequence could be expressed like so:
def fibo(n):
if n == 0:
return 0
elif n == 1:
return 1
return fibo(n-1) + fibo(n-2)
Or, iteratively:
n = 10
fibo_nums = [0, 1]
while len(fibo_nums) <= n:
fibo_nums.append(fibo_nums[-2] + fibo_nums[-1])
Presumably if you have a recursive problem you could perform a similar unpacking.

Related

return the only number in the range that is missing from the array

I am trying to use hash table to solve this question. The question description is: "Given an array nums containing n distinct numbers in the range [0, n], return the only number in the range that is missing from the array."
My approach so far is to import the dictionary and use it as a list. Then I am enumerating through the integer array that is given to me. So, for now, nums=[0,1,2,3,4,6]. I have to return number 5 as the missing number. After enumerating, I am trying to go over the items of the dictionary and see which number is missing. If the length of the v is none inside the index, then I will return this line int((((length * (length+1))/2) - sums))
from collections import defaultdict
class Solution(object):
def missingNumber(self, nums):
d = defaultdict(list)
length = len(nums)
sums = sum(nums)
for numbers, index in enumerate(nums):
d[index].append(numbers)
for k,v in d.items():
if len(v) == 0 :
return int((((length * (length+1))/2) - sums))
I am confused about how to show the if statement. Like how from the list it can be recognized that 5 is missing. Also, if there are more than 2 numbers are missing, then what approach will be the best to take? As if the example was: nums = [0,1,2,6,8]
Pardon for not having enough knowledge. I am just a beginner trying to practice questions everyday.
If you can use sum then no loop is required:
sum1 = sum(nums)
sum2 = sum(range(n))
missing_num = sum2 - sum1
Can we assume more than simply that the list contains indices with one missing? If, for example, we know that the list is sorted, we can get a logarithmic time solution using a binary search.
def bsearch(x: list[int]) -> int:
"""Return missing index in a sorted list."""
low, high = 0, len(x)
while low < high:
mid = (low + high) // 2
if x[mid] == mid:
# lower half have matching indices; explore upper
low = mid + 1
else:
# mid's value is too high; lower half has missing index
high = mid
return low
It exploits that below the missing value, i == x[i], but after the missing value, the array values are off by one, i == x[i] + 1. By testing the middle point in an interval, we can work out whether the missing element is to the left or right, and by doing this, we get a O(log n) solution.
If the elements are not sorted, you can of course sort them first and then do a binary search. A comparison sort would take O(n log n) and a bucket/radix sort O(n). There is nothing much good to say about first sorting the elements, then, as others have suggested, you can get the answer in O(n) without sorting.
I can try to explain that solution in a little more detail.
The sum of all numbers up to n is n * (n+1) // 2.
sum(range(n+1)) == n * (n+1) // 2
So
n*(n+1)//2 = 0 + 1 + 2 + ... + i-1 + i + i+1 + ... + n
The sum of the numbers that actually appear is sum(x), so if i is missing:
sum(x) = 0 + 1 + 2 + ... + i-1 + 0 + i+1 +... + n
Subtract the latter from the former and you get the missing value:
n*(n+1)//2: 0 + 1 + 2 + ... + i-1 + i + i+1 + ... + n
- sum(x): -(0 + 1 + 2 + ... + i-1 + 0 + i+1 + ... + n)
= 0 + 0 + 0 + ... + 0 + i + 0 + ... + 0
= i
so the solution is
def general_solution(x: list[int]) -> int:
return len(x) * (len(x) + 1) // 2 - sum(x)
Computing len(x) * (len(x) + 1) // 2 takes constant time, but sum(x) takes O(n), so the total running time is O(n).
If your array is sorted, the binary search approach will be much faster, but if the array is not sorted, a linear time solution isn't bad. There will certainly not be need for anything slower than O(n).
Hey this is my solution.
Subtract the sum of the elements in the list from the sum of the consecutive numbers you have (n * (n+1) / 2) and you will get the result.
class Solution:
def MissingNumber(self, nums):
l = len(nums)
total = l * (l + 1) / 2
sum_giv_list = sum(nums)
return total - sum_giv_list
You don't need a dict, you can do it in a for loop
for i in range(n)
if i not in nums:
return i
if you are not told what n is then you can do something like:
for i in range(nums[-1])
if i not in nums:
return i
if there are a possibility of more than one number missing then you can add them to a list before returning them
missing_nums = []
for i in range(nums[-1]):
if i not in nums:
missing_nums.append(i)
return missing_nums

Taylor series for log(x)

I'm trying to evaluate a Taylor polynomial for the natural logarithm, ln(x), centred at a=1 in Python. I'm using the series given on Wikipedia however when I try a simple calculation like ln(2.7) instead of giving me something close to 1 it gives me a gigantic number. Is there something obvious that I'm doing wrong?
def log(x):
n=1000
s=0
for i in range(1,n):
s += ((-1)**(i+1))*((x-1)**i)/i
return s
Using the Taylor series:
Gives the result:
EDIT: If anyone stumbles across this an alternative way to evaluate the natural logarithm of some real number is to use numerical integration (e.g. Riemann sum, midpoint rule, trapezoid rule, Simpson's rule etc) to evaluate the integral that is often used to define the natural logarithm;
That series is only valid when x is <= 1. For x>1 you will need a different series.
For example this one (found here):
def ln(x): return 2*sum(((x-1)/(x+1))**i/i for i in range(1,100,2))
output:
ln(2.7) # 0.9932517730102833
math.log(2.7) # 0.9932517730102834
Note that it takes a lot more than 100 terms to converge as x gets bigger (up to a point where it'll become impractical)
You can compensate for that by adding the logarithms of smaller factors of x:
def ln(x):
if x > 2: return ln(x/2) + ln(2) # ln(x) = ln(x/2 * 2) = ln(x/2) + ln(2)
return 2*sum(((x-1)/(x+1))**i/i for i in range(1,1000,2))
which is something you can also do in your Taylor based function to support x>1:
def log(x):
if x > 1: return log(x/2) - log(0.5) # ln(2) = -ln(1/2)
n=1000
s=0
for i in range(1,n):
s += ((-1)**(i+1))*((x-1)**i)/i
return s
These series also take more terms to converge when x gets closer to zero so you may want to work them in the other direction as well to keep the actual value to compute between 0.5 and 1:
def log(x):
if x > 1: return log(x/2) - log(0.5) # ln(x/2 * 2) = ln(x/2) + ln(2)
if x < 0.5: return log(2*x) + log(0.5) # ln(x*2 / 2) = ln(x*2) - ln(2)
...
If performance is an issue, you'll want to store ln(2) or log(0.5) somewhere and reuse it instead of computing it on every call
for example:
ln2 = None
def ln(x):
if x <= 2:
return 2*sum(((x-1)/(x+1))**i/i for i in range(1,10000,2))
global ln2
if ln2 is None: ln2 = ln(2)
n2 = 0
while x>2: x,n2 = x/2,n2+1
return ln2*n2 + ln(x)
The program is correct, but the Mercator series has the following caveat:
The series converges to the natural logarithm (shifted by 1) whenever −1 < x ≤ 1.
The series diverges when x > 1, so you shouldn't expect a result close to 1.
The python function math.frexp(x) can be used to advantage here to modify the problem so that the taylor series is working with a value close to one. math.frexp(x) is described as:
Return the mantissa and exponent of x as the pair (m, e). m is a float
and e is an integer such that x == m * 2**e exactly. If x is zero,
returns (0.0, 0), otherwise 0.5 <= abs(m) < 1. This is used to “pick
apart” the internal representation of a float in a portable way.
Using math.frexp(x) should not be regarded as "cheating" because it is presumably implemented just by accessing the bit fields in the underlying binary floating point representation. It isn't absolutely guaranteed that the representation of floats will be IEEE 754 binary64, but as far as I know every platform uses this. sys.float_info can be examined to find out the actual representation details.
Much like the other answer does you can use the standard logarithmic identities as follows: Let m, e = math.frexp(x). Then log(x) = log(m * 2e) = log(m) + e * log(2). log(2) can be precomputed to full precision ahead of time and is just a constant in the program. Here is some code illustrating this to compute the two similar taylor series approximations to log(x). The number of terms in each series was determined by trial and error rather than rigorous analysis.
taylor1 implements log(1 + x) = x1 - (1/2) * x2 + (1/3) * x3 ...
taylor2 implements log(x) = 2 * [t + (1/3) * t3 + (1/5) * t5 ...], where t = (x - 1) / (x + 1).
import math
import struct
_LOG_OF_2 = 0.69314718055994530941723212145817656807550013436025
def taylor1(x):
m, e = math.frexp(x)
log_of_m = 0
num_terms = 36
sign = 1
m_minus1_power = m - 1
for k in range(1, num_terms + 1):
log_of_m += sign * m_minus1_power / k
sign = -sign
m_minus1_power *= m - 1
return log_of_m + e * _LOG_OF_2
def taylor2(x):
m, e = math.frexp(x)
num_terms = 12
half_log_of_m = 0
t = (m - 1) / (m + 1)
t_squared = t * t
t_power = t
denominator = 1
for k in range(num_terms):
half_log_of_m += t_power / denominator
denominator += 2
t_power *= t_squared
return 2 * half_log_of_m + e * _LOG_OF_2
This seems to work well over most of the domain of log(x), but as x approaches 1 (and log(x) approaches 0) the transformation provided by x = m * 2e actually produces a less accurate result. So a better algorithm would first check if x is close to 1, say abs(x-1) < .5, and if so the just compute the taylor series approximation directly on x.
My answer is just using the Taylor series for In(x). I really hope this helps. It is simple and straight to the point.
enter image description here

Passing big arguments in change-making problem

I have problem with change-making problem algorithm.
My function coin_change_solutions works well with small numbers.
For example if we pass [1,10,25] as coins and 32 as S (change that we want to get) it will return [10,10,10,1,1]. Problem occurs when I want to pass bigger numbers as I want to operate on cents, not on dollars so that I have fixed-point arithmetic (it's a must because operating on floating-point arithmetic won't be a good idea later on).
So when I pass an array with all the denominations in cents [1,5,10,25,50,100,200,500,1000,2000,10000,50000] and 50000 as change my compiler stops and doesn't show any result.
Do you know what should I do so that the algorithm has high efficiency and can be passed all the nominals in cents?
def coin_change_solutions(coins, S):
# create an S x N table for memoization
N = len(coins)
sols = [[[] for n in range(N + 1)] for s in range(S + 1)]
for n in range(0, N + 1):
sols[0][n].append([])
# fill table using bottom-up dynamic programming
for s in range(1, S+1):
for n in range(1, N+1):
without_last = sols[s][n - 1]
if (coins[n - 1] <= s):
with_last = [list(sol) + [coins[n-1]] for sol in sols[s - coins[n - 1]][n]]
else:
with_last = []
sols[s][n] = without_last + with_last
x = min(sols[S][N], key=len)
return x
Not the solution to your query, but a better solution with less space:
dp = [0] + [float('inf') for i in range(S)]
for i in range(1, S+1):
for coin in coins:
if i - coin >= 0:
dp[i] = min(dp[i], dp[i-coin] + 1)
if dp[-1] == float('inf'):
return -1
return dp[-1]
Assume dp[i] is the fewest number of coins making up amount S, then for every coin in coins, dp[i] = min(dp[i - coin] + 1).
The time complexity is O(amount * coins.length) and the space complexity is O(amount).

How to find sum of cubes of the divisors for every number from 1 to input number x in python where x can be very large

Examples,
1.Input=4
Output=111
Explanation,
1 = 1³(divisors of 1)
2 = 1³ + 2³(divisors of 2)
3 = 1³ + 3³(divisors of 3)
4 = 1³ + 2³ + 4³(divisors of 4)
------------------------
sum = 111(output)
1.Input=5
Output=237
Explanation,
1 = 1³(divisors of 1)
2 = 1³ + 2³(divisors of 2)
3 = 1³ + 3³(divisors of 3)
4 = 1³ + 2³ + 4³(divisors of 4)
5 = 1³ + 5³(divisors of 5)
-----------------------------
sum = 237 (output)
x=int(raw_input().strip())
tot=0
for i in range(1,x+1):
for j in range(1,i+1):
if(i%j==0):
tot+=j**3
print tot
Using this code I can find the answer for small number less than one million.
But I want to find the answer for very large numbers. Is there any algorithm
for how to solve it easily for large numbers?
Offhand I don't see a slick way to make this truly efficient, but it's easy to make it a whole lot faster. If you view your examples as matrices, you're summing them a row at a time. This requires, for each i, finding all the divisors of i and summing their cubes. In all, this requires a number of operations proportional to x**2.
You can easily cut that to a number of operations proportional to x, by summing the matrix by columns instead. Given an integer j, how many integers in 1..x are divisible by j? That's easy: there are x//j multiples of j in the range, so divisor j contributes j**3 * (x // j) to the grand total.
def better(x):
return sum(j**3 * (x // j) for j in range(1, x+1))
That runs much faster, but still takes time proportional to x.
There are lower-level tricks you can play to speed that in turn by constant factors, but they still take O(x) time overall. For example, note that x // j == 1 for all j such that x // 2 < j <= x. So about half the terms in the sum can be skipped, replaced by closed-form expressions for a sum of consecutive cubes:
def sum3(x):
"""Return sum(i**3 for i in range(1, x+1))"""
return (x * (x+1) // 2)**2
def better2(x):
result = sum(j**3 * (x // j) for j in range(1, x//2 + 1))
result += sum3(x) - sum3(x//2)
return result
better2() is about twice as fast as better(), but to get faster than O(x) would require deeper insight.
Quicker
Thinking about this in spare moments, I still don't have a truly clever idea. But the last idea I gave can be carried to a logical conclusion: don't just group together divisors with only one multiple in range, but also those with two multiples in range, and three, and four, and ... That leads to better3() below, which does a number of operations roughly proportional to the square root of x:
def better3(x):
result = 0
for i in range(1, x+1):
q1 = x // i
# value i has q1 multiples in range
result += i**3 * q1
# which values have i multiples?
q2 = x // (i+1) + 1
assert x // q1 == i == x // q2
if i < q2:
result += i * (sum3(q1) - sum3(q2 - 1))
if i+1 >= q2: # this becomes true when i reaches roughly sqrt(x)
break
return result
Of course O(sqrt(x)) is an enormous improvement over the original O(x**2), but for very large arguments it's still impractical. For example better3(10**6) appears to complete instantly, but better3(10**12) takes a few seconds, and better3(10**16) is time for a coffee break ;-)
Note: I'm using Python 3. If you're using Python 2, use xrange() instead of range().
One more
better4() has the same O(sqrt(x)) time behavior as better3(), but does the summations in a different order that allows for simpler code and fewer calls to sum3(). For "large" arguments, it's about 50% faster than better3() on my box.
def better4(x):
result = 0
for i in range(1, x+1):
d = x // i
if d >= i:
# d is the largest divisor that appears `i` times, and
# all divisors less than `d` also appear at least that
# often. Account for one occurence of each.
result += sum3(d)
else:
i -= 1
lastd = x // i
# We already accounted for i occurrences of all divisors
# < lastd, and all occurrences of divisors >= lastd.
# Account for the rest.
result += sum(j**3 * (x // j - i)
for j in range(1, lastd))
break
return result
It may be possible to do better by extending the algorithm in "A Successive Approximation Algorithm for Computing the Divisor Summatory Function". That takes O(cube_root(x)) time for the possibly simpler problem of summing the number of divisors. But it's much more involved, and I don't care enough about this problem to pursue it myself ;-)
Subtlety
There's a subtlety in the math that's easy to miss, so I'll spell it out, but only as it pertains to better4().
After d = x // i, the comment claims that d is the largest divisor that appears i times. But is that true? The actual number of times d appears is x // d, which we did not compute. How do we know that x // d in fact equals i?
That's the purpose of the if d >= i: guarding that comment. After d = x // i we know that
x == d*i + r
for some integer r satisfying 0 <= r < i. That's essentially what floor division means. But since d >= i is also known (that's what the if test ensures), it must also be the case that 0 <= r < d. And that's how we know x // d is i.
This can break down when d >= i is not true, which is why a different method needs to be used then. For example, if x == 500 and i == 51, d (x // i) is 9, but it's certainly not the case that 9 is the largest divisor that appears 51 times. In fact, 9 appears 500 // 9 == 55 times. While for positive real numbers
d == x/i
if and only if
i == x/d
that's not always so for floor division. But, as above, the first does imply the second if we also know that d >= i.
Just for Fun
better5() rewrites better4() for about another 10% speed gain. The real pedagogical point is to show that it's easy to compute all the loop limits in advance. Part of the point of the odd code structure above is that it magically returns 0 for a 0 input without needing to test for that. better5() gives up on that:
def isqrt(n):
"Return floor(sqrt(n)) for int n > 0."
g = 1 << ((n.bit_length() + 1) >> 1)
d = n // g
while d < g:
g = (d + g) >> 1
d = n // g
return g
def better5(x):
assert x > 0
u = isqrt(x)
v = x // u
return (sum(map(sum3, (x // d for d in range(1, u+1)))) +
sum(x // i * i**3 for i in range(1, v)) -
u * sum3(v-1))
def sum_divisors(n):
sum = 0
i = 0
for i in range (1, n) :
if n % i == 0 and n != 0 :
sum = sum + i
# Return the sum of all divisors of n, not including n
return sum
print(sum_divisors(0))
# 0
print(sum_divisors(3)) # Should sum of 1
# 1
print(sum_divisors(36)) # Should sum of 1+2+3+4+6+9+12+18
# 55
print(sum_divisors(102)) # Should be sum of 2+3+6+17+34+51
# 114

What will be the complexity of this code for counting number of different BST(binary search tree)s from N nodes?

def count(n):
if n == 0 or n == 1:
return 1
else:
sum = 0
left = 0
right = 0
for x in range(1,n+1):
left = count(x-1)
right = count(n-x)
sum += left * right
return sum
I was reading this post and I wondered
if no of different binary search trees from n nodes is
(2n)! / ((n+1)! * n!)
from this post.
Then
what will be the time complexity ? O(n!) ?
what will be the recurrence relation ?
When you call count(n), it's calling each of count(0) to count(n-1) twice.
So I think you can write the recurrence like this:
T(n) = 2 * sum[T(0) upto T(n-1)] + nk where k represents the multiplication and summation part.
Now consider:
T(n+1) = 2 * sum[T(0) upto T(n)] + (n+1)k
= 2 * sum[T(0) upto T(n-1)] + 2T(n) + nk + k
= T(n) + 2T(n) + k
= 3T(n) + O(1)
Solving this, it appears to be of O(3^n) complexity.

Categories