You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
325 lines
7.7 KiB
325 lines
7.7 KiB
package util
|
|
|
|
import model.Coord
|
|
import scala.math._
|
|
import scala.collection.mutable.ArrayBuffer
|
|
import scala.collection.mutable
|
|
import model.Customer
|
|
import scala.io.BufferedSource
|
|
import scala.reflect.ClassTag
|
|
import com.typesafe.scalalogging.Logger
|
|
import com.typesafe.scalalogging.LazyLogging
|
|
|
|
object Util extends LazyLogging {
|
|
private val r = 6471.00 // km
|
|
|
|
def toRad(num: Double): Double = {
|
|
val a = num * Pi / 180
|
|
a
|
|
}
|
|
|
|
def getHaversineDistance(c1: Coord, c2: Coord): Double = {
|
|
val x1 = c1.latitude - c2.latitude
|
|
val x2 = c1.longitude - c2.longitude
|
|
|
|
val dlat = toRad(x1)
|
|
val dlon = toRad(x2)
|
|
|
|
val a = pow(sin(dlat / 2), 2) +
|
|
cos(toRad(c1.latitude)) *
|
|
cos(toRad((c2.latitude))) *
|
|
pow(sin(dlon / 2), 2)
|
|
|
|
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
|
|
val d = r * c
|
|
d
|
|
}
|
|
|
|
def getHaversineDistance(lat: Double, lon: Double): Double = {
|
|
val x1 = lat
|
|
val x2 = lon
|
|
|
|
val dlat = toRad(x1)
|
|
val dlon = toRad(x2)
|
|
|
|
val a = pow(sin(dlat / 2), 2) +
|
|
cos(toRad(x1)) *
|
|
cos(toRad((0))) *
|
|
pow(sin(dlon / 2), 2)
|
|
|
|
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
|
|
val d = r * c
|
|
d
|
|
}
|
|
|
|
def primTraverse(
|
|
arr1: Array[Array[Int]],
|
|
comp: Int,
|
|
cond: (Int, Int) => Boolean,
|
|
cb: (Int, Int, Array[Array[Int]], Array[Array[Int]]) => Array[Array[Int]]
|
|
): Unit = {
|
|
val n = arr1.length
|
|
val selected = Array.ofDim[Boolean](n)
|
|
|
|
val arr2: Array[Array[Int]] = Array.ofDim(n, n)
|
|
|
|
selected(0) = true
|
|
|
|
// val mst: Array[Array[Int]] = Array.ofDim(5, 5)
|
|
|
|
for (_ <- 0 until n - 1) {
|
|
// var min = 999999
|
|
var x = 0
|
|
var y = 0
|
|
for (i <- 0 until n) {
|
|
if (selected(i) == true) {
|
|
for (j <- 0 until n) {
|
|
if (selected(j) == false && arr1(i)(j) != 0) {
|
|
if (cond(comp, arr1(i)(j))) {
|
|
x = i
|
|
y = j
|
|
cb(x, y, arr1, arr2)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
// mst(x)(y) = mst(x)(y)
|
|
selected(y) = true
|
|
}
|
|
}
|
|
|
|
def mstUsingPrims[T: ClassTag](
|
|
edges: Array[Array[T]]
|
|
)(implicit num: Numeric[T]): Array[Array[T]] = {
|
|
val n = edges.length
|
|
val selected: ArrayBuffer[Boolean] = ArrayBuffer.fill(n)(false)
|
|
|
|
selected(0) = true
|
|
|
|
val mst: Array[Array[T]] = Array.ofDim[T](n, n)
|
|
|
|
for (_ <- 0 until n - 1) {
|
|
var min = 999999
|
|
var x = 0
|
|
var y = 0
|
|
for (i <- 0 until n) {
|
|
if (selected(i) == true) {
|
|
for (j <- 0 until n) {
|
|
if (selected(j) == false && edges(i)(j) != 0) {
|
|
if (num.gt(num.fromInt(min), edges(i)(j))) {
|
|
min = num.toInt(edges(i)(j))
|
|
x = i
|
|
y = j
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// println(s"Edge selected $x - $y : ${edges(x)(y)}")
|
|
mst(x)(y) = edges(x)(y)
|
|
mst(y)(x) = edges(x)(y)
|
|
|
|
selected(y) = true
|
|
}
|
|
mst
|
|
}
|
|
|
|
def findClusters[T](
|
|
mst: Array[Array[T]],
|
|
centroids: IndexedSeq[Int],
|
|
removed: IndexedSeq[(Int, Int, T)]
|
|
)(implicit num: Numeric[T]) =
|
|
(
|
|
centroids,
|
|
centroids
|
|
.map(d => {
|
|
val y = DFS(d, mst)
|
|
y(0) -> y
|
|
})
|
|
.toMap,
|
|
removed
|
|
)
|
|
|
|
def makeAdjacencyList(
|
|
mst: Array[Array[Int]],
|
|
centroids: IndexedSeq[Int]
|
|
): ArrayBuffer[ArrayBuffer[Int]] = {
|
|
val n = mst.length
|
|
// val selected: ArrayBuffer[Boolean] = ArrayBuffer.fill(n)(false)
|
|
val buf: ArrayBuffer[ArrayBuffer[Int]] =
|
|
ArrayBuffer.fill(n)(ArrayBuffer.empty)
|
|
// for (_ <- 0 until n -1) {
|
|
|
|
// }
|
|
for (i <- 0 until n) {
|
|
for (j <- 0 until n) {
|
|
if (mst(i)(j) != 0) {
|
|
// println(s" $i $j = ${mst(i)(j)}")
|
|
buf(i) += j
|
|
}
|
|
}
|
|
}
|
|
buf
|
|
}
|
|
|
|
def findCentroids[T](
|
|
mst: Array[Array[T]]
|
|
)(
|
|
implicit ev: Numeric[T]
|
|
): (Array[Array[T]], IndexedSeq[Int], IndexedSeq[(Int, Int, T)]) = {
|
|
val n = mst.length
|
|
val centroids: mutable.Set[Int] = mutable.Set.empty
|
|
val removed: ArrayBuffer[(Int, Int, T)] = ArrayBuffer.empty
|
|
for (i <- 0 until n) {
|
|
for (j <- 0 until n) {
|
|
if (ev.gt(mst(i)(j), ev.fromInt(20)) && mst(i)(j) != 0) {
|
|
// println(s" $i $j = ${mst(i)(j)}")
|
|
centroids += i
|
|
centroids += j
|
|
removed.append((i, j, mst(i)(j)))
|
|
mst(i)(j) = ev.zero
|
|
}
|
|
}
|
|
}
|
|
(mst, centroids.toIndexedSeq, removed.toIndexedSeq)
|
|
}
|
|
|
|
def DFS[T](
|
|
start: Int,
|
|
graph: Array[Array[T]]
|
|
)(implicit num: Numeric[T]): ArrayBuffer[Int] = {
|
|
val visited = Array.fill(graph.size)(false)
|
|
val buf = ArrayBuffer[Int]()
|
|
def loop(
|
|
start: Int,
|
|
graph: Array[Array[T]],
|
|
visited: Array[Boolean]
|
|
): Unit = {
|
|
visited(start) = true
|
|
buf += start
|
|
// print(s"$start ")
|
|
|
|
for (i <- 0 until graph.size) {
|
|
if (num.gt(graph(start)(i), num.fromInt(0)) && (!visited(i))) {
|
|
loop(i, graph, visited);
|
|
}
|
|
}
|
|
}
|
|
|
|
loop(start, graph, visited)
|
|
// println()
|
|
buf
|
|
}
|
|
|
|
def DFS(
|
|
start: Int,
|
|
graph: Array[Array[Int]],
|
|
num: Int,
|
|
cond: (Int, Int) => Boolean
|
|
): ArrayBuffer[Int] = {
|
|
val visited = Array.fill(graph.size)(false)
|
|
val buf = ArrayBuffer[Int]()
|
|
def loop(
|
|
start: Int,
|
|
graph: Array[Array[Int]],
|
|
visited: Array[Boolean]
|
|
): Unit = {
|
|
visited(start) = true
|
|
buf += start
|
|
// print(s"$start ")
|
|
|
|
for (i <- 0 until graph.size) {
|
|
if (graph(start)(i) > 0 && cond(graph(start)(i), num) && (!visited(i))) {
|
|
loop(i, graph, visited);
|
|
}
|
|
}
|
|
}
|
|
|
|
loop(start, graph, visited)
|
|
// println()
|
|
buf
|
|
}
|
|
|
|
def groupClusters[T](
|
|
centroids: IndexedSeq[Int],
|
|
clusters: Map[Int, ArrayBuffer[Int]],
|
|
removed: IndexedSeq[(Int, Int, T)]
|
|
)(implicit num: Numeric[T]) =
|
|
centroids
|
|
.map(c => {
|
|
val cluster = clusters(c)
|
|
val lst = removed
|
|
.filter(r => {
|
|
c == r._1
|
|
})
|
|
.sortWith((x, y) => {
|
|
num.lt(x._3, y._3)
|
|
})
|
|
.map(l => {
|
|
(l._2, l._3)
|
|
})
|
|
c -> lst
|
|
})
|
|
.toMap
|
|
|
|
def getCustomers(
|
|
infile: BufferedSource
|
|
): String Either IndexedSeq[Customer] = {
|
|
var customers: String Either IndexedSeq[Customer] = Right(IndexedSeq.empty)
|
|
val it = infile.getLines
|
|
@annotation.tailrec
|
|
def loop(
|
|
lst: IndexedSeq[Customer],
|
|
iter: Iterator[String]
|
|
): String Either IndexedSeq[Customer] = {
|
|
if (!iter.hasNext)
|
|
return Right(lst)
|
|
val line = iter.next
|
|
val arr = line.split(",").map(_.trim)
|
|
arr match {
|
|
case Array(latitude, longitude, workLoad) => {
|
|
val cust =
|
|
Customer(
|
|
Coord(latitude.toDouble, longitude.toDouble),
|
|
workLoad.toFloat
|
|
)
|
|
loop(lst :+ cust, iter)
|
|
}
|
|
case _ =>
|
|
Left(
|
|
"Error reading customers from" +
|
|
s" file - ${arr.mkString(", ")}"
|
|
)
|
|
}
|
|
}
|
|
try {
|
|
customers = loop(IndexedSeq.empty, it)
|
|
} catch {
|
|
case e: NumberFormatException =>
|
|
customers = Left(
|
|
s"Expected number but received string ${e.getMessage()}"
|
|
)
|
|
case e: NullPointerException =>
|
|
customers = Left("Input was null")
|
|
}
|
|
customers
|
|
}
|
|
|
|
def formAdjMatrix(customers: IndexedSeq[Customer]): Array[Array[Double]] = {
|
|
val n = customers.length
|
|
val edges: Array[Array[Double]] = Array.ofDim(n, n)
|
|
for (i <- 0 until n) {
|
|
for (j <- i until n) {
|
|
val weight =
|
|
getHaversineDistance(customers(i).location, customers(j).location)
|
|
edges(i)(j) = weight
|
|
edges(j)(i) = weight
|
|
}
|
|
}
|
|
edges
|
|
}
|
|
|
|
}
|