Searching through a matrix with Ruby - ruby

I have a matrix like this:
0 1 0 0 1 0
1 0 1 0 1 0
0 1 0 1 0 0
0 0 1 0 1 1
1 1 0 1 0 0
0 0 0 1 0 0
How can I define a matrix in Ruby, and then search through it?
I would like to write a program which searches through all rows and returns rows with the highest sum of "1".

Quick way to define a matrix in Ruby:
Array.new 6, Array.new(6, 0)
# => [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
]
The above code initializes an array with 6 items and defaults their values to the 2nd argument which is another array with 6 items and default values of 0.
In other more imperative languages you would use nested loops:
matrix = []
for x in [0,1,2,3,4,5]
for y in [0,1,2,3,4,5]
matrix[x] ||= [] # create the row as an empty array
matrix[x] << y # push the y value to it
end
end
# matrix is now:
# => [
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5]
]
To search through the matrix and find the row with greatest sum:
greatest_sum_row_index = 0
greatest_sum = 0
matrix.each_with_index do |row, i|
# row.inject(:+) is a shortcut to adding each int in the array and returning the sum
sum = row.inject(:+)
if sum > greatest_sum
greatest_sum = sum
greatest_sum_row_index = i
end
end
# highest_row is now the index of the greatest sum row
matrix[greatest_sum_row_index] # returns the row with the greatest sum

If the array were large and memory requirements were a concern, you might do something like this:
def rows_with_most_ones(arr)
arr.each_with_index.with_object([-1, nil]) do |(row,i),best|
tot = row.count(1)
case tot<=>best.first
when 1 then best.replace([tot, [i]])
when 0 then best.last << i
end
end
end
arr = [[0, 1, 0, 0, 1, 0],
[1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 0],
[0, 0, 1, 0, 1, 1],
[1, 1, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0]]
rows_with_most_ones(arr)
#=> [3, [1, 3, 4]]

Related

Incorrect output for covering all graph edges

I need to cover all the edges of a graph with the minimum number of paths. I read that the Eulerian path method is needed here. I tried to reproduce it, but it doesn't work correctly.
def split_graph(graph, n):
stack = []
path = []
some_list = []
for i in range(n):
some_list .append(sum(graph[i]))
cur = 0
while (len(stack) > 0 or sum(graph[cur]) != 0):
if (sum(graph[cur]) == 0):
path.append(cur+1)
cur = stack[-1]
del stack[-1]
else:
for i in range(n):
if (graph[cur][i] == 1):
stack.append(cur)
graph[cur][i] = 0
graph[i][cur] = 0
cur = i
print(path)
if __name__ == '__main__':
graph = [[0, 1, 0, 1, 0, 0],
[1, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 1],
[0, 0, 0, 1, 1, 0]]
n = len(graph)
split_graph(graph, n)
Required output (There may be several options, this is one of them):
[6, 7, 4, 1, 2, 3]
[2,4]
[7, 5]
My output:
[3, 5, 6, 1, 4, 2]
How can this be fixed?

How can I generate all the possible binary lists with 4 elements? (Using Python)

I'm wondering is there a way I can generate a list whose sublists are all the possible combinations of 0 and 1 with 4 elements? To generate an individual sublist, I have
import random
binum = np.random.randint(2, size=4).tolist()
But how can I get a full list to contain all the possible sublists, each appears once? So the desired output looks like:
[[0,0,0,0],[0,0,0,1]...[1,0,0,1]...[1,1,1,1]]
(There're 16 elements in total for this case). Thanks:)
You basically want all the binary numbers from 0 to 2**n. So, you could use the following code to generate them:
def get_binary_numbers(n):
format_str = f"0{n}b"
max_binary_number = 2**n
return [list(format(i, format_str)) for i in range(max_binary_number)]
Be careful with its performance, if you need big values of n then maybe use the same idea with other ways to generate the lists.
Just iterate over numbers from 0 to 15 and get their binary representation:
In [1]: N = 4
In [2]: def getbit(value, bitnum):
...: mask = 1 << bitnum
...: return (value & mask) >> bitnum
...:
In [3]: [[getbit(i, j) for j in range(N)] for i in range(2 ** N)]
Out[3]:
[[0, 0, 0, 0],
[1, 0, 0, 0],
[0, 1, 0, 0],
[1, 1, 0, 0],
[0, 0, 1, 0],
[1, 0, 1, 0],
[0, 1, 1, 0],
[1, 1, 1, 0],
[0, 0, 0, 1],
[1, 0, 0, 1],
[0, 1, 0, 1],
[1, 1, 0, 1],
[0, 0, 1, 1],
[1, 0, 1, 1],
[0, 1, 1, 1],
[1, 1, 1, 1]]
Same in numpy:
np.unpackbits(np.expand_dims(np.arange(2 ** N, dtype=np.uint8), -1),
axis=1, bitorder='little', count=N)
Also, you can generate the next element by implementing increment operation on binary array:
def gen(l=4):
a = [0 for _ in range(l)]
while True:
yield a.copy()
i = 0
while i < l and a[i]:
a[i] = 0
i += 1
if i == l:
break
a[i] = 1
You can get the cartesian product of your input using itertools library
from itertools import product
bit_sets = product(range(2), repeat=4)
# format your data code ...
more details at https://docs.python.org/3.1/library/itertools.html?highlight=combinations#itertools.product
This just needs the actual necessary parameters from #yesk13's answer, really, using the cartesian product from itertools
for p in it.product([0, 1], repeat=4):
print(p)
(0, 0, 0, 0)
(0, 0, 0, 1)
(0, 0, 1, 0)
(0, 0, 1, 1)
(0, 1, 0, 0)
(0, 1, 0, 1)
(0, 1, 1, 0)
(0, 1, 1, 1)
(1, 0, 0, 0)
(1, 0, 0, 1)
(1, 0, 1, 0)
(1, 0, 1, 1)
(1, 1, 0, 0)
(1, 1, 0, 1)
(1, 1, 1, 0)
(1, 1, 1, 1)

Iterating over integer arrays with fixed sum in Julia

I am looking for an algorithm to iterate over all arrays of length n whose entries are integers between 0 and d and whose sum is k*d. It would be even better if there is a way to do this with built-in Julia functions and iterators. The algorithm should be non-recursive and memory efficient as I am hoping to use this for reasonable values of n.
For small values of n, d, and k, I've written down all such arrays in lexicographical ordering, but I haven't been able to come up with code for iterating through all such arrays.
I think this should work but it requires Combinatorics.jl and ResumableFunctions.jl
using Combinatorics, ResumableFunctions
#resumable function gen_all(n, k, d)
for x in partitions(k*d + n, n)
x = x .- 1
if all(x .<= d)
ys = Set(permutations(x))
for y in ys
#yield y
end
end
end
end
for ga in gen_all(5, 2, 2)
println(ga)
end
gives
[2, 0, 0, 2, 0]
[2, 0, 0, 0, 2]
[0, 0, 2, 2, 0]
[0, 2, 2, 0, 0]
[2, 0, 2, 0, 0]
[0, 2, 0, 2, 0]
[2, 2, 0, 0, 0]
[0, 0, 0, 2, 2]
[0, 0, 2, 0, 2]
[0, 2, 0, 0, 2]
[0, 2, 0, 1, 1]
[0, 1, 1, 0, 2]
[0, 1, 2, 0, 1]
[0, 1, 1, 2, 0]
[2, 1, 1, 0, 0]
[2, 1, 0, 0, 1]
[0, 0, 1, 1, 2]
[1, 2, 1, 0, 0]
[1, 2, 0, 0, 1]
[0, 1, 2, 1, 0]
[0, 1, 0, 1, 2]
[1, 0, 0, 1, 2]
[0, 2, 1, 1, 0]
[2, 0, 0, 1, 1]
[1, 0, 2, 0, 1]
[1, 2, 0, 1, 0]
[0, 1, 0, 2, 1]
[2, 0, 1, 0, 1]
[0, 2, 1, 0, 1]
[1, 0, 1, 2, 0]
[0, 0, 1, 2, 1]
[1, 0, 0, 2, 1]
[2, 1, 0, 1, 0]
[1, 1, 0, 0, 2]
[1, 0, 2, 1, 0]
[1, 0, 1, 0, 2]
[1, 1, 0, 2, 0]
[0, 0, 2, 1, 1]
[2, 0, 1, 1, 0]
[1, 1, 2, 0, 0]
[1, 1, 1, 0, 1]
[1, 1, 0, 1, 1]
[1, 0, 1, 1, 1]
[1, 1, 1, 1, 0]
[0, 1, 1, 1, 1]

Why does my diagonal matrix calculator not return the total?

My method should take an array of subarrays, find the sum of the first value of the first array, the second value of the second array, the third value of the third array, and so on. Some examples of inputs and expected results are as follows:
exampleArray = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
]
diagonalSum(exampleArray) # => 4
exampleArray = [
[1, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 1]
]
diagonalSum(exampleArray) # => 5
I wrote this:
def diagonalSum(matrix)
total = 0
counter = 0
while matrix.length <= counter + 1 do
total += matrix[counter][counter]
counter += 1
end
total
end
and it returns 0.
It's easiest to convert the array to a matrix and apply Matrix#trace.
require 'matrix'
arr = [[1, 0, 0, 7],
[0, 2, 0, 0],
[0, 0, 3, 0],
[8, 0, 0, 4]]
Matrix[*arr].trace
#=> 10
According to the code you provide, in which the input is an array of arrays, the first advice I could give you is that in Ruby you must avoid using for/while loops and make use of iterators such as each/each_with_index instead (based on this Ruby style guide and the suggestions of #tadman and #Yu Hao).
The each with index iterator takes a Ruby block with the current array of the iteration along with its index position, so you don't need to define your own index variable and update it in every iteration.
Applying this to your code will result in the following:
def diagonal_sum(matrix)
total = 0
matrix.each_with_index do |row, index|
total+=row[index]
end
total
end
Also note that the convention in Ruby is to write variable and method names in snake_case (according to the previous style guide).

Ruby - Graph adjacency matrix into variable

I am trying to edit an algorithm found here.
I want the adjacency matrix to be loaded from file (formatting of the file doesn't matter to me, it can be either like this [0,1,1,0] or just 0110) with G = file.read().split("\n")
However, I get an error no implicit conversion of Fixnum into String (TypeError)
And I already know I need to convert this string to ints, but how to do it properly to not lose the formatting required by this DFS method?
I guess it's pretty easy, but I'm a begginer in Ruby (and graphs :v) and can't get it to work...
Edit:
So the code I'm using to read from file to an array of arrays is:
def read_array(file_path)
File.foreach(file_path).with_object([]) do |line, result|
result << line.split.map(&:to_i)
end
end
And the result I get from a file (for example)
01101010
01010101
01010110
10101011
01011111
is this:
=> [[[1101010], [1010101], [1010110], [10101011], [1011111]]]
What I need, however, is:
=> [[[1,1,0,1,0,1,0], [1,0,1,0,1,0,1], [1,0,1,0,1,1,0], [1,0,1,0,1,0,1,1], [1,0,1,1,1,1,1]]]
So that it would work with the algorithm mentioned in the first line of my post (I'll copy it here, if it takes too much place I can delete it and leave link only):
G = [0,1,1,0,0,1,1], # A
[1,0,0,0,0,0,0],
[1,0,0,0,0,0,0],
[0,0,0,0,1,1,0],
[0,0,0,1,0,1,1],
[1,0,0,1,1,0,0],
[1,0,0,0,1,0,0] # G
LABLES = %w(A B C D E F G)
def dfs(vertex)
print "#{LABLES[vertex]} " # visited
edge = 0
while edge < G.size
G[vertex][edge] = 0
edge += 1
end
edge = 0
while edge < G.size
if ( G[edge][vertex] != 0 && edge != vertex)
dfs(edge)
end
edge += 1
end
end
dfs(0)
split's default separator is a whitespace. To make it split every char you need to explicitly say it:
'01101101'.split.map(&:to_i)
# => [ 1101101 ]
'01101101'.split('').map(&:to_i)
# => [ 0, 1, 1, 0, 1, 1, 0, 1 ]
you can also use chars to do the same job:
'01101101'.chars.map(&:to_i)
# => [ 0, 1, 1, 0, 1, 1, 0, 1 ]
I don't know how your read_array is used, but it can be simplified to:
def read_array(file_path)
File.foreach(file_path).map do |line|
line.chomp.chars.map(&:to_i)
end
end
read_array('my_file.txt')
# => [[1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1]]
If you still get the extra [, you can either take only the first item:
my_array[0]
Or (if there is more than one item the uber-array) - use flat_map:
uber_array = [[[1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1]],
[[1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1]]]
uber_array.flat_map { |a| a }
# => [[1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1]]

Resources