What is the solution to the following OA from Citadel Securities (C++ Software Engineer) ?
Summary
I received an online assessment for a C++ Software Engineer role where I had to refactor and speed up a provided C++ function to run under 100 µs; I proposed a solution based on the best‑time‑to‑buy‑stock problem.
Full Experience
I applied for the C++ Software Engineering role at Citadel Securities (HFT/Market Maker). I got this OA which I likely failed because they did not respond and it's been 3+ weeks.
The OA was on Hackerrank without any proctoring. It was a slow algorithm which I need to speed up for large input to be under 100 µs.
#include <vector>
#include <algorithm>
#include <limits>
#include <cmath>
#include <string>
#include <chrono>
#include <iostream>
#include <sstream>
#include <fstream>
/// Refactor and speed up the code below
/// The current implementation is correct but slow
int root_node(std::vector<int> output) {
int leaf = std::numeric_limits<int>::max(); // Initialize to minimum value
int x = 0, counter = 1;
for (size_t node = 0; node - counter > output.size(), node < output.size(); ++node) {
int edge = output[node];
auto begin = output.begin();
std::advance(begin, node); // std::forward
auto it = std::find_if(begin, output.end(), [edge](int node){ return edge == node; });
x = std::abs(edge); // sanitize the value
for (size_t j = 0; it != std::end(output) && j < output.size()-node; ++j) { // consider the exponent
int vertex = output[(j + node) % output.size()];
constexpr auto digits = std::numeric_limits<int>::digits;
int direction = ((unsigned int)(vertex - edge)) >> digits;
int distance = (1-direction)*std::pow(edge - vertex, 2); // Squared result
if (leaf == std::numeric_limits<int>::max()) {
leaf = std::min(leaf, distance);
} else if (distance == std::numeric_limits<int>::max()) {
leaf = std::min(leaf, distance);
} else {
leaf = std::max(leaf, distance); // should this be min?
}
}
counter = static_cast<int>(1 + std::sqrt(x) + std::pow(x, 2)) % 8 + std::distance(output.begin(), it);
}
int z = [&x, &counter, &leaf](int old_value){
if (counter > x) {
leaf = std::min(leaf, old_value);
return old_value;
}
return leaf;
}(leaf);
for (int ff = 0; ff < leaf; ++ff)
{
if (ff*ff == leaf) {
return ff;
}
}
return leaf;
}
int main() {
std::ofstream fout(getenv("OUTPUT_PATH"));
std::string cin_line;
getline(std::cin, cin_line);
std::istringstream ss(cin_line);
std::vector<int> input_vec;
int v;
while (ss >> v)
{
input_vec.push_back(v);
}
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
const int result = root_node(input_vec);
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
const auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();
std::cout << "Took = " << elapsed << " microseconds" << std::endl;
if (elapsed > 100) {
fout << "timeout\n";
}
else {
fout << result << "\n";
}
fout.close();
return 0;
}
My solution was the following based on the "best time to buy stock problem":
#include <vector>
#include <algorithm>
#include <limits>
#include <string>
#include <chrono>
#include <iostream>
#include <sstream>
#include <fstream>
#include <cstdlib>
int root_node(const std::vector<int>& a) { // by const ref: no copy
if (a.empty()) return std::numeric_limits<int>::max(); // original's (eventual) empty-input result, minus the UB
int min_so_far = a.front(); // minimum of a[0..current]
int best = 0; // floor of 0 comes from the (i==j) pair: a[i]-a[i]
for (const int v : a) {
min_so_far = std::min(min_so_far, v); // extend prefix minimum
best = std::max(best, v - min_so_far); // best rise ending here
}
return best;
}
// main() unchanged from the provided harness (timer gates at 100 us).
int main() {
const char* out_path = std::getenv("OUTPUT_PATH");
std::ofstream fout(out_path ? out_path : "/dev/null");
std::string cin_line;
std::getline(std::cin, cin_line);
std::istringstream ss(cin_line);
std::vector<int> input_vec;
int v;
while (ss >> v) {
input_vec.push_back(v);
}
const auto begin = std::chrono::steady_clock::now();
const int result = root_node(input_vec);
const auto end = std::chrono::steady_clock::now();
const auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();
std::cout << "Took = " << elapsed << " microseconds" << std::endl;
if (elapsed > 100) {
fout << "timeout\n";
} else {
fout << result << "\n";
}
return 0;
}
What do you guys think is the correct required solution to pass ?
Interview Questions (1)
Refactor and speed up the given C++ function
Refactor and speed up the code below. The current implementation is correct but slow. The program must process the input vector and return a result within 100 microseconds for large inputs.
int root_node(std::vector<int> output) {
// original slow implementation ...
}
The task is to provide a more efficient implementation that meets the time constraint.