Is there a faster way to solve the following problem? - python

A is a mn matrix
B is a nn matrix
I want to return matrix C of size m*n such that:
In python it could be like below
for i in range(m):
for j in range(n):
C[i][j] = 0
for k in range(n):
C[i][j] += max(0, A[i][j] - B[j][k])
this runs on O(m*n^2)
if A[i][j] - B[j][k] is always > 0 it could easily be improved as
C[i][j] = n*A[i][j] - sum(B[j])
but it is possible to improve as well when there are cases of A[i][j] - B[j][k]< 0 ? I think some divide and conquer algorithms might help here but I am not familiar with them.

For each j, You can sort each column B[j][:] and compute cumulative sums.
Then for a given A[i][j] you can find the sum of B[j][k] that are larger than A[i][j] in O(log n) time using binary search. If there's x elements of B[j][:] that are greater than A[i][j] and their sum is S, then C[i][j] = A[i][j] * x - S.
This gives you an overall O((m+n)n log n) time algorithm.

I would look on much simpler construct and go from there..
lets say the max between 0 and the addition wasn't there.
so the answer would be : a(i,j)n - sum(b(j,)
on this you could just go linearly by sum each vector and erase it from a(i,j)n
and because you need sum each vector in b only once per j it can be done in max(mn,nn)
now think about simple solution for the max problem...
if you would find which elements in b(j,) is bigger than a(i,j) you could just ignore their sum and substract their count from the multipication of a(i,j)
All of that can be done by ordering the vector b(j,) by size and make a summing array to each vector from the biggest to lowest (it can be done in nnlog(n) because you order each b(j,) vector once)
then you only need to binary search where is the a(i,j) in the ordered vector and take the sum you already found and subtract it from a(i,j) * the position you found in the binary search.
Eventually you'll get O( max( mnlog(n),nnlog(n) ) )
I got for you also the implementation:
import numpy as np
M = 4
N = 7
array = np.random.randint(100, size=(M,N))
array2 = np.random.randint(100, size=(N,N))
def matrixMacossoOperation(a,b, N, M):
cSlow = np.empty((M,N))
for i in range(M):
for j in range(N):
cSlow[i][j] = 0
for k in range(N):
cSlow[i][j] += max(0, a[i][j] - b[j][k])
for i in range(N):
b[i].sort()
sumArr = np.copy(b)
for j in range(N):
for i in range(N - 1):
sumArr[j][i + 1] += sumArr[j][i]
c = np.empty((M,N))
for i in range(M):
for j in range(N):
sumIndex = np.searchsorted(b[j],a[i][j])
if sumIndex == 0:
c[i][j] = 0;
else:
c[i][j] = ((sumIndex) * a[i][j]) - sumArr[j][sumIndex - 1]
print(c)
assert(np.array_equal(cSlow,c))
matrixMacossoOperation(array,array2,N,M)

Related

Problem with comparing Merge and Insertion Sort

Im trying to run insertion sort and merge sort and plot them. Im taking the time for 5 different N and plot them. I want to do this three times such that Insertion time < merge time, Insertion time = merge time and Insertion time > merge time. However, No matter what I set as N, Insertion sort is always much faster. This is my output for N = 5000
N Values: [1, 1001, 2001, 3001, 4001]
Merge Sort: [0.005, 11.198, 21.965, 35.996, 49.971000000000004]
Insertion Sort: [0.002, 0.268, 0.545, 0.9129999999999999, 1.177]
I have tried different N up to like 10000000 and merge sort is always slower. What am I missing here?
def insertion_sort(array):
start_time = datetime.datetime.now()
for j in range(1, len(array)):
key = array[j]
i = j - 1
while i >= 0 and array[i] > key:
array[i + 1] = array[i]
i -= 1
array[i + 1] = key
time_diff = datetime.datetime.now() - start_time
return time_diff.total_seconds() * 1000
def merge_sort(arr, p, r):
start_time = datetime.datetime.now()
if p < r:
m = (p + (r - 1)) // 2
merge_sort(arr, p, m)
merge_sort(arr, m + 1, r)
merge(arr, p, m, r)
time_diff = datetime.datetime.now() - start_time
return time_diff.total_seconds() * 1000
def merge(arr, p, q, r):
n1 = q - p + 1
n2 = r - q
L = [0] * (n1 + 1)
R = [0] * (n2 + 1)
for i in range(0, n1):
L[i] = arr[p + i]
for j in range(0, n2):
R[j] = arr[q + 1 + j]
i = 0
j = 0
k = p
while i < n1 and j < n2:
if L[i] <= R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
while i < n1:
arr[k] = L[i]
i += 1
k += 1
while j < n2:
arr[k] = R[j]
j += 1
k += 1
return arr
x, y1, y2 = [], [], []
N = 5000
for i in range(1, N, N // 5):
array = [j for j in range(i)]
array = array[::-1] # Array in reversed order
x.append(i)
y1.append(merge_sort(array, 0, len(array) - 1))
y2.append(insertion_sort(array))
What am I missing here?
Your test is not correct, as you don't provide the same array order to the two sorting algorithms.
As the first algorithm sorts the array (in-place), the second gets a sorted array.
To make a fair comparison, make sure to make a copy of the original array, e.g. using [:]:
y1.append(merge_sort(array[:], 0, len(array) - 1))
y2.append(insertion_sort(array[:]))
And now the results will show what you really expected.
Insertion sort runs very fast on sorted arrays. It just does n comparisons, and that's it.
So, a question for you: Why is insertion sort called with a sorted array in your code?
Hint: Try changing the order of these two lines and see how the running times change:
y1.append(merge_sort(array, 0, len(array) - 1))
y2.append(insertion_sort(array))
You're missing several significant factors:
Algorithmic complexity is a ratio as N approaches infinity. You're nowhere near infinity. :-) Some algorithms have a high overhead, such that their efficiencies don't begin to dominate execution time until you get to much larger lists.
If you want to see these efficiency effects, you have to efficiently implement the algorithms. Your code has a lot of superfluous overhead, especially in the merge sort. I recommend that you research a better implementation, as you're doing some extra copying and list building that adds nothing to the final result.
Whatever you choose to implement, you need to research its properties. As you can see from the raw figures and the graph, both of your functions are still dependent most on the linear and constant components of the implementation, and have no yet reached the parts of the curve dominated by the N^2 and N log N terms.

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]

Converting SimpleCluster1D pseudo code to python

I refer to the dissertation written by Marcel R. Ackermann found https://d-nb.info/100345531X/34 . In the dissertation, Marcel wrote a pseudo-code for optimal 1-Dimensional K-Median algorithm. It is shown as such:
pseudo-code for optimal K-Median
I tried to convert the code into python, as shown below:
import math
import statistics
def cost(arr, median):
cost = 0
for i in range(len(arr)):
cost = cost + abs(arr[i] - median)
return cost
def simpleCluster1D(arr, k):
n = len(arr)
B = [[0] * k for i in range(n)]
C = [[0] * k for i in range(n)]
for i in range(k):
c = statistics.median(arr[:i+1])
B[i][0] = cost(arr[:i+1], c)
C[i][0] = c
for j in range(1, k):
for i in range(j, n):
B[i][j] = math.inf
C[i][j] = []
for t in range (j, i+1):
c = statistics.median(arr[t:i+1])
b = B[t-1][j-1] + cost(arr[t:i+1],c)
if b < B[i][j]:
B[i][j] = b
tmp = C[t-1][j-1]
C[i][j] = [C[t-1][j-1]] + [c]
return C[n-1][k-1]
However, the results i obtained is not intuitive.
For example, when
arr = [50,60,70,80]
k = 2
simpleCluster1D(arr, k)
The result is [0,80], which is wrong. The answer should be [55,75] or [50,70].
I don't know where I have gone wrong.
I am wondering if anyone can help me with this conversion? I am a little confused as to the declaration of the array C - column 1 of the array contains the median, and column 2 contains a list in each array index. How do I do that?
Also, are the libraries/packages available online for R/Python (e.g flexclust in R and pyclustering in Python) already has a built-in optimal 1-D solver? I know that for d >1, it is impossible to achieve optimal result and thus heuristics are used to obtain local optimal solution. Which is why I concluded that these libraries will also solve 1-D problems with heuristics and hence answer is not deterministic. Am I right to come to that conclusion?
I don't know where I have gone wrong.
You haven't. The error is in the dissertation; the line
1: for i = 1,2,...,k do
has to be
1: for i = 1,2,...,n do
- otherwise the rows from k+1 to n of the arrays B and C aren't fully initialized.

Index out of range error sieving B-smooth numbers with logarithms

So I have been trying to implement the quadratic sieve and I did step 1 which is(in code):
f = 44
b = ceil(sqrt(n))
factorBase = [i for i in genPrimes(f) if jacobi(n, i) == 1]
t = [modInverse(n, p) for p in factorBase]
sol1 = [(t[i] - b) % factorBase[i] for i in range(len(t))]
sol2 = [(-t[i] - b) % factorBase[i] for i in range(len(t))]
l = [ln(p) for p in factorBase]
size = 60
Here genPrimes() is Sieve of Eratosthenes, jacobi checks if n is quadratic residue mod i, and modInverse is Tonelli-Shanks algorithm.
The next step in the quadratic sieve is this:
Initialize a sieve array to 0's. For each odd prime p in the factor
base, add l[p] to the locations sol1[p] + ip and soln[p] + ip of the sieve array, for
i = 0, 1, 2,... For the prime p = 2, sieve only with sol1.
Or as explained here:
Then I have to add values from 𝑙𝑝 to sieving array to positions π‘ π‘œπ‘™1[𝑗]+π‘–βˆ—factor_base[j] and
π‘ π‘œπ‘™1[𝑗]+π‘–βˆ— factor_base[j], where 0≀𝑖≀ size and 0≀𝑗≀|factor_base|. And for prime 𝑝=2 add 𝑙𝑝 only to
positions with sol1.
Now say these were my lists:
sieveArray = [0 for i in range(60)]
factorBase = [2,3,7,17,23,29,37,41]
sol1 = [0,0,2,13,11,26,10,28]
sol2 = [0,1,5,14,8,10,17,26]
l = [0.69,1.1,1.95,2.83,3.14,3.37,3.61,3.71] # logs of factorbase rounded to 2 decimals
I should, following what was said above get this new list:
sieveArray = [1.79,1.1,2.64,1.1,1.79,1.95,1.79,1.1,3.83,3.05,8.77,3.14,3.74,3.93,3.52,1.1,3.74,3.61,1.79,3.0
5,0.69,1.1,1.79,1.95,1.79,1.1,9.72,1.1,5.5,0.0,6.57,7.07,0.69,3.05,4.93,0.0,1.79,3.05,0.69,4.47
,3.74,0.0,1.79,1.1,2.64,1.1,1.79,8.39,4.62,1.1,0.69,3.05,1.79,0.0,10.49,4.47,0.69,4.24,3.74,0.0]
However whenever I try to program I mess up horribly and index out of range errors. Here is my failed code:
for j in factorBase:
if j != 2:
for i in range(0, size):
try:
sieveArray[sol1[j] + (i * factorBase[j])] += l[j]
sieveArray[sol2[j] + (i * factorBase[j])] += l[j]
except:
continue
#Didn't implement case for 2 since I didn't know what I was doing.
Can someone please explain to me what I am doing wrong and how I can get expected results. Also If you want the actual explanation on what the step is go to https://pdfs.semanticscholar.org/5c52/8a975c1405bd35c65993abf5a4edb667c1db.pdf page 6(qs algorithm) step 2 or https://math.stackexchange.com/questions/183183/quadratic-sieve-algorithm?rq=1.
Error when there are no try-except statements:
sieveArray[sol1[j] + (i * factorBase[j])] += j
IndexError: list index out of range
Note: No Index Out of Range error because of the try-except but the code doesn't work properly at all still.

Count all sub-arrays having sum divisible by k

While I was studying for interviews, I found this question and solution on GeeksForGeeks, but don't understand the solution.
What it says is
Let there be a subarray (i, j) whose sum is divisible by k
sum(i, j) = sum(0, j) - sum(0, i-1)
Sum for any subarray can be written as q*k + rem where q is a
quotient and rem is remainder Thus,
sum(i, j) = (q1 * k + rem1) - (q2 * k + rem2)
sum(i, j) = (q1 - q2)k + rem1-rem2
We see, for sum(i, j) i.e. for sum of any subarray to be
divisible by k, the RHS should also be divisible by k.
(q1 - q2)k is obviously divisible by k, for (rem1-rem2) to
follow the same, rem1 = rem2 where
rem1 = Sum of subarray (0, j) % k
rem2 = Sum of subarray (0, i-1) % k
First of all, I don't get what q1 and q2 indicate.
def subCount(arr, n, k):
# create auxiliary hash
# array to count frequency
# of remainders
mod =[]
for i in range(k + 1):
mod.append(0)
cumSum = 0
for i in range(n):
cumSum = cumSum + arr[i]
mod[((cumSum % k)+k)% k]= mod[((cumSum % k)+k)% k] + 1
result = 0 # Initialize result
# Traverse mod[]
for i in range(k):
if (mod[i] > 1):
result = result + (mod[i]*(mod[i]-1))//2
result = result + mod[0]
return result
And in this solution code, I don't get the role of mod. What is the effect of incrementing the cound of ((cumSum % k)+k)% kth array?
It would be great if this can be explained step by step easily. Thanks.
Are you familiar with integer modulo/remainder operation?
7 modulo 3 = 1 because
7 = 2 * 3 + 1
compare
N % M = r
because N might be represented as
N = q * M + r
here r is remainder and q is result of integer division like
7 // 3 = 2
For modulo k there might be k distinct remainders 0..k-1
mod array contains counters for every possible remainder. When remainder for every subrange sum is calculated, corresponding counter is incremented, so resulting mod array data looks like [3,2,5,0,7] three zero remainders, two remainders are equal to 1...

Categories