Related
Firstly, I would like to apologise for my bad English.
when I submit the code below to DomJudge I got TimeLimit ERROR.
I can't think of ways to solve this question albeit I searched all over the internet and still couldn't find a solution.
Can someone give me a hint?
Question:
Here are N linear function fi(x) = aix + bi, where 1 ≤ i ≤ N。Define F(x) = maxifi(x). Please compute
the following equation for the input c[i], where 1 ≤ i ≤ m.
**Σ(i=1 to m) F(c[i])**
For example, given 4 linear function as follows. f1(x) = –x, f2 = x, f3 = –2x – 3, f4 = 2x – 3. And the
input is c[1] = 4, c[2] = –5, c[3] = –1, c[4] = 0, c[5] = 2. We have F(c[1]) = 5, F(c[2]) = 7, F(c[3])
= 1, F(c[4]) = 0, F(c[5]) = 2. Then,
**Σ(i=1 to 5)𝐹(𝑐[𝑖])
= 𝐹(𝑐[1]) + 𝐹(𝑐[2]) + 𝐹(𝑐[3]) + 𝐹(𝑐[4]) + 𝐹([5]) = 5 + 7 + 1 + 0 + 2 = 15**
Input Format:
The first line contains two positive integers N and m. The next N lines will contain two integers ai
and bi, and the last line will contain m integers c[1], c[2], c[3],…, c[m]. Each element separated by
a space.
Output Format:
Please output the value of the above function.
question image:https://i.stack.imgur.com/6HeaA.png
Sample Input:
4 5
-1 0
1 0
-2 -3
2 -3
4 -5 -1 0 2
Sample Output:
15
My Program
#include <iostream>
#include <vector>
struct L
{
int a;
int b;
};
int main()
{
int n{ 0 };
int m{ 0 };
while (std::cin >> n >> m)
{
//input
std::vector<L> arrL(n);
for (int iii{ 0 }; iii < n; ++iii)
{
std::cin >> arrL[iii].a >> arrL[iii].b;
}
//find max in every linear polymore
int answer{ 0 };
for (int iii{ 0 }; iii < m; ++iii)
{
int input{ 0 };
int max{ 0 };
std::cin >> input;
max = arrL[0].a * input + arrL[0].b;
for (int jjj{ 1 }; jjj < n; ++jjj)
{
int tmp{arrL[jjj].a * input + arrL[jjj].b };
if (tmp > max)max = tmp;
}
answer += max;
}
//output
std::cout << answer << '\n';
}
return 0;
}
Your solution is O(n*m).
A faster solution is obtained by iteratively determinating the "dominating segments", and the correspong crossing points, called "anchors" in the following.
Each anchor is linked to two segments, on its left and on its right.
The first step consists in sorting the lines according to the a values, and then adding each new line iteratively.
When adding line i, we know that this line is dominant for large input values, and must be added (even if it will be removed in the following steps).
We calculate the intersection of this line with the previous added line:
if the intersection value is higher than the rightmost anchor, then we add a new anchor corresponding to this new line
if the intersection value is lower than the rightmost anchor, then we know that we have to suppress this last anchor value. In this case, we iterate the process, calculating now the intersection with the right segment of the previous anchor.
Complexity is dominated by sorting: O(nlogn + mlogm). The anchor determination process is O(n).
When we have the anchors, then determining the rigtht segment for each input x value is O(n+ m). If needed, this last value could be further reduced with a binary search (not implemented).
Compared to first version of the code, a few errors have been corrected. These errors were concerning some corner cases, with some identical lines at the extreme left (i.e. lowest values of a). Besides, random sequences have been generated (more than 10^7), for comparaison of the results with those obtained by OP's code. No differences were found. It is likely that if some errors remain, they correspond to other unknown corner cases. The algorithm by itself looks quite valid.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cassert>
// lines of equation `y = ax + b`
struct line {
int a;
int b;
friend std::ostream& operator << (std::ostream& os, const line& coef) {
os << "(" << coef.a << ", " << coef.b << ")";
return os;
}
};
struct anchor {
double x;
int segment_left;
int segment_right;
friend std::ostream& operator << (std::ostream& os, const anchor& anc) {
os << "(" << anc.x << ", " << anc.segment_left << ", " << anc.segment_right << ")";
return os;
}
};
// intersection of two lines
double intersect (line& seg1, line& seg2) {
double x;
x = double (seg1.b - seg2.b) / (seg2.a - seg1.a);
return x;
}
long long int max_funct (std::vector<line>& lines, std::vector<int> absc) {
long long int sum = 0;
auto comp = [&] (line& x, line& y) {
if (x.a == y.a) return x.b < y.b;
return x.a < y.a;
};
std::sort (lines.begin(), lines.end(), comp);
std::sort (absc.begin(), absc.end());
// anchors and dominating segments determination
int n = lines.size();
std::vector<anchor> anchors (n+1);
int n_anchor = 1;
int l0 = 0;
while ((l0 < n-1) && (lines[l0].a == lines[l0+1].a)) l0++;
int l1 = l0 + 1;
if (l0 == n-1) {
anchors[0] = {0.0, l0, l0};
} else {
while ((l1 < n-1) && (lines[l1].a == lines[l1+1].a)) l1++;
double x = intersect(lines[l0], lines[l1]);
anchors[0] = {x, l0, l1};
for (int i = l1 + 1; i < n; ++i) {
if ((i != (n-1)) && lines[i].a == lines[i+1].a) continue;
double x = intersect(lines[anchors[n_anchor-1].segment_right], lines[i]);
if (x > anchors[n_anchor-1].x) {
anchors[n_anchor].x = x;
anchors[n_anchor].segment_left = anchors[n_anchor - 1].segment_right;
anchors[n_anchor].segment_right = i;
n_anchor++;
} else {
n_anchor--;
if (n_anchor == 0) {
x = intersect(lines[anchors[0].segment_left], lines[i]);
anchors[0] = {x, anchors[0].segment_left, i};
n_anchor = 1;
} else {
i--;
}
}
}
}
// sum calculation
int j = 0; // segment index (always increasing)
for (int x: absc) {
while (j < n_anchor && anchors[j].x < x) j++;
line seg;
if (j == 0) {
seg = lines[anchors[0].segment_left];
} else {
if (j == n_anchor) {
if (anchors[n_anchor-1].x < x) {
seg = lines[anchors[n_anchor-1].segment_right];
} else {
seg = lines[anchors[n_anchor-1].segment_left];
}
} else {
seg = lines[anchors[j-1].segment_right];
}
}
sum += seg.a * x + seg.b;
}
return sum;
}
int main() {
std::vector<line> lines = {{-1, 0}, {1, 0}, {-2, -3}, {2, -3}};
std::vector<int> x = {4, -5, -1, 0, 2};
long long int sum = max_funct (lines, x);
std::cout << "sum = " << sum << "\n";
lines = {{1,0}, {2, -12}, {3, 1}};
x = {-3, -1, 1, 5};
sum = max_funct (lines, x);
std::cout << "sum = " << sum << "\n";
}
One possible issue is the loss of information when calculating the double x corresponding to line intersections, and therefoe to anchors. Here is a version using Rational to avoid such loss.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cassert>
struct Rational {
int p, q;
Rational () {p = 0; q = 1;}
Rational (int x, int y) {
p = x;
q = y;
if (q < 0) {
q -= q;
p -= p;
}
}
Rational (int x) {
p = x;
q = 1;
}
friend std::ostream& operator << (std::ostream& os, const Rational& x) {
os << x.p << "/" << x.q;
return os;
}
friend bool operator< (const Rational& x1, const Rational& x2) {return x1.p*x2.q < x1.q*x2.p;}
friend bool operator> (const Rational& x1, const Rational& x2) {return x2 < x1;}
friend bool operator<= (const Rational& x1, const Rational& x2) {return !(x1 > x2);}
friend bool operator>= (const Rational& x1, const Rational& x2) {return !(x1 < x2);}
friend bool operator== (const Rational& x1, const Rational& x2) {return x1.p*x2.q == x1.q*x2.p;}
friend bool operator!= (const Rational& x1, const Rational& x2) {return !(x1 == x2);}
};
// lines of equation `y = ax + b`
struct line {
int a;
int b;
friend std::ostream& operator << (std::ostream& os, const line& coef) {
os << "(" << coef.a << ", " << coef.b << ")";
return os;
}
};
struct anchor {
Rational x;
int segment_left;
int segment_right;
friend std::ostream& operator << (std::ostream& os, const anchor& anc) {
os << "(" << anc.x << ", " << anc.segment_left << ", " << anc.segment_right << ")";
return os;
}
};
// intersection of two lines
Rational intersect (line& seg1, line& seg2) {
assert (seg2.a != seg1.a);
Rational x = {seg1.b - seg2.b, seg2.a - seg1.a};
return x;
}
long long int max_funct (std::vector<line>& lines, std::vector<int> absc) {
long long int sum = 0;
auto comp = [&] (line& x, line& y) {
if (x.a == y.a) return x.b < y.b;
return x.a < y.a;
};
std::sort (lines.begin(), lines.end(), comp);
std::sort (absc.begin(), absc.end());
// anchors and dominating segments determination
int n = lines.size();
std::vector<anchor> anchors (n+1);
int n_anchor = 1;
int l0 = 0;
while ((l0 < n-1) && (lines[l0].a == lines[l0+1].a)) l0++;
int l1 = l0 + 1;
if (l0 == n-1) {
anchors[0] = {0.0, l0, l0};
} else {
while ((l1 < n-1) && (lines[l1].a == lines[l1+1].a)) l1++;
Rational x = intersect(lines[l0], lines[l1]);
anchors[0] = {x, l0, l1};
for (int i = l1 + 1; i < n; ++i) {
if ((i != (n-1)) && lines[i].a == lines[i+1].a) continue;
Rational x = intersect(lines[anchors[n_anchor-1].segment_right], lines[i]);
if (x > anchors[n_anchor-1].x) {
anchors[n_anchor].x = x;
anchors[n_anchor].segment_left = anchors[n_anchor - 1].segment_right;
anchors[n_anchor].segment_right = i;
n_anchor++;
} else {
n_anchor--;
if (n_anchor == 0) {
x = intersect(lines[anchors[0].segment_left], lines[i]);
anchors[0] = {x, anchors[0].segment_left, i};
n_anchor = 1;
} else {
i--;
}
}
}
}
// sum calculation
int j = 0; // segment index (always increasing)
for (int x: absc) {
while (j < n_anchor && anchors[j].x < x) j++;
line seg;
if (j == 0) {
seg = lines[anchors[0].segment_left];
} else {
if (j == n_anchor) {
if (anchors[n_anchor-1].x < x) {
seg = lines[anchors[n_anchor-1].segment_right];
} else {
seg = lines[anchors[n_anchor-1].segment_left];
}
} else {
seg = lines[anchors[j-1].segment_right];
}
}
sum += seg.a * x + seg.b;
}
return sum;
}
long long int max_funct_op (const std::vector<line> &arrL, const std::vector<int> &x) {
long long int answer = 0;
int n = arrL.size();
int m = x.size();
for (int i = 0; i < m; ++i) {
int input = x[i];
int vmax = arrL[0].a * input + arrL[0].b;
for (int jjj = 1; jjj < n; ++jjj) {
int tmp = arrL[jjj].a * input + arrL[jjj].b;
if (tmp > vmax) vmax = tmp;
}
answer += vmax;
}
return answer;
}
int main() {
long long int sum, sum_op;
std::vector<line> lines = {{-1, 0}, {1, 0}, {-2, -3}, {2, -3}};
std::vector<int> x = {4, -5, -1, 0, 2};
sum_op = max_funct_op (lines, x);
sum = max_funct (lines, x);
std::cout << "sum = " << sum << " sum_op = " << sum_op << "\n";
}
To reduce the time complexity from O(n*m) to something near O(nlogn), we need to order the input data in some way.
The m points where the target function is sampled can be easily sorted using std::sort, which has an O(mlogm) complexity in terms of comparisons.
Then, instead of searching the max between all the lines for each point, we can take advantage of a divide-and-conquer technique.
Some considerations can be made in order to create such an algorithm.
If there are no lines or no sample points, the answer is 0.
If there is only one line, we can evaluate it at each point, accumulating the values and return the result. In case of two lines we could easily accumulate the max between two values. Searching for the intersection and then separating the intervals to accumulate only the correct value may be more complicated, with multiple corner cases and overflow issues.
A recursive function accepting a list of lines, points and two special lines left and right, may start by calculating the middle point in the set of points. Then it could find the two lines (or the only one) that have the greater value at that point. One of them also have the greatest slope between all the ones passing for that maximum point, that would be the top "right". The other one, the top "left" (which may be the same one), have the least slope.
We can partition all the remaining lines in three sets.
All the lines having a greater slope than the "top right" one (but lower intercept). Those are the ones that we need to evaluate the sum for the subset of points at the right of the middle point.
All the lines having a lower slope than the "top left" one (but lower intercept). Those are the ones that we need to evaluate the sum for the subset of points at the left of the middle point.
The remaining lines, that won't partecipate anymore and can be removed. Note this includes both "top right" and "top left".
Having splitted both the points and the lines, we can recurively call the same function passing those subsets, along with the previous left line and "top left" as left and right to the left, and the "top right" line with the previous right as left and right to the right.
The return value of this function is the sum of the value at the middle point plus the return values of the two recursive calls.
To start the procedure, we don't need to evaluate the correct left and right at the extreme points, we can use an helper function as entry point which sorts the points and calls the recursive function passing all the lines, the points and two dummy values (the lowest possible line, y = 0 + std::numeric_limits<T>::min()).
The following is a possible implementation:
#include <algorithm>
#include <iostream>
#include <vector>
#include <numeric>
#include <limits>
struct Line
{
using value_type = long;
using result_type = long long;
value_type slope;
value_type intercept;
auto operator() (value_type x) const noexcept {
return static_cast<result_type>(x) * slope + intercept;
}
static constexpr Line min() noexcept {
return { 0, std::numeric_limits<value_type>::min()};
}
};
auto result_less_at(Line::value_type x)
{
return [x] (Line const& a, Line const& b) { return a(x) < b(x); };
}
auto slope_less_than(Line::value_type slope)
{
return [slope] (Line const& line) { return line.slope < slope; };
}
auto slope_greater_than(Line::value_type slope)
{
return [slope] (Line const& line) { return slope < line.slope; };
}
auto accumulate_results(Line const& line)
{
return [line] (Line::result_type acc, Line::value_type x) {
return acc + line(x);
};
}
struct find_max_lines_result_t
{
Line::result_type y_max;
Line left, right;
};
template< class LineIt, class XType >
auto find_max_lines( LineIt first_line, LineIt last_line
, Line left, Line right
, XType x )
{
auto result{ [left, right] (const auto max_left, const auto max_right)
-> find_max_lines_result_t {
if ( max_left < max_right )
return { max_right, right, right };
else if ( max_right < max_left )
return { max_left, left, left };
else
return { max_left, left, right };
}(left(x), right(x))
};
std::for_each( first_line, last_line
, [x, &result] (Line const& line) mutable {
auto const y{ line(x) };
if ( y == result.y_max ) {
if ( result.right.slope < line.slope )
result.right = line;
if ( line.slope < result.left.slope )
result.left = line;
}
else if ( result.y_max < y ) {
result = {y, line, line};
}
} );
return result;
}
template< class SampleIt >
auto sum_left_right_values( SampleIt const first_x, SampleIt const last_x
, Line const left, Line const right )
{
return std::accumulate( first_x, last_x, Line::result_type{},
[left, right] (Line::result_type acc, Line::value_type x) {
return acc + std::max(left(x), right(x)); } );
}
template< class LineIt, class XType >
auto find_max_result( LineIt const first_line, LineIt const last_line
, Line const left, Line const right
, XType const x )
{
auto const y_max{ std::max(left(x), right(x)) };
LineIt const max_line{ std::max_element(first_line, last_line, result_less_at(x)) };
return max_line == last_line ? y_max : std::max(y_max, (*max_line)(x));
}
template <class LineIt, class SampleIt>
auto sum_lines_max_impl( LineIt const first_line, LineIt const last_line,
SampleIt const first_x, SampleIt const last_x,
Line const left, Line const right )
{
if ( first_x == last_x ) {
return Line::result_type{};
}
if ( first_x + 1 == last_x ) {
return find_max_result(first_line, last_line, left, right, *first_x);
}
if ( first_line == last_line ) {
return sum_left_right_values(first_x, last_x, left, right);
}
auto const mid_x{ first_x + (last_x - first_x - 1) / 2 };
auto const top{ find_max_lines(first_line, last_line, left, right, *mid_x) };
auto const right_begin{ std::partition( first_line, last_line
, slope_less_than(top.left.slope) ) };
auto const right_end{ std::partition( right_begin, last_line
, slope_greater_than(top.right.slope) ) };
return top.y_max + sum_lines_max_impl( first_line, right_begin
, first_x, mid_x
, left, top.left )
+ sum_lines_max_impl( right_begin, right_end
, mid_x + 1, last_x
, top.right, right );
}
template <class LineIt, class SampleIt>
auto sum_lines_max( LineIt first_line, LineIt last_line
, SampleIt first_sample, SampleIt last_sample )
{
if ( first_line == last_line )
return Line::result_type{};
std::sort(first_sample, last_sample);
return sum_lines_max_impl( first_line, last_line
, first_sample, last_sample
, Line::min(), Line::min() );
}
int main()
{
std::vector<Line> lines{ {-1, 0}, {1, 0}, {-2, -3}, {2, -3} };
std::vector<long> points{ 4, -5, -1, 0, 2 };
std::cout << sum_lines_max( lines.begin(), lines.end()
, points.begin(), points.end() ) << '\n';
}
Testable here.
I have a class that I'm trying to use, but in main function ёstartё doesn't execute with following error expression preceeding of apparent call must have pointer-to func type
#include <queue>
#include <limits>
#include <cmath>
#include <iostream>
// represents a single pixel
class Node {
public:
int idx; // index in the flattened grid
float cost; // cost of traversing this pixel
Node(int i, float c) : idx(i), cost(c) {}
};
bool operator<(const Node &n1, const Node &n2) {
return n1.cost > n2.cost;
}
bool operator==(const Node &n1, const Node &n2) {
return n1.idx == n2.idx;
}
// various grid heuristics:
// http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#S7
float linf_norm(int i0, int j0, int i1, int j1) {
return std::max(std::abs(i0 - i1), std::abs(j0 - j1));
}
// manhattan distance
float l1_norm(int i0, int j0, int i1, int j1) {
return std::abs(i0 - i1) + std::abs(j0 - j1);
}
// weights: flattened h x w grid of costs
// h, w: height and width of grid
// start, goal: index of start/goal in flattened grid
// diag_ok: if true, allows diagonal moves (8-conn.)
// paths (output): for each node, stores previous node in path
class Astar {
public:
float* weights = nullptr;
int h;
int w;
int start;
int goal;
bool diag_ok;
int* paths = nullptr;
void setVariables(float* weightsInput, int hInput, int wInput, int startInput, int goalInput, bool diag_okInput, int* pathsInput) {
weights = weightsInput;
h = hInput;
w = wInput;
start = startInput;
goal = goalInput;
diag_ok = diag_okInput;
paths = pathsInput;
}
void start() {
const float INF = std::numeric_limits<float>::infinity();
std::cout << "width : " << w << " " << "height : " << h << std::endl;
std::cout << "start : " << start << " goal : " << goal << std::endl;
Node start_node(start, 0.);
Node goal_node(goal, 0.);
float* costs = new float[h * w];
for (int i = 0; i < h * w; ++i)
costs[i] = INF;
costs[start] = 0.;
std::priority_queue<Node> nodes_to_visit;
nodes_to_visit.push(start_node);
int* nbrs = new int[3];
bool solution_found = false;
while (!nodes_to_visit.empty()) {
// .top() doesn't actually remove the node
Node cur = nodes_to_visit.top();
if (cur == goal_node) {
solution_found = true;
break;
}
nodes_to_visit.pop();
int row = cur.idx / w;
int col = cur.idx % w;
bool allowDiag;
// check bounds and find up to eight neighbors: top to bottom, left to right
// can move only right\down\down - right so we can max have 3 neighbours
nbrs[0] = (col + 1 < w) ? cur.idx + 1 : -1; // right
nbrs[1] = (row + 1 < h) ? cur.idx + w : -1; // down
allowDiag = (weights[cur.idx + w + 1] == 14) ? true : false;
nbrs[2] = (allowDiag) ? cur.idx + w + 1 : -1; // down-right
std::cout << "right-bottom node : " << weights[cur.idx + w + 1] << std::endl;
float heuristic_cost;
for (int i = 0; i < 3; ++i) {
std::cout << "neighbours : " << nbrs[i] << " ";
if (nbrs[i] >= 0) {
// the sum of the cost so far and the cost of this move
float new_cost = costs[cur.idx] + weights[nbrs[i]];
if (new_cost < costs[nbrs[i]]) {
// estimate the cost to the goal based on legal moves
if (allowDiag) {
heuristic_cost = linf_norm(nbrs[i] / w, nbrs[i] % w,
goal / w, goal % w);
}
else {
heuristic_cost = l1_norm(nbrs[i] / w, nbrs[i] % w,
goal / w, goal % w);
}
// paths with lower expected cost are explored first
float priority = new_cost + heuristic_cost;
nodes_to_visit.push(Node(nbrs[i], priority));
costs[nbrs[i]] = new_cost;
paths[nbrs[i]] = cur.idx;
}
}
}
std::cout << "\n";
}
delete[] costs;
delete[] nbrs;
//return solution_found;
}
};
int main() {
Astar astarPathfinding;
float* weights;
int h;
int w;
int start;
int goal;
bool diag_ok;
int* paths;
astarPathfinding.setVariables(weights, h, w, start, goal, diag_ok, paths);
astarPathfinding.start(); // error
return 0;
}
You have "start" as member and "start" as function.
Rename one of them will fix your error.
Given two points in the x, y plane:
x, f(x)
1, 3
2, 5
I can interpolate them using Lagrange and find f(1.5), which result in 4. Thinking a little I managed to find a way to discover the coefficients of the equation:
void l1Coefficients(const vector<double> &x, const vector<double> &y) {
double a0 = y[0]/(x[0]-x[1]);
double a1 = y[1]/(x[1]-x[0]);
double b0 = (-x[1]*y[0])/(x[0]-x[1]);
double b1 = (-x[0]*y[1])/(x[1]-x[0]);
double a = a0 + a1;
double b = b0 + b1;
cout << "P1(x) = " << a << "x +" << b << endl;
}
That gives me P1(x) = 2x +1.
Thinking a little more I was able to extend that to 2nd order equations. So, given the points:
1, 1
2, 4
3, 9
I found the equation P2(x) = 1x^2 +0x +0 with the following:
void l2Coefficients(const vector<double> &x, const vector<double> &y) {
double a0 = y[0] / ((x[0]-x[1])*(x[0]-x[2]));
double a1 = y[1] / ((x[1]-x[0])*(x[1]-x[2]));
double a2 = y[2] / ((x[2]-x[0])*(x[2]-x[1]));
double b0 = -(x[1]+x[2])*y[0] / ((x[0]-x[1])*(x[0]-x[2]));
double b1 = -(x[0]+x[2])*y[1] / ((x[1]-x[0])*(x[1]-x[2]));
double b2 = -(x[0]+x[1])*y[2] / ((x[2]-x[0])*(x[2]-x[1]));
double c0 = (x[1]*x[2])*y[0] / ((x[0]-x[1])*(x[0]-x[2]));
double c1 = (x[0]*x[2])*y[1] / ((x[1]-x[0])*(x[1]-x[2]));
double c2 = (x[0]*x[1])*y[2] / ((x[2]-x[0])*(x[2]-x[1]));
double a = a0 + a1 + a2;
double b = b0 + b1 + b2;
double c = c0 + c1 + c2;
cout << "P2(x) = " << a << "x^2 +" << b << "x +" << c << endl;
}
Working hard I actually was able to find the coefficients for equations of order up to 4th.
How to find the coefficients of order n equations? Where
Pn(x) = c_2x^2 + c_1x^1 + c_0x^0 + ...
It's a simple linear algebra problem.
We have a set of N samples of the form xk -> f(xk) and we know the general form of function f(x), which is:
f(x) = c0x0 + c1x1 + ... + cN-1xN-1
We want to find the coefficients c0 ... cN-1. To achieve that, we build a system of N equations of the form:
c0xk0 + c1xk1 + ... + cN-1xkN-1 = f(xk)
where k is the sample number. Since xk and f(xk) are constants rather than variables, we have a linear system of equations.
Expressed in terms of linear algebra, we have to solve:
Ac = b
where A is a Vandermonde matrix of powers of x and b is a vector of f(xk) values.
To solve such a system, you need a linear algebra library, such as Eigen. See here for example code.
The only thing that can go wrong with such an approach is the system of linear equations being under-determined, which will happen if your N samples can be fit with with a polynomial of degree less than N-1. In such a case you can still solve this system with Moore-Penrose pseudo inverse like this:
c = pinv(A)*b
Unfortunately, Eigen doesn't have a pinv() implementation, though it's pretty easy to code it by yourself in terms of Singular Value Decomposition (SVD).
I created a naive implementation of the matrix solution:
#include <iostream>
#include <vector>
#include <stdexcept>
class Matrix
{
private:
class RowIterator
{
public:
RowIterator(Matrix* mat, int rowNum) :_mat(mat), _rowNum(rowNum) {}
double& operator[] (int colNum) { return _mat->_data[_rowNum*_mat->_sizeX + colNum]; }
private:
Matrix* _mat;
int _rowNum;
};
int _sizeY, _sizeX;
std::vector<double> _data;
public:
Matrix(int sizeY, int sizeX) : _sizeY(sizeY), _sizeX(sizeX), _data(_sizeY*_sizeX){}
Matrix(std::vector<std::vector<double> > initList) : _sizeY(initList.size()), _sizeX(_sizeY>0 ? initList.begin()->size() : 0), _data()
{
_data.reserve(_sizeY*_sizeX);
for (const std::vector<double>& list : initList)
{
_data.insert(_data.end(), list.begin(), list.end());
}
}
RowIterator operator[] (int rowNum) { return RowIterator(this, rowNum); }
int getSize() { return _sizeX*_sizeY; }
int getSizeX() { return _sizeX; }
int getSizeY() { return _sizeY; }
Matrix reduce(int rowNum, int colNum)
{
Matrix mat(_sizeY-1, _sizeX-1);
int rowRem = 0;
for (int y = 0; y < _sizeY; y++)
{
if (rowNum == y)
{
rowRem = 1;
continue;
}
int colRem = 0;
for (int x = 0; x < _sizeX; x++)
{
if (colNum == x)
{
colRem = 1;
continue;
}
mat[y - rowRem][x - colRem] = (*this)[y][x];
}
}
return mat;
}
Matrix replaceCol(int colNum, std::vector<double> newCol)
{
Matrix mat = *this;
for (int y = 0; y < _sizeY; y++)
{
mat[y][colNum] = newCol[y];
}
return mat;
}
};
double solveMatrix(Matrix mat)
{
if (mat.getSizeX() != mat.getSizeY()) throw std::invalid_argument("Not square matrix");
if (mat.getSize() > 1)
{
double sum = 0.0;
int sign = 1;
for (int x = 0; x < mat.getSizeX(); x++)
{
sum += sign * mat[0][x] * solveMatrix(mat.reduce(0, x));
sign = -sign;
}
return sum;
}
return mat[0][0];
}
std::vector<double> solveEq(std::vector< std::pair<double, double> > points)
{
std::vector<std::vector<double> > xes(points.size());
for (int i = 0; i<points.size(); i++)
{
xes[i].push_back(1);
for (int j = 1; j<points.size(); j++)
{
xes[i].push_back(xes[i].back() * points[i].first);
}
}
Matrix mat(xes);
std::vector<double> ys(points.size());
for (int i = 0; i < points.size(); i++)
{
ys[i] = points[i].second;
}
double w = solveMatrix(mat);
std::vector<double> result(points.size(), 0.0);
if(w!=0)
for (int i = 0; i < ys.size(); i++)
{
result[i] = solveMatrix(mat.replaceCol(i, ys));
result[i] /= w;
}
return result;
}
void printCoe(std::vector<double> coe)
{
std::cout << "f(x)=";
bool notFirstSign = false;
for (int i = coe.size() - 1; i >= 0; i--)
{
if (coe[i] != 0.0)
{
if (coe[i] >= 0.0 && notFirstSign)
std::cout << "+";
notFirstSign = true;
if (coe[i] != 1.0)
if (coe[i] == -1.0)
std::cout << "-";
else
std::cout << coe[i];
if (i == 1)
std::cout << "x";
if (i>1)
std::cout << "x^" << i;
}
}
std::cout << std::endl;
}
int main()
{
std::vector< std::pair<double, double> > points1 = { {3,31}, {6,94}, {4,48}, {0,4} };
std::vector<double> coe = solveEq(points1);
printCoe(coe);
std::vector< std::pair<double, double> > points2 = { { 0,0 },{ 1,-1 },{ 2,-16 },{ 3,-81 },{ 4,-256 } };
printCoe(solveEq(points2));
printCoe(solveEq({ { 0,0 },{ 1,1 },{ 2,8 },{ 3,27 } }));
std::cin.ignore();
return 0;
}
I'm getting the following errors due to the namespace cpl?
I included Wavepacket.cpp and Vector.hpp below.
obj\Debug\wavepacket.o||In function `Z10initializev':|
wavepacket.cpp|79|undefined reference to `cpl::Vector::Vector(int)'|
wavepacket.cpp|79|undefined reference to `cpl::Vector::operator=(cpl::Vector const&)'|
wavepacket.cpp|80|undefined reference to `cpl::ComplexVector::ComplexVector(int)'|
wavepacket.cpp|80|undefined reference to `cpl::ComplexVector::operator=(cpl::ComplexVector const&)'|
wavepacket.cpp|81|undefined reference to `cpl::ComplexVector::ComplexVector(int)'|
wavepacket.cpp|81|undefined reference to `cpl::ComplexVector::operator=(cpl::ComplexVector const&)'|
wavepacket.cpp|101|undefined reference to `cpl::ComplexVector::ComplexVector(int)'|
wavepacket.cpp|101|undefined reference to `cpl::ComplexVector::operator=(cpl::ComplexVector const&)'|
wavepacket.cpp|102|undefined reference to `cpl::ComplexVector::ComplexVector(int)'|
wavepacket.cpp|102|undefined reference to `cpl::ComplexVector::operator=(cpl::ComplexVector const&)'|
wavepacket.cpp|103|undefined reference to `cpl::ComplexVector::ComplexVector(int)'|
wavepacket.cpp|103|undefined reference to `cpl::ComplexVector::operator=(cpl::ComplexVector const&)'|
obj\Debug\wavepacket.o||In function `Z8timeStepv':|
wavepacket.cpp|124|undefined reference to `cpl::solveTridiagonalCyclic(cpl::ComplexVector&, cpl::ComplexVector&, cpl::ComplexVector&, std::complex<double>, std::complex<double>, cpl::ComplexVector&, cpl::ComplexVector&)'|
wavepacket.cpp|126|undefined reference to `cpl::solveTridiagonal(cpl::ComplexVector&, cpl::ComplexVector&, cpl::ComplexVector&, cpl::ComplexVector&, cpl::ComplexVector&)'|
obj\Debug\wavepacket.o||In function `_static_initialization_and_destruction_0':|
wavepacket.cpp|22|undefined reference to `cpl::Vector::Vector(int)'|
wavepacket.cpp|71|undefined reference to `cpl::ComplexVector::ComplexVector(int)'|
wavepacket.cpp|71|undefined reference to `cpl::ComplexVector::ComplexVector(int)'|
wavepacket.cpp|72|undefined reference to `cpl::ComplexVector::ComplexVector(int)'|
wavepacket.cpp|72|undefined reference to `cpl::ComplexVector::ComplexVector(int)'|
wavepacket.cpp|72|undefined reference to `cpl::ComplexVector::ComplexVector(int)'|
||=== Build finished: 20 errors, 0 warnings ===|
Wavepacket.cpp
#include <cmath>
#include <complex>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <string>
#include <sstream>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include "Vector.hpp"
const double pi = 4*std::atan(1.0);
double h_bar = 1; // natural units
double mass = 1; // natural units
// The spatial grid
int N = 200; // number of interior grid points
double L = 100; // system extends from x=0 to x=L
double h = L / (N + 1); // grid size
double tau = 1; // time step
cpl::Vector x; // coordinates of grid points
bool periodic = true; // false = oo potential, true = periodic
// The potential V(x)
double V0 = 1.0; // height of potential well
double Vwidth = 10; // width of potential well
double Vcenter = 0.75 * L; // center of potential well
bool gaussian; // false = step potential
double V(double x) {
double halfWidth = std::abs(0.5 * Vwidth);
if (gaussian) {
double dx = (x - Vcenter) / halfWidth;
return V0 * std::exp( - dx * dx / 2);
} else {
if (std::abs(x - Vcenter) <= halfWidth)
return V0;
else
return 0;
}
}
// Inital wave packet
double x0 = L / 4; // location of center
double E = 1; // average energy
double sigma0 = L / 10; // width of wave packet
double Norm_psi; // norm of psi
double k0; // average wavenumber
double velocity; // average velocity
void getInput() {
std::cout << "Time-dependent Schroedinger Equation\n";
std::cout << "Enter size of x region L = ";
std::cin >> L;
std::cout << "Enter number of grid points N = ";
std::cin >> N;
std::cout << "Enter integration time step tau = ";
std::cin >> tau;
std::cout << "Enter width of potential = ";
std::cin >> Vwidth;
std::cout << "Enter height of potential V0 = ";
std::cin >> V0;
std::cout << "Enter width of packet sigma = ";
std::cin >> sigma0;
std::cout << "Enter energy of packet E = ";
std::cin >> E;
}
double t; // time
cpl::ComplexVector psi, chi; // complex wavefunction
cpl::ComplexVector a, b, c; // to represent tridiagonal Q matrix
std::complex<double> alpha, beta; // corner elements of Q
void initialize () {
t = 0;
// reset vectors
x = cpl::Vector(N);
psi = cpl::ComplexVector(N);
chi = cpl::ComplexVector(N);
// reset the lattice
h = L / (N + 1);
for (int j = 0; j < N; j++)
x[j] = (j + 1) * h;
// inititalize the packet
k0 = std::sqrt(2*mass*E - h_bar*h_bar/2/sigma0/sigma0) / h_bar;
velocity = k0 / mass;
Norm_psi = 1 / std::sqrt(sigma0 * std::sqrt(pi));
for (int j = 0; j < N; j++) {
double expFactor = std::exp(-(x[j] - x0) * (x[j] - x0)
/ (2 * sigma0 * sigma0));
psi[j] = std::complex<double>(
Norm_psi * std::cos(k0 * x[j]) * expFactor,
Norm_psi * std::sin(k0 * x[j]) * expFactor);
}
// elements of tridiagonal matrix Q = (1/2)(1 + i tau H / (2 hbar))
a = cpl::ComplexVector(N);
b = cpl::ComplexVector(N);
c = cpl::ComplexVector(N);
for (int j = 0; j < N; j++) {
const std::complex<double> i(0.0, 1.0);
b[j] = 0.5 + i * tau / (4 * h_bar) *
(V(x[j]) + h_bar * h_bar / (mass * h * h));
a[j] = c[j] = - i * tau * h_bar / (8 * mass * h * h);
}
alpha = c[N-1];
beta = a[0];
}
double T = 5; // time to travel length L
double framesPerSec = 50; // animation rate for screen redraws
void timeStep() {
static std::clock_t clockStart;
static bool done;
if (!done) {
double t0 = t;
do {
if (periodic)
solveTridiagonalCyclic(a, b, c, alpha, beta, psi, chi);
else
solveTridiagonal(a, b, c, psi, chi);
for (int j = 0; j < N; j++)
psi[j] = chi[j] - psi[j];
t += tau;
} while (std::abs(velocity * (t - t0)) < L / T / framesPerSec);
done = true;
}
std::clock_t clockNow = std::clock();
double seconds = (clockNow - clockStart) / double(CLOCKS_PER_SEC);
if ( seconds < 1 / framesPerSec ) {
return;
} else {
clockStart = clockNow;
done = false;
}
glutPostRedisplay();
glFlush();
}
void drawText(const std::string& str, double x, double y) {
glRasterPos2d(x, y);
int len = str.find('\0');
for (int i = 0; i < len; i++)
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, str[i]);
}
bool showRealImaginary; // false = probability only
void display() {
glClear(GL_COLOR_BUFFER_BIT);
if (showRealImaginary) {
glColor3f(0, 0, 1); // real part of psi blue
glBegin(GL_LINES);
for (int j = 1; j < N; j++) {
glVertex2d(x[j-1], psi[j-1].real());
glVertex2d(x[j], psi[j].real());
}
glEnd();
glColor3f(0, 1, 0); // imaginary part of psi green
glBegin(GL_LINES);
for (int j = 1; j < N; j++) {
glVertex2d(x[j-1], psi[j-1].imag());
glVertex2d(x[j], psi[j].imag());
}
glEnd();
}
glColor3f(1, 0, 0); // probability red
double pOld = psi[0].real() * psi[0].real() +
psi[0].imag() * psi[0].imag();
glBegin(GL_LINES);
for (int j = 1; j < N; j++) {
double p = psi[j].real() * psi[j].real() +
psi[j].imag() * psi[j].imag();
glVertex2d(x[j-1], 4 * pOld);
glVertex2d(x[j], 4 * p);
pOld = p;
}
glEnd();
glColor3ub(255, 165, 0); // potential orange
double Vold = V(x[1]);
glBegin(GL_LINES);
for (int j = 1; j < N; j++) {
double Vnew = V(x[j]);
glVertex2d(x[j-1], 0.2 * Vold);
glVertex2d(x[j], 0.2 * Vnew);
Vold = Vnew;
}
glEnd();
glColor3f(0, 0, 0); // text black
std::ostringstream os;
os << (periodic ? "Periodic " : "Infinite Wall ")
<< "Boundary Conditions" << std::ends;
drawText(os.str(), 0.02 * L, 0.28);
os.seekp(0); // beginning of string stream
os << "0" << std::ends;
drawText(os.str(), 0, -0.02);
drawText("0", 0, -0.02);
os.seekp(0);
os << "x = " << L << std::ends;
drawText(os.str(), (1 - 0.1) * L, -0.02);
os.seekp(0);
os << "t = " << t << std::ends;
drawText(os.str(), 0.02 * L, -0.29);
glutSwapBuffers();
}
void reshape(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, L, -0.3, 0.3);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
bool running; // to control animation
void mouse(int button, int state, int x, int y) {
switch (button) {
case GLUT_LEFT_BUTTON:
if (state == GLUT_DOWN) {
if (running) {
glutIdleFunc(NULL);
running = false;
} else {
glutIdleFunc(timeStep);
running = true;
}
}
break;
default:
break;
}
}
void menu(int menuItem) {
switch (menuItem) {
case 1:
gaussian = !gaussian;
break;
case 2:
periodic = !periodic;
break;
case 3:
showRealImaginary = !showRealImaginary;
break;
case 4:
if (running) {
glutIdleFunc(NULL);
running = false;
}
initialize();
glutPostRedisplay();
break;
default:
break;
}
}
int main(int argc, char *argv[]) {
getInput();
initialize();
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(600, 400);
glutInitWindowPosition(100, 100);
glutCreateWindow("Schroedinger Wave Packet Motion");
glClearColor(1.0, 1.0, 1.0, 0.0);
glShadeModel(GL_FLAT);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutCreateMenu(menu);
glutAddMenuEntry("Potential: Square/Gaussian", 1);
glutAddMenuEntry("Boundaries: Dirichlet/Periodic", 2);
glutAddMenuEntry("Real & Imag: Show/Hide", 3);
glutAddMenuEntry("Reset", 4);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutMainLoop();
}
Vector.hpp
#ifndef CPL_VECTOR_HPP
#define CPL_VECTOR_HPP
#include <complex>
#include <iostream>
namespace cpl {
class Vector {
public:
Vector(int dim = 1);
Vector(const Vector& dv);
~Vector() { delete [] v; }
int dimension() const { return dim; }
void resize(const int);
const double operator[](const int i) const { return v[i]; }
double& operator[](const int i) { return v[i]; }
Vector& operator = (const Vector& dv);
Vector& operator += (const Vector& dv);
Vector& operator -= (const Vector& dv);
Vector& operator *= (double d);
Vector& operator /= (double d);
double abs();
double norm();
double dot(const Vector& dv);
friend std::ostream& operator<<(std::ostream& os, const Vector& dv);
private:
int dim;
double *v;
};
inline Vector operator + (const Vector& dv) {
return dv;
}
extern Vector operator - (const Vector& dv);
extern Vector operator * (const Vector& dv, double d);
extern Vector operator * (double d, const Vector& dv);
extern Vector operator / (const Vector& dv, double d);
extern Vector operator + (const Vector& v1, const Vector& v2);
extern Vector operator - (const Vector& v1, const Vector& v2);
class ComplexVector {
public:
ComplexVector(int dim = 1);
ComplexVector(const ComplexVector& cv);
~ComplexVector() { delete [] v; }
int dimension() const { return dim; }
const std::complex<double> operator[](const int i) const { return v[i]; }
std::complex<double>& operator[](const int i) { return v[i]; }
ComplexVector& operator = (const ComplexVector& cv);
private:
int dim;
std::complex<double> *v;
};
class FFT {
public:
FFT() { N = 0; f = 0; inverse = false; }
void transform(ComplexVector& data);
void inverseTransform(ComplexVector& data);
Vector power(ComplexVector& data);
private:
int N;
ComplexVector *f;
bool inverse;
void bitReverse();
void DanielsonLanczos(int n);
};
extern void solveTridiagonal(
ComplexVector& a, ComplexVector& b, ComplexVector& c,
ComplexVector& r, ComplexVector& u);
extern void solveTridiagonalCyclic(
ComplexVector& a, ComplexVector& b, ComplexVector& c,
std::complex<double> alpha, std::complex<double> beta,
ComplexVector& r, ComplexVector& x);
} /* end namespace cpl */
#endif /* CPL_VECTOR_HPP */
EDIT I didn't want to delete this post incase someone needed it but I forgot to use Vector.cpp which is below.
#include "Vector.hpp"
namespace cpl {
Vector::Vector(int dim) {
v = new double [this->dim = dim];
for (int i = 0; i < dim; i++) v[i] = 0;
}
Vector::Vector(const Vector& dv) {
v = new double [dim = dv.dim];
for (int i = 0; i < dim; i++) v[i] = dv.v[i];
}
void Vector::resize(const int dimension) {
delete [] v;
v = new double [dim = dimension];
for (int i = 0; i < dim; i++) v[i] = 0;
}
Vector& Vector::operator = (const Vector& dv) {
if (this != &dv) {
if (dim != dv.dim) {
delete [] v;
v = new double [dim = dv.dim];
}
for (int i = 0; i < dim; i++) v[i] = dv[i];
}
return *this;
}
Vector& Vector::operator += (const Vector& dv) {
for (int i = 0; i < dim; i++) v[i] += dv[i];
return *this;
}
Vector& Vector::operator -= (const Vector& dv) {
for (int i = 0; i < dim; i++) v[i] -= dv[i];
return *this;
}
Vector& Vector::operator *= (double d) {
for (int i = 0; i < dim; i++) v[i] *= d;
return *this;
}
Vector& Vector::operator /= (double d) {
for (int i = 0; i < dim; i++) v[i] /= d;
return *this;
}
Vector operator - (const Vector& dv) {
int dim = dv.dimension();
Vector temp(dim);
for (int i = 0; i < dim; i++) temp[i] = -dv[i];
return temp;
}
Vector operator * (const Vector& dv, double d) {
int dim = dv.dimension();
Vector temp(dim);
for (int i = 0; i < dim; i++) temp[i] = dv[i] * d;
return temp;
}
Vector operator * (double d, const Vector& dv) {
int dim = dv.dimension();
Vector temp(dim);
for (int i = 0; i < dim; i++) temp[i] = dv[i] * d;
return temp;
}
Vector operator / (const Vector& dv, double d) {
int dim = dv.dimension();
Vector temp(dim);
for (int i = 0; i < dim; i++) temp[i] = dv[i] / d;
return temp;
}
Vector operator + (const Vector& v1, const Vector& v2) {
int dim = v1.dimension();
Vector temp(dim);
for (int i = 0; i < dim; i++) temp[i] = v1[i] + v2[i];
return temp;
}
Vector operator - (const Vector& v1, const Vector& v2) {
int dim = v1.dimension();
Vector temp(dim);
for (int i = 0; i < dim; i++) temp[i] = v1[i] - v2[i];
return temp;
}
double Vector::abs() {
return std::sqrt(norm());
}
double Vector::norm() {
double sum = 0;
for (int i = 0; i < dim; i++) sum += v[i] * v[i];
return sum;
}
double Vector::dot(const Vector& dv) {
double sum = 0;
for (int i = 0; i < dim; i++) sum += v[i] * dv[i];
return sum;
}
std::ostream& operator<<(std::ostream& os, const Vector& dv) {
for (int i = 0; i < dv.dim; i++) {
os << dv.v[i];
if (i < dv.dim-1)
os << '\t';
else
os << '\n';
}
return os;
}
// ComplexVector implementation
ComplexVector::ComplexVector(int dim) {
v = new std::complex<double> [this->dim = dim];
for (int i = 0; i < dim; i++) v[i] = 0.0;
}
ComplexVector::ComplexVector(const ComplexVector& cv) {
v = new std::complex<double> [dim = cv.dim];
for (int i = 0; i < dim; i++) v[i] = cv.v[i];
}
ComplexVector& ComplexVector::operator = (const ComplexVector& cv) {
if (this != &cv) {
if (dim != cv.dim) {
delete [] v;
v = new std::complex<double> [dim = cv.dim];
}
for (int i = 0; i < dim; i++) v[i] = cv[i];
}
return *this;
}
// FFT implementation
void FFT::transform(ComplexVector& data) {
N = data.dimension();
f = &data;
bitReverse();
for (int n = 1; n < N; n *= 2)
DanielsonLanczos(n);
for (int i = 0; i < N; ++i)
(*f)[i] /= std::sqrt(double(N));
}
void FFT::inverseTransform(ComplexVector& data) {
inverse = true;
transform(data);
inverse = false;
}
void FFT::bitReverse() {
int j = 1;
for (int i = 1; i < N; ++i) {
if (i < j) {
std::complex<double> temp = (*f)[i-1];
(*f)[i-1] = (*f)[j-1];
(*f)[j-1] = temp;
}
int k = N / 2;
while ( k < j ) {
j -= k;
k /= 2;
}
j += k;
}
}
void FFT::DanielsonLanczos(int n) {
const double pi = 4 * atan(1.0);
std::complex<double> W(0, pi / n);
W = inverse ? std::exp(-W) : std::exp(W);
std::complex<double> W_j(1, 0);
for (int j = 0; j < n; ++j) {
for (int i = j; i < N; i += 2 * n) {
std::complex<double> temp = W_j * (*f)[n+i];
(*f)[n+i] = (*f)[i] - temp;
(*f)[i] += temp;
}
W_j *= W;
}
}
Vector FFT::power(ComplexVector& data) {
Vector P(1 + N / 2);
P[0] = std::norm(data[0]) / double(N);
for (int i = 1; i < N / 2; i++)
P[i] = (std::norm(data[i]) + std::norm(data[N-i])) / double(N);
P[N/2] = std::norm(data[N/2]) / double(N);
return P;
}
// Solving tridiagonal complex matrices
void solveTridiagonal(
ComplexVector& a, ComplexVector& b, ComplexVector& c,
ComplexVector& r, ComplexVector& u)
{
int n = a.dimension();
ComplexVector gamma(n);
std::complex<double> beta = b[0];
u[0] = r[0] / beta;
for (int j = 1; j < n; j++) {
gamma[j] = c[j-1] / beta;
beta = b[j] - a[j] * gamma[j];
u[j] = (r[j] - a[j] * u[j-1]) / beta;
}
for (int j = n - 2; j >= 0; j--)
u[j] -= gamma[j+1] * u[j+1];
}
void solveTridiagonalCyclic(
ComplexVector& a, ComplexVector& b, ComplexVector& c,
std::complex<double> alpha, std::complex<double> beta,
ComplexVector& r, ComplexVector& x)
{
int n = a.dimension();
ComplexVector bb(n), u(n), z(n);
std::complex<double> gamma = -b[0];
bb[0] = b[0] - gamma;
bb[n-1] = b[n-1] - alpha * beta / gamma;
for (int i = 1; i < n-1; i++)
bb[i] = b[i];
solveTridiagonal(a, bb, c, r, x);
u[0] = gamma;
u[n-1] = alpha;
for (int i = 1; i < n-1; i++)
u[i] = 0.0;
solveTridiagonal(a, bb, c, u, z);
std::complex<double> fact = x[0] + beta * x[n-1] / gamma;
fact /= 1.0 + z[0] + beta * z[n-1] / gamma;
for (int i = 0; i < n; i++)
x[i] -= fact * z[i];
}
} /* end namespace cpl */
it's probably your build script that's not configured correctly. Your code compiled for me when I used the following commands:
g++ -c Vector.cpp -o Vector.o
g++ -c Wavepacket.cpp -o Wavepacket.o
g++ Vector.o Wavepacket.o -lGL -lGLU -lglut -o app
I was asked this Interview Question (C++,algos)and had no idea how to solve it.
Given an array say Arr[N] containing Cartesian coordinates of N distinct points count the number of triples (Arr[P], Arr[Q], Arr[R]) such that P < Q < R < N and the points Arr[P], Arr[Q], Arr[R] are collinear (i.e lie on the same straight line).
Any ideas? What algorithm can I use for this?
The following is probably not optimized, but its complexity is the one your interviewer requested.
First create a list of (a,b,c) values for each couple of points (N² complexity)
--> (a,b,c) stands for the cartesian equation of a straight line a*x+b*y+c=0
Given two points and their coordinates (xa, ya) and (xb, yb), computing (a,b,c) is simple.
Either you can find a solution to
ya=alpha*xa+beta
yb=alpha*xb+beta
(if (xb-xa) != 0)
alpha = (yb-ya)/(xb-xa)
beta = ya - alpha*xa
a = alpha
b = -1
c = beta
or to
xa = gamma*ya+delta
xb = gamma*yb+delta
(you get the point)
The solvable set of equations can then be rewritten in the more general form
a*x+b*y+c = 0
Then sort the list (N² log(N²) complexity therefore N²log(N) complexity).
Iterate over elements of the list. If two sequential elements are equal, corresponding points are collinear. N² complexity.
You might want to add a last operation to filter duplicate results, but you should be fine, complexity-wise.
EDIT : i updated a bit the algorithm while coding it to make it more simple and optimal. Here it goes.
#include <map>
#include <set>
#include <vector>
#include <iostream>
struct StraightLine
{
double a,b,c;
StraightLine() : a(0.),b(0.),c(0.){}
bool isValid() { return a!=0. || b!= 0.; }
bool operator<(StraightLine const& other) const
{
if( a < other.a ) return true;
if( a > other.a ) return false;
if( b < other.b ) return true;
if( b > other.b ) return false;
if( c < other.c ) return true;
return false;
}
};
struct Point {
double x, y;
Point() : x(0.), y(0.){}
Point(double p_x, double p_y) : x(p_x), y(p_y){}
};
StraightLine computeLine(Point const& p1, Point const& p2)
{
StraightLine line;
if( p2.x-p1.x != 0.)
{
line.b = -1;
line.a = (p2.y - p1.y)/(p2.x - p1.x);
}
else if( p2.y - p1.y != 0. )
{
line.a = -1;
line.b = (p2.x-p1.x)/(p2.y-p1.y);
}
line.c = - line.a * p1.x - line.b * p1.y;
return line;
}
int main()
{
std::vector<Point> points(9);
for( int i = 0 ; i < 3 ; ++i )
{
for( int j = 0; j < 3 ; ++j )
{
points[i*3+j] = Point((double)i, (double)j);
}
}
size_t nbPoints = points.size();
typedef std::set<size_t> CollinearPoints;
typedef std::map<StraightLine, CollinearPoints> Result;
Result result;
for( int i = 0 ; i < nbPoints ; ++i )
{
for( int j = i + 1 ; j < nbPoints ; ++j )
{
StraightLine line = computeLine(points[i], points[j]);
if( line.isValid() )
{
result[line].insert(i);
result[line].insert(j);
}
}
}
for( Result::iterator currentLine = result.begin() ; currentLine != result.end(); ++currentLine )
{
if( currentLine->second.size() <= 2 )
{
continue;
}
std::cout << "Line";
for( CollinearPoints::iterator currentPoint = currentLine->second.begin() ; currentPoint != currentLine->second.end() ; ++currentPoint )
{
std::cout << " ( " << points[*currentPoint].x << ", " << points[*currentPoint].y << ")";
}
std::cout << std::endl;
}
return 0;
}
For the count of Collinear triplets, identify a line with any two points and then check whether a new line formed by any other two points might be coinciding or parallel and that needs to be taken care of while computing the collinear triplets.
To solve:
First, collect all points on a line using Map<Line, Set<Point2d>>
as suggested in the question itself.
For triplets filter out those lines which have at least three points
Then compute nC3 for each of those add to global result.
Code for the above problem below
import java.util.*;
public class CollinearTriplets {
public static void main(String[] args) {
Point2d A[] = new Point2d[8];
A[0] = new Point2d(0, 0);
A[1] = new Point2d(1, 1);
A[2] = new Point2d(2, 2);
A[3] = new Point2d(3, 3);
A[4] = new Point2d(3, 2);
A[5] = new Point2d(4, 2);
A[6] = new Point2d(5, 1);
A[7] = new Point2d(4, 4);
System.out.println(countCollinear(A));
}
public static int factorial(int n) {
int fact = 1;
for (int i = 1; i <= n; i++) {
fact = fact * i;
}
return fact;
}
private static int combinations(int n, int r) {
return factorial(n) / (factorial(n - r) * factorial(r));
}
private static long countCollinear(Point2d[] points) {
Map<Line, Set<Point2d>> lineToPoints = new HashMap<>();
long result = 0;
for (int i = 0; i < points.length; i++) {
for (int j = i + 1; j < points.length; j++) {
double slope = 0d, xIntercept, yIntercept; // Default slope paralell to y-axis
if (points[i].x == points[j].x) {
slope = Double.MAX_VALUE; // Horizontal slope parallel to x-axis
} else if (points[i].y != points[j].y) {
xIntercept = points[j].x - points[i].x;
yIntercept = points[j].y - points[i].y;
slope = yIntercept / xIntercept;
}
Line currLine = new Line(points[i], slope);
if (Objects.isNull(lineToPoints.get(currLine))) {
lineToPoints.put(currLine, new HashSet<>());
}
lineToPoints.get(currLine).add(points[i]);
lineToPoints.get(currLine).add(points[j]);
}
}
for (Line line : lineToPoints.keySet()) {
int size = lineToPoints.get(line).size();
if (size >= 3) {
result = result + combinations(size, 3);
}
}
return result;
}
/**
* Line which contains the starting point and slope so that you can identify exact line
* equals method is overridden to check whether any new line is coinciding or parallel
*/
static class Line {
Point2d point;
double slope;
public Line(Point2d point, double slope) {
this.point = point;
this.slope = slope;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Line)) return false;
Line line = (Line) o;
if (line.slope == this.slope)
return ((((double) (line.point.y - this.point.y)) / (line.point.x - this.point.x)) == this.slope);
return false;
}
#Override
public int hashCode() {
return Objects.hash(slope);
}
}
static class Point2d {
int x;
int y;
public Point2d(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point2d)) return false;
Point2d point2d = (Point2d) o;
return x == point2d.x &&
y == point2d.y;
}
#Override
public int hashCode() {
return Objects.hash(x, y);
}
}
}
The time complexity for above code O(N^2) and space complexity is O(N)
If it's 2 dimension points: 3 points (P,Q,R) are collinear if (P,Q), (P,R) define the same slope.
m = (p.x - q.x) / (p.y - q.y) ; slope
Somehow you need to check all possible combinations and check, an efficient algo is trick as the first naive is N*(N-1)*(N-2)...
Instead of 3 loops, whish is O(n³), precompute the slopes of all lines given by two points Arr[P], Arr[Q]. That's O(n²). Then compare these slopes.
You can improve that further sorting the lines by their slope during computation or afterwards, which is O(n log n). After that finding lines with the same slope is O(n).
But you may have to pay a price for that by implementing a data structure, when you want to know, which points are collinear.
I think the key point of an interview question is not to give the perfect algorithm, but to identify and discuss the problems within an idea.
Edit:
Brute force approach:
#include <iostream>
#include <vector>
struct Point { int x, y; };
bool collinear(Point P, Point Q, Point R)
{
// TODO: have to look up for math ... see icCube's answer
return false;
}
int main()
{
std::vector<Point> v;
Point a;
while (std::cin >> a.x >> a.y)
{
v.push_back(a);
}
int count = 0;
for (int p = 0; p < v.size(); ++p)
{
for (int q = p+1; q < v.size(); ++q)
{
for (int r = q+1; r < v.size(); ++r)
{
if (collinear(v[p], v[q], v[r])) ++count;
}
}
}
std::cout << count << '\n';
return 0;
}
It's trivial to see that you can get all the pairs of points and their slope & y-intercepts in O(n^2) time. So the output is:
IndexB Slope Y-Intercept IndexA
Of course, we won't insert any entries where IndexA = IndexB.
Let's have this table indexed on (IndexB,Slope,Y), which forces our insert into this table as O(log(n))
After we fill out this table with new records (B',S',Y',A'), we check to see if we already have an element such that B'=A of the existing table and B!=A' of the new record (meaning we have a unique triplet) that matches the slope and Y-intercept (meaning collinear). If this is the case and A < B < B', increment the count by 1.
EDIT: One clarifying remark. We need to make sure that we fill this table "backwards" first, taking all the pairs that wouldn't satisfy A < B (< C). This ensures that they will exist in the table before we start testing for their existence.
EDIT: Wow my C++ is rusty... took a while.
#include <iostream>
#include <vector>
#include <set>
#include <stdlib.h>
#include <math.h>
using namespace std;
#define ADD_POINT(xparam,yparam) { point x; x.x = xparam; x.y = yparam; points.push_back(x); };
#define EPSILON .001
class line {
public:
double slope;
double y;
int a;
int b;
bool operator< (const line &other) const{
if(this->a < other.a)
return true;
else if(this->a==other.a){
if(this->slope-other.slope < -EPSILON)
return true;
else if(fabs(this->slope-other.slope) < EPSILON){
if(this->y-other.y < -EPSILON)
return true;
else
return false;
}else
return false;
}else
return false;
}
line(double slope, double y, int a, int b){
this->slope = slope;
this->y = y;
this->a = a;
this->b = b;
}
line(const line &other){
this->slope = other.slope;
this->y = other.y;
this->a = other.a;
this->b = other.b;
}
};
class point {
public:
double x;
double y;
};
int main(){
vector<point> points;
ADD_POINT(0,0);
ADD_POINT(7,28);
ADD_POINT(1,1);
ADD_POINT(2,3);
ADD_POINT(2,4);
ADD_POINT(3,5);
ADD_POINT(3,14);
ADD_POINT(5,21);
ADD_POINT(9,35);
multiset<line> lines;
for(unsigned int x=0;x<points.size();x++){
for(unsigned int y=0;y<points.size();y++){
if(x!=y){ // No lines with the same point
point a = points[x];
point b = points[y];
double slope = (a.y-b.y)/(a.x-b.x);
double yint;
yint = a.y-a.x*slope;
line newline(slope,yint,x,y);
lines.insert(newline);
}
}
}
for(multiset<line>::const_iterator p = lines.begin(); p != lines.end(); ++p){
//cout << "Line: " << p->a << " " << p->b << " " << p->slope << " " << p->y << endl;
line theline = *p;
line conj(theline.slope,theline.y,theline.b,-1);
multiset<line>::iterator it;
pair<multiset<line>::iterator,multiset<line>::iterator> ret;
ret = lines.equal_range(conj);
for(it = ret.first; it!=ret.second; ++it){
//cout << " Find: " << it->a << " " << it->b << " " << it->slope << " " << it->y << endl;
int a = theline.a;
int b = theline.b;
int c = it->b;
if(a < b && b < c){
cout << a << " " << b << " " << c << std::endl;
}
}
}
//cout << points[0].x << std::endl;
}
I have this solution tell if there is a better one,
Sort all the points according to the slope they make with the x axis or any other axis you want ( O(n* logn) ). Now all you have to do if go through the sorted list and find points which have same slope either inpositive or negative direction( this can be done in linear time i.e. O(n) ) . Lets say you get m such points for one case then increment the answer by C(m,3)..
Total time depends on how good you implement C(m,3)
But asymptotically O(N logN)
Edit: After seeing icCube's comment i realize that we cannot take any axis..so for the above defined algo taking the slope calculating point as one of the n points ( thus n times ) should be my best guess. But it makes the algorithm N*N*Log(N)