我可以想象有一些算法问题确切地描述了我的问题,但我找不到任何问题。我基本上想做的是:
假设我有一些数据结构,其中一个Line类型的对象包含两个(或更多,但在我的情况下,两个就足够了)Point(x,y)类型的对象。
线表示折线图中从一个点A(x,y)到另一个点B(x,y)的直线。
现在我有一个这样的行列表。注意,它们也可能在x坐标上重叠。例如,我有一条从(0,0)到(3,1)的线和另一条从(2,0)到(3,2)的线。我现在想要“累积”这个行列表,并得到一个点列表(稍后绘制折线图)。
对于上面的例子,这意味着我想要{(0,0); (2,0,67); (3,2)}。这是一个美丽的形象,希望我的问题更清晰:
背景:我正在编写血液酒精含量水平计算器。我有几种饮料,其中包括:体积,百分比,开始时间,结束时间等。
我想假设血液酒精含量水平从开始时间到结束时间的线性上升减去该时间段内的酒精减少量。在我的想法中,现在很容易计算出每种饮料的单一“线条”,但是为了得到一个完整的线图来表示你整个时间内的血液酒精含量水平,我现在必须“添加”/“累积”所有这些“线”在一起。
至少那是我的想法,这将是我的方法,如果你有不同的方法/建议,请告诉我。
答案 0 :(得分:1)
算法的主要思想:
间隔中包含的行的总和。 现在我们每个间隔生成一个新行。该行将是该区间中包含的行的总和。
为了能够对两行进行求和,我们将两行转换为函数(Slope y-intercept form),我们执行求和并创建一个新行。
任何直线方程的斜率截距形式由下式给出:
y = mx + b
其中:
通过任意两点(x1, y1)
和(x2, y2)
的直线的斜率m由下式给出:
线的y轴截距b是线与y轴交叉的点处的y值。由于点(x1, y1)
我们有y1 = mx1 + b
,因此y轴截距b可以通过以下公式计算:
b = y1 - mx1
' x'新线的点数值将是区间的限制,“' y'值将是将函数应用于' x'值。
代码:(注意:getter / setter被忽略)
LineFunction:
public class LineFunction {
private final double m, b;
public LineFunction(Line l) {
/**
* y= ((y_b-y_a)/(x_b-x_a))*(x-x_a) + y_a
*
* ((y_b-y_a)/(x_b-x_a))==> m
*
* y = m *(x-x_a)+y_a
*
* y= m*x -m*x_a +y_a
*
* -m*x_a +y_a -> b
*
* y = m*x + b
*/
double y_a, y_b, x_a, x_b;
x_a = l.getP1().getX();
y_a = l.getP1().getY();
x_b = l.getP2().getX();
y_b = l.getP2().getY();
m = (y_b - y_a) / (x_b - x_a);
b = -m * x_a + y_a;
}
private LineFunction(double m, double b) {
this.m = m;
this.b = b;
}
public double computeFor(double xValue) {
return this.m * xValue + this.b;
}
public LineFunction sum(LineFunction other) {
return new LineFunction(this.m + other.m, this.b + other.b);
}
@Override
public String toString() {
return "y = " + m + "x + " + b;
}
}
此类表示类型 y = mx + b 的简单函数。基本上,需要一行并将其转换为函数。
行:
public class Line {
private final Point p1, p2;
private final LineFunction lineFunction;
public Line(Point p1, Point p2) {
this.p1 = p1;
this.p2 = p2;
this.lineFunction = new LineFunction(this);
}
public Line(Line o) {
this.p1 = o.p1;
this.p2 = o.p2;
this.lineFunction = new LineFunction(this);
}
public Line sum(Line other,Point p1,Point p2) {
LineFunction s= this.lineFunction.sum(other.lineFunction);
return new Line(new Point(p1.getX(),s.computeFor(p1.getX())),new Point(p2.getX(),s.computeFor(p2.getX())));
}
public boolean isInInterval(Point p) {
return p.getX() >= this.p1.getX() && p.getX() < this.p2.getX();
}
@Override
public String toString() {
return "{"+p1+","+p2+"}";
}
}
Line
由两个点定义,从Line
我们可以得到定义它的函数。它有方法检查点的 x 值是否在行的起始和结束 x 之间。
为了完成算法的第1点,我们需要知道每一行的所有点:
public static ArrayList<Point> getAllPoints(ArrayList<Line> lines) {
HashSet<Point> points = new HashSet<Point>();
for (Line line : lines)
{
points.add(line.getP1());
points.add(line.getP2());
}
ArrayList<Point> res = new ArrayList<Point>(points);
Collections.sort(res);
return res;
}
此方法返回定义间隔的所有点。必须订购积分,所以
public class Point implements Comparable<Point>{
private long x;
private double y;
@Override
public int compareTo(Point o) {
int cmp1=Long.compare(this.x, o.x);
return cmp1 != 0 ? cmp1 : Double.compare(this.y, o.y) ;
}
@Override
public String toString() {
return "(" + x + "," + y + ")";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (x ^ (x >>> 32));
long temp;
temp = Double.doubleToLongBits(y);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Point other = (Point) obj;
if (x != other.x)
return false;
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
return false;
return true;
}
}
对于算法的第二步,我们需要知道哪条线属于给定的区间:
public static ArrayList<Line> filter(Point p, ArrayList<Line> lines) {
ArrayList<Line> filtered = new ArrayList<Line>();
for (Line line : lines)
if (line.isInInterval(p))
filtered.add(line);
return filtered;
}
唯一剩下的是一组线的总和:
public static ArrayList<Line> sumAll(ArrayList<Line> lines) {
ArrayList<Point> points = getAllPoints(lines);
ArrayList<Line> result = new ArrayList<>();
for (int i = 0; i < points.size() - 1; i++)
{
Point current = points.get(i);
Point next = points.get(i + 1);
ArrayList<Line> filtered = filter(current, lines);
Line acc = new Line(new Point(current.getX(), 0), new Point(
next.getX(), 0));
for (Line lf : filtered)
{
acc = acc.sum(lf, current, next);
}
result.add(acc);
}
return result;
}
一个简单的例子:
public static void main(String[] args) {
Line l1 = new Line(new Point(0, 0), new Point(3, 1));
Line l2 = new Line(new Point(2, 0), new Point(3, 1));
Line l3 = new Line(new Point(4, 7), new Point(8, 2));
Line l4 = new Line(new Point(5, 4), new Point(6, 1));
Line l5 = new Line(new Point(9, 6), new Point(10, 1));
ArrayList<Line> lines = new ArrayList<Line>();
lines.add(l1);
lines.add(l2);
lines.add(l3);
lines.add(l4);
lines.add(l5);
ArrayList<Line> res = sumAll(lines);
for (Line line : res)
{
System.out.println(line);
}
}
输出:
{(0,0.0),(2,0.6666666666666666)}
{(2,0.666666666666667),(3,2.0)}
{(3,0.0),(4,0.0)} ----> There's no line in this interval.
{(4,7.0),(5,5.75)}
{(5,9.75),(6,5.5)}
{(6,4.5),(8,2.0)}
{(8,0.0),(9,0.0)}
{(9,6.0),(10,1.0)}
如果我遗漏了任何内容,请不要犹豫,发表评论。