10 KiB
id, title, challengeType, forumTopicId, dashedName
| id | title | challengeType | forumTopicId | dashedName |
|---|---|---|---|---|
| 5951a53863c8a34f02bf1bdc | Problema do par mais próximo | 5 | 302232 | closest-pair-problem |
--description--
Forneça uma função para encontrar os dois pontos mais próximos entre um conjunto de pontos dados em duas dimensões.
A solução simples é um algoritmo O(n^2) (que podemos chamar de algoritmo de força bruta). O pseudocódigo (usando índices) poderia ser, simplesmente:
bruteForceClosestPair de P(1), P(2), ... P(N)
se N < 2 então
retorne ∞
senão
minDistance ← |P(1) - P(2)|
minPoints ← { P(1), P(2) }
paraCada i ∈ [1, N-1]
paraCada j ∈ [i+1, N]
se |P(i) - P(j)| < minDistance então
minDistance ← |P(i) - P(j)|
minPoints ← { P(i), P(j) }
fimSe
fimPara
fimPara
retorne minDistance, minPoints
fimSe
Um algoritmo melhor com base na abordagem recursiva de dividir e conquistar, com complexidade O(n\log n), teria, como pseudocódigo:
closestPair de (xP, yP)
onde xP é P(1) .. P(N) ordenado pela coordenada x, e
yP é P(1) .. P(N) ordenado pela coordenada y (ordem ascendente)
se N ≤ 3 então
retorne pontos mais próximos de xP usando o algoritmo de força bruta
senão
xL ← pontos de xP de 1 a ⌈N/2⌉
xR ← pontos de xP de ⌈N/2⌉+1 a N
xm ← xP(⌈N/2⌉)x
yL ← { p ∈ yP : px ≤ xm }
yR ← { p ∈ yP : px > xm }
(dL, pairL) ← closestPair de (xL, yL)
(dR, pairR) ← closestPair de (xR, yR)
(dmin, pairMin) ← (dR, pairR)
se dL < dR então
(dmin, pairMin) ← (dL, pairL)
fimSe
yS ← { p ∈ yP : |xm - px| < dmin }
nS ← número de pontos em yS
(closest, closestPair) ← (dmin, pairMin)
para i de 1 a nS - 1
k ← i + 1
enquanto k ≤ nS e yS(k)y - yS(i)y < dmin
se |yS(k) - yS(i)| < closest então
(closest, closestPair) ← (|yS(k) - yS(i)|, {yS(k), yS(i)})
fimSe
k ← k + 1
fimEnquanto
fimPara
retorne closest, closestPair
fimSe
Para a entrada, espere que o argumento seja um array de objetos Point com membros x e y definidos como números. Retorna um objeto que contém os pares chave-valor de distance e pair (o par com os dois pontos mais próximos).
Por exemplo, getClosestPair com o array de entrada points:
const points = [
new Point(1, 2),
new Point(3, 3),
new Point(2, 2)
];
Retornaria:
{
distance: 1,
pair: [
{
x: 1,
y: 2
},
{
x: 2,
y: 2
}
]
}
Observação: ordene o array de pair por seus valores em x na ordem de incrementação.
--hints--
getClosestPair deve ser uma função.
assert(typeof getClosestPair === 'function');
getClosestPair(points1).distance deve ser 0.0894096443343775.
assert.equal(getClosestPair(points1).distance, answer1.distance);
getClosestPair(points1).pair deve ser [ { x: 7.46489, y: 4.6268 }, { x: 7.46911, y: 4.71611 } ].
assert.deepEqual(
JSON.parse(JSON.stringify(getClosestPair(points1))).pair,
answer1.pair
);
getClosestPair(points2).distance deve ser 65.06919393998976.
assert.equal(getClosestPair(points2).distance, answer2.distance);
getClosestPair(points2).pair deve ser [ { x: 37134, y: 1963 }, { x: 37181, y: 2008 } ].
assert.deepEqual(
JSON.parse(JSON.stringify(getClosestPair(points2))).pair,
answer2.pair
);
getClosestPair(points3).distance deve ser 6754.625082119658.
assert.equal(getClosestPair(points3).distance, answer3.distance);
getClosestPair(points3).pair deve ser [ { x: 46817, y: 64975 }, { x: 48953, y: 58567 } ].
assert.deepEqual(
JSON.parse(JSON.stringify(getClosestPair(points3))).pair,
answer3.pair
);
--seed--
--after-user-code--
const points1 = [
new Point(0.748501, 4.09624),
new Point(3.00302, 5.26164),
new Point(3.61878, 9.52232),
new Point(7.46911, 4.71611),
new Point(5.7819, 2.69367),
new Point(2.34709, 8.74782),
new Point(2.87169, 5.97774),
new Point(6.33101, 0.463131),
new Point(7.46489, 4.6268),
new Point(1.45428, 0.087596)
];
const answer1 = {
distance: 0.0894096443343775,
pair: [
{
x: 7.46489,
y: 4.6268
},
{
x: 7.46911,
y: 4.71611
}
]
};
const points2 = [
new Point(37100, 13118),
new Point(37134, 1963),
new Point(37181, 2008),
new Point(37276, 21611),
new Point(37307, 9320)
];
const answer2 = {
distance: 65.06919393998976,
pair: [
{
x: 37134,
y: 1963
},
{
x: 37181,
y: 2008
}
]
};
const points3 = [
new Point(16910, 54699),
new Point(14773, 61107),
new Point(95547, 45344),
new Point(95951, 17573),
new Point(5824, 41072),
new Point(8769, 52562),
new Point(21182, 41881),
new Point(53226, 45749),
new Point(68180, 887),
new Point(29322, 44017),
new Point(46817, 64975),
new Point(10501, 483),
new Point(57094, 60703),
new Point(23318, 35472),
new Point(72452, 88070),
new Point(67775, 28659),
new Point(19450, 20518),
new Point(17314, 26927),
new Point(98088, 11164),
new Point(25050, 56835),
new Point(8364, 6892),
new Point(37868, 18382),
new Point(23723, 7701),
new Point(55767, 11569),
new Point(70721, 66707),
new Point(31863, 9837),
new Point(49358, 30795),
new Point(13041, 39744),
new Point(59635, 26523),
new Point(25859, 1292),
new Point(1551, 53890),
new Point(70316, 94479),
new Point(48549, 86338),
new Point(46413, 92747),
new Point(27186, 50426),
new Point(27591, 22655),
new Point(10905, 46153),
new Point(40408, 84202),
new Point(52821, 73520),
new Point(84865, 77388),
new Point(99819, 32527),
new Point(34404, 75657),
new Point(78457, 96615),
new Point(42140, 5564),
new Point(62175, 92342),
new Point(54958, 67112),
new Point(4092, 19709),
new Point(99415, 60298),
new Point(51090, 52158),
new Point(48953, 58567)
];
const answer3 = {
distance: 6754.625082119658,
pair: [
{
x: 46817,
y: 64975
},
{
x: 48953,
y: 58567
}
]
}
--seed-contents--
const Point = function(x, y) {
this.x = x;
this.y = y;
};
Point.prototype.getX = function() {
return this.x;
};
Point.prototype.getY = function() {
return this.y;
};
function getClosestPair(pointsArr) {
return true;
}
--solutions--
const Point = function(x, y) {
this.x = x;
this.y = y;
};
Point.prototype.getX = function() {
return this.x;
};
Point.prototype.getY = function() {
return this.y;
};
const mergeSort = function mergeSort(points, comp) {
if(points.length < 2) return points;
var n = points.length,
i = 0,
j = 0,
leftN = Math.floor(n / 2),
rightN = leftN;
var leftPart = mergeSort( points.slice(0, leftN), comp),
rightPart = mergeSort( points.slice(rightN), comp );
var sortedPart = [];
while((i < leftPart.length) && (j < rightPart.length)) {
if(comp(leftPart[i], rightPart[j]) < 0) {
sortedPart.push(leftPart[i]);
i += 1;
}
else {
sortedPart.push(rightPart[j]);
j += 1;
}
}
while(i < leftPart.length) {
sortedPart.push(leftPart[i]);
i += 1;
}
while(j < rightPart.length) {
sortedPart.push(rightPart[j]);
j += 1;
}
return sortedPart;
};
const closestPair = function _closestPair(Px, Py) {
if(Px.length < 2) return { distance: Infinity, pair: [ new Point(0, 0), new Point(0, 0) ] };
if(Px.length < 3) {
//find euclid distance
var d = Math.sqrt( Math.pow(Math.abs(Px[1].x - Px[0].x), 2) + Math.pow(Math.abs(Px[1].y - Px[0].y), 2) );
return {
distance: d,
pair: [ Px[0], Px[1] ]
};
}
var n = Px.length,
leftN = Math.floor(n / 2),
rightN = leftN;
var Xl = Px.slice(0, leftN),
Xr = Px.slice(rightN),
Xm = Xl[leftN - 1],
Yl = [],
Yr = [];
//separate Py
for(var i = 0; i < Py.length; i += 1) {
if(Py[i].x <= Xm.x)
Yl.push(Py[i]);
else
Yr.push(Py[i]);
}
var dLeft = _closestPair(Xl, Yl),
dRight = _closestPair(Xr, Yr);
var minDelta = dLeft.distance,
closestPair = dLeft.pair;
if(dLeft.distance > dRight.distance) {
minDelta = dRight.distance;
closestPair = dRight.pair;
}
//filter points around Xm within delta (minDelta)
var closeY = [];
for(i = 0; i < Py.length; i += 1) {
if(Math.abs(Py[i].x - Xm.x) < minDelta) closeY.push(Py[i]);
}
//find min within delta. 8 steps max
for(i = 0; i < closeY.length; i += 1) {
for(var j = i + 1; j < Math.min( (i + 8), closeY.length ); j += 1) {
var d = Math.sqrt( Math.pow(Math.abs(closeY[j].x - closeY[i].x), 2) + Math.pow(Math.abs(closeY[j].y - closeY[i].y), 2) );
if(d < minDelta) {
minDelta = d;
closestPair = [ closeY[i], closeY[j] ]
}
}
}
return {
distance: minDelta,
pair: closestPair.sort((pointA, pointB) => pointA.x - pointB.x)
};
};
function getClosestPair(points) {
const sortX = function(a, b) { return (a.x < b.x) ? -1 : ((a.x > b.x) ? 1 : 0); }
const sortY = function(a, b) { return (a.y < b.y) ? -1 : ((a.y > b.y) ? 1 : 0); }
const Px = mergeSort(points, sortX);
const Py = mergeSort(points, sortY);
return closestPair(Px, Py);
}