checking multiple values at once - ruby

So, pondering the X/Y of it all, I guess the real question here is- how DO you store data for a logic puzzle like this one? (Scroll to the bottom for a link to the puzzle.) I have chosen this hash, where every piece of the puzzle gets its own key + 5 offsets which can be marked T, F or nil. I am using 9 for nil because I can add 9, and the logic I am using leans on simple math.
I need to check seven values, and if all of them are a certain way, I then go set some other values. This check runs three times- it cycles through the three positions where the set of four nines and three zeros can appear
Here's my hash- in actual use, it starts out all nines, 9 = null, 1 = true, 0 = false
$l1 = {
1 => [9,9,9,9,9], 2 => [9,9,9,9,9], 3 => [ , ,9, ,0], 4 => [ , , , ,0], 5 => [ , , , ,0],
6 => [9,9,9,9,9], 7 => [9,9,9,9,9], 8 => [ , ,9, ,9], 9 => [ , ,9, ,0], 10 => [ , , , ,0],
11 => [9,9,9,9,9], 12 => [9,9,9,9,9], 13 => [ , , , ,9], 14 => [ , ,9, ,9], 15 => [ , ,9, ,0],
16 => [9,9,9,9,9], 17 => [9,9,9,9,9], 18 => [ , , , ,0], 19 => [ , , , ,9], 20 => [ , ,9, ,9],
21 => [9,9,9,9,9], 22 => [9,9,9,9,9], 23 => [ , , , ,0], 24 => [ , , , ,0], 25 => [ , , , ,9]}
Shown here as an example, the three instances of the pattern we are checking for appear in the 3rd, 4th, and 5th columns. I have removed the other values so you can see it better.
The pattern itself should appear in the second column, [2,7,12,17,22].
Here's the first iteration of the check using &&:
if $l1[2][2] == 9 && $l1[7][2] == 9 && $l1[7][4] == 9 && $l1[12][4] == 9 && $l1[2][4] == 0 && $l1[17][4] == 0 && $l1[22][4] == 0
$l1[7][0] = 0
$l1[7][1] = 0
$l1[7][3] = 0
end
I also tried it with seven nested if statements, and it just stops matching two or three steps in even though the pattern is a perfect match.
How else can you check something like this? Thanks in advance for any help!
EDIT: expanding the explanation- I have zero formal training in programming other than a few courses at codecademy. I am building logic to solve the "Whose Fish?" puzzle, http://www.coudal.com/thefish.php , and this puzzle goes something like this-
There are five men, from five countries, living in a row in five different colored houses, they smoke different brands, drink different drinks, and have different pets.
There are 15 clues and no way to solve this without a pencil and paper or a spreadsheet... my goal is to make logic which solves the puzzle withOUT making any intuitive leaps myself- they have to be coded into the program. Clues + logic either solve it, or I need to re-tune the logic.
I have a lot of the logic built already, but the specific logic for this special clue "the green house is on the left of the white house" is proving pretty difficult to build.
I didn't want to mire eveyone down in a very long story, but it looks unavoidable...
So yes, the data all being stored in one hash like this is possibly counter-intuitive, but it's all I could come up with for data storage that I could figure out how to easily dump out and view to debug, and it's easy to look at for me as a beginner, and I have already switched it from five separate hashes- one for each man- because I ran into problems understanding how to build the logic when there were five hashes. Thanks again for looking into this with me.

Well, I do not know why it is working now, but I took my three nested passes and made them into their own methods, and it's working right.
Before it was like this:
if X == 1
if Y == 2
if Z == 3
Q = 0
end
end
elsif X == 2
if Y == 3
if Z == 4
Q = 1
end
end
end
Now it's like this
def first
if X == 1
if Y == 2
if Z == 3
Q = 0
end
end
end
end
def second
if X == 2
if Y == 3
if Z == 4
Q = 1
end
end
end
end

Related

Ruby: find multiples of 3 and 5 up to n. Can't figure out what's wrong with my code. Advice based on my code please

I have been attempting the test below on codewars. I am relatively new to coding and will look for more appropriate solutions as well as asking you for feedback on my code. I have written the solution at the bottom and for the life of me cannot understand what is missing as the resultant figure is always 0. I'd very much appreciate feedback on my code for the problem and not just giving your best solution to the problem. Although both would be much appreciated. Thank you in advance!
The test posed is:
If we list all the natural numbers below 10 that are multiples of 3 or
5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Finish the solution so that it returns the sum of all the multiples of
3 or 5 below the number passed in. Additionally, if the number is
negative, return 0 (for languages that do have them).
Note: If the number is a multiple of both 3 and 5, only count it once.
My code is as follows:
def solution(number)
array = [1..number]
multiples = []
if number < 0
return 0
else
array.each { |x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
}
end
return multiples.sum
end
In a situation like this, when something in your code produces an unexpected result you should debug it, meaning, run it line by line with the same argument and see what each variable holds. Using some kind of interactive console for running code (like irb) is very helpfull.
Moving to your example, let's start from the beginning:
number = 10
array = [1..number]
puts array.size # => 1 - wait what?
puts array[0].class # => Range
As you can see the array variable doesn't contain numbers but rather a Range object. After you finish filtering the array the result is an empty array that sums to 0.
Regardless of that, Ruby has a lot of built-in methods that can help you accomplish the same problem typing fewer words, for example:
multiples_of_3_and_5 = array.select { |number| number % 3 == 0 || number % 5 == 0 }
When writing a multiline block of code, prefer the do, end syntax, for example:
array.each do |x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
end
I'm not suggesting that this is the best approach per se, but using your specific code, you could fix the MAIN problem by editing the first line of your code in one of 2 ways:
By either converting your range to an array. Something like this would do the trick:
array = (1..number).to_a
or by just using a range INSTEAD of an array like so:
range = 1..number
The latter solution inserted into your code might look like this:
number = 17
range = 1..number
multiples = []
if number < 0
return 0
else range.each{|x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
}
end
multiples.sum
#=> 60
The statement return followed by end suggests that you were writing a method, but the def statement is missing. I believe that should be
def tot_sum(number, array)
multiples = []
if number < 0
return 0
else array.each{|x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
}
end
return multiples.sum
end
As you point out, however, this double-counts numbers that are multiples of 15.
Let me suggest a more efficient way of writing that. First consider the sum of numbers that are multiples of 3 that do not exceed a given number n.
Suppose
n = 3
m = 16
then the total of numbers that are multiples of three that do not exceed 16 can be computed as follows:
3 * 1 + 3 * 2 + 3 * 3 + 3 * 4 + 3 * 5
= 3 * (1 + 2 + 3 + 4 + 5)
= 3 * 5 * (1 + 5)/2
= 45
This makes use of the fact that 5 * (1 + 5)/2 equals the sum of an algebraic series: (1 + 2 + 3 + 4 + 5).
We may write a helper method to compute this sum for any number n, with m being the number that multiples of n cannot exceed:
def tot_sum(n, m)
p = m/n
n * p * (1 + p)/2
end
For example,
tot_sum(3, 16)
#=> 45
We may now write a method that gives the desired result (remembering that we need to account for the fact that multiples of 15 are multiples of both 3 and 5):
def tot(m)
tot_sum(3, m) + tot_sum(5, m) - tot_sum(15, m)
end
tot( 9) #=> 23
tot( 16) #=> 60
tot(9999) #=> 23331668

Ruby equivalent to JavaScript operator `||`

How can this be achieved in Ruby? Can it be done without repeating the variable?
Javascript:
b = a || 7
This assigns a if a is not 0 and 7 otherwise
One specific case is converting date.wday to 7 if it returns 0 (Sunday).
Just out of curiosity:
class Object
def javascript_or?(other)
(is_a?(FalseClass) || nil? || '' == self || 0 == self) ? nil : self
end
end
and:
a = b.javascript_or?(7)
There are only two falsy values in Ruby: nil and false. So, if you really want this approach
a = b == 0 ? 7 : b
is a plausible solution, because 0 can't be evaluated as false.
However, a better option for your need is cwday, and not wday. Then you don't need to make this comparison anymore, because it returns 1 for Monday, 2 for Tuesday, and finally 7 for Sunday, as you need.
date = Date.new(2016,19,6) # => Jun 19 2016, Sunday
date.cwday # => 7
For the particular case of 0 and 7:
a = (b + 6) % 7 + 1
:)
You can use ternary operator:
date.wday == 0 ? 7 : date.wday
What you're describing here is less of a logical problem and more of a mapping one:
WEEKDAY_MAP = Hash.new { |h,k| h[k] = k < 7 ? k : nil }.merge(0 => 7)
This one re-writes 1..6 to be the same, but 0 becomes 7. All other values are nil.
Then you can use this to re-write your day indicies:
b = WEEKDAY_MAP[a]
If at some point you want to tinker with the logic some more, that's also possible.

Bitmasking in Ruby: Get numbers which generated the bitmask

Currently I am storing a combination of a group of items as a single integer (bitmasked value) as in the example below:
Example:
1 - Orange
2 - Banana
4 - Cherry
8 - Apple
And then if the user selects Orange (1) and Apple (8) then the sum of those is 9.
And it is always and only when those two items are combined this value is 9.
And you are able to recover the original two numbers which were used to generate this number.
Here is an example website which does the trick:
http://www.tfxsoft.com/playground/calc.php
What I need:
I need an algoritm (preferrably in Ruby) which would take the sum (9) of those two bitmask values and return the values which it "contains" ( 1 and 4).
I think this might be what you're looking for:
FRUIT = { 1 => 'Orange', 2 => 'Banana', 4 => 'Cherry', 8 => 'Apple' }
def mask_fruit(a, b) a | b end
def unmask_fruit(masked)
FRUIT.select { |k, _| (masked | k) == masked }
end
mask = mask_fruit 1, 8 # => 9
unmask_fruit mask # => {1=>"Orange", 8=>"Apple"}
As I understand you want to find position of bits. Here is simple but not optimal solution:
2.1.5 :033 > 9.to_s(2).reverse.chars.map.with_index { |b, i| b == "1" ? i + 1 : nil }.compact
=> [1, 4]

Iterate through three values stored in two places

Edited some more. This is a tiny sample of a larger processing piece. My puzzle-solving logic needs to check a one of five attributes for one person against one of five attributes for his four neighbors. This example only shows three. In $hash1, the truth table, rows are people, columns are attributes. We're assuming managing the attributes- I will leave that info out as it's not central to this.
$hash1[2][2] is an 'edge', so it only needs to check against one neighbor, $hash[6][value1]
$hash1[5][2] is a 'middle', so it needs to check against two neighbors, $hash[3][value2] and $hash[9][value2]
The values in $hash1 are 9 for nil, 1 for true, 0 for false
so, in this case, "The man who wears red shirts lives next to the man who drives a Ford"
If we're checking to see if red shirt has been assigned, we need to look through keys 2, 5, 8. When the logic hits $hash1[5][2], and red shirt is 1, logic will check to see if one neighbor is nil and the other has a car which is not a Ford. If true, the nil car gets set to Ford.
$hash1 = {1 => [9,9,9], 2 => [9,9,9], 3 => [9,9,9],
4 => [9,9,9], 5 => [9,1,9], 6 => [9,9,9],
7 => [9,9,9], 8 => [9,9,9], 9 => [9,9,1], 99 => [1,1,1]}
$arry1 = [2,5,8]
$hash2 = { 6 => 99, 3 => 9, 99 => 6}
(entry 99 is there to allow the code to use the same checking mechanism for edges as middles, because if it's the guy on the end, then he only has one neighbor, so if he has a red shirt, his neighbor definitely drives a Ford.)
I need it to loop three times total:
checking 2 and 6 , 99
then 5 and 3 , 9
then 8 and 99 , 6
I have everything working except for looping with the three values from two separate places- I am tinkering with this now-
$arry1.zip($hash2).map(&:flatten).each |key,key2,key3|
Except what it is doing is merging them into this:
<2, 6, 99> <5, 99, 6> <8>
For a longer array, same pattern- the first and last hash pairs come in and get paired with the first two array values, and the rest of the array values are unchanged.
Assuming your code is complete, you should be able to substitute:
val1.each do |key1|
val2.each do |key2,key3|
with
val1.zip(val_2).map(&:flatten).each do |key1, key2, key3|
Additional note: When you're done with solving the problem, post it to codereview.stackexchange.com` so we can do sth with the smell. :)

Matlab best match of a sequence within a matrix

I want to find the best match of a sequence of integers within a NxN matrix. The problem is that I don't know how to extract the position of this best match. The following code that I have should calculate the edit distance but I would like to know where in my grid that edit distance is shortest!
function res = searchWordDistance(word,grid)
% wordsize = length(word); % extract the actual size
% [x ,y] = find(word(1) == grid);
D(1,1,1)=0;
for i=2:length(word)+1
D(i,1,1) = D(i-1,1,1)+1;
end
for j=2:length(grid)
D(1,1,j) = D(1,1,j-1)+1;
D(1,j,1) = D(1,j-1,1)+1;
end
% inspect the grid for best match
for i=2:length(word)
for j=2:length(grid)
for z=2:length(grid)
if(word(i-1)==grid(j-1,z-1))
d = 0;
else
d=1;
end
c1=D(i-1,j-1,z-1)+d;
c2=D(i-1,j,z)+1;
c3=D(i,j-1,z-1)+1;
D(i,j,z) = min([c1 c2 c3]);
end
end
end
I have used this code (in one less dimension) to compare two strings.
EDIT Using a 5x5 matrix as example
15 17 19 20 22
14 8 1 15 24
11 4 17 3 2
14 2 1 14 8
19 23 5 1 22
now If I have a sequence [4,1,1] and [15,14,12,14] they should be found using the algorithm. The first one is a perfect match(diagonal starts at (3,2)). The second one is on the first column and is the closest match for that sequence since only one number is wrong.

Resources