Skip to content

Instantly share code, notes, and snippets.

@yuba
Created July 4, 2016 12:26
Show Gist options
  • Save yuba/155c9e29f0767083dc508b2a98d00e8a to your computer and use it in GitHub Desktop.
Save yuba/155c9e29f0767083dc508b2a98d00e8a to your computer and use it in GitHub Desktop.
n次多項式を表現し、二分法で根を求めることができるクラス。 new Polynomial(-3, -2, 1 , -4, 0, 2).solve() などとやるとdouble配列で根が返ってくる。2次式でも1次式でさえ公式使わず二分法。
import java.util.HashSet;
import java.util.Set;
/**
* xの多項式を表します
*/
public class Polynomial
{
/**
* @param a 定数, 1次係数, 2次係数, 3次係数, ... と係数を指定します
*/
public Polynomial(double... a)
{
if (a == null) throw new NullPointerException();
this.a = a;
}
/**
* xを与えたときの式の値を求めます
*/
public double evaluate(double x)
{
double result = 0;
double xn = 1;
for (int i = 0; i < a.length; ++i)
{
result += a[i] * xn;
xn *= x;
}
return result;
}
/**
* この多項式の次数を返します。
* @return 定数式の場合は0を返します。
*/
public int getDegree()
{
for (int i = a.length - 1; i > 0; --i)
{
if (a[i] != 0) return i;
}
return 0;
}
/**
* この多項式を微分した多項式(導関数)を返します
* @return
*/
public Polynomial getDerivative()
{
int degree = getDegree();
if (degree == 0) return new Polynomial(0); // 定数式の微分は0というやはり定数式
double[] derivative = new double[degree];
for (int i = 1; i <= degree; ++i)
{
derivative[i - 1] = a[i] * i;
}
return new Polynomial(derivative);
}
public double signum(double x)
{
final int degree = getDegree();
if (degree > 0)
{
if (x == Double.POSITIVE_INFINITY) return Math.signum(a[degree]);
if (x == Double.NEGATIVE_INFINITY) return Math.signum(a[degree]) * ((degree & 1) == 0 ? 1.0 : -1.0);
}
return Math.signum(evaluate(x));
}
public double[] solve()
{
if (getDegree() == 0) return new double[0];
return solveInRanges(getDerivative().solve());
}
public double[] solveInRanges(double... xs)
{
if (xs == null) throw new NullPointerException();
Set<Double> solutions = new HashSet<>();
if (xs.length == 0)
{
appendSolutionIfSolved(solutions, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
}
else
{
appendSolutionIfSolved(solutions, Double.NEGATIVE_INFINITY, xs[0]);
appendSolutionIfSolved(solutions, xs[xs.length - 1], Double.POSITIVE_INFINITY);
for (int i = 1; i < xs.length; ++i)
{
appendSolutionIfSolved(solutions, xs[i - 1], xs[i]);
}
}
final double[] result = new double[solutions.size()];
int index = 0;
for (double d: solutions) result[index++] = d;
return result;
}
private void appendSolutionIfSolved(final Set<Double> solutions, final double l, final double r)
{
double lSgn = signum(l), rSgn = signum(r);
if (lSgn == 0) solutions.add(l);
else if (rSgn == 0) solutions.add(r);
else if (lSgn != rSgn) solutions.add(solveInner(l, lSgn, r, rSgn));
}
public double solveInRange(double l, double r)
{
if (l >= r) throw new IllegalArgumentException("l should less than r");
double lSgn = signum(l), rSgn = signum(r);
if (lSgn == 0.0) return l;
if (rSgn == 0.0) return r;
if (lSgn == rSgn) throw new IllegalArgumentException("l, r are both " + (lSgn == 1.0 ? "positive" : "negative"));
return solveInner(l, lSgn, r, rSgn);
}
private double solveInner(final double l, final double lSgn, final double r, final double rSgn)
{
double p = pivot(l, r);
if (l == p || p == r) return p;
double pSgn = signum(p);
if (pSgn == 0.0) return p;
if (pSgn == lSgn) return solveInner(p, pSgn, r, rSgn);
else return solveInner(l, lSgn, p, pSgn);
}
private double pivot(final double l, final double r)
{
if (l == Double.NEGATIVE_INFINITY) return r > 0 ? 0 : r * 2 - 1;
if (r == Double.POSITIVE_INFINITY) return l < 0 ? 0 : l * 2 + 1;
return (l + r) / 2;
}
@Override
public String toString()
{
final StringBuilder builder = new StringBuilder();
for (int i = a.length - 1; i >= 0; --i)
{
if (a[i] != 0) {
if (builder.length() > 0) builder.append(" + ");
if (i == 0 || a[i] != 1) builder.append(a[i]);
if (i > 0)
{
builder.append("x");
if (i > 1) builder.append(i);
}
}
}
return builder.toString();
}
private double[] a;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment