I would like to know if there is a better way (in the case my implementation is correct) to find a sub-sequence of integers in a given array. I have implemented the solution using golang (if this is an impediment for a review I could use a different language). If I am not mistaken the bellow implementation is close to O(b).
package main
import "fmt"
func main() {
a := []int{1, 2, 3}
b := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
r := match(a, b)
fmt.Println("Match found for case 1: ", r)
a = []int{1, 2, 3}
b = []int{4, 5, 6, 7, 8, 9}
r = match(a, b)
fmt.Println("Match found for case 2: ", r)
a = []int{1, 2, 3}
b = []int{1, 5, 3, 7, 8, 9}
r = match(a, b)
fmt.Println("Match found for case 3: ", r)
a = []int{1, 2, 3}
b = []int{4, 5, 1, 7, 3, 9}
r = match(a, b)
fmt.Println("Match found for case 4: ", r)
a = []int{1, 2, 3}
b = []int{4, 5, 6, 1, 2, 3}
r = match(a, b)
fmt.Println("Match found for case 5: ", r)
a = []int{1, 2, 3}
b = []int{1, 2, 1, 2, 3}
r = match(a, b)
fmt.Println("Match found for case 6: ", r)
a = []int{1, 2, 3, 4, 5}
b = []int{4, 1, 5, 3, 6, 1, 2, 4, 4, 5, 7, 8, 1, 2, 2, 4, 1, 3, 3, 4}
r = match(a, b)
fmt.Println("Match found for case 7: ", r)
a = []int{1, 2, 1, 2, 1}
b = []int{1, 1, 2, 2, 1, 2, 1}
r = match(a, b)
fmt.Println("Match found for case 8: ", r)
}
func match(a []int, b []int) bool {
if len(b) < len(a) {
return false
}
lb := len(b) - 1
la := len(a) - 1
i := 0
j := la
k := 0
counter := 0
for {
if i > lb || j > lb {
break
}
if b[i] != a[k] || b[j] != a[la] {
i++
j++
counter = 0
continue
} else {
i++
counter++
if k < la {
k++
} else {
k = 0
}
}
if counter >= la+1 {
return true
}
}
return counter >= la+1
}
Correctness
As discussed in the comment section, there are a family of string matching algorithms, which normally categorized into single pattern and multiple pattern matching algorithm. In your case it belongs to single pattern string matching problem.
From my knowledge, the most well-known algorithm is KMP algorithm which uses dynamic programming, and an alternative named Rabin-Karp's algorithm which uses rolling hash technique to speed up the process. Both runs in O(max(a,b)).
However, your code is not very alike to these algorithm's normal implementation, at least to my experience. Therefore I suspect the correctness of your code at the first place. You can try cases like a = {1, 2, 1, 2, 1}, b { 1, 1, 2, 2, 1, 2, 1 } to see it is not giving correct result.
Therefore you can
Abandon current algorithm and learn those standard one, implement them
Outline the logic and sketch a proof of your current algorithm, compared it with the logic behind those standard algorithms to verify its correctness
I will leave this part to you
Complexity
To directly answer your OP:
No, O(max(a,b)) is the optimal you can achieve in this problem, which is also the complexity of the standard known algorithms mentioned above.
My understanding is that, it actually makes sense as at worst case, you HAVE TO read each character of the longer string at least 1 time.
Your current algorithm is also O(b) clearly, as you loop using i from 0 to length of b, and no matter which condition you fall into i will increase by 1, giving total O(b)
Therefore complexity is actually not the problem, the correctness is the problem.
Since you are only looking for a sequence, i would probably convert everything to string type and use the standard strings package.
Playground
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Contains("1, 2, 3, 4, 5, 6, 7, 8, 9", "1, 2, 3"))
fmt.Println(strings.Contains("4, 5, 6, 7, 8, 9", "1, 2, 3"))
fmt.Println(strings.Contains("1, 5, 3, 7, 8, 9", "1, 2, 3"))
fmt.Println(strings.Contains("4, 5, 1, 7, 3, 9", "1, 2, 3"))
fmt.Println(strings.Contains("4, 5, 6, 1, 2, 3", "1, 2, 3"))
fmt.Println(strings.Contains("4, 5, 6, 1, 2, 3, 2", "1, 2, 2, 3"))
fmt.Println(strings.Contains("1, 2, 1, 2, 3", "1, 2, 3"))
}
Related
arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map {|n| n + 1}
arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map {|n| n += 1}
These both return [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] but i'm not understanding whats the difference in using + or += in a map array. Why would I use one over the other?
In ruby in most cases the last expression is returned.
Inside the block (in both cases) you have only one expression and this will be the result per item.
One expression is n + 1 and this will be 1 + 1, 2 + 1, 3 + 1, etc
The other expression is n += 1 and this will be n = n + 1 so n = 1 + 1, n = 2 + 1, n = 3 + 1
The same result, but in the second you make an extra assignment
The first expression n + 1 is in some way is more efficient because you do not assign the value again to n
The second expression n +=1 could be useful if you need to make other operations with n inside of the block
There is no difference, because only the return value inside map block matters. You can do what you like with n but if you return something else, that's what counts:
>> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map {|n| n += 1; 1} # return 1 for everything
=> [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
For example, n += 1 and n + 1 both return 2 if n is 1, so there is no difference.
It is however significant inside the map block:
>> [1].map {|n| n + 10; n} # `+` does not change `n`
=> [1]
>> [1].map {|n| n += 10; n} # `+=` does change `n`
=> [11]
The Array#map function iterates over Array and executes the block once for each element in it's own scope. Each time the n += 1 executes, Array#map first sets the value of n to to the element being mapped. It is not held over or accumulated for subsequent iterations.
If you wanted to accumulate on purpose, you have to add something to a variable outside the block.
irb(main):001:0> a = 0
=> 0
irb(main):002:0> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map {|n| a += n }
=> [1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
irb(main):003:0> a
=> 55
A random 1..7 options for 1..13 positions model below produces an example output
[3,2,2,3,6,7,1,4,2,4,5,2,3]
I want to constrain it so that if the value is the same, then another value stated within another associated parameter array must also match.
set of int: optA = 1..7;
set of int: position = 1..13;
enum optB = {A,B,C,D};
array[position] of optB: opts = [A,A,A,B,B,B,B,C,C,C,D,D,D];
array[position] of var optA: result;
constraint forall(i in positions)(
forall(j in optA)(
% NO IDEA HOW TO FORMULATE EQUALITY HERE, IF THIS IS EVEN THE RIGHT IDEA
)
);
In other words, if result[1], result[3], result[8] and result[9] are each assigned a 4 from optA, and second constraint looks at whether they have matching values from optB, which in this case they don't, so the result is invalid.
Any of A,A,A cannot have the same optA values as any of C,C,C, while within their set they can all be the same or different.
So, with this additional constraint, a valid solution could be
[1,2,2, 3,3,4,4, 5,5,5, 6,6,7]
Here's a solution that might be what you want. I understand this as the As is a chunk (set) that can take any value, but not the values that are in the chunks of B, C or D.
The approach here is to simply check that if opts[i] != opts[j] then result[i] != result[j]. It that what you want?
set of int: optA = 1..7;
set of int: position = 1..13;
enum optB = {A,B,C,D};
array[position] of optB: opts = [A,A,A,B,B,B,B,C,C,C,D,D,D];
array[position] of var optA: result;
constraint
% ensure that the "chunks" are different
forall(i,j in position where i < j) (
if opts[i] != opts[j] then
result[i] != result[j]
else
true
endif
)
;
solve satisfy;
output [
"result: \(result)\n",
];
There's a lot of solutions (namely 2102520 solutions). Here are some of them:
result: [3, 3, 3, 4, 4, 4, 4, 2, 2, 2, 1, 1, 1]
----------
result: [3, 3, 3, 5, 4, 4, 4, 2, 2, 2, 1, 1, 1]
----------
result: [3, 3, 3, 6, 4, 4, 4, 2, 2, 2, 1, 1, 1]
----------
result: [3, 3, 3, 7, 4, 4, 4, 2, 2, 2, 1, 1, 1]
----------
result: [3, 3, 3, 4, 5, 4, 4, 2, 2, 2, 1, 1, 1]
----------
I am trying to find the common elements in two arrays.
pairs = Array.new
a = exchange_one.get_symbols
b = exchange_two.get_symbols
c = a+b
c.uniq{|pair| pairs << pair}
I am combining the two arrays using +
Then I am calling uniq to remove the duplicate, but passing it to a block so the found duplicates can be added to an array before they are deleted.
For some reason the array pairs is just the entire c array.
What is the correct way to find array similarities.
If your goal is simply to determine which elements are the same between two arrays, you can use the intersection operator Array#&.
a = exchange_one.get_symbols
b = exchange_two.get_symbols
intersection = a & b
First understand what are you doing and what you want.
For eg.
a = 15.times.map { rand 6 }
#=> [1, 0, 5, 3, 1, 3, 4, 1, 3, 2, 1, 2, 4, 2, 3]
b = 15.times.map { rand 6 }
#=> [3, 3, 3, 1, 3, 1, 3, 1, 5, 1, 4, 2, 0, 0, 4]
Now what are you doing
c = a + b
#=> [1, 0, 5, 3, 1, 3, 4, 1, 3, 2, 1, 2, 4, 2, 3, 3, 3, 3, 1, 3, 1, 3, 1, 5, 1, 4, 2, 0, 0, 4]
c - only combine arrays irrespective of content hence get all values.
Now
pairs = Array.new
c.uniq{|pair| pairs << pair}
Here uniq is just act as a iterator means if you check 'pair' then it iterate all the values of 'c' and insert those values in 'pairs' array.
check this
c.uniq{|pair| puts pair}
Thats why you are getting all values within 'pairs' array.
The best way to find similarity in arrays is (a&b), but you can make changes in your code as follow to achieve it.
pairs = (arr1+arr2).uniq
OR
pairs = arr1 & arr2 #best and efficient way.
Suppose:
arr1 = 15.times.map { rand 6 }
#=> [1, 0, 4, 0, 2, 3, 1, 0, 2, 4, 4, 1, 3, 1, 1]
arr2 = 15.times.map { rand 6 }
#=> [5, 5, 4, 1, 5, 1, 5, 0, 4, 0, 2, 0, 4, 5, 0]
arr1 contains 5 1s and arr2 contains 2 1s. If, by "common elements" you wish to report that both arrays contain [5, 2].min #=> 2 1s, and similar counts for the other elements that appear in either array, you can do the following:
h1 = count(arr1)
#=> {1=>5, 0=>3, 4=>3, 2=>2, 3=>2}
h2 = count(arr2)
#=> {5=>5, 4=>3, 1=>2, 0=>4, 2=>1}
(h1.keys | h2.keys).each_with_object({}) { |k,h| h[k] = [h1[k], h2[k]].min }
#=> {1=>2, 0=>3, 4=>3, 2=>1, 3=>0, 5=>0}
def count(arr)
arr.each_with_object(Hash.new(0)) { |n,h| h[n] += 1 }
end
Problem:
I have two arrays A and B:
A = [0, 1, 2, 3]; %A will always be from 0 to N where N in this case is 3.
B = [0, 1, 3, 1, 9, 4, 6, 2, 5, 9, 10, 11, 3, 8, 1, 5, 9, 10];
weights_B = [3, 4, 5, 6];
I want to compare the first element of A to the first 3 elements of B and the second element of A to the next 4 elements of B. If the elements of A are equal I remove it from B. So in example:
if (A(1) == B(1:3))
remove A(1) from B
Similarly,
I want to compare A(2) to the next 4 elements of B i.e. to B(4:7):
if (A(2) == B(4:7))
remove A(2) from B
I want to compare A(3) to the next 5 elements of B i.e. to B(8:12)
if (A(3) == B(8:12))
remove A(3) from B
I want to compare A(4) to the next 6 elements of B i.e. to B(13:18)
if (A(4) == B(13:18))
remove A(4) from B
Note: The array weights_B determines the number of elements in B that should be respectively compared to A(1), A(2), .. , A(4)
So in the end B should have the following elements:
B = [1, 3, 9, 4, 6, 5, 9, 10, 11, 8, 1, 5, 9, 10];
Needed Solution:
Is there any way I can do this without having to hard-code the indices?
Here's a way without hard-coding:
Bw = mat2cell(B, 1, weights_B); % split into chunks
result = cell(size(Bw)); % initiallize result
for k = 1: numel(A)
result{k} = Bw{k}(Bw{k}~=A(k)); % fill each chunk of the result
end
result = [result{:}]; % concatenate into a row vector
For the sake of diversity, here's a way to do this using splitapply:
function out = q50982235
A = 0:3;
B = [0, 1, 3, 1, 9, 4, 6, 2, 5, 9, 10, 11, 3, 8, 1, 5, 9, 10];
weights_B = [3, 4, 5, 6];
a_ind = 0; % acts as a "global" variable for the inner function
G = repelem( 1:numel(weights_B), weights_B ); % this creates a vector of groups
out = cell2mat( splitapply(#movdif, B, G) );
function out = movdif(B)
a_ind = a_ind + 1;
out = {B(B ~= A(a_ind))};
end
end
The above works because the order of processed groups is predictable.
This solution requires R2015b.
Try this
A = [0, 1, 2, 3];
B = [0, 1, 3, 1, 9, 4, 6, 2, 5, 9, 10, 11, 3, 8, 1, 5, 9, 10];
weights_B = A + A(end);
border_0 = zeros(size(A));
border_1 = zeros(size(A));
border_0(1) = 1;
border_1(end) = length(B);
for i= 2:length(A)
border_0(i) = border_0(i-1) + weights_B(i-1);
border_1(i-1) = border_0(i)-1;
end
C = [];
for i= 1:length(border_0)
shift = 0;
if (i > 1)
shift = border_1(i-1);
end
C = [C B( find(B(border_0(i):border_1(i))~=A(i)) + shift )]
end
A = [0, 1];
B = [0, 1, 3, 1, 4, 5, 6];
% Split B into cells
C{1} = B(1:3) ; % this can be coded if more splits are required
C{2} = B(4:end) ;
% removing the lements
for i = 1:2
C{i}(C{i}==A(i))=[] ; % remove the elements in C{i} present in A(i)
end
cell2mat(C)
Since you want to compare the elements of A with first 3 and then 4 elements of B respectively, you would need to involve indexes.
You could simply use loop for it.
for(int i=0;i<B.length;i++){
if((A[0]==B[i])&&i<3){
B[i]=B[i+1];
}
else if((A[0]==B[i])&&i>3){}
B[i]=B[i+1];
}
Then adjust the updated size of array B.
Imagine one has an array such as:
a = [0, 1, 2, 3, 4, 2, 5, 1, 7, 6, 4, 5]
And one wishes to create an array consisting of the first n elements, starting with the first element in the array, that are a monotonic sequence increasing by one. Given a above, that array would be [0, 1, 2, 3, 4].
One could use slice_when, such as:
a.slice_when { |a, b| a != b - 1 }.first
The drawback of this approach is that slice_when continues to iterate over the array elements 2, 5, 1, and so on, until the end. In this case, iterating over the remaining values is useless, since one really just wants the first slice.
What is the elegant way to express this in Ruby, that ceases iterating once the first increasing sequence is selected?
How about lazy evaluation?
a = [0, 1, 2, 3, 4, 2, 5, 1, 7, 6, 4, 5]
a.lazy.slice_when { |a, b| a != b - 1 }.first
=> [0, 1, 2, 3, 4]
Enumerable#lazy
You could write the following.
def stairstep(arr)
return [] if arr.empty?
enum = arr.first.step
arr.take_while { |x| x == enum.next }
end
stairstep [1, 2, 3, 4, 6, 5, 1, 7, 6, 4, 5]
#=> [1, 2, 3, 4]
Perhaps something like this:
a = [0, 1, 2, 3, 4, 2, 5, 1, 7, 6, 4, 5]
(a.size - 1).times { |i| break a[0..i] if a[i] > a[i + 1] }
#=> [0,1,2,3,4]