script for calculating boundaries between elements of matrix - performance

The following script gives me sum of boundaries of each element of matrix. boundaries are calculated about elements with value 1 that are next to each other. product of this summation named contact perimeter.
but Is there any other way that I can summarize or vectorize my raw and simple script?
I ask this request because my real matrix is very large and use of "for" increase the time of calculation.
Thank you.
a1=[1 1 0 1 0 1;
0 1 1 0 0 1;
1 1 0 1 0 1;
1 1 0 0 1 0;
0 0 0 1 1 1]
m=5
n=6
cmp=zeros(m,n)
cmp1=zeros(m,n)
for i=1:m-1
for j=1:n
if a1(i,j)==a1(i+1,j) && a1(i,j)==1
cmp(i,j)=1
end
end
for i=1:m
for j=1:n-1
if a1(i,j)==a1(i,j+1) && a1(i,j)==1
cmp1(i,j)=1
end
end
end
end
cmtotal=cmp+cmp1
pc=sum(sum(cmtotal))

This should be pretty efficient one -
%// Case1 and case 2 matches
case1_matches = a1(1:end-1,:) == a1(2:end,:) & a1(1:end-1,:)==1
case2_matches = a1(:,1:end-1) == a1(:,2:end) & a1(:,1:end-1)==1
%// Get sum of those matches for the final output, equivalent to your pc
out = sum(case1_matches(:)) + sum(case2_matches(:))
You can replace the sum(..(:)) with nnz(), but I doubt that it will be any better than sum in terms of runtime performance in accordance with benchmarks of sum against nnz.

Related

Ruby Complexity / Time explaination

I am using HackerRank to improve my ruby skills. I am on the Data Structures - Array Manipulation problem: https://www.hackerrank.com/challenges/crush/problem
My solution is listed first, a supposedly "correct" solution I haven't been able to implement is listed last. My solution works for all test cases except **VERY ** large numbers, it fails due to timeout. I know this is because of a loop, but can someone offer a different solution, and explain why theirs works?
EXPLAINATION OF PROBLEM:
Sample Input
5 3
1 2 100
2 5 100
3 4 100
#n=5 m=3, then following numbers leftIndex=1 rightIndex=3 sum=100 and so on
Sample Output
200
Explanation
After the first update list will be 100 100 0 0 0.
After the second update list will be 100 200 100 100 100.
After the third update list will be 100 200 200 200 100.
The required answer will be 200.
OR
Explain how the why the bottom solution can work without looping?
#Problem Data Set, my solution times out for the listed data set
10000000 100000
[
[1400906,9889280,90378],
[6581237,9872072,87106],
[4386373,9779851,52422],
[198648,4373818,5289]
]
# For this problem, variables will look like:
n = 10000000
m = 100000
q = [[1400906,9889280,90378],[6581237,9872072,87106],[4386373,9779851,52422],[198648,4373818,5289]]
def arrayManipulation(n, q)
arr = Array.new(n,0)
max = 0
q.size.times do |i|
left, right, value = q[i][0],q[i][1],q[i][2]
left -= 1
(left...right).each do |j|
arr[j] += value
max = arr[j] if max < arr[j]
end
end
return max
end
# this times out on HackerRank, please explain better solution,
#or why the bottom solution works!
result = arrayManipulation n, queries
Working SOlution on Discussion board
N, M = gets.chomp.split(' ').map(&:to_i)
# create array of zeros of length N + 1
arr = Array.new(N + 1, 0)
M.times do
# cycle through and get the inputs
start, finish, value = gets.chomp.split(' ').map(&:to_i)
# increment value at start of sequence
arr[start - 1] += value
# decrement value at first position after sequence
arr[finish] -= value
end
tmp = 0
max = 0
arr.each do |value|
# step through summing array
tmp += value
# capture the max value of tmp
max = tmp if max < tmp
end
puts max
Your code is O(m * n) as you have a nested loop to update the array for each entry.
The working code is O(m + n): it never really stores the array, it stores the delta array, the array of changes that makes it possible to reconstruct the array. In your first example, it would store:
0 0 0 0 0 0
+100 0 -100 0 0 0
+100 +100 -100 0 0 -100
+100 +100 0 0 -100 -100
This requires only a loop of m iterations, no nested loops; then you need another loop of n iterations to run through the array, keep running total, and identify the maximum of it.

All possible N choose K WITHOUT recusion

I'm trying to create a function that is able to go through a row vector and output the possible combinations of an n choose k without recursion.
For example: 3 choose 2 on [a,b,c] outputs [a,b; a,c; b,c]
I found this: How to loop through all the combinations of e.g. 48 choose 5 which shows how to do it for a fixed n choose k and this: https://codereview.stackexchange.com/questions/7001/generating-all-combinations-of-an-array which shows how to get all possible combinations. Using the latter code, I managed to make a very simple and inefficient function in matlab which returned the result:
function [ combi ] = NCK(x,k)
%x - row vector of inputs
%k - number of elements in the combinations
combi = [];
letLen = 2^length(x);
for i = 0:letLen-1
temp=[0];
a=1;
for j=0:length(x)-1
if (bitand(i,2^j))
temp(k) = x(j+1);
a=a+1;
end
end
if (nnz(temp) == k)
combi=[combi; derp];
end
end
combi = sortrows(combi);
end
This works well for very small vectors, but I need this to be able to work with vectors of at least 50 in length. I've found many examples of how to do this recursively, but is there an efficient way to do this without recursion and still be able to do variable sized vectors and ks?
Here's a simple function that will take a permutation of k ones and n-k zeros and return the next combination of nchoosek. It's completely independent of the values of n and k, taking the values directly from the input array.
function [nextc] = nextComb(oldc)
nextc = [];
o = find(oldc, 1); %// find the first one
z = find(~oldc(o+1:end), 1) + o; %// find the first zero *after* the first one
if length(z) > 0
nextc = oldc;
nextc(1:z-1) = 0;
nextc(z) = 1; %// make the first zero a one
nextc(1:nnz(oldc(1:z-2))) = 1; %// move previous ones to the beginning
else
nextc = zeros(size(oldc));
nextc(1:nnz(oldc)) = 1; %// start over
end
end
(Note that the else clause is only necessary if you want the combinations to wrap around from the last combination to the first.)
If you call this function with, for example:
A = [1 1 1 1 1 0 1 0 0 1 1]
nextCombination = nextComb(A)
the output will be:
A =
1 1 1 1 1 0 1 0 0 1 1
nextCombination =
1 1 1 1 0 1 1 0 0 1 1
You can then use this as a mask into your alphabet (or whatever elements you want combinations of).
C = ['a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k']
C(find(nextCombination))
ans = abcdegjk
The first combination in this ordering is
1 1 1 1 1 1 1 1 0 0 0
and the last is
0 0 0 1 1 1 1 1 1 1 1
To generate the first combination programatically,
n = 11; k = 8;
nextCombination = zeros(1,n);
nextCombination(1:k) = 1;
Now you can iterate through the combinations (or however many you're willing to wait for):
for c = 2:nchoosek(n,k) %// start from 2; we already have 1
nextCombination = nextComb(A);
%// do something with the combination...
end
For your example above:
nextCombination = [1 1 0];
C(find(nextCombination))
for c = 2:nchoosek(3,2)
nextCombination = nextComb(nextCombination);
C(find(nextCombination))
end
ans = ab
ans = ac
ans = bc
Note: I've updated the code; I had forgotten to include the line to move all of the 1's that occur prior to the swapped digits to the beginning of the array. The current code (in addition to being corrected above) is on ideone here. Output for 4 choose 2 is:
allCombs =
1 2
1 3
2 3
1 4
2 4
3 4

Change the matrix values using index vector

I have the following array:
AA = zeros(5,3);
AA(1,3)=1;
AA(3,3)=1;
AA(4,2)=1;
and I want to place the value one in the collumns defined by the following
vector a = [0; 2; 0; 0; 1]. Each value of this vector refers to the collumn
index that we want to change in each row. When zero apears no changes should be made.
Desired output:
0 0 1
0 1 0
0 0 1
0 1 0
1 0 0
Could you please suggest a way to do this without for loop? The goal is
a faster execution.
Thanks!!!
Approach 1
nrows = size(AA,1) %// Get the no. of rows, as we would use this parameter later on
%// Calculate the linear indices with `a` as the column indices and
%// [1:nrows] as the row indices
idx = (a-1)*nrows+[1:nrows]' %//'
%// Select the valid linear indices (ones that have the corresponding a as non-zeros
%// and use them to index into AA and set those as 1's
AA(idx(a~=0))=1
Code output with given AA -
>> AA
AA =
0 0 1
0 1 0
0 0 1
0 1 0
1 0 0
Approach 2
AA(sub2ind(size(AA),find(a~=0),a(a~=0)))=1
Breaking it down to few steps for explanation:
find(a~=0) and a(a~=0) gets us the VALID row and columns indices respectively as needed for sub2ind(size(),row,column) format.
sub2ind gets us the linear indices, which we can use to index into input matrix AA and set those in AA as 1's.

Counting subrows in each row of a matrix in Matlab?

I need an algorithm in Matlab which counts how many adjacent and non-overlapping (1,1) I have in each row of a matrix A mx(n*2) without using loops. E.g.
A=[1 1 1 0 1 1 0 0 0 1; 1 0 1 1 1 1 0 0 1 1] %m=2, n=5
Then I want
B=[2;3] %mx1
Specific case
Assuming A to have ones and zeros only, this could be one way -
B = sum(reshape(sum(reshape(A',2,[]))==2,size(A,2)/2,[]))
General case
If you are looking for a general approach that must work for all integers and a case where you can specify the pattern of numbers, you may use this -
patt = [0 1] %%// pattern to be found out
B = sum(reshape(ismember(reshape(A',2,[])',patt,'rows'),[],2))
Output
With patt = [1 1], B = [2 3]
With patt = [0 1], B = [1 0]
you can use transpose then reshape so each consecutive values will now be in a row, then compare the top and bottom row (boolean compare or compare the sum of each row to 2), then sum the result of the comparison and reshape the result to your liking.
in code, it would look like:
A=[1 1 1 0 1 1 0 0 0 1; 1 0 1 1 1 1 0 0 1 1] ;
m = size(A,1) ;
n = size(A,2)/2 ;
Atemp = reshape(A.' , 2 , [] , m ) ;
B = squeeze(sum(sum(Atemp)==2))
You could pack everything in one line of code if you want, but several lines is usually easier for comprehension. For clarity, the Atemp matrix looks like that:
Atemp(:,:,1) =
1 1 1 0 0
1 0 1 0 1
Atemp(:,:,2) =
1 1 1 0 1
0 1 1 0 1
You'll notice that each row of the original A matrix has been broken down in 2 rows element-wise. The second line will simply compare the sum of each row with 2, then sum the valid result of the comparisons.
The squeeze command is only to remove the singleton dimensions not necessary anymore.
you can use imresize , for example
imresize(A,[size(A,1),size(A,2)/2])>0.8
ans =
1 0 1 0 0
0 1 1 0 1
this places 1 where you have [1 1] pairs... then you can just use sum
For any pair type [x y] you can :
x=0; y=1;
R(size(A,1),size(A,2)/2)=0; % prealocarting memory
for n=1:size(A,1)
b=[A(n,1:2:end)' A(n,2:2:end)']
try
R(n,find(b(:,1)==x & b(:,2)==y))=1;
end
end
R =
0 0 0 0 1
0 0 0 0 0
With diff (to detect start and end of each run of ones) and accumarray (to group runs of the same row; each run contributes half its length rounded down):
B = diff([zeros(1,size(A,1)); A.'; zeros(1,size(A,1))]); %'// columnwise is easier
[is js] = find(B==1); %// rows and columns of starts of runs of ones
[ie je] = find(B==-1); %// rows and columns of ends of runs of ones
result = accumarray(js, floor((ie-is)/2)); %// sum values for each row of A

converting sequences to lex order

I have a function that generates binary sequences with a fixed number of 1's (the rest are 0's). I need a function that takes a sequences and returns the position of that sequence in lexicographic order. For example, the 10 sequences of length 5 with 3 1's are
0 0 1 1 1
0 1 0 1 1
0 1 1 0 1
0 1 1 1 0
1 0 0 1 1
1 0 1 0 1
1 0 1 1 0
1 1 0 0 1
1 1 0 1 0
1 1 1 0 0
I need a function that takes, for example 0 1 1 0 1 and returns 3 since it's the third in the list.
The only thing I can think of, which is way too inefficient, is to generate all of the sequences (easy), store them (takes too much space), then search for the given sequence in the list (too slow), and return its position. Is there a faster way to do this? Some easy trick that I don't see?
We call the set of sequences of length n with k 1's binseq(n,k). This problem can then be solved recursively, as follows:
Base case: If S has length 1, it's in position 1.
If S starts with a 0, its position is the same as the position of tail(S) (S with the first element removed) in binseq(n-1, k).
If S starts with a 1, its position is equal to the position of tail(S) in binseq(n-1, k-1) plus the number of sequences in binseq(n-1, k).
In python code:
#!/usr/bin/env python
def binom(n, k):
result = 1
for i in range(1, k+1):
result = result * (n-i+1) / i
return result
def lexpos(seq):
if len(seq) == 1:
return 1
elif seq[0] == 0:
return lexpos(seq[1:])
else:
return binom(len(seq)-1, seq.count(1)) + lexpos(seq[1:])
Or the iterative version, as suggested by Abhishek Bansal:
def lexpos_iter(seq):
pos = 1
for i in xrange(len(seq)):
if seq[i] == 1:
pos += binom(len(seq)-i-1, seq[i:].count(1))
return pos

Resources