Algorithm 更好的方法(功能性/不可变,但性能更好)编码一种算法,该算法在编码过程中从集合中删除项
我是Scala和函数式编程的初学者,我有一些算法代码可能有一些味道,因为它使用了可变性,但也有一个bug,部分原因是它是可变性的。我有两组二维点。集合1中的每个点都要与集合2中最近的点相关联(给定集合1相对于集合2的比例和偏移)。我们需要1对1的相关性,而不是1对多的相关性。我认为这意味着,当我们在集合2中找到与集合1中的点最近的点时,我们应该从集合2中删除前者,因为我们不希望再次匹配它。然而,也许有一种不变的/功能性的方式 下面的代码有一个错误,如果所有的点都从set2NotYetMatched中删除,那么我们调用empty.minBy;这可以通过修改代码使其功能更少来解决,可能是使用for或while构造。但在我这么做之前,有没有更好的编码方法?以下是当前代码:Algorithm 更好的方法(功能性/不可变,但性能更好)编码一种算法,该算法在编码过程中从集合中删除项,algorithm,scala,functional-programming,immutability,Algorithm,Scala,Functional Programming,Immutability,我是Scala和函数式编程的初学者,我有一些算法代码可能有一些味道,因为它使用了可变性,但也有一个bug,部分原因是它是可变性的。我有两组二维点。集合1中的每个点都要与集合2中最近的点相关联(给定集合1相对于集合2的比例和偏移)。我们需要1对1的相关性,而不是1对多的相关性。我认为这意味着,当我们在集合2中找到与集合1中的点最近的点时,我们应该从集合2中删除前者,因为我们不希望再次匹配它。然而,也许有一种不变的/功能性的方式 下面的代码有一个错误,如果所有的点都从set2NotYetMatche
protected def calcDistanceCostBetweenSets(
set1: Seq[Point], set1StartX: Double, set1XScale: Double, set2Window: PointWindow, xImportance: Double
): CloudMatch = {
require (!set1.isEmpty)
var set2NotYetMatched = scala.collection.mutable.Set.empty ++= set2Window.subsetWithMargins
val correlations = set1 map(set1Point => {
val minDistPointAndDist = set2NotYetMatched.map(set2Point =>
(
set2Point,
DistanceCostCalculator.calcPointToPointDistanceSquared(
set1Point, set2Point, set1StartX, set2Window.marginStartX, set1XScale, xImportance)))
.minBy(_._2)
set2NotYetMatched -= ( minDistPointAndDist._1)
new PointCorrelation(set1Point, Collections.singletonList(minDistPointAndDist._1), minDistPointAndDist._2)
})
val distanceCost = correlations.map(_.getCost).foldLeft(0.0)(_ + _)
return new CloudMatch(
set2Window.firstPointAlongX,
set2Window.startIndexInOriginalSubset,
distanceCost,
Lists.newArrayList(JavaConversions.asJavaIterable(correlations)))
}
虽然我对更好的答案很感兴趣,但我提出了一个更好的解决方案,使用递归算法(无可变性),使用尾部递归,每次递归时删除一项:
protected def calcDistanceCostBetweenSets(set1: Seq[Point], set1StartX: Double, set1XScale: Double, set2Window: PointWindow, xImportance: Double): CloudMatch = {
require (!set1.isEmpty)
val correlations = findCorrelations(set1.toSet, set1StartX, set1XScale, set2Window.subsetWithMargins.toSet, set2Window.startX, xImportance)
val distanceCost = correlations.map(_.getCost).foldLeft(0.0)(_ + _)
new CloudMatch(
set2Window.firstPoint,
set2Window.startIndexInOriginalSubset,
distanceCost,
Lists.newArrayList(JavaConversions.asJavaIterable(correlations sortBy(_.getActualEvent.getStartX))))
}
private def findCorrelations(set1: Set[Point], set1StartX: Double, set1XScale: Double, set2Points: Set[Point], set2StartX: Double, xImportance: Double
): List[EventCorrelation] = {
findCorrelations(Nil, set1, set1 head, set2Points,
DistanceCostCalculator.calcPointToPointDistanceSquared(_, _,
set1StartX, set2StartX, set1XScale, xImportance))
}
private def findCorrelations(
correlations: List[EventCorrelation],
set1: Set[Point],
set1PointToFind: Point,
set2: Set[Point],
calculateCost : (Point, Point) => Double
): List[EventCorrelation] = {
val minDistPoint = set2.map(scorePoint =>
(scorePoint, calculateCost(set1PointToFind, scorePoint))
).minBy(_._2)
val correlation = new EventCorrelation(set1PointToFind, Collections.singletonList(minDistPoint._1), minDistPoint._2)
val remainingSet1 = set1 - set1PointToFind;
val remainingSet2 = set2 - minDistPoint._1
if(remainingSet1.isEmpty || remainingSet2.isEmpty) correlations
else findCorrelations(
correlation :: correlations,
remainingSet1,
remainingSet1 head,
remainingSet2,
calculateCost);
}
我认为一个好的方法是使用
foldLeft
跟踪当前的相关性和set2中保留的元素
有关findCorrelations
方法,请参见下文。它类似于递归解决方案。一个显著的区别是您不必维护remainingSet
。你觉得怎么样
private def findCorrelations(
set1: Set[Point],
set1StartX: Double,
set1XScale: Double,
set2Points: Set[Point],
set2StartX: Double,
xImportance: Double
): List[EventCorrelation] = {
def calculateCost(p1: Point, p2: Point): Double = {
DistanceCostCalculator.calcPointToPointDistanceSquared(
p1, p2,
set1StartX, set2StartX, set1XScale, xImportance)
}
set1.foldLeft((List.empty[EventCorrelation], set2Points)) {
case ((correlationsAcc, set2PointsRemaining), set1PointToFind) =>
if (set2PointsRemaining.isEmpty) {
correlationsAcc
} else {
val (set2MinPoint, minDist) = set2PointsRemaining.map(scorePoint =>
(scorePoint, calculateCost(set1PointToFind, scorePoint))
).minBy(_._2)
val correlation = new EventCorrelation(
set1PointToFind,
Collections.singletonList(set2MinPoint),
minDist)
(correlationsAcc + correlation, set2PointsRemaining - set2MinPoint)
}
}
有趣而优雅!非常感谢。