Related
I am working in MATLAB to process two 512x512 images, the domain image and the range image. What I am trying to accomplish is the following:
Divide both domain and range images into 8x8 pixel blocks
For each 8x8 block in the domain image, I have to apply a linear transformations to it and compare each of the 4096 transformed blocks with each of the 4096 range blocks.
Compute error in each case between the transformed block and the range image block and find the minimum error.
Finally I'll have for each 8x8 range block, the id of the 8x8 domain block for which the error was minimum (error between the range block and the transformed domain block)
To achieve this, I have written the following code:
RangeImagecolor = imread('input.png'); %input is 512x512
DomainImagecolor = imread('input.png'); %Range and Domain images are identical
RangeImagetemp = rgb2gray(RangeImagecolor);
DomainImagetemp = rgb2gray(DomainImagecolor);
RangeImage = im2double(RangeImagetemp);
DomainImage = im2double(DomainImagetemp);
%For the (k,l)th 8x8 range image block
for k = 1:64
for l = 1:64
minerror = 9999;
min_i = 0;
min_j = 0;
for i = 1:64
for j = 1:64
%here I compute for the (i,j)th domain block, the transformed domain block stored in D_trans
error = 0;
D_trans = zeros(8,8);
R = zeros(8,8); %Contains the pixel values of the (k,l)th range block
for m = 1:8
for n = 1:8
R(m,n) = RangeImage(8*k-8+m,8*l-8+n);
%ApplyTransformation can depend on (k,l) so I can't compute the transformation outside the k,l loop.
[m_dash,n_dash] = ApplyTransformation(8*i-8+m,8*j-8+n);
D_trans(m,n) = DomainImage(m_dash,n_dash);
error = error + (R(m,n)-D_trans(m,n))^2;
end
end
if(error < minerror)
minerror = error;
min_i = i;
min_j = j;
end
end
end
end
end
As an example ApplyTransformation, one can use the identity transformation:
function [x_dash,y_dash] = Iden(x,y)
x_dash = x;
y_dash = y;
end
Now the problem I am facing is the high computation time. The order of computation in the above code is 64^5, which is of the order 10^9. This computation should take at the worst minutes or an hour. It takes about 40 minutes to compute just 50 iterations. I don't know why the code is running so slow.
Thanks for reading my question.
You can use im2col* to convert the image to column format so each block forms a column of a [64 * 4096] matrix. Then apply transformation to each column and use bsxfun to vectorize computation of error.
DomainImage=rand(512);
RangeImage=rand(512);
DomainImage_col = im2col(DomainImage,[8 8],'distinct');
R = im2col(RangeImage,[8 8],'distinct');
[x y]=ndgrid(1:8);
function [x_dash, y_dash] = ApplyTransformation(x,y)
x_dash = x;
y_dash = y;
end
[x_dash, y_dash] = ApplyTransformation(x,y);
idx = sub2ind([8 8],x_dash, y_dash);
D_trans = DomainImage_col(idx,:); %transformation is reduced to matrix indexing
Error = 0;
for mn = 1:64
Error = Error + bsxfun(#minus,R(mn,:),D_trans(mn,:).').^2;
end
[minerror ,min_ij]= min(Error,[],2); % linear index of minimum of each block;
[min_i min_j]=ind2sub([64 64],min_ij); % convert linear index to subscript
Explanation:
Our goal is to reduce number of loops as much as possible. For it we should avoid matrix indexing and instead we should use vectorization. Nested loops should be converted to one loop. As the first step we can create a more optimized loop as here:
min_ij = zeros(4096,1);
for kl = 1:4096 %%% => 1:size(D_trans,2)
minerror = 9999;
min_ij(kl) = 0;
for ij = 1:4096 %%% => 1:size(R,2)
Error = 0;
for mn = 1:64
Error = Error + (R(mn,kl) - D_trans(mn,ij)).^2;
end
if(Error < minerror)
minerror = Error;
min_ij(kl) = ij;
end
end
end
We can re-arrange the loops and we can make the most inner loop as the outer loop and separate computation of the minimum from the computation of the error.
% Computation of the error
Error = zeros(4096,4096);
for mn = 1:64
for kl = 1:4096
for ij = 1:4096
Error(kl,ij) = Error(kl,ij) + (R(mn,kl) - D_trans(mn,ij)).^2;
end
end
end
% Computation of the min
min_ij = zeros(4096,1);
for kl = 1:4096
minerror = 9999;
min_ij(kl) = 0;
for ij = 1:4096
if(Error(kl,ij) < minerror)
minerror = Error(kl,ij);
min_ij(kl) = ij;
end
end
end
Now the code is arranged in a way that can best be vectorized:
Error = 0;
for mn = 1:64
Error = Error + bsxfun(#minus,R(mn,:),D_trans(mn,:).').^2;
end
[minerror ,min_ij] = min(Error, [], 2);
[min_i ,min_j] = ind2sub([64 64], min_ij);
*If you don't have the Image Processing Toolbox a more efficient implementation of im2col can be found here.
*The whole computation takes less than a minute.
First things first - your code doesn't do anything. But you likely do something with this minimum error stuff and only forgot to paste this here, or still need to code that bit. Never mind for now.
One big issue with your code is that you calculate transformation for 64x64 blocks of resulting image AND source image. 64^5 iterations of a complex operation are bound to be slow. Rather, you should calculate all transformations at once and save them.
allTransMats = cell(64);
for i = 1 : 64
for j = 1 : 64
allTransMats{i,j} = getTransformation(DomainImage, i, j)
end
end
function D_trans = getTransformation(DomainImage, i,j)
D_trans = zeros(8);
for m = 1 : 8
for n = 1 : 8
[m_dash,n_dash] = ApplyTransformation(8*i-8+m,8*j-8+n);
D_trans(m,n) = DomainImage(m_dash,n_dash);
end
end
end
This serves to get allTransMat and is OUTSIDE the k, l loop. Preferably as a simple function.
Now, you make your big k, l, i, j loop, where you compare all the elements as needed. Comparison could be also done block-wise instead of filling a small 8x8 matrix, yet doing it per element for some reason.
m = 1 : 8;
n = m;
for ...
R = RangeImage(...); % This will give 8x8 output as n and m are vectors.
D = allTransMats{i,j};
difference = sum(sum((R-D).^2));
if (difference < minDifference) ...
end
Even though this is a simple no transformations case, this speeds up code a lot.
Finally, are you sure you need to compare each block of transformed output with each block in the source? Typically you compare block1(a,b) with block2(a,b) - blocks (or pixels) on the same position.
EDIT: allTransMats requires k and l too. Ouch. There is NO WAY to make this fast for a single iteration, as you require 64^5 calls to ApplyTransformation (or a vectorization of that function, but even then it might not be fast - we would have to see the function to help here).
Therefore, I will re-iterate my advice to generate all transformations and then perform lookup: this upper part of the answer with allTransMats generation should be changed to have all 4 loops and generate allTransMats{i,j,k,l};. It WILL be slow, there is no way around that as I mentioned in the upper part of edit. But, it is a cost you pay once, as after saving the allTransMats, all further image analyses will be able to simply load it instead of generating it again.
But ... what do you even do? Transformation that depends on source and destination block indices plus pixel indices (= 6 values total) sounds like a mistake somewhere, or a prime candidate to optimize instead of all the rest.
I need to perform the following computation in an image processing project. It is the logarthmic of the summation of H3. I've written the following code but this loop has a very high computation time. Is there any way to eliminate the for loop?
for k=1:i
for l=1:j
HA(i,j)=HA(i,j)+log2((H3(k,l)/probA).^q);
end;
end;
Thanks in advance!
EDIT:
for i=1:256
for j=1:240
probA = 0;
probC = 0;
subProbA = H3(1:i,1:j);
probA = sum(subProbA(:));
probC = 1-probA;
for k=1:i
for l=1:j
HA(i,j)=HA(i,j)+log2((H3(k,l)/probA).^q);
end;
end;
HA(i,j)=HA(i,j)/(1-q);
for k=i+1:256
for l=j+1:240
HC(i,j)=HC(i,j)+log2((H3(k,l)/probC).^q);
end;
end;
HC(i,j)=HC(i,j)/(1-q);
e1(i,j) = HA(i,j) + HC(i,j);
if e1(i) >= emax
emax = e1(i);
tt1 = i-1;
end;
end;
end;
Assuming the two loops are nested inside some other outer loops that are iterated with i and j (though using i and j as iterators are not the best practices) and also assuming that probA and q are scalars, try this -
HA(i,j) = sum(sum(log2((H3(1:i,1:j)./probA).^q)))
Using the above code snippet, yon can replace your actual code posted in the EDIT section with this -
for i=1:256
for j=1:240
subProbA = H3(1:i,1:j);
probA = sum(subProbA(:));
probC = 1-probA;
HA(i,j) = sum(sum(log2((subProbA./probA).^q)))./(1-q);
HC(i,j) = sum(sum(log2((subProbA./probC).^q)))./(1-q);
e1(i,j) = HA(i,j) + HC(i,j);
if e1(i) >= emax
emax = e1(i);
tt1 = i-1;
end
end
end
Note that in this code, probA = 0; and probC = 0; are removed as they are over-written anyway later in the original code.
Assuming that q is scalar value, this code removes all the four for loops. Also in your given code you are calculating the maximum value of e1 only along the first column. If that is so then you should put in out of the second loop
height = 256;
width = 240;
a = repmat((1:height)',1,width);
b = repmat(1:width,height,1);
probA = arrayfun(#(ii,jj)(sum(sum(H3(1:ii,1:jj)))),a,repmat(1:width,height,1));
probC = 1 - probA;
HA = arrayfun(#(ii,jj)(sum(sum(log2((H3(1:ii,1:jj)/probA(ii,jj)).^q)))/(1-q)),a,b);
HC = arrayfun(#(ii,jj)(sum(sum(log2((H3(ii+1:height,jj+1:width)/probC(ii,jj)).^q)))/(1-q)),a,b);
e1 = HA + HC;
[emax tt_temp] = max(e1(:,1));
tt1 = tt_temp - 1;
I have a program that stores the last three recent scores of a game. However, I would like to store only the best scores. Eg. Recent Scores: Tom - 12, Sam - 14, Sue - 16. If i played the game and got a new score of 20, i would like it to store the new score of 20 (with name) and the other two scores of Sam and Sue ...thereby losing Tom. (I'm not worried about order).
Const NoOfRecentScores = 3;
TRecentScore = Record
Name : String;
Score : Integer;
End;
TRecentScores = Array[1..NoOfRecentScores] of TRecentScore;
Var
RecentScores : TRecentScores;
When i play the game, i call a procedure called UpdateRecentScores. Here it is:
Procedure UpdateRecentScores(Var RecentScores : TRecentScores; Score : Integer);
Var
PlayerName : String;
Count,count2 : Integer;
FoundSpace : Boolean;
MinMark,position: Integer;
ScorePresent:boolean = false;
Begin
PlayerName := GetPlayerName;
FoundSpace := False;
Count :=1;
While Not FoundSpace And (Count <= NoOfRecentScores)
Do If RecentScores[Count].Name = ''
Then FoundSpace := True
Else Count := Count + 1;
Here is the part i am struggling with. If no score is previously entered, then i have to accept that the first entered score is going to be the minimum:
If ScorePresent = False then
begin
MinMark:=Score;
ScorePresent:=True;
RecentScores[Count].Name := PlayerName;
RecentScores[Count].Score := Score;
writeln('Minimum Mark is: ',MinMark);
end
...the problem with the above, however, is that if the first score is a very high score, that becomes my minimum score!
Below, i am simply saying that if the Score achieved is greater than the MinMark (i.e. the minimum score) then the score should be stored in the array.
else if Score> MinMark then
begin
For count:= 1 to NoOfRecentScores do
begin
if RecentScores[count].score<Score then
position:=count;
RecentScores[position].Name := PlayerName;
RecentScores[position].Score := Score;
end;
End;
end;
As you can see, i am trying to check what the MinMark is first of all. Then, compare the score that i've just got with the MinMarker to see if it should be stored.
To clarify therefore, I want to save the best 3 scores and not recent scores.
To store n best scores, it is convenient to order them from high to low.
Let us go with an example first. Suppose you have the following four records: Tim - 14, Susan - 7, Don - 5, and Derek - 12. Then the array will look like [('Tim', 14), ('Derek', 12), ('Susan', 7), ('Don', 5)].
Now, what happens when Bert achieves a score of 9? Turns out we just want to insert a pair in a sorted array, so that it becomes [('Tim', 14), ('Derek', 12), ('Bert', 9), ('Susan', 7), ('Don', 5)]. After that, we drop the last element.
If we have to modify the array in place, we find the position pos where to insert Bert (it is position 3), then move everything in positions pos..(n-1) into positions (pos+1)..n, and after that, write Ben and his score to position pos.
Since you're using such a small array (3 records), it's relatively simple. (If your array was considerably larger, you'd want to keep it sorted and use a faster means of locating the right position for it, but your array is extremely small.)
Let's say you now have three RecentScore records, containing Tim - 14, Susan - 7, Derek - 12.
You need to find out if there's a score lower than the one the user just attained by Gemma (9) which is in the CurrentScore record (type TRecentScore), and if so replace it with Gemma's name and score.
Here's a working console project (compiled and run in XE5):
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TRecentScore = record
Name: string;
Score: Integer;
end;
TRecentScores = array of TRecentScore;
// Function to find lower score than the one we're trying to add.
// Returns the index of the next lower item if there is one, or
// -1 if there isn't one.
function FindLowerScore(Scores: TRecentScores; CheckScore: Integer): Integer;
var
i: Integer;
MinScore: Integer; // Lowest score found
begin
Result := -1; // No index found
MinScore := CheckScore; // Lowest score so far
for i := Low(Scores) to High(Scores) do
if Scores[i].Score < MinScore then // Lower than CheckScore?
begin
MinScore := Scores[i].Score; // Yep. Store it (new lowest)
Result := i; // and where it was found
end;
end;
// Utility procedure to display list of scores
procedure PrintScores(const Prelude: string; Scores: TRecentScores);
var
Score: TRecentScore;
begin
WriteLn(Prelude);
for Score in Scores do
Writeln(' ' + Score.Name + ' = ' + IntToStr(Score.Score));
end;
var
RecentScores: TRecentScores;
CurrentScore: TRecentScore;
i: Integer;
begin
SetLength(RecentScores, 3);
RecentScores[0].Name := 'Tim';
RecentScores[0].Score := 14;
RecentScores[1].Name := 'Susan';
RecentScores[1].Score := 7;
RecentScores[2].Name := 'Derek';
RecentScores[2].Score := 12;
// Show scores where we begin
PrintScores('Before', RecentScores);
CurrentScore.Name := 'Gemma'; CurrentScore.Score := 9;
// Check for lower score than Gemma's
i := FindLowerScore(RecentScores, CurrentScore.Score);
if i = -1 then
WriteLn('No lower score found!')
else
begin
// We have a lower score in the array. Update that one
// with our new score.
RecentScores[i].Name := CurrentScore.Name;
RecentScores[i].Score := CurrentScore.Score;
PrintScores('After', RecentScores);
end;
ReadLn;
end.
Here is what i used, following your advice...
Procedure UpdateRecentScores(Var RecentScores : TRecentScores; Score : Integer);
Var
PlayerName : String;
Count : Integer;
FoundSpace : Boolean;
Begin
PlayerName := GetPlayerName;
FoundSpace := False;
Count := 1;
While Not FoundSpace And (Count <= NoOfRecentScores)
Do If RecentScores[Count].Name = ''
Then
begin
FoundSpace := True;
RecentScores[Count].Name := PlayerName;
RecentScores[Count].Score := Score;
end
Else Count := Count + 1;
If Not FoundSpace
Then
Begin
SortRecentScores(RecentScores); // sort them into order of score
if score > RecentScores[NoOfRecentScores].Score then
begin
RecentScores[NoOfRecentScores].Name:= PlayerName;
RecentScores[NoOfRecentScores].Score:= Score;
end;
End;
End;
I have to analyse a set of images, and these are the operations I need to perform:
sum another set of images (called open beam in the code), calculate the median and rotate it by 90 degrees;
load a set of images, listed in the file "list.txt";
the images have been collected in groups of 3. For each group, I want to produce an image whose intensity values are 3 times the image median above a certain threshold and otherwise equal to the sum of the intensity values;
for each group of three images, subtract the open beam median (calculated in 1.) from the combined image (calculated in 3.)
Considering one of the tifs produced using the process above, I have that the maximum value is 65211, which is not 3* the median for the three images of the corresponding group (I checked considering the pixel position). Do you have any suggestion on why this happens, and how I could fix it?
The code is reported below. Thanks!
%Here we calculate the average for the open beam
clear;
j = 0;
for i=1:5
s = sprintf('/Users/Alberto/Desktop/Midi/17_OB_2.75/midi_%04i.fits',i);
j = j+1;
A(j,:,:) = uint16(fitsread(s));
end
OB_median = median(A,1);
OB_median = squeeze(OB_median);
OB_median_rot=rot90(OB_median);
%Here we calculate, for each projection, the average value from the three datasets
%Read list of images from text file
fid = fopen('/Users/Alberto/Desktop/Midi/list.txt', 'r');
a = textscan(fid, '%s');
fclose(fid);
%load images
j = 0;
for i = 1:1:42 %556 entries; 543 valid values
s = sprintf('/Users/Alberto/Desktop/Midi/%s',a{1,1}{i,1});
j = j+1;
A(j,:,:) = uint16(fitsread(s));
end
threshold = 80 %This is a discretional number. I put it after noticing
%that we get the same number of pixels with a value >100 if we use 80 or 50.
k = 0;
for ii = 1:3:42
N(1,:,:) = A(ii,:,:);
N(2,:,:) = A(ii+1,:,:);
N(3,:,:) = A(ii+2,:,:);
median_N = median(N,1);
median_N = squeeze(median_N);
B(:,:) = zeros(2160,2592);
for i = 1:1:2160
for j = 1:1:2592
RMS(i,j) = sqrt((double(N(1,i,j).^2) + double(N(2,i,j).^2) + double(N(3,i,j).^2))/3);
if RMS(i,j) > threshold
%B(i,j) = 30;
B(i,j) = 3*median_N(i,j);
else
B(i,j) = A(ii,i,j) + A(ii+1,i,j) + A(ii+2,i,j);
%B(i,j) = A(ii,i,j);
end
end
end
k = k+1;
filename = sprintf('/Users/Alberto/Desktop/Midi/Edited_images/Despeckled_images/despeckled_image_%03i.tif',k);
%Now we rotate the matrix
B_rot=rot90(B);
imwrite(B_rot, filename);
%imwrite(uint16(B_rot), filename);
%Now we subtract the OB median
B_final_rot = double(B_rot) - 3*double(OB_median_rot);
filename = sprintf('/Users/Alberto/Desktop/Midi/Edited_images/Final_image/final_image_%03i.tif',k);
imwrite(uint16(B_final_rot), filename);
end
The maximum integer that can be represented by the uint16 data type is
>> a=100000; uint16(a)
ans =
65535
To circumvent this limitation you need to rescale your data as type double and adjust the range (the image contrast) to agree with the limits imposed by the uint16 data type, before saving as uint16.
Out of curiosity, I was checking out the problem set to the 2009 ACM International Collegiate Programming Contest. The questions are pretty interesting. They're available at http://cm.baylor.edu/resources/pdf/2009Problems.pdf. I could not come up with an algorithm that solved problem 1, which I will reproduce here. It set off a lively discussion in the office, and we think we're pretty close to an answer, but we'd really appreciate it if somebody could find/work out a full solution (code not required).
I will reproduce problem here for your convenience:
Problem 1
Consider the task of scheduling the airplanes that are landing at an airport. Incoming airplanes report their positions, directions, and speeds, and then the controller has to devise a landing schedule that brings all airplanes safely to the ground. Generally, the more time there is between successive landings, the “safer” a landing schedule is. This extra time gives pilots the opportunity to react to changing weather and other surprises.
Luckily, part of this scheduling task can be automated – this is where you come in. You will be given scenarios of airplane landings. Each airplane has a time window during which it can safely land. You must compute an order for landing all airplanes that respects these time windows. Furthermore, the airplane landings should be stretched out as much as possible so that the minimum time gap between successive landings is as large as possible. For example, if three airplanes land at 10:00am, 10:05am, and 10:15am, then the smallest gap is five minutes, which occurs between the first two airplanes. Not all gaps have to be the same, but the smallest gap should be as large as possible.
Input
The input file contains several test cases consisting of descriptions of landing scenarios. Each test case starts with a line containing a single integer n (2 ≤ n ≤ 8), which is the number of airplanes in the scenario. This is followed by n lines, each containing two integers ai, bi, which give the beginning and end of the closed interval [ai, bi] during which the ith plane can land safely. The numbers ai and bi are specified in minutes and satisfy 0 ≤ ai ≤ bi ≤ 1440.
The input is terminated with a line containing the single integer zero.
Output
For each test case in the input, print its case number (starting with 1) followed by the minimum achievable time gap between successive landings. Print the time split into minutes and seconds, rounded to the closest second. Follow the format of the sample output.
Sample Input
3
0 10
5 15
10 15
2
0 10
10 20
0
Sample Output
Case 1: 7:30
Case 2: 20:00
I'll give a sketch of the algorithm.
First you binary search through the answer (minimal interval between flights). To do that, for each selected interval T you must be able to check whether it is possible to achieve it. If it is possible to achieve T, then you try making it smaller, if it is not - make it bigger.
To check whether you can achieve T, try all n! orders in which the planes may be landing (8! is small enough for this algo to work in time). For each permutation P1...Pn, you try assigning the times in a greedy manner:
int land = a[0];
for (int i = 1; i < n; i++) {
land = max(a[i], land + **T**);
if (land > b[i]) return "CAN NOT ACHIEVE INTERVAL T";
}
return "CAN ACHIEVE";
This optimization problem can be solved by linear programming http://en.wikipedia.org/wiki/Linear_programming
I would do something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef uint MASK;
#define INPUT_SCALE 60
#define MAX_TIME (1440 * 60)
void readPlaneData(int& endTime, MASK landingMask[MAX_TIME], int index)
{
char buf[128];
gets(buf);
int start, end;
sscanf(buf, "%d %d", &start, &end);
for(int i=start * INPUT_SCALE; i<=end * INPUT_SCALE; i++)
landingMask[i] |= 1 << index;
if(end * INPUT_SCALE > endTime)
endTime = end * INPUT_SCALE;
}
int findNextLandingForPlane(MASK landingMask[MAX_TIME], int start, int index)
{
while(start < MAX_TIME)
{
if(landingMask[start] & (1 << index))
return start;
start++;
}
return -1;
}
bool canLandPlanes(int minTime, MASK landingMask[MAX_TIME], int planeCount)
{
int next = 0;
for(int i=0; i<planeCount; i++)
{
int nextForPlane = findNextLandingForPlane(landingMask, next, i);
if(nextForPlane == -1)
return false;
next = nextForPlane + minTime;
}
return true;
}
int main(int argc, char* argv[])
{
while(true)
{
char buf[128];
gets(buf);
int count = atoi(buf);
if(count == 0)
break;
MASK landingMask[MAX_TIME];
memset(landingMask, 0, sizeof(landingMask));
int endTime = 0;
for(int i=0; i<count; i++)
readPlaneData(endTime, landingMask, i);
while((endTime > 0) && !canLandPlanes(endTime, landingMask, count))
endTime--;
printf("%d:%02d\n", endTime / 60, endTime % 60);
}
}
Here's some Ruby code that brute-forces the solution. Note that test_case_one actually fails because I have commented out the code that would make this work with seconds (instead of just whole minutes).
The brute-force strategy is to permute all the sequences in which the planes may land. For each landing sequence, create the product of all possible landing times. This is fine with whole minutes, brutal with seconds.
But of course premature optimization, evil, and all that, so this is a first step:
require 'test/unit'
class SampleTests < Test::Unit::TestCase
def test_case_one
problem = Problem.new
problem.add_plane(Plane.new(0, 10))
problem.add_plane(Plane.new(5, 15))
problem.add_plane(Plane.new(10, 15))
problem.solve()
minimum_gap = problem.minimum_gap()
assert_equal(7.5, minimum_gap)
end
def test_case_two
problem = Problem.new
problem.add_plane(Plane.new(0,10))
problem.add_plane(Plane.new(10, 20))
problem.solve()
minimum_gap = problem.minimum_gap()
assert_equal(20, minimum_gap)
end
def test_case_three
problem = Problem.new
problem.add_plane(Plane.new(0, 2))
problem.add_plane(Plane.new(7, 10))
problem.add_plane(Plane.new(4, 6))
minimum_gap = problem.minimum_gap()
assert_equal(5, minimum_gap)
end
def test_case_four
problem = Problem.new
problem.add_plane(Plane.new(1439, 1440))
problem.add_plane(Plane.new(1439, 1440))
problem.add_plane(Plane.new(1439, 1440))
assert_equal(0, problem.minimum_gap())
end
def test_case_five
problem = Problem.new
problem.add_plane(Plane.new(0, 10))
problem.add_plane(Plane.new(1, 2))
assert_equal(9, problem.minimum_gap())
end
def test_case_six
problem = Problem.new
problem.add_plane(Plane.new(8, 9))
problem.add_plane(Plane.new(0, 10))
assert_equal(9, problem.minimum_gap())
end
end
class Plane
def initialize(min, max)
#ts = Array.new
#This is a cheat to prevent combinatorial explosion. Just ignore 60 seconds in a minute!
#min = min * 60
#max = max * 60
min.upto(max) { | t | #ts << t}
end
#Array of times at which the plane might land.
def times
return #ts
end
end
#from 'permutation' gem
class Array
def permute(prefixed=[])
if (length < 2)
# there are no elements left to permute
yield(prefixed + self)
else
# recursively permute the remaining elements
each_with_index do |e, i|
(self[0,i]+self[(i+1)..-1]).permute(prefixed+[e]) { |a| yield a }
end
end
end
end
class Problem
def initialize
#solved = false
#maximum_gap = 0
#planes = Array.new
end
def add_plane(plane)
#planes << plane
end
#given a particular landing schedule, what's the minimum gap?
#A: Sort schedule and spin through it, looking for the min diff
#Note that this will return 0 for invalid schedules (planes landing simultaneously)
def gap_for(schedule)
schedule.sort!
min_gap = 1440
0.upto(schedule.length - 2) { | i |
gap = schedule[i + 1] - schedule[i]
if gap < min_gap
min_gap = gap
end
}
return min_gap
end
#Brute-force strategy
#Get every possible plane sequence (permute)
#Get every possible schedule for that sequence (brute_force_schedule)
#Check that schedule
def solve
#planes.permute { | sequence |
schedules = brute_force_schedule(sequence)
schedules.each { | schedule |
schedule.flatten!
gap = gap_for(schedule)
if gap > #maximum_gap
#puts "Found a new one: #{schedule.inspect}"
#maximum_gap = gap
end
}
}
end
#The list of all possible schedules associated with an array of planes
def brute_force_schedule(planes)
head = planes[0]
tail = planes[1..-1]
if tail.empty?
#Last element, return the times
return head.times.to_a
else
#Recurse and combine (product)
return head.times.to_a.product(brute_force_schedule(tail))
end
end
def minimum_gap
unless #solved
solve
end
return #maximum_gap
end
end