Max product of 3 integers in an array - python

I am trying to solve a problem where I want to find the max product of any 3 integers in an array.
I tried my solution:
def maximumProduct(nums):
"""
:type nums: List[int]
:rtype: int
"""
list_of_ints = nums
t = sorted(list_of_ints[:4])
max_pos = t[2]
max_pos_2 = t[1]
max_pos_3 = t[0]
min_neg = 0
min_neg_2 = 0
for x in list_of_ints[3:]:
if x<0 and x< min_neg:
temp = min_neg
min_neg = x
min_neg_2 = temp
elif x<0 and x<min_neg_2:
min_neg_2 = x
if x>0 and x>max_pos:
temp = max_pos
max_pos = x
temp2 = max_pos_2
max_pos_2 = temp
max_pos_3 = temp2
elif x>0 and x>max_pos_2:
temp = max_pos_2
max_pos_2 = x
max_pos_3 = temp
elif x>0 and x>max_pos_3:
max_pos_3 = x
return max(max_pos*max_pos_2*max_pos_3, min_neg*min_neg_2*max_pos)
The above solution fails on input nums = [-1, -2, -3]. The expected output is -6, the output of the program is 0.
This is due to the initialization of min_neg and min_neg_1 to zero.
How to initialize to avoid this issue? I always have trouble setting up the right initializations.

Approach
To solve this kind of exercise efficiently, you need to recognize that there are only a few possible cases. Focus on the signs of the numbers and think what would be the sign of the result:
+ * + * + = + (good)
+ * + * - = - (bad)
+ * - * - = + (good)
- * - * - = - (bad)
anything * 0 = 0 (neutral)
So, if the list has both negative and negative numbers, the answer is either the product of the three largest numbers, or the product the two smallest (negative) numbers and the largest number (positive).
If this condition is not true, the answer must be the product of the largest numbers in the array.
O(n log (n)) solution
So, the answer has to take either the three last elements in the array after sorting, or the two first ones and the last one, and multiply them together. The simplest and most elegant way to do it is to first sort the list of numbers:
def maximum_product(nums): # O(n log(n)) solution
nums.sort()
assert len(nums) >= 3 # assume the input has been validated
a1 = nums[-1] * nums[-2] * nums[-3]
a2 = nums[0] * nums[1] * nums[-1]
return max(a1, a2)
O(n) solution
However, you can also find the maximal three and the minimal two numbers in O(n) time. One efficient approach how to find the maximum N numbers in a container is to keep a heap of size N while iterating through it. At the end, the heap contains the answer: a partially sorted list of N largest elements.
Python module heapq offers convenient API for this: the functions nlargest() and nsmallest(). So here we go:
import heapq
def maximum_product(nums): # O(n) solution
assert len(nums) >= 3 # assume the input has been validated
max3 = heapq.nlargest(3, nums)
min2 = heapq.nsmallest(2, nums)
a1 = max3[0] * max3[1] * max3[2]
a2 = min2[0] * min2[1] * max(max3)
return max(a1, a2)

You can initialize to negative infinity: min_neg = float('-inf')

You can use itertools.combinations to make doing it very easy. What you're calling an "array" is referred to as a "list" in Python, by-the-way.
from itertools import combinations
def maximum_product(nums):
return max(trio[0] * trio[1] * trio[2] for trio in combinations(nums, 3))
nums = [-1, -2, -3, 9]
print(maximum_product(nums)) # -> 54
This could be generalized to determine the maximum product of N integers by (also) using functools.reduce() to compute the product of N items:
from functools import reduce
from itertools import combinations
def maximum_product(nums, group_size):
return max(reduce(lambda a, b: a*b, nums, 1)
for group in combinations(nums, group_size))
nums = [-1, -2, -3, 9]
print(maximum_product(nums, 3)) # -> 54
Of course, this generality will slow execution down a bit...

Try this:
arr = sorted(list_of_ints)
def getMax(t):
maxP = t[0] * t[1] * t[2]
i = 0
while t[i] > -1:
i += 1
maxN = t[i] * t[i + 1] * t[0]
return max(maxP, maxN)
print(getMax(arr))

I tried kfx's O(n) solution but it was not taking into account possible negative ints for me. I added a few lines to adjust for that, and also two more possible combinations and it worked. Correct me if I am wrong but I think this is still O(n) notation right?
def maximum_product(nums):
max3 = heapq.nlargest(3, nums)
min3 = heapq.nsmallest(3, nums)
a1 = max3[0] * max3[1] * max3[2]
a2 = min3[0] * min3[1] * min3[2]
a3 = min3[0] * max3[0] * max3[1]
a4 = min3[0] * min3[1] * max3[0]
if abs(min(a1, a2, a3, a4)) > abs(max(a1, a2, a3, a4)):
return min(a1, a2, a3, a4)
else:
return max(a1, a2, a3, a4)

Related

minimal absolute value of the difference between A[i] and B[i] (array A is strictly increasing, array B is strictly decreasing)

Given two sequences A and B of the same length: one is strictly increasing, the other is strictly decreasing.
It is required to find an index i such that the absolute value of the difference between A[i] and B[i] is minimal. If there are several such indices, the answer is the smallest of them. The input sequences are standard Python arrays. It is guaranteed that they are of the same length. Efficiency requirements: Asymptotic complexity: no more than the power of the logarithm of the length of the input sequences.
I have implemented index lookup using the golden section method, but I am confused by the use of floating point arithmetic. Is it possible to somehow improve this algorithm so as not to use it, or can you come up with a more concise solution?
import random
import math
def peak(A,B):
def f(x):
return abs(A[x]-B[x])
phi_inv = 1 / ((math.sqrt(5) + 1) / 2)
def cal_x1(left,right):
return right - (round((right-left) * phi_inv))
def cal_x2(left,right):
return left + (round((right-left) * phi_inv))
left, right = 0, len(A)-1
x1, x2 = cal_x1(left, right), cal_x2(left,right)
while x1 < x2:
if f(x1) > f(x2):
left = x1
x1 = x2
x2 = cal_x1(x1,right)
else:
right = x2
x2 = x1
x1 = cal_x2(left,x2)
if x1 > 1 and f(x1-2) <= f(x1-1): return x1-2
if x1+2 < len(A) and f(x1+2) < f(x1+1): return x1+2
if x1 > 0 and f(x1-1) <= f(x1): return x1-1
if x1+1 < len(A) and f(x1+1) < f(x1): return x1+1
return x1
#value check
def make_arr(inv):
x = set()
while len(x) != 1000:
x.add(random.randint(-10000,10000))
x = sorted(list(x),reverse = inv)
return x
x = make_arr(0)
y = make_arr(1)
needle = 1000000
c = 0
for i in range(1000):
if abs(x[i]-y[i]) < needle:
c = i
needle = abs(x[i]-y[i])
print(c)
print(peak(x,y))
Approach
The poster asks about alternative, simpler solutions to posted code.
The problem is a variant of Leetcode Problem 852, where the goal is to find the peak index in a moutain array. We convert to a peak, rather than min, by computing the negative of the abolute difference. Our aproach is to modify this Python solution to the Leetcode problem.
Code
def binary_search(x, y):
''' Mod of https://walkccc.me/LeetCode/problems/0852/ to use function'''
def f(m):
' Absoute value of difference at index m of two arrays '
return -abs(x[m] - y[m]) # Make negative so we are looking for a peak
# peak using binary search
l = 0
r = len(arr) - 1
while l < r:
m = (l + r) // 2
if f(m) < f(m + 1): # check if increasing
l = m + 1
else:
r = m # was decreasing
return l
Test
def linear_search(A, B):
' Linear Search Method '
values = [abs(ai-bi) for ai, bi in zip(A, B)]
return values.index(min(values)) # linear search
def make_arr(inv):
random.seed(10) # added so we can repeat with the same data
x = set()
while len(x) != 1000:
x.add(random.randint(-10000,10000))
x = sorted(list(x),reverse = inv)
return x
# Create data
x = make_arr(0)
y = make_arr(1)
# Run search methods
print(f'Linear Search Solution {linear_search(x, y)}')
print(f'Golden Section Search Solution {peak(x, y)}') # posted code
print(f'Binary Search Solution {binary_search(x, y)}')
Output
Linear Search Solution 499
Golden Section Search Solution 499
Binary Search Solution 499

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

Counting number of ways I can have unique numbers in array

I am trying to find the number of ways to construct an array such that consecutive positions contain different values.
Specifically, I need to construct an array with elements such that each element 1 between and k , all inclusive. I also want the first and last elements of the array to be 1 and x.
Complete problem statement:
Here is what I tried:
def countArray(n, k, x):
# Return the number of ways to fill in the array.
if x > k:
return 0
if x == 1:
return 0
def fact(n):
if n == 0:
return 1
fact_range = n+1
T = [1 for i in range(fact_range)]
for i in range(1,fact_range):
T[i] = i * T[i-1]
return T[fact_range-1]
ways = fact(k) / (fact(n-2)*fact(k-(n-2)))
return int(ways)
In short, I did K(C)N-2 to find the ways. How could I solve this?
It passes one of the base case with inputs as countArray(4,3,2) but fails for 16 other cases.
Let X(n) be the number of ways of constructing an array of length n, starting with 1 and ending in x (and not repeating any numbers). Let Y(n) be the number of ways of constructing an array of length n, starting with 1 and NOT ending in x (and not repeating any numbers).
Then there's these recurrence relations (for n>1)
X(n+1) = Y(n)
Y(n+1) = X(n)*(k-1) + Y(n)*(k-2)
In words: If you want an array of length n+1 ending in x, then you need an array of length n not ending in x. And if you want an array of length n+1 not ending in x, then you can either add any of the k-1 symbols to an array of length n ending in x, or you can take an array of length n not ending in x, and add any of the k-2 symbols that aren't x and don't repeat the last value.
For the base case, n=1, if x is 1 then X(1)=1, Y(1)=0 otherwise, X(1)=0, Y(1)=1
This gives you an O(n)-time method of computing the result.
def ways(n, k, x):
M = 10**9 + 7
wx = (x == 1)
wnx = (x != 1)
for _ in range(n-1):
wx, wnx = wnx, wx * (k-1) + wnx*(k-2)
wnx = wnx % M
return wx
print(ways(100, 5, 2))
In principle you can reduce this to O(log n) by expressing the recurrence relations as a matrix and computing the matrix power (mod M), but it's probably not necessary for the question.
[Additional working]
We have the recurrence relations:
X(n+1) = Y(n)
Y(n+1) = X(n)*(k-1) + Y(n)*(k-2)
Using the first, we can replace the Y(_) in the second with X(_+1) to reduce it down to a single variable. Then:
X(n+2) = X(n)*(k-1) + X(n+1)*(k-2)
Using standard techniques, we can solve this linear recurrence relation exactly.
In the case x!=1, we have:
X(n) = ((k-1)^(n-1) - (-1)^n) / k
And in the case x=1, we have:
X(n) = ((k-1)^(n-1) - (1-k)(-1)^n)/k
We can compute these mod M using Fermat's little theorem because M is prime. So 1/k = k^(M-2) mod M.
Thus we have (with a little bit of optimization) this short program that solves the problem and runs in O(log n) time:
def ways2(n, k, x):
S = -1 if n%2 else 1
return ((pow(k-1, n-1, M) + S) * pow(k, M-2, M) - S*(x==1)) % M
could you try this DP version: (it's passed all tests) (it's inspired by #PaulHankin and take DP approach - will run performance later to see what's diff for big matrix)
def countArray(n, k, x):
# Return the number of ways to fill in the array.
big_mod = 10 ** 9 + 7
dp = [[1], [1]]
if x == 1:
dp = [[1], [0]]
else:
dp = [[1], [1]]
for _ in range(n-2):
dp[0].append(dp[0][-1] * (k - 1) % big_mod)
dp[1].append((dp[0][-1] - dp[1][-1]) % big_mod)
return dp[1][-1]

Is there another way to find item n in this recursive sequence?

The sequence is:
an = an-1 + (2 * an-2)
a0 = 1, a1= 1. Find a100
The way I did it is making a list.
#List 'a' with a0 = 1 , a1 = 1.
a = [1,1]
#To get the a100, implement 'i' as the index value of the list.
for i in range (2,101):
x = a[i-1] + (2 * a[i-2])
print( str(len(a)) + (": ") + str(x))
#Save new value to list
a.append(x)
Is there another way to do this where you can just directly get the value of a100? Or the value of a10000.. it will take up so much memory.
For this specific case, the sequence appears to be known as the Jacobsthal sequence. Wikipedia gives a closed form expression for a_n that can be expressed as follows:
def J(n):
return (2**(n+1) - (1 if n % 2 else -1))//3
Slightly more generally, you can use fast matrix exponentiation to find a specific value of a_n in O(log n) matrix operations. The approach here is a slight modification of this.
import numpy as np
def a(n):
mat = np.array([[1, 2], [1, 0]], dtype=object) # object for large integers
return np.linalg.matrix_power(mat, n)[0,0]
Here is the value for a_1000:
7143390714575115472989500327066678737076032078036890716291669255802340340832907483287989192104639054183964486117020978834580968571282093623989718383132383202623045183216153990280716403374914094585302788102030983322387960844932511706110362630718041943047464318457694778440286554435082924558137112046251
This recurrence relation has a closed form solution:
a = lambda n: (2**(n+1) + (-1)**n)//3
Then
a(0) == 1
a(1) == 1
a(2) == 3
...
Use Wolfram Alpha solve for the closed form solution.
For a more general solution, sympy's rsolve can generate a formula for linear recurrences. And then use substitution to find particular values.
from sympy import rsolve, Function, symbols
f = Function('f')
n = symbols('n', integer=True)
T = f(n) - f(n - 1) - 2 * f(n - 2)
sol = rsolve(T, f(n), {f(0): 1, f(1): 1})
print(sol.factor())
for k in range(6):
print(f'a[{10**k}] = {sol.subs({n:10**k})}')
This finds the formula: ((-1)**n + 2*2**n)/3 and substituting various values gives:
a[1] = 1
a[10] = 683
a[100] = 845100400152152934331135470251
a[1000] = 7143390714575115472989500327066678737076032078036890716291669255802340340832907483287989192104639054183964486117020978834580968571282093623989718383132383202623045183216153990280716403374914094585302788102030983322387960844932511706110362630718041943047464318457694778440286554435082924558137112046251
a[10000] = 13300420779205055899224947751223900558823312212574616365680059665686292553481297754613307789357463065266220752948806082847704327566275854078395857288064215971903820031195863017843497700844039930347033391278795541028339072307078736457006049910726416592060326596558672835961088838567081045539649268371274925376816731095916294031173247751323635481912358774462877183753093841891253840488152356727760984122637587639312975932940560640357511880709747618222262691017043766353735428453489979600223956211100972865182186443850404115054687605329465453071585497122508186691535256991501267222976387636433705286400943222614410489725426171396919846079518533884638490449629415374679171890883668485549192847140249201910928687618755494267749463781127049374279769561549759200832570764870138287994839741197500087328573494472227205070621546774178994858997503894208562707691159300991409504210074059830342802209213468621093971730976504006937230561044048029975244677676707947087336124281517272447267049737611904634607637370045500833604005013228174598706158078702963192048604263495032226147988471602982108251173897742022519137359868942131422329103081800375446624970338827853981873988860876269047918349845673238184625284288814399599917924440538912558558685095521850114849105048496522741529593155873907738282168861316542080131736118854643798317265443020838956090639908522753418790270855651099392460347365053921743882641323846748271362887055383912692879736402269982104388805781403942200602501882277026496929598476838303527006808207298214407168983217160516849324232198998893837958637097759081249712999519344381402467576288757211476207860932148655897231556293513976121900670048618498909700385756334067235325208259649285799693889564105871362639412657210097186118095746465818754306322522134720983321447905340926047485500603884544957480384983947611769143791817076603055269994974019086721023722205420067991783904156229025970272783748933896591684108429045765889012975813584862160062970831282169566933785351515891836917604484599090827358327607311145704700506065400164526586785514617302254188281302685535172938965970009784445593131997924161090875584262602248970534271757827918474036922817159666073457645479797721100990086996148246631809842046103645478455250800241851505149187576887740797874187195112987924800865762440512367759907023068198581038345298256830912964615391929510632144672034080214910330858779357159414245558929061170945822567007313514409276959727327732103102944890874437957354081499958646666151187821572015407908429716866090505450005466559490856410166587392640154829574782514412057571343645656039081553195235917082324370960357975081345975714019208241045008362225535513352731779100379038105003677818345932796086474225126766610787543447696005152433715459704967280220123536564742545543604882702212692308056024281175802607700426526000495235781464187268985316355546978912530579053491968145752746720495213034211965438416298865678974339803258684849814383125421063166939821410053665460303868944551299858094210708807124261007787849536528397806251

Better approach to find Nth term of the sequence

Given :
I : a positive integer
n : a positive integer
nth Term of sequence for input = I :
F(I,1) = (I * (I+1)) / 2
F(I,2) = F(I,1) + F(I-1,1) + F(I-2,1) + .... F(2,1) + F(1,1)
F(I,3) = F(I,2) + F(I-1,2) + F(I-2,2) + .... F(2,2) + F(2,1)
..
..
F(I,n) = F(I,n-1) + F(I-1,n-1) + F(I-2,n-1) + .... F(2,n-1) + F(1,n-1)
nth term --> F(I,n)
Approach 1 : Used recursion to find the above :
def recursive_sum(I, n):
if n == 1:
return (I * (I + 1)) // 2
else:
return sum(recursive_sum(j, n - 1) for j in range(I, 0, -1))
Approach 2 : Iteration to store reusable values in a dictionary. Used this dictionary to get the nth term.:
def non_recursive_sum_using_data(I, n):
global data
if n == 1:
return (I * (I + 1)) // 2
else:
return sum(data[j][n - 1] for j in range(I, 0, -1))
def iterate(I,n):
global data
data = {}
i = 1
j = 1
for i in range(n+1):
for j in range(I+1):
if j not in data:
data[j] = {}
data[j][i] = recursive_sum(j,i)
return data[I][n]
The recursion approach is obviously not efficient due to maximum recursion depth. Also the next approach's time and space complexity will be poor.
Is there better way to recurse ? or a different approach than recursion ?
I am curious if we can find a formula for nth term.
You could just cache your recursive results:
from functools import lru_cache
#lru_cache(maxsize=None)
def recursive_sum(I, n):
if n == 1:
return (I * (I + 1)) // 2
return sum(recursive_sum(j, n - 1) for j in range(I, 0, -1))
That way you can get the readability and brevity of the recursive approach without most of the performance issues since the function is only called once for each argument combination (I, n).
Using the usual binomial(n,k) = n!/(k!*(n-k)!), you have
F(I,n) = binomial(I+n, n+1).
Then you can choose the method you like most to compute binomial coefficients.
Here an example:
def binomial(n, k):
numerator = denominator = 1
t = max(k, n-k)
for low,high in enumerate(range(t+1, n+1), 1):
numerator *= high
denominator *= low
return numerator // denominator
def F(I,n): return binomial(I+n, n+1)
The formula for the nth term of the sequence is the one you have already mentioned.
Also rightly so you have identified that it will lead to an inefficient algorithm and stack overflow.
You can look into dynamic programming approach where u calculate F(I,N) just once and just reuse the value.
For example this is how the fibonacci seq is calculated.
[just-example] https://www.geeksforgeeks.org/program-for-nth-fibonacci-number/
You need to find the same pattern and cache the value
I have an example for this here in this small code written in golang
https://play.golang.org/p/vRi-QMj7z2v
the standard DP
One can do a (tiny) bit of math to rewrite your function:
F(i,n) = sum_{k=0}^{i-1} F(i-k, n-1) = sum_{k=1}^{i} F(k, n-1)
Now notice, that if you consider a matrix F_{ixn}, to compute F(i,n) we just need to add the elements of the previous column.
x----+---
| + |
|----+ |
|----+-F(i,n)
We conclude that we can build the first layer (aka column). Then the second one. And so forth until we get to the n-th layer.
Finally we take the last element of our final layer which is F(i,n)
The computation time is about O(I*n)
More math based but faster
An other way is to consider our layer as a vector variable X.
We can write the recurrence relation as
X_n = MX_{n-1}
where M is a triangular matrix with 1 in the lower part.
We then want to compute the general term of X_n so we want to compute M^n.
By following Yves Daoust
(I just copy from the link above)
Coefficients should be indiced _{n+1} and _n, but here it is _1 and '' for readability
Moreover the matrix is upper triangular but we can just take the transpose afterwards...
a_1 b_1 c_1 d_1 1 1 1 1 a b c d
a_1 b_1 c_1 = 0 1 1 1 * 0 a b c
a_1 b_1 0 0 1 1 0 0 a b
a_1 0 0 0 1 0 0 0 a
by going from last row to first:
a = 1
from b_1 = a+b = 1 + b = n, b = n
from c_1 = a+b+c = 1+n+c, c = n(n+1)/2
from d_1 = a+b+c+d = 1+n+n(n+1)/2 +d, d = n(n+1)(n+2)/6
I have not proved it but I hint that e_1 = n(n+1)(n+2)(n+3)/24 (so basically C_n^k)
(I think the proof lies more in the fact that F(i,n) = F(i,n-1) + F(i-1,n) )
More generally instead of taking variables a,b,c... but X_n(0), X_n(1)...
X_n(0) = 1
X_n(i) = n*...*(n+i-1) / i!
And by applying recusion for computing X:
X_n(0) = 1
X_n(i) = X_n(i-1)*(n+i-1)/i
Finally we deduce F(i,n) as the scalar product Y_{n-1} * X_1 where Y_n is the reversed vector of X_n and X_1(n) = n*(n+1)/2
from functools import lru_cache
#this is copypasted from schwobaseggl
#lru_cache(maxsize=None)
def recursive_sum(I, n):
if n == 1:
return (I * (I + 1)) // 2
return sum(recursive_sum(j, n - 1) for j in range(I, 0, -1))
def iterative_sum(I,n):
layer = [ i*(i+1)//2 for i in range(1,I+1)]
x = 2
while x <= n:
next_layer = [layer[0]]
for i in range(1,I):
#we don't need to compute all the sum everytime
#take the previous sum and add it the new number
next_layer.append( next_layer[i-1] + layer[i] )
layer = next_layer
x += 1
return layer[-1]
def brutus(I,n):
if n == 1:
return I*(I+1)//2
X_1 = [ i*(i+1)//2 for i in range(1, I+1)]
X_n = [1]
for i in range(1, I):
X_n.append(X_n[-1] * (n-1 + i-1) / i )
X_n.reverse()
s = 0
for i in range(0, I):
s += X_1[i]*X_n[i]
return s
def do(k,n):
print('rec', recursive_sum(k,n))
print('it ', iterative_sum(k,n))
print('bru', brutus(k,n))
print('---')
do(1,4)
do(2,1)
do(3,2)
do(4,7)
do(7,4)

Categories