Skip to content

Instantly share code, notes, and snippets.

@yadav26
Last active March 7, 2024 05:52
Show Gist options
  • Save yadav26/ea3fbd10b67065273d5245ace01edaa3 to your computer and use it in GitHub Desktop.
Save yadav26/ea3fbd10b67065273d5245ace01edaa3 to your computer and use it in GitHub Desktop.
KnightDialler_SequenceMapper
/***********************************************************************************************
Anshul Yadav
er.anshulyadav@gmail.com
yadav26@github
This is for reference, acacia system knight sequncer
************************************************************************************************
Compile and run :
g++ -std=c++17 -Wpedantic -O3 source.cpp -o knightSequence.out && ./knightSequence.out
g++ -std=c++17 -Wpedantic -O3 source.cpp -o knightSequence.out && ./knightSequence.out -c{8} -p [10]
*/
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <sstream>
#include <chrono>
#include <thread>
#define ll long long
using namespace std;
using VVINT = vector<vector<int>>;
constexpr int isAEIOU[] = { /*a*/1,0,0,0,/*e*/1,0,0,0,/*i*/1,0,0,0,0,0,/*o*/1,0,0,0,0,0,/*u*/1, 0, 0, 0, 0, 0 };
constexpr int MAX_SEQUENCE_LEN = 10;
class KeyBoardSequenceFinder {
//currently its hard coded, but should be dynamically generated
ll dp[100][18][3]; // 100 - max key length (currently only 10 used), 18 - keybord keys , 3 max vowels possible 0,1 and 2
const std::map<char, int> keyIdentity = {
{ 'A',0 }, { 'B', 1 }, { 'C', 2 }, { 'D', 3 }, { 'E',4 },
{ 'F',5 }, { 'G', 6 }, { 'H', 7 }, { 'I', 8 }, { 'J',9 },
{ 'K',10 }, { 'L',11 }, { 'M',12 }, { 'N',13 }, { 'O',14 },
{ '1',15 }, { '2',16 }, { '3',17 }
};
VVINT kbLayout = {
/*A:0 */{11,7}, /*B:1 */{10,8,12}, /*C:2 */{13,5,11,9}, /*D:3 */{14,6,12,}, /*E:4 */{7,13},
/*F:5 */{12,2,15}, /*G:6 */{13,3,16}, /*H:7 */{4,10,0,17,15,14}, /*I:8 */{1,11,16}, /*J:9 */{2,12,17},
/*K:10*/{7,1,16}, /*L:11*/{0,2,8,17}, /*M:12*/{3,1,5,9}, /*N:13*/{2,4,6,15}, /*O:14*/{3,7,16},
/*1:15*/{5,7,13}, /*2:16*/{6,8,10,14}, /*3:17*/{7,9,11}
};
public:
int max_vow = 2;
int start_key_len = 1;
int final_key_len = MAX_SEQUENCE_LEN;
static inline int use_cpu_cores = std::thread::hardware_concurrency();
bool profile_enabled = false;
bool do_range_run = false;
std::vector<int> keys;
KeyBoardSequenceFinder(int argc, char*argv[]){
memset(dp, -1, sizeof(dp));
std::transform(keyIdentity.begin(), keyIdentity.end(), std::back_inserter(keys),
[](const auto& pair) { return pair.second; });
UsageGuide();
std::string arg;
stringstream ss;
for (int i = 1; i < argc; ++i) {
ss << argv[i];
ss << " ";
}
handleInputs(argc, ss.str());
//reset max_vow here currently hardcoded to 2
}
VVINT build_keyboard_layout() {
VVINT knightMoves = {
/*A:0 */{11,7}, /*B:1 */{10,8,12}, /*C:2 */{13,5,11,9}, /*D:3 */{14,6,12,}, /*E:4 */{7,13},
/*F:5 */{12,2,15}, /*G:6 */{13,3,16}, /*H:7 */{4,10,0,17,15,14}, /*I:8 */{1,11,16}, /*J:9 */{2,12,17},
/*K:10*/{7,1,16}, /*L:11*/{0,2,8,17}, /*M:12*/{3,1,5,9}, /*N:13*/{2,4,6,15}, /*O:14*/{3,7,16},
/*1:15*/{5,7,13}, /*2:16*/{6,8,10,14}, /*3:17*/{7,9,11}
};
return std::move(knightMoves);
}
//debug purpose only
void printArr(const vector<int>& v) {
cout << "[";
for (int i = 0; i < v.size(); ++i) {
cout << v[i] << (i == v.size() - 1 ? "" : ", ");
}
cout << "] ";
}
//flibility for user inputs
void UsageGuide() {
cout << "\n --------------------------------------------------------------------------------------------------- ";
cout << "\n -Binary Flags ------------------------------------------------------------------------------------- ";
cout << "\n --------------------------------------------------------------------------------------------------- ";
cout << "\n 1. Default key length of 10 (./a.out) --------------------------------------------------------------";
cout << "\n 2. To calculate valid paths of keys len 15 pass argument (./a.out 15) ";
cout << "\n 3. To calculate range of inputs varying length from 1 to 10(./a.out [1-10]).";
cout << "\n 4. To force run on limited cpu core use flag \"-c{1}\" e.g.(./a.out -c{N} ) will force using N cores.";
cout << "\n 5. To profile the binary and print running time use flag \"-p\" e.g.(./a.out -p ) .";
cout << "\n 6. To profile the binary results flag \"-p\" e.g.(./a.out -p ) will print time of execution.";
cout << "\n ---------------------------------------------------------------------------------------------------- ";
cout << "\n -----------------------------------THANKS----------------------------------------------------------- ";
cout << "\n -------------------------------ANSHUL YADAV--------------------------------------------------------- ";
cout << "\n ---------------------------------------------------------------------------------------------------- ";
}
void handleInputs(int c, string in) {
size_t pos = in.find("-p");
if (pos != std::string::npos) {
profile_enabled = true;
in.erase(pos, 2);
}
pos = in.find("-c");
if (pos != std::string::npos) {
int ipos = 2;
if (in[pos + 2] == '{') {
ipos = in.find('}', pos + 3);
if (ipos != std::string::npos)
{
string s = in.substr(pos + 3, ipos - 1 - (pos + 2));
use_cpu_cores = atoi(s.c_str());
}
}
in.erase(pos, ipos - pos + 1);
}
pos = in.find("[");
if (pos != std::string::npos) {
int epos = in.find(']', pos + 1);
do_range_run = true;
//running in range
int mp = in.find('-', pos + 1);
if (mp == -1) {
start_key_len = 1;
string s = in.substr(pos + 1, epos - 1 - (pos));
final_key_len = atoi(s.c_str());
}
else {
string s = in.substr(pos + 1, mp - 1);
start_key_len = atoi(s.c_str());
string e = in.substr(mp + 1, epos - 1 - (mp));
final_key_len = atoi(e.c_str());
if (start_key_len > final_key_len)
swap(final_key_len, start_key_len);
}
}
else {
try {
start_key_len = final_key_len = std::stoi(in);
}
catch (...)
{
cout << "\n HandleInput - Exception (stoi) invalid numeric params(" << in << ")\n";
start_key_len = final_key_len = 10;
}
}
}
//no meorization
//ll Solution1(const vector<int>& keys, uint32_t left_moves, uint32_t va) {
ll Solution1(const vector<int>&keys, int cellid, uint32_t left_moves, uint32_t vowels_left) {
if (!left_moves)
return 1;// we got a valid path
left_moves = left_moves - 1;
ll total_paths = 0;
for (const auto keyIdx : keys) {
if (vowels_left > 0 || !isAEIOU[keyIdx]) {
auto lessOneVa = vowels_left - isAEIOU[keyIdx];
total_paths += Solution1(kbLayout[keyIdx], keyIdx, left_moves, lessOneVa);
}
}
return total_paths;
}
//solution 2 faster with memorization
ll Solution2(const vector<int>& keys, int cellid, uint32_t left_moves, uint32_t vowels_left) {
if (!left_moves)
return 1;// we got a valid path
if (-1 != dp[left_moves][cellid][vowels_left]) {
return dp[left_moves][cellid][vowels_left];//we already have this calculated
}
ll total_paths = 0;
for (const auto keyIdx : keys) {
if (vowels_left > 0 || !isAEIOU[keyIdx]) {
total_paths += Solution2(kbLayout[keyIdx], keyIdx, left_moves - 1, vowels_left - isAEIOU[keyIdx]);
}
}
return dp[left_moves][cellid][vowels_left] = total_paths;
}
void thCallBack(vector<int>v, int cellid, int chunkSize, int len, ll& res)
{
for (int i = cellid; i < (cellid + chunkSize > kbLayout.size() ? kbLayout.size() : cellid + chunkSize); ++i)
//To use memorization solution
res += Solution2(kbLayout[i], i, len - 1, max_vow - isAEIOU[i]);
//To use non memorization solution comment above line and uncomment below line
//res += Solution1(kbLayout[i], i, len - 1, max_vow - isAEIOU[i]);
}
void NonThreadedSolution_X() {
//ll ans1 = 0;
//for(int i=0;i< kbLayout.size();++i)
// ans1 += Solution1(kbLayout[i], final_key_len-1, max_vow - isAEIOU[i]);
//cout << "Solution 1(no memoization): " <<ans1;
//cout << endl;
memset(dp, -1, sizeof(dp));
ll ans2 = 0;
for (int i = 0; i < kbLayout.size(); ++i)
ans2 += Solution2(kbLayout[i], i, final_key_len - 1, max_vow - isAEIOU[i]);
cout << "\nSolution 2(memoization): " << ans2;
cout << endl;
}
//Concurrent multithread solution
void MultiThreadedSolution_X( )
{
auto start = std::chrono::steady_clock::now();
const int tth = KeyBoardSequenceFinder::use_cpu_cores;// Get number of available cores
int chunkSize = keys.size() / tth;
int chunkCount = keys.size() % chunkSize == 0 ? keys.size() / chunkSize : (keys.size() / chunkSize) + 1;
for (int run = start_key_len; run <= final_key_len; ++run)
{
auto start_run = std::chrono::steady_clock::now();
ll ans = 0;
vector<thread> threads(chunkCount); // one threads per workload
std::vector<ll> results(chunkCount);//gather output result per thread
for (int tid = 0; tid < chunkCount; ++tid) {
auto start = keys.begin() + (tid * chunkSize);
auto endPos = start;
if (tid == chunkCount - 1) {
//BugFix for the last work load
endPos = keys.end();
}
else
endPos = keys.begin() + ((tid + 1) * chunkSize);
vector<int> vec = { start, endPos };
std::thread th(&KeyBoardSequenceFinder::thCallBack, this, vec, (tid * chunkSize), chunkSize, run, std::ref(results[tid]));
threads[tid] = std::move(th);
}
for (std::thread& t : threads) {
t.join();
}
for (int r : results) {
ans += r;
}
cout << "\nFor key len = " << run << ", Total Valid sequences = " << ans;
threads.clear();
results.clear();
auto end_run = std::chrono::steady_clock::now();
auto elapsed_run = end_run - start_run;
double elapsed_seconds_run = std::chrono::duration<double>(elapsed_run).count();
if (profile_enabled)std::cout << ", Elapsed time(sec): " << elapsed_seconds_run;
}
cout << "\nUsed cpu cores = " << tth;
auto end = std::chrono::steady_clock::now();
auto elapsed = end - start;
double elapsed_seconds = std::chrono::duration<double>(elapsed).count();
if (profile_enabled)std::cout << "\n Total Elapsed time: " << elapsed_seconds << " seconds" << std::endl;
}
};
int main(int argc, char* argv[])
{
KeyBoardSequenceFinder SeqCalculator(argc,argv);
SeqCalculator.MultiThreadedSolution_X();
//for simple non threaded solution
//SeqCalculator.NonThreadedSolution_X();
return 0;
}
@yadav26
Copy link
Author

yadav26 commented Mar 7, 2024

Solution2 function is what all you want. Rest code is harware concurrency I did to refresh my skills.

@yadav26
Copy link
Author

yadav26 commented Mar 7, 2024

For key len = 1, Total Valid sequences = 18, Elapsed time(sec): 0.0015133
For key len = 2, Total Valid sequences = 60, Elapsed time(sec): 0.0005201
For key len = 3, Total Valid sequences = 214, Elapsed time(sec): 0.0004622
For key len = 4, Total Valid sequences = 732, Elapsed time(sec): 0.0003648
For key len = 5, Total Valid sequences = 2486, Elapsed time(sec): 0.0004601
For key len = 6, Total Valid sequences = 8392, Elapsed time(sec): 0.0004138
For key len = 7, Total Valid sequences = 27912, Elapsed time(sec): 0.0007859
For key len = 8, Total Valid sequences = 93204, Elapsed time(sec): 0.0017012
For key len = 9, Total Valid sequences = 306288, Elapsed time(sec): 0.00047
For key len = 10, Total Valid sequences = 1013398, Elapsed time(sec): 0.000532

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment