Chris Pine Ruby ch 10, recursion - ruby

I have been trying to learn ruby using the book 'learn to program' by Chris Pine. I was actually getting excited when going through the book until I got to chapter 10 and the examples used. Now this chapter alone and its examples have completely deflated all of my excitement to continue with this book. In this example I have completely no idea how its trying to count the tiles, or why he uses world [y],[x] when the method was defined with the attribute of continent_size world, x,y? Im not sure how the recursion in this example works. Can someone shed some more light onto this example as to what the author was actually trying to do?
M = 'land'
o = 'water'
world = [
[o,o,o,o,o,M,o,o,o,o,o],
[o,o,o,o,M,M,o,o,o,o,o],
[o,o,o,o,o,M,o,o,M,M,o],
[o,o,o,M,o,M,o,o,o,M,o],
[o,o,o,o,o,M,M,o,o,o,o],
[o,o,o,o,M,M,M,M,o,o,o],
[M,M,M,M,M,M,M,M,M,M,M],
[o,o,o,M,M,o,M,M,M,o,o],
[o,o,o,o,o,o,M,M,o,o,o],
[o,M,o,o,o,M,M,o,o,o,o],
[o,o,o,o,o,M,o,o,o,o,o]]
def continent_size world, x ,y
if x < 0 or x > 10 or y < 0 or y > 10
return 0
end
if world[y][x] != 'land'
return 0
end
size = 1
world [y][x] = 'counted land'
size = size + continent_size(world, x-1, y-1)
size = size + continent_size(world, x , y-1)
size = size + continent_size(world, x+1, y-1)
size = size + continent_size(world, x-1, y )
size = size + continent_size(world, x+1, y )
size = size + continent_size(world, x-1, y+1)
size = size + continent_size(world, x , y+1)
size = size + continent_size(world, x+1, y+1)
size
end
puts continent_size(world, 5, 5)

This is called a flood fill. What it's doing is counting the size of all the pieces of 'land' that are connected to the initial starting point. Note that it doesn't count all of the 'land' symbols, just the ones on that it can't get to because of water.
Flood fill is a form of something called depth first search which is a way to traverse a graph (here, a discrete 'map'). It can be summarized like so:
Visit the current position/graph node, count it and mark it as visited
Check all connected nodes (here, anything up, down, left or right), if they are not visited and they are land, recursively visit them
He might be doing y, x for the following reason: the logical format of a 2D array is organized first by row, then by column. The row could be thought of as the y axis and the column as the x.

For what it's worth I when I worked through this problem in the book I also noticed the transposition of x & y when world is called. I looked on the Pragmatic Programmer's website to see if this was listed in the errata, but it is not.
I thought it was typo and flipped them to x, y. The code works either way.
It doesn't really matter, since the starting point of 5,5 is arbitrary and the code will check all eight tiles around x,y (or y,x) regardless until it hits the "edge" of the array/world.

Taking a few steps back from the other answers, the recursion here is in that continent_size is called eight times from within continent_size.
So the method is being called eight times inside itself.
But ... each of those eight inner methods call continent_size a further eight times.
And so on, and so on.
It's bonkers to get your head around it, but when you do, it feels like you can see The Matrix. Albeit very briefly.
I stumbled across this question looking for some help with the extension bit of the task (how to avoid the error if one of your 'explorers' falls off the edge of the world).
I ended up solving this with a rescue:
# If it's off the edge of the world, it's as good as water
square = world[y][x] rescue 'o'
if square != 'Land'
return 0
end
I don't know if this is the best way to do it, but it seems quite elegant to me.

I feel pretty dirty about the way I solved it, and am here looking for a better answer. I created a new variable, E = 'edge', and changed any character that touched the edge of the map to E. Then I added this code to the top of the continent_size method:
if world[y][x] == 'edge'
return 1
end
It works. :/

It looks like the rescue approach still crashes when the top edge of the world is all 'o's. A simple approach to solving this is to write a conditional that checks if either coordinate (x,y) is outside of the boundary (i.e. outside of 0 or world.length-1) and return 0 if that condition is met.

I also noticed the transposition of x and y in Pine's code.
I think the reasoning might be that he arranged the "world" array so that there is one sub-array on each line. The first number in square brackets following "world" (world[0]) refers to the index of the element (sub-array) within world. Since these are stacked vertically it is your y-axis. The second bracketed number (world[0][5]) refers to the element within the sub-array. These run horizontally so the second number refers to your x-axis. Writing the method to take parameter x then parameter y allows you to enter the staring location in the conventional (x,y) format while within the method the variables are transposed to accomplish the task. I think. I'm completely new to this though.
Also, if anyone has a clean solution to extended version of this exercise where where the continent "borders the edge of the world" I would love to see it

It also took me some time to get the head around this example. I tried to solve the task like this, at first:
if ( world[y] > world[y].length || world[x] > world[x].length ) || ( world[y] < world[y].length || world[x] < world[x].length )
return 0
end
But I kept getting errors of the "undefined method ">" for an array"
I then realised that the solution could be in conditioning "x" and "y", if they are more than an array (10) or lower than (0):
if x > 10 || x < 0 || y > 10 || y < 0
return 0
end
The problem here is that it works on this particular size of arrays...if the world is bigger than 10 - the program would count every "land" it encounters as 0.
So I guess it's half-solution only...

Related

Algorithm to 'count the number of ways a floor of size 5*N can be filled with tiles of sizes 1*5 and 2*5'

Here is the part of the question with copied for ref:
*You are given a floor of size 5xN. You have tiles of 2 different sizes: 1x5 and 2x5. Of course, you can rotate the tiles to get 2 more tile sizes: 5x1 and 5x2. You have to do the flooring using these tiles in the following way:
Floor space should be completely covered by tiles.
You cannot break tiles, ie, you have to use a tile entirely or not at all.
Any tile should not extend beyond the floor space.
Tiles should be placed parallel to the floor boundaries.
Your task is to find the number of ways in which you can lay the tiles on the floor*
Can I get some help with the approach. Thanks in advance.
Edit: I understand now when we have to count the ways to fill floor of size 5*N with tiles of size 5*1. With dp we can achieve it like this
dp[1]=1,dp[2]=1,dp[3]=1,dp[4]=1,dp[5]=2
and dp[n]=dp[n-1]+dp[n-5]
http://www.geeksforgeeks.org/count-number-ways-tile-floor-size-n-x-m-using-1-x-m-size-tiles/
But I don't understand how to formulate dp[n] when there are more than one tile of different sizes. You are given a floor of size 5xN. You have tiles of 2 different sizes: 1x5 and 2x5.
This answer from https://math.stackexchange.com/a/664236/468110 by Steve Kass helped me formulate the recurrence. Posting my solution, for someone may find it useful.
There are total 10 ways first column of 5*n can be tiled by dominos of size 1*5 and 2*5 shown and named in picture below:
We'll count the number with each kind of beginning configuration and add the results up to get f(n).ways to begin tiling
Any 5*(n-1) can extend to give 5*n tiling and there are no other way to get a "type a" 5*n tiling. So, there are f(n-1) "type a" tilings. Similarly, there are f(n-2) "type b" 5*n tiling. Similarly type c to j all have f(n-5) tiling.
That makes:
f(n)=f(a)+f(b)+f(c)+f(d)+f(e)+f(f)+f(g)+f(h)+f(i)+f(j)
f(n)=f(a)+f(b)+8*f(c)
f(n)=f(n-1)+f(n-2)+ 8*f(n-5)
Below is the c++ code used and tested:
int dp[1000000]={0};
int count(int n){
if(n<0){
return 0;
}
dp[0]=1;
dp[1]=1;
dp[2]=2;
if(dp[n]>0){
return dp[n];
}
dp[n] = count(n-1)+count(n-2)+8*count(n-5);
return dp[n];
}
Some DP with memoization should do the trick:
def solve(x, memo={}):
# Access already calculated values
if x in memo:
return memo[x]
# Base cases
if x < 0:
raise Exception("Negative values are not allowed")
if x < 2:
return 1
if x == 2:
return 2
# Place a 1x5 or a 2x5
res = solve(x - 1) + solve(x - 2)
# Fill a space of 5x5
if 5 <= x:
res += 8 * solve(x - 5)
# Store result and return
memo[x] = res
return res
for x in range(100):
print "{}: {}".format(x, solve(x))

Checking for (x,y) in two 1D arrays one x and one y in Ruby

INTRO:
I am trying to program Snakes in Ruby in order to get myself more familiar with Ruby which I just recently started to learn because of its reputation.
So I saved all the coordinates of the Snake in and two arrays one for all the X coordinates and one for all the Y coordinates like this x=[2,0,0]and y=[2,0,0]. When the snakes eats food a new value is added to both arrays like this x[f]=y[f-1] and y[f]=y[f-1] So every part of the snake inherits the position of the part before. So far so good. But then I realized that the food which is placed through the rand() command some times is on the same position as the a part of the snake.
PROBLEM:
That is where my problem is. I tried to fix it like this:
while randomNumberIsSnake == true
if $x.include? randomX
if $y.include? randomY
randomX = 2 + rand(18)
randomY = 2 + rand(18)
else
randomNumberIsSnake = false
end
else
randomNumberIsSnake = false
end
end
To check if the X coordinates of the food is equal to the X coordinate of a part of the snake (in the array). If that is true than it also checks if the Y coordinate is in the Y array. It will then get a new number till that isn't the case anymore.
It seemed to work just right till I looked at it again and found that bug that if the $y.include? randomYand $x.include? randomXboth return true it doesn't necessarily mean that the food is on the same coordinates as a part of the snake but rather if the food was (4,4) and the snake has a part on (4,8) and a part on (8,4) it will also return true. Which creates a situation like this.
-------#
-#-- #
-------#
#####
Where # even though clearly not on the line of #'s still will make the code return true.
QUESTION:
Is there a way to rephrase the code in order to avoid that or would that require to have only one 2D array and not two 1D arrays. Like I mentioned earlier I am still a leaner in Ruby and there for every help would be highly appreciated.
You should write your checker like this one:
coordinates = loop do
randomX = 2 + rand(18)
randomY = 2 + rand(18)
randomNumberIsSnake = false
$x.size.times do |index|
randomNumberIsSnake = true if $x[index] == randomX && $y[index] == randomY
end
break [randomX, randomY] unless randomNumberIsSnake
end
Then in your coordinates local variable you'll have this values.
But I'd recommend you to store all free cell for current moment. Then take one randomly and remove from this array. If for some step array is empty there is no free cells at all. This solution is possible if that's not huge amount of cells.

Global minimum in a huge convex matrix by using small matrices

I have a function J(x,y,z) that gives me the result of those coordinates. This function is convex. What is needed from me is to find the minimum value of this huge matrix.
At first I tried to loop through all of them, calculate then search with min function, but that takes too long ...
so I decided to take advantage of the convexity.
Take a random(for now) set of coordinates, that will be the center of my small 3x3x3 matrice, find the local minimum and make it the center for the next matrice. This will continue until we reach the global minimum.
Another issue is that the function is not perfectly convex, so this problem can appear as well
so I'm thinking of a control measure, when it finds a fake minimum, increase the search range to make sure of it.
How would you advise me to go with it? Is this approach good? Or should I look into something else?
This is something I started myself but I am fairly new to Matlab and I am not sure how to continue.
clear all
clc
min=100;
%the initial size of the search matrix 2*level +1
level=1;
i=input('Enter the starting coordinate for i (X) : ');
j=input('Enter the starting coordinate for j (Y) : ');
k=input('Enter the starting coordinate for k (Z) : ');
for m=i-level:i+level
for n=j-level:j+level
for p=k-level:k+level
A(m,n,p)=J(m,n,p);
if A(m,n,p)<min
min=A(m,n,p);
end
end
end
end
display(min, 'Minim');
[r,c,d] = ind2sub(size(A),find(A ==min));
display(r,'X');
display(c,'Y');
display(d,'Z');
Any guidance, improvement and constructive criticism are appreciated. Thanks in advance.
Try fminsearch because it is fairly general and easy to use. This is especially easy if you can specify your function anonymously. For example:
aFunc = #(x)100*(x(2)-x(1)^2)^2+(1-x(1))^2
then using fminsearch:
[x,fval] = fminsearch( aFunc, [-1.2, 1]);
If your 3-dimensional function, J(x,y,z), can be described anonymously or as regular function, then you can try fminsearch. The input takes a vector so you would need to write your function as J(X) where X is a vector of length 3 so x=X(1), y=X(2), z=X(3)
fminseach can fail especially if the starting point is not near the solution. It is often better to refine the initial starting point. For example, the code below samples a patch around the starting vector and generally improves the chances of finding the global minimum.
% deltaR is used to refine the start vector with scatter min search over
% region defined by a path of [-deltaR+starVec(i):dx:deltaR+startVec(i)] on
% a side.
% Determine dx using maxIter.
maxIter = 1e4;
dx = max( ( 2*deltaR+1)^2/maxIter, 1/8);
dim = length( startVec);
[x,y] = meshgrid( [-deltaR:dx:deltaR]);
xV = zeros( length(x(:)), dim);
% Alternate patches as sequential x-y grids.
for ii = 1:2:dim
xV(:, ii) = startVec(ii) + x(:);
end
for ii = 2:2:dim
xV(:, ii) = startVec(ii) + y(:);
end
% Find the scatter min index to update startVec.
for ii = 1: length( xV)
nS(ii)=aFunc( xV(ii,:));
end
[fSmin, iw] = min( nS);
startVec = xV( iw,:);
fSmin = fSmin
startVec = startVec
[x,fval] = fminsearch( aFunc, startVec);
You can run a 2 dimensional test case f(x,y)=z on AlgorithmHub. The app is running the above code in Octave. You can edit the in-line function (possibly even try your problem) from this web-site as well.

How map/tween a number based on a dynamic curve

I am really lacking terminology here, so any help with that appreciate. Even it doesn't answer the question it can hopefully get me closer to an answer.
How can I get y from a function of p where the curviness is also a variable (possible between 0 and 1? Or whatever is best?).
I am presuming p is always between 1 and 0, as is the output y.
The graphic is just an illustration, I don't need that exact curve but something close to this idea.
Pseudo code is good enough as an answer or something c-style (c, javascript, etc).
To give a little context, I have a mapping function where one parameter can be the – what I have called – easing function. There are based on the penner equations. So, for example if I wanted to do a easeIn I would provide:
function (p) { return p * p; };
But I would love to be able to do what is in the images: varying the ease dynamically. With a function like:
function (p, curviness) { return /* something */; }
You might try messing around with a Superellipse, it seems to have the shape malleability you're looking for. (Special case: Squircle)
Update
Ok, so the equation for the superellipse is as follows:
abs(x/a)^n + abs(y/b)^n = 1
You're going to be working in the range from [0,1] in both so we can discard the absolute values.
The a and b are for the major and minor ellipse axes; we're going to set those to 1 (so that the superellipse only stretches to +/-1 in either direction) and only look at the first quadrant ([0, 1], again).
This leaves us with:
x^n + y^n = 1
You want your end function to look something like:
y = f(p, n)
so we need to get things into that form (solve for y).
Your initial thought on what to do next was correct (but the variables were switched):
y^n = 1 - p^n
substituting your variable p for x.
Now, initially I'd thought of trying to use a log to isolate y, but that would mean we'd have to take log_y on both sides which would not isolate it. Instead, we can take the nth root to cancel the n, thus isolating y:
y = nthRoot(n, 1 - p^n)
If this is confusing, then this might help: square rooting is just raising to a power of 1/2, so if you took a square root of x you'd have:
sqrt(x) == x^(1/2)
and what we did was take the nth root, meaning that we raised things to the 1/n power, which cancels the nth power the y had since you'd be multiplying them:
(y^n)^(1/n) == y^(n * 1/n) == y^1 == y
Thus we can write things as
y = (1 - p^n)^(1/n)
to make things look better.
So, now we have an equation in the form
y = f(p, n)
but we're not done yet: this equation was working with values in the first quadrant of the superellipse; this quadrant's graph looks different from what you wanted -- you wanted what appeared in the second quadrant, only shifted over.
We can rectify this by inverting the graph in the first quadrant. We'll do this by subtracting it from 1. Thus, the final equation will be:
y = 1 - (1 - p^n)^(1/n)
which works just fine by my TI-83's reckoning.
Note: In the Wikipedia article, they mention that when n is between 0 and 1 then the curve will be bowed down/in, when n is equal to 1 you get a straight line, and when n is greater than 1 then it will be bowed out. However, since we're subtracting things from 1, this behavior is reversed! (So 0 thru 1 means it's bowed out, and greater than 1 means it's bowed in).
And there you have it -- I hope that's what you were looking for :)
Your curviness property is the exponent.
function(p, exp) { return Math.pow(p, exp); }
exp = 1 gives you the straight line
exp > 1 gives you the exponential lines (bottom two)
0 < exp < 1 gives you the logarithmic lines (top two)
To get "matching" curviness above and below, an exp = 2 would match an exp = 1/2 across the linear dividing line, so you could define a "curviness" function that makes it more intuitive for you.
function curvyInterpolator(p, curviness) {
curviness = curviness > 0 ? curviness : 1/(-curviness);
return Math.pow(p, curviness);
}

OpenCV: parabola detection using Hough Transform

I want to detect parabola(s) of type : y^2 = 4a*x in an image[size: 512 X 512]. I prepared an accumulator array, acc[size: 512 X 512 X 512]. I prepared a MATRIX corresponding to that image. I used hough-transform. This is how I did it:
for x = 1 to 512
for y= 1 to 512
if image_matrix(x,y)> 245//almost white value, so probable to be in parabola
{
for x1= 1 to 512
for y1= 1 to 512
{
calculate 'a' from (y-y1)^2 = 4*a*(x-x1).
increment acc(i,j,k) by 1
}
}
if acc(i,j,k) has a maximum value.
{
x1=i, y1=j,a =k
}
I faced following problems:
1) acc[512][512][512] takes large memory. It needs huge computation.How can I decrease array size and thus minimize computation?
2) Not always max valued-entry of acc(i,j,k) give intended output. Sometimes second or third maximum, and even 10'th maximum value give the intended output. I need approx. value of 'a', 'x1','y1'(not exact value).
Please help me. Is there any wrong in my concept?
What i'm going to say may only partly answer your question, but it should work.
If you want to find these type of parabolas
y^2 = 4a*x
Then they are parametrized by only one parameter which is 'a'. Therefore, i don't really understand why you use a accumulator of 3 dimensions.
For sure, if you want to find a parabola with a more general equation like :
y = ax^2 + bx + c
or in the y direction by replacing x by y, you will need a 3-dimension accumulator like in your example.
I think in your case the problem could be solved easily, saying you only need one accumulator (as you have only one parameter to accumulate : a)
That's what i would suggest :
for every point (x,y) of your image (x=0 exclusive) {
calculate (a = y^2 / 4x )
add + 1 in the corresponding 'a' cell of your accumulator
(eg: a = index of a simple table)
}
for all the cells of your accumulator {
if (cell[idx] > a certain threshold) there is a certain parabola with a = idx
}
I hope it can help you,
This is as well an interesting thing to look at :
Julien,

Resources