Modifying 2D array elements - ruby

Basically I'm trying to modify an element in a 2D array. How the array is initialized seems to be effecting the behaviour. I'm new to Ruby so I'm not sure how initializing an array would effect this. What am I missing to get the desired result from the desired initialization method?
arr_1 = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] # undesired initialization
arr_2 = Array.new(3, Array.new(3, 0)) # desired initialization
arr_1[0][0] = 99
puts arr_1 # desired result below
=begin
99
0
0
0
0
0
0
0
0
=end
arr_2[0][0] = 99
puts arr_2 # undesired result
=begin
99
0
0
99
0
0
99
0
0
=end

Related

convert adjacency matrix to adjacency list to represent a graph

I am trying to write a code in Ruby to convert a given graph's adjacency matrix to adjacency list. When I verified my solution for the given input mentioned here my output was slightly different. I need some direction in understanding where exactly is the issue. Below is my code:
def convert_adj_matrix_to_list(arr)
list = {}
start = 0
last = 0
arr.each_index do |row|
# row = [0, 0, 1]
puts "row = #{arr[row]}"
for col in 0..arr[row].size-1
puts "row = #{row}, col = #{col}"
puts "arr[#{row}][#{col}] = #{arr[row][col]}"
list[row] = col if arr[row][col] == 1
end
puts
end
list
end
arr = [ [0, 0, 1], [0, 0, 1], [1, 1, 0] ]
puts convert_adj_matrix_to_list(arr)
Output:
row = [0, 0, 1]
row = 0, col = 0
arr[0][0] = 0
row = 0, col = 1
arr[0][1] = 0
row = 0, col = 2
arr[0][2] = 1
row = [0, 0, 1]
row = 1, col = 0
arr[1][0] = 0
row = 1, col = 1
arr[1][1] = 0
row = 1, col = 2
arr[1][2] = 1
row = [1, 1, 0]
row = 2, col = 0
arr[2][0] = 1
row = 2, col = 1
arr[2][1] = 1
row = 2, col = 2
arr[2][2] = 0
{0=>2, 1=>2, 2=>1}
Printing inside a function like this is a side effect and should only be done for debugging. Better to print in the caller.
Logically, your code is only keeping track of the last link seen in the matrix and returns a hash that maps ints to ints rather than a hash that maps ints to int arrays.
You can get the hash by mapping each row, then filtering the row's indexes based on whether the cell has the value 1. Once you've built a 2d array of pairs with the first item in a pair being the source node integer and the second item in a pair being the array of its edges to other nodes, call .to_h on it to produce a hash.
def adj_matrix_to_list(matrix)
matrix.each_with_index.map do |row, i|
[i, row.each_index.select {|j| row[j] == 1}]
end.to_h
end
matrix = [[0, 0, 1],
[0, 0, 1],
[1, 1, 0]]
p adj_matrix_to_list(matrix) # {0=>[2], 1=>[2], 2=>[0, 1]}
Having said that, using a hash with keys 0, 1, 2 ... n is an antipattern, since arrays are a faster and more natural way to represent a sequentially-indexed list of things. I'd do:
def adj_matrix_to_list(matrix)
matrix.map do |row|
row.each_index.select {|i| row[i] == 1}
end
end
matrix = [[0, 0, 1],
[0, 0, 1],
[1, 1, 0]]
p adj_matrix_to_list(matrix) # [[2], [2], [0, 1]]
and usage is the same as the hash version.

algorithm to convert array integers into RGB value (similar to excel)

Given an array of positive and negative integers...
I would like to return a green or red gradient color relative to its value in the array.
Similar to excel conditional formatting, the greens should be darker the closer they are to the maximum. lighter if they're closer to the minimum.
SIMILAR TO THIS IMAGE BELOW:
Currently i'm doing this
if value == Float::INFINITY
"rgba(0,255,0,1)"
elsif value > 0
"rgba(0,255,0, #{Rational(value, maximum).to_f.round(2)})"
else
"rgba(255,0,0,#{Rational(value, minimum).to_f.abs.round(2)})"
end
Suppose you have an array of floats (or strings or BigDecimals converted to floats):
arr = [
[25.1, 13.5, 4.3],
[28.3, 11.6, 5.9],
[16.5, 17.3, 6.4]
]
It's of course arbitrary how these numbers are to be converted to shades of red and green, but here is one possibility. Suppose we compute:
mn, mx = arr.flatten.minmax
#=> [4.3, 28.3]
av = (mn+mx).fdiv(2)
#=> 16.3
Then the red hues could decrease linearly from 255 at 28.3 to 0 at 16.3 and the green hues could increase linearly from 0 at 16.3 to 255 at 4.3:
def rg_gradient(arr)
mn, mx = arr.flatten.minmax
av = (mn+mx).fdiv(2)
above = mx-av
below = av-mn
arr.map do |a|
a.map { |n| n > av ? [(255*(n-av)/above).round, 0] :
[0, (255*(1-(av-n)/below)).round] }
end
end
rg_gradient(arr)
#=> [[[187, 0], [ 0, 195], [0, 0]],
# [[255, 0], [ 0, 155], [0, 34]],
# [[ 4, 0], [21, 0], [0, 45]]]

iterate with a method within ruby class

Class Image initializes with an array of 0's and 1's. I have method transform, such that
[[0,0,0],
[0,1,0],
[0,0,0]]
returns
[[0,1,0],
[1,1,1],
[0,1,0]]
I want to implement method blur(n), which iterates n times with transform, such calling blur(2) with
[[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,1,0,0,0,0],
[0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0]]
returns
[[0,0,0,0,1,0,0,0,0],
[0,0,0,1,1,1,0,0,0],
[0,0,1,1,1,1,1,0,0],
[0,0,0,1,1,1,0,0,0],
[0,0,0,0,1,0,0,0,0]]
I'm trying to use transform iteratively to achieve this, but I'm getting undefined method 'map' for #<Context::Image:0x000000012eb020> when calling blur with an instance of Image. How can I iterate over each successive transformation, such that blur returns the latest version with the maximum n transformations?
class Image
attr_accessor :array
def initialize(array)
self.array = array
end
def output_image
self.array.each do |item|
puts item.join
end
end
def transform #changes adjacent a 1's adjacent 0's into 1
cloned = self.array.map(&:clone)
#scan original array for 1; map crosses into clone if found
self.array.each.with_index do |row, row_index|
row.each.with_index do |cell, col|
if cell == 1
cloned[row_index][col+1] = 1 unless col+1 >= row.length #copy right
cloned[row_index+1][col] = 1 unless row_index+1 >= cloned.length # copy down
cloned[row_index][col-1] = 1 unless col.zero? # copy left
cloned[row_index-1][col] = 1 unless row_index.zero? #copy up
end
end
end
cloned
end
def blur(n) #should call transform iteratively n times
blurred = Image.new(self)
n.times do
blurred = blurred.transform
end
blurred
end
end
You could use the Matrix class.
require 'matrix'
class Matrix
def el(r,c)
if r < 0 || r >= row_count || c < 0 || c >= column_count
0
else
self[r,c]
end
end
def transform
Matrix.build(row_count, column_count) { |r,c|
[el(r,c), el(r-1,c), el(r+1,c), el(r,c-1), el(r,c+1)].max }
end
end
Given a row-column pair, r, c, the helper method el returns 0 if the row or column is outside the bounds of the matrix and the value at [r,c] otherwise.
nrows = 5
ncols = 5
m = Matrix.build(nrows, ncols) { |r,c| (r==nrows/2 && c==ncols/2) ? 1 : 0 }
#=> Matrix[[0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 1, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 0, 0, 0]]
m = m.transform
#=> Matrix[[0, 0, 0, 0, 0],
# [0, 0, 1, 0, 0],
# [0, 1, 1, 1, 0],
# [0, 0, 1, 0, 0],
# [0, 0, 0, 0, 0]]
m = m.transform
# Matrix[[0, 0, 1, 0, 0],
# [0, 1, 1, 1, 0],
# [1, 1, 1, 1, 1],
# [0, 1, 1, 1, 0],
# [0, 0, 1, 0, 0]]
m.to_a
#=> [[0, 0, 1, 0, 0],
# [0, 1, 1, 1, 0],
# [1, 1, 1, 1, 1],
# [0, 1, 1, 1, 0],
# [0, 0, 1, 0, 0]]
map is a method available to an Array, but not to your custom class Image.
I suggest calling map on your instance variable #array instead. Then, when your transforms are completed, create a new Image instance with that transformed array.
Below is an example of code that should work. Note that transform and blur take input arrays as parameters, so they do not rely on any instance state. Therefore, I've made them class methods instead of instance methods. This allows your users to use them without having to create an instance, if all they want to do is the array transformation. It also makes those methods easy to extract to a module in future refactorings. I've added an instance method, blurred_image, which applies the transformation to the instance and returns a new Image instance.
def self.transform(input_array) #changes adjacent a 1's adjacent 0's into 1
cloned = input_array.map(&:clone)
#scan original array for 1; map crosses into clone if found
input_array.each.with_index do |row, row_index|
row.each.with_index do |cell, col|
if cell == 1
cloned[row_index][col+1] = 1 unless col+1 >= row.length #copy right
cloned[row_index+1][col] = 1 unless row_index+1 >= cloned.length # copy down
cloned[row_index][col-1] = 1 unless col.zero? # copy left
cloned[row_index-1][col] = 1 unless row_index.zero? #copy up
end
end
end
cloned
end
def self.blur(input_array, transform_count) #should call transform iteratively n times
blurred = input_array
transform_count.times { blurred = transform(blurred) }
Image.new(blurred)
end
def blurred_image(transform_count)
self.class.new(self.class.blur(array, transform_count))
end

Way to count number of digits in a Bignum

I am writing a ruby method as follows:
def build_array(word)
word_copy = word
array = []
length = word_copy.length
for i in 1..length do
array[i] = word_copy % 10
word_copy = word_copy/10;
end
puts array
end
I would like to create an iterator which counts from 1 to the number of digits in a Bignum.
Bignum.length is not valid ruby. Alternatively, is there a way to bust up a Bignum into an array of its constituent digits (Which is what I am trying to implement).
Thanks!
x = 424723894070784320891478329472334238899
Math.log10(x).floor + 1 # => 39
or
Math.log10(x + 1).ceil # => 39
You could convert it to String and count its size
b = 2_999_999_000_000_000
p b.class
#=> Bignum
p b.to_s.size
#=> 16
p b.to_s.split("").map(&:to_i)
#=> [2, 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Distributions using nested loops

I would like to write a program which generates all distributions for a given n.
For example, if I enter n equal to 7, the returned result will be:
7
6 1
5 2
5 1 1
4 3
4 2 1
4 1 1 1
3 3 1
3 2 2
3 2 1 1
3 1 1 1 1
2 2 2 1
2 2 1 1 1
2 1 1 1 1 1
1 1 1 1 1 1 1
I wrote the following code:
def sum(a, n)
for i in 1..a.length
a.each do |a|
z = a+i
if z == n
print i
puts a
end
end
end
end
def distribution(n)
numbers_container = []
for i in 1..n-1
numbers_container<<i
end
sum(numbers_container,n)
end
puts "Enter n"
n = gets.chomp.to_i
distribution(n)
I'm stuck in the part where the program needs to check the sum for more than two augends. I don't have an idea how can I write a second loop.
I suggest you use recursion.
Code
def all_the_sums(n, mx=n, p=[])
return [p] if n.zero?
mx.downto(1).each_with_object([]) { |i,a|
a.concat(all_the_sums(n-i, [n-i,i].min, p + [i])) }
end
Example
all_the_sums(7)
#=> [[7],
# [6, 1],
# [5, 2], [5, 1, 1],
# [4, 3], [4, 2, 1], [4, 1, 1, 1],
# [3, 3, 1], [3, 2, 2], [3, 2, 1, 1], [3, 1, 1, 1, 1],
# [2, 2, 2, 1], [2, 2, 1, 1, 1], [2, 1, 1, 1, 1, 1],
# [1, 1, 1, 1, 1, 1, 1]]
Explanation
The argument mx is to avoid the generation of permuations of results. For example, one sequence is [4,2,1]. There are six permutations of the elements of this array (e.g., [4,1,2], [2,4,1] and so on), but we want just one.
Now consider the calculations performed by:
all_the_sums(3)
Each eight-space indentation below reflects a recursive call to the method.
We begin with
n = 3
mx = 3
p = []
return [[]] if 3.zero? #=> no return
# first value passed block by 3.downto(1)..
i = 3
a = []
# invoke all_the_sums(0, [0,3].min, []+[3])
all_the_sums(0, 0, [3])
return [[3]] if 0.zero? #=> return [[3]]
a.concat([[3]]) #=> [].concat([[3]]) => [[3]]
# second value passed block by 3.downto(1)..
i = 2
a = [[3]]
# invoke all_the_sums(1, [1,2].min, []+[2])
all_the_sums(1, 1, [2])
return [[2]] if 1.zero? #=> do not return
# first and only value passed block by 1.downto(1)..
i = 1
a = []
# invoke all_the_sums(0, [0,1].min, [2]+[1])
all_the_sums(0, 0, [2,1])
return [[2,1]] if 0.zero? #=> [[2,1]] returned
a.concat([[2,1]]) #=> [].concat([[2,1]]) => [[2,1]]
return a #=> [[2,1]]
a.concat([[2,1]]) #=> [[3]].concat([[2,1]]) => [[3],[2,1]]
# third and last value passed block by 3.downto(1)..
i = 1
a = [[3],[2,1]]
# invoke all_the_sums(2, [2,1].min, [1])
all_the_sums(2, 1, [1])
return [] if 2.zero? #=> [] not returned
# first and only value passed block by 1.downto(1)..
i = 1
a = []
# invoke all_the_sums(1, [1,1].min, [1]+[1])
all_the_sums(1, 1, [1,1])
return [1,1] if 1.zero? #=> [1,1] not returned
# first and only value passed block by 1.downto(1)..
i = 1
a = []
# invoke all_the_sums(0, [0,1].min, [1,1]+[1]])
all_the_sums(0, 0, [1,1,1])
return [1,1,1] if 1.zero?
#=> return [1,1,1]
a.concat([[1,1,1]]) #=> [[1,1,1]]
return a #=> [[1,1,1]]
a.concat([[1,1,1]]) #=> [].concat([[1,1,1]]) => [[1,1,1]]
return a #=> [[1,1,1]]
a.concat([[1,1,1]]) #=> [[3],[2,1]].concat([[1,1,1]])
return a #=> [[3],[2,1],[1,1,1]]
You can use unary with parameters to have infinite amounts of parameters:
def test_method *parameters
puts parameters
puts parameters.class
end
test_method("a", "b", "c", "d")
So, parameters inside the block becomes an array of parameters. You can then easly loop through them:
parameters.each { |par| p par }
Also, don't use for loops for this as they are less readable than using each methods.
[1..n-1].each do i
# body omitted
end
I think you be able to work it out if you tried to call sum recursively. After this bit:
print i
puts a
Try calling sum again, like this:
sum((1..a).to_a, a)
It won't solve it, but it might lead you in the right direction.

Resources