Threesum algorithm with Ruby - ruby

The question is - how many three numbers exits (of the provided) that sum to zero?
I'm wondering, how to implement this brute force method (below) in ruby? The main aspect of this is : what is better to use in a place of for loop? times?
a - is array of integers, the data that's provided
int N = a.length;
int count = 0;
for(int i = 0; i<N; i++)
for(int j = i+1; j<N; j++)
for(int k = j+ 1; k<N; k++)
if (a[i] + a[j] + a[k] == 0 )
count++;
return count;

How about:
a.combination(3).count{|x,y,z| x+y+z==0}
But I don't get the connection with Rails here ;)

def two_sum(nums, target)
seen = Hash.new
result = []
nums.each_with_index do |e, i|
if seen.has_key?(target-e)
result << [seen[target-e], i]
else
seen[e] = i
end
end
return result
end
def three_sum(nums)
nums.sort!
seen = Hash.new
results = []
nums.each_with_index do |e, i|
if !seen.has_key?(e)
two_sum(nums[i + 1 .. -1], 0 - e).each do |pair|
results << [e] + pair.map{ |j| nums[j + i + 1] }
end
seen[e] = i
end
end
return results.count
end

Related

find all common part between vector

I have n vectors with unique integers. I want to find all the common part between them. Is there any specific algorithm or data struct for this problem?
example:
std::vector<int> a = {0,1,2,3,4,5,6,7,8,9,10,11,12,13};
std::vector<int> b = {1,7,8,9,2,10,11,12};
std::vector<int> c = {4,9,8,7,0,1,2,3};
result:
ignore result with only one interge
7,8,9 between a and b
10,11,12 bewteen a and b
0,1,2,3 between a and c
if you want all common subarrays with a length greater than 1, then for each element from the first array iterate over all elements in the second array if you match two elements then go to the next element in the first and second array, and so on.
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (arr1[i] == arr2[j]) {
int ii = i, jj = j, cnt = 0;
std::vector<int> res;
res.push_back(arr1[ii]);
while (++ii < n and ++jj < m and arr1[ii] == arr2[jj])res.push_back(arr1[ii]);
if (res.size() > 1) {
for (auto x: res)std::cout << x << " ";
}
}
}
}
time complexity:O(n^3)
and this another way by LCS.
memset(dp, 0, sizeof dp);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = 0;
if (arr1[i] == arr2[j]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
std::cout << dp[i][j] << " ";
}
std::cout << "\n";
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (dp[i][j] > 1) {
for (int ii = i, jj = j, k = dp[i][j]; k; ii--, jj--, k--) {
std::cout << arr1[ii] << " ";
}
std::cout << "\n";
}
}
}
O(n^3)
It seems to me that you are looking for Longest Common Subsequence
These images are calculated by a diff like program which compares lines (unordered), like shortest edit distance
Blue lines : Deleted lines to come from left to right
Red lines : Changed lines
Green lines: Inserted lines
Lines without color are unchanged = longest common subsequence. Diff result looks pretty much the same as the given results.
Reference:
A Fast Algorithm for computing longest common subsequences
by James W. Hunt and Thomas G. Szymanski
from Communications of the ACM May 1977 Volume 20 no. 5

Go through all the major diagonals in matrix, including principal

How do I go through all the diagonals in the matrix? There were similar questions, like this one, but they just calculate the sum. I need to perform certain operations going through all the diagonals, not just summing. To be clear, I need to traverse through it in way like in the picture:
I've came up with this solution, but it is awful:
for(int j = 0; j < m; j++) {
for(int i = 0; i < n && i + j < m; i++) {
cout << matrix[i][i + j] << " ";
}
cout << endl;
}
for(int i = 1; i < n; i++) {
for(int j = 0; j < m && i + j < n; j++) {
cout << matrix[j + i][j] << " ";
}
cout << endl;
}
For the matrix n*m I first go through every diagonal right from the main one, and then left from the main one, but this solution seems ugly to me.
You can use a simpler iteration at the cost of a modulo operation:
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cout << a[(i+j)%n][j] << " ";
}
cout << endl;
}
You need a first loop to find the starting point of each diagonal, and a second loop to follow the diagonal.
Here is a pseudo-code, assuming the point (0, 0) is the lower one.
(i_start, j_start) = (1, 0)
While (1)
If (i_start, j_start) out of bound: break
(i, j) = (i_start, j_start)
While (1)
If (i, j) out of bound: break
Write matrix[i][j]
i--, j++
End while
If (i_start not max) i_start++
Else j_start++
End while

Slow array access in ruby?

I would like to present you this small (in this reduced form senseless) snippet of ruby code, which runs awfully slow:
MAX = 28123
M = 7000
abundant = Array.new(M) {|k| k}
checked = Array.new(MAX) {|k| false}
for a in 0..M-1 do
for b in a..M-1 do
checked[abundant[a] + abundant[b]] = true if abundant[a] + abundant[b] < MAX
end
end
It takes about 10 seconds to execute, whereas the equivalent C++ code runs in about 0.2 secs:
int main()
{
int abundant[M];
bool checked[MAX];
for (int n = 0; n < M; n++)
abundant[n] = n;
for (int n = 0; n < MAX; n++)
checked[n] = false;
for (int a = 0; a < M; a++)
for (int b = a; b < M; b++)
if (abundant[a] + abundant[b] < MAX)
checked[abundant[a] + abundant[b]] = true;
}
What am I doing wrong in the ruby implementation? I'm new to ruby - do I use any statement which is known to be thaaat slow?
Ruby is definitely a lot slower than C++, so there is not a lot you could do to make your code faster.
I believe the following code has the same behaviour and is a bit faster (±25%):
MAX = 28123
M = 7000
checked = Array.new(MAX) {|k| false}
(0..M - 1).each do |a|
(a..M - 1).each do |b|
checked[a + b] = true if a + b < MAX
end
end
Using #each makes no difference, but making fewer array access does. I believe one of the reasons C++ is so much faster is because it makes no boundary check for array accesses while Ruby has to do it.
Could you change the C++ version to use std::vector and .at() to access the array and then compare with the Ruby version?

Insertion sort in Ruby [duplicate]

This question already has answers here:
Learning Insertion Sort in Ruby
(6 answers)
Closed 5 years ago.
Here is the algorithm of insertion sort in C++ (from tutorial):
void insertionSort(int arr[], int length) {
int i, j, tmp;
for (i = 1; i < length; i++) {
j = i;
while (j > 0 && arr[j - 1] > arr[j]) {
tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
j--;
}
}
}
and here is what I'm doing in Ruby
a = [12, 1, 18, -3, -2, 66, 31]
puts a
def insertion_sort(source)
source.to_enum.with_index(1).each do |item, i|
j = i
while((j>0) && (source[j-1] > source[j]))
source[j], source[j-1] = source[j-1], source[j]
j -= 1
end
end
end
insertion_sort(a)
puts a
it throws an error of comparison of Fixnum with nil failed (ArgumentError). Probably because of overflow.
What did I do wrong?
In the C++ version you have (i = 1; i < length; i++). Which means, it will not run the last round where i = length. That would be out of the array.
In ruby, because you set the offset of the index to 1, the last round, you would have i = length. Hence source[length] is out of source, so returns nil.
1 > nil # source[j-1] > source[j] when j = length
# ArgumentError: comparison of Fixnum with nil failed
While #oldergod already answer your question, I just wanna add some fix to the problem.
Here sample of code taken from algorithm gem
def insertion_sort(container)
return container if container.size < 2
(1..container.size-1).each do |i|
value = container[i]
j = i-1
while j >= 0 and container[j] > value do
container[j+1] = container[j]
j = j-1
end
container[j+1] = value
end
container
end
Here you iterate over a number, 1, the second index of the container, to container.size - 1, the last index.

LCS ALGORITHM ( example )

There's a dynamic programming algorithm to find the Longest Common Subsequence of two sequences. How can I find the LCS algorithm of two sequences X and Y. (Test of correctness)
(a) X = ABEDFEESTYH Y=ABEDFEESTYHABCDF
(b) X = BFAAAABBBBBJPRSTY Y=ABCDEFGHIJKLMNOPRS
(c) X = ϕ (Empty Sequence), Y = BABADCAB
Here is an online calculator
http://igm.univ-mlv.fr/~lecroq/seqcomp/node4.html
Java
public class LCS {
public static void main(String[] args) {
String x = StdIn.readString();
String y = StdIn.readString();
int M = x.length();
int N = y.length();
// opt[i][j] = length of LCS of x[i..M] and y[j..N]
int[][] opt = new int[M+1][N+1];
// compute length of LCS and all subproblems via dynamic programming
for (int i = M-1; i >= 0; i--) {
for (int j = N-1; j >= 0; j--) {
if (x.charAt(i) == y.charAt(j))
opt[i][j] = opt[i+1][j+1] + 1;
else
opt[i][j] = Math.max(opt[i+1][j], opt[i][j+1]);
}
}
// recover LCS itself and print it to standard output
int i = 0, j = 0;
while(i < M && j < N) {
if (x.charAt(i) == y.charAt(j)) {
System.out.print(x.charAt(i));
i++;
j++;
}
else if (opt[i+1][j] >= opt[i][j+1]) i++;
else j++;
}
System.out.println();
}
}
EDIT: C++ implementation: http://comeoncodeon.wordpress.com/2009/08/07/longest-common-subsequence-lcs/
There's a dynamic programming algorithm to find the Longest Common Subsequence of two sequences (assuming that's what you meant by LCS): http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
Here's the recurrence (from Wikipedia):
and the pseudocode (also from Wikipedia):
function LCSLength(X[1..m], Y[1..n])
C = array(0..m, 0..n)
for i := 0..m
C[i,0] = 0
for j := 0..n
C[0,j] = 0
for i := 1..m
for j := 1..n
if X[i] = Y[j]
C[i,j] := C[i-1,j-1] + 1
else:
C[i,j] := max(C[i,j-1], C[i-1,j])
return C[m,n]
It's then possible to reconstruct what the longest subsequences are from the C array (see the Wikipedia article).
I have written a implementation in Ruby. It's a extension of String Class :
class String
def lcs_with(str)
unless str.is_a? String
raise ArgumentError,"Need a string"
end
n = self.length + 1
m = str.length + 1
matrix = Array.new(n, nil)
matrix.each_index do |i|
matrix[i] = Array.new(m, 0)
end
1.upto(n - 1) do |i|
1.upto(m - 1) do |j|
if self[i] == str[j]
matrix[i][j] = matrix[i - 1][j - 1] + 1
else
matrix[i][j] = [matrix[i][j - 1], matrix[i - 1][j]].max
end
end
end
matrix[self.length][str.length]
end
end
puts "ABEDFEESTYH".lcs_with "ABEDFEESTYHABCDF"

Resources