-
-
Save yadav26/ea3fbd10b67065273d5245ace01edaa3 to your computer and use it in GitHub Desktop.
| /*********************************************************************************************** | |
| 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; | |
| } |
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
Solution2 function is what all you want. Rest code is harware concurrency I did to refresh my skills.