This solution looks ugly. I've tried to simplify using a vector and cycling a vector but would it be faster? I tried making up a formula to retrieve these numbers but I'm terrible at math!
if (SkillValue > 115 && SkillValue <= 135)
{
return 100 / 2;
}
else if (SkillValue > 135 && SkillValue <= 160)
{
return 100 / 3;
}
else if (SkillValue > 160 && SkillValue <= 190)
{
return 100 / 4;
}
else if (SkillValue > 190 && SkillValue <= 215)
{
return 100 / 5;
}
else if (SkillValue > 215 && SkillValue <= 295)
{
return 100 / 6;
}
else if (SkillValue > 295 && SkillValue <= 315)
{
return 100 / 9;
}
else if (SkillValue > 315 && SkillValue <= 355)
{
return 100 / 10;
}
else if (SkillValue > 355 && SkillValue <= 425)
{
return 100 / 11;
}
else if (SkillValue > 425 && SkillValue < 450)
{
return 100 / 12;
}
return 100;
}
There doesn't appear to be any pattern to the bounds for each if condition, or at least any pattern that would let you write this as a closed form check.
However, you are currently checking all the if conditions, which is O(n) where n is the number of conditions. You can do this in O(log(n)) by using std::lower_bound on a range to find the value of the denominator, like this:
std::array bounds { 115, 135, 160, 190, ... };
std::array dens {1, 2, 3, 4, ... , 1}; // extra 1 at the end to account for the
// case that none of the bounds match
auto it = std::lower_bound(std::begin(bounds), std::end(bounds), SkillValue);
return 100 / dens[std::distance(std::begin(bounds), it)];
Right off the bat, you are repeating logic in each conditional that can be refactored out of the previous conditionals by switching to the following form:
if (SkillValue <= 115) {
return 100/1;
} else if (SkillValue <= 135) {// the 'else' explicitly means that it is already NOT <= 115, aka IS > 115, so no need to repeat with a '&&'
return 100/2;
} else if (SkillValue <= 160) {
return 100/3;
} else if (SkillValue <= 190) {
return 100/4;
}//... and so on
It's hard to find an equation to compute your result because your ranges are not equal in size, and the divisor jumps at 295.
To make a more compact version, here's a way:
#include <algorithm>
#include <iterator>
// 296 repeats to compensate for the jump in your divisor
constexpr int ranges[] = {0, 115, 136, 161, 191, 216, 296, 296, 296, 316, 356, 426, 456};
int compute(int SkillValue) {
if (SkillValue <= 115 or SkillValue >= 450) {
return 100;
}
auto elt = std::upper_bound(std::begin(ranges), std::end(ranges), SkillValue);
return 100 / (elt == std::end(ranges) ? 1 : elt - std::begin(ranges));
}
This computes the same output as your original function, but I can't actually say I like it any better.
Using a map, it's a little easier to read:
#include <map>
const std::map<int,int> ranges {
{115, 1},
{135, 2},
{160, 3},
{190, 4},
{215, 5},
{295, 6},
{315, 9},
{355, 10},
{425, 11},
{450, 12}
};
int compute(int SkillValue) {
if (SkillValue <= 115 or SkillValue >= 450) return 100;
auto elt = ranges.upper_bound(SkillValue-1);
return 100 / (elt == std::end(ranges) ? 1 : elt->second);
}
Related
// Do i have to place parathesis somewhere in the last else if to prioritize?
const dolphinScore = 88 + 91 + 110 / 3;
console.log(dolphinScore);
const koalaScore = 88 + 91 + 110 / 3;
console.log(koalaScore);
//88 + 91 + 110 / 3
const minScore = 100;
if (dolphinScore > koalaScore && minScore) {
console.log(`The dolhins win with a score ${dolphinScore}`)
} else if (koalaScore > dolphinScore && minScore) {
console.log(`The koalas win with a score of ${koalaScore}`)
} else if (dolphinScore == koalaScore && >= minScore) {
console.log("This is a draw , no one wins");
}
Expected the else if to execute but it isnt working go easy on me im re leaning the basics!
Else if statement not executing , its supposed to console.log the last statement in this case but its acting as if the (dolphinScore == koalaScore && >= minScore) isnt true?
I think that the problem lies in this line:
if (dolphinScore > koalaScore && minScore)
It should be:
if (dolphinScore > koalaScore && dolphinsScore > minScore)
The && operation must be between 2 boolean statement.
Changed the constant variables dophinsScore & koalaScore to just regular declarations and it executed heres my new code that works , could someone let me know if this is the proper way to solve this issue , thank you and happy programming!
let dolphinScore = 88 + 91 + 110 / 3;
console.log(dolphinScore);
let koalaScore = 88 + 91 + 110 / 3;
console.log(koalaScore);
//88 + 91 + 110 / 3
const minScore = 100;
if (dolphinScore > koalaScore && minScore) {
console.log(`The dolhins win with a score ${dolphinScore}`)
} else if (koalaScore > dolphinScore && minScore) {
console.log(`The koalas win with a score of ${koalaScore}`)
} else if (dolphinScore == koalaScore || dolphinScore, koalaScore = minScore) {
console.log("This is a draw , no one wins");
}
Today I discovered that GCC does some amazing magic for optimizing switches in this code:
StairsType GetStairsType(uint8_t tileId, uint8_t dlvl)
{
if (dlvl == 0)
return StairsType::Part;
if (tileId == 48) {
return dlvl >= 21 ? /* Crypt */ StairsType::Down : /* Caves */ StairsType::Part;
}
switch (tileId) {
case 57: return StairsType::Down;
// many more tile IDs go here
}
}
Have a look at this 🪄:
https://godbolt.org/z/snY3jv8Wz
(gcc is on the left, clang is on the right)
Somehow GCC manages to compile this to 1/4 of the code compared to Clang.
What does this asm do, conceptually?
How does GCC do this?
Both compilers compile this part in the obvious way:
if (dlvl == 0)
return StairsType::Part;
When it comes to this part:
if (tileId == 48) {
// This ID is used by both Caves and Crypt.
return dlvl >= 21 ? /* Crypt */ StairsType::Down : /* Caves */ StairsType::Part;
}
gcc checks tileId==48 directly, while clang decides to merge it into the switch statement.
Both compilers decide to calculate dlvl >= 21 ? 1 : 4 branchlessly in more-or-less the same way, but with different exact instructions. GCC sneakily uses the carry flag to calculate ((dlvl >= 21 ? 0 : -1) & 3) + 1 while clang straightforwardly calculates ((dlvl < 21) * 3) + 1.
// GCC
cmp sil, 21
sbb eax, eax
and eax, 3
add eax, 1
// clang
xor eax, eax
cmp sil, 21
setb al
lea eax, [rax + 2*rax]
inc eax
When it comes to the switch statement, clang implements it with a jump table. The lowest entry is 16 and the highest is 160, so it subtracts 16 and then checks whether the number is greater than 144. There's a well-known trick to save one check, where numbers smaller than 16 wrap around to very big numbers, so they're greater than 144. For whatever reason, it chooses to preload the number 1 (StairsType::Down) into the return value register before doing the jump.
Meanwhile on gcc, it first checks if the tile ID is between 16 and 78. If so, it subtracts 16 and checks some bitmasks:
if(tileID < 16) {
return 0;
} else if(tileID <= 78) {
mask = (1 << (tileID - 16));
if(2307251517974380578 & mask) return 2;
if(4611688220747366401 & mask) return 1;
return ((421920768 & mask) != 0) << 2;
} else {
tileID += 125; // The same as tileID -= 131
// because it's being treated as a byte
if(tileID > 29) { // values lower than 131 wrapped around
return 0;
}
// notice that if we get here it means the tileID is >= 131 and <= 160
// << only looks at the bottom 5 bits of tileID (i.e. tileID & 31)
return -((541065279 & (1 << tileID)) != 0) & 3;
}
Let's try that again, but with the bitmasks in binary and let's figure out what those return statements return:
if(tileID < 16) {
return StairsType::Invalid;
} else if(tileID <= 78) {
mask = (1 << (tileID - 16));
// tile 17, 21, 35, 36, 38, 51, 56, 64, 66, 77
if(0010000000000101000000010000100000000000010110000000000000100010 & mask) return StairsType::Up;
// tile 16, 39, 40, 46, 47, 57, 78
if(0100000000000000000000100000000011000100100000000000000000000001 & mask) return StairsType::Down;
// tile 33, 34, 37, 40, 43, 44
return ((00011001001001100000000000000000 & mask) != 0) ? StairsType::Part : StairsType::Invalid;
} else {
if(tileID < 131 || tileID > 160) {
return 0;
}
// tile 131, 132, 133, 134, 135, 136, 153, 160
return (00100000010000000000000000111111 & (1 << (tileID - 131))) ? StairsType::ShortcutToTown : StairsType::Invalid;
}
Seems that the compiler noticed that you grouped your tile IDs into somewhat logical groups. E.g. above 130 there are only shortcuts to town.
I have no idea how compiler writers come up with this stuff.
I'm wondering if there is any easier way to prevent accessing array beyond range than using if() statement.
I have switch case code for arduino like this with many cases:
switch(a){
case 3:
case 27:
for (int i = 0; i < 8; i++){
leds[ledMapArray[x][i]] = CRGB(0,255,0);
leds[ledMapArray[i][y]] = CRGB(0,255,0);
if ((x + i < 8) && (y + i < 8)) leds[ledMapArray[x + i][y + i]] = CRGB(0,255,0);
if ((x - i >= 0) && (y - i >= 0)) leds[ledMapArray[x - i][y - i]] = CRGB(0,255,0);
if ((x + i < 8) && (y - i >= 0)) leds[ledMapArray[x + i][y - i]] = CRGB(0,255,0);
if ((x - i >= 0) && (y + i < 8)) leds[ledMapArray[x - i][y + i]] = CRGB(0,255,0);
}
break;
case 4:
case 28:
if (x + 1 < 8) leds[ledMapArray[x + 1][y]] = CRGB(0,255,0);
if (x - 1 >= 0) leds[ledMapArray[x - 1][y]] = CRGB(0,255,0);
if (y - 1 >= 0) leds[ledMapArray[x][y - 1]] = CRGB(0,255,0);
if (y + 1 < 8) leds[ledMapArray[x][y + 1]] = CRGB(0,255,0);
if ((x + 1 < 8) && (y + 1 < 8)) leds[ledMapArray[x + 1][y + 1]] = CRGB(0,255,0);
if ((x - 1 >= 0) && (y - 1 >= 0)) leds[ledMapArray[x - 1][y - 1]] = CRGB(0,255,0);
if ((x + 1 < 8) && (y - 1 >= 0)) leds[ledMapArray[x + 1][y - 1]] = CRGB(0,255,0);
if ((x - 1 >= 0) && (y + 1 < 8)) leds[ledMapArray[x - 1][y + 1]] = CRGB(0,255,0);
break;
...
ledMapArray is 8x8 array where x and y value may be <7,0>. Here are some definitions:
// CRGB is structure from FastLed library
CRGB leds[NUM_LEDS]; // from FastLed library to control LED strip
// Array to show how LED strip looks like, values in array represents leds
const short ledMapArray[8][8] = {{0, 1, 2, 3, 4, 5, 6, 7},
{15, 14, 13, 12, 11, 10, 9, 8},
{16, 17, 18, 19, 20, 21, 22, 23},
{31, 30, 29, 28, 27, 26, 25, 24},
{32, 33, 34, 35, 36, 37, 38, 39},
{47, 46, 45, 44, 43, 42, 41, 40},
{48, 49, 50, 51, 52, 53, 54, 55},
{63, 62, 61, 60, 59, 58, 57, 56}};
The point of this switch case is to light up specific LEDs from LED strip. I want to show allowed moves for chess pieces on smart chessboard.
Is there any better way to do this?
The Answer was written when the question used the tag c, not c++ and edited later. The FastLED library is clearly implemented in C++.
You could wrap the array access in a function that implements the checks.
The following function assumes that the array leds and ledMapArray are file scope variables. Otherwise the function would need more arguments. In C++, the function will also work if the function and the variables are members of the same class.
Instead of a hard-coded number 8, the check should better be implemented based on the number of elements in the array. (Something like sizeof(array)/sizeof(array[0]). I would need to see the definition of leds and ledMapArray.)
Note that the function implements a bounds check for ledMapArray only, not for leds.
void setLed(int x, int y, some_type crgb)
{
if((x >= 0) && (x < 8) && (y >= 0) && (y < 8))
{
leds[ledMapArray[x][y]] = crgb;
}
}
The function could also be replaced with a macro which would work for local array variables as well as for global variables.
#define setLed(x, y, crgb) do { \
if((x >= 0) && (x < 8) && (y >= 0) && (y < 8)) { \
leds[ledMapArray[x][y]] = crgb; \
} \
} while(0)
switch(x){
case 3:
case 27:
for (int i = 0; i < 8; i++){
setLed(x, i, CRGB(0,255,0));
setLed(i, y, CRGB(0,255,0));
setLed(x + i, y + i, CRGB(0,255,0));
setLed(x - i, y - i, CRGB(0,255,0));
setLed(x + i, y - i, CRGB(0,255,0));
setLed(x - i, y + i, CRGB(0,255,0));
}
break;
case 4:
case 28:
setLed(x + 1, y, CRGB(0,255,0));
/* etc ... */
Instead of repeatedly using anonymous objects with the same constructor CRGB(0,255,0), you could use a named object.
CRGB greenColor(0,255,0);
setLed(x, i, greenColor);
setLed(i, y, greenColor);
/* etc ... */
Or use pre-defined color objects from the library.
setLed(x, i, CRGB::Green);
setLed(i, y, CRGB::Green);
/* etc ... */
Problem setting
The problem consists of sampling out of a year of 365 days n days, in such a way that
the days are drawn by uniform probability distribution
the days comply to have a minimum distance given by min_dist
the result is given as numeric vector
Example
With n= 12 and min_dist= 20 a proper result might be the vector
[1] 4 43 69 97 129 161 192 215 243 285 309 343 as diff of this vector is [1] 39 26 28 32 32 31 23 28 42 24 34, all values larger or equal to min_dist= 20.
Question
I have solved this problem with
function sample_r() in native R
function sample_cpp() in c++ using the fantastic Rcpp interface package
The c++ solution turns out to much slower (on my Mac factor 60x). I am a Rccp newbie, hence my own research capabilities are limited - please forgive.
What can I do to refactor the c++ code to be faster than native R code ?
Reproducible code (.cpp file)
#include <Rcpp.h>
using namespace Rcpp;
using namespace std;
// [[Rcpp::export]]
IntegerVector sample_cpp(int n, int min_dist= 5L, int seed= 42L) {
IntegerVector res_empty= Rcpp::rep(NA_INTEGER, n);
IntegerVector res;
IntegerVector available_days_full= Rcpp::seq(1, 365);
IntegerVector available_days;
IntegerVector forbidden_days;
IntegerVector forbidden_space = Rcpp::seq(-(min_dist-1), (min_dist-1));
bool fail;
Environment base("package:base");
Function set_seed = base["set.seed"];
set_seed(seed);
do {
res= res_empty;
available_days = available_days_full;
fail= FALSE;
for(int i= 0; i < n; ++i) {
res[i]= sample(available_days, 1, FALSE)[0];
forbidden_days= res[i]+forbidden_space;
available_days= setdiff(available_days, forbidden_days);
if(available_days.size() <= 1){
fail= TRUE;
break;
}
}
}
while(fail== TRUE);
std::sort(res.begin(), res.end());
return res;
}
/*** R
# c++ function
(r= sample_cpp(n= 12, min_dist= 20, seed=1))
diff(r)
# R function
sample_r= function(n= 12, min_dist=5, seed= 42){
if(n*min_dist>= 365) stop("Infeasible.")
set.seed(seed)
repeat{
res= numeric(n)
fail= FALSE
available_days= seq(365)
for(i in seq(n)){
if(length(available_days) <= 1){
fail= TRUE
break()
}
res[i]= sample(available_days, 1)
forbidden_days= res[i]+(-(min_dist-1):(min_dist-1))
available_days= setdiff(available_days, forbidden_days)
}
if(fail== FALSE) return(sort(res))
}
}
(r= sample_r(n= 12, min_dist= 20, seed= 40))
diff(r)
# Benchmark
library(rbenchmark)
benchmark(cpp= sample_cpp(n= 12, min_dist = 28),
r= sample_r(n= 12, min_dist = 28),
replications = 50)[,1:4]
*/
Benchmark:
test replications elapsed relative
1 cpp 50 28.005 63.217
2 r 50 0.443 1.000
Edit:
OK, I tried to optimize (as far as I am capable of c++), still the c++ implementation is behind, but now only marginally.
#include <Rcpp.h>
using namespace Rcpp;
using namespace std;
// [[Rcpp::export]]
IntegerVector sample_cpp(int n, int min_dist= 5L, int seed= 42L) {
IntegerVector res;
IntegerVector available_days;
IntegerVector forbidden_days;
IntegerVector forbidden_space = Rcpp::seq(-(min_dist-1), (min_dist-1));
bool fail;
Environment base("package:base");
Function set_seed = base["set.seed"];
set_seed(seed);
do {
res= Rcpp::rep(NA_INTEGER, n);
available_days = Rcpp::seq(1, 365);
fail= FALSE;
for(int i= 0; i < n; ++i) {
if(available_days.size() < n-i){
fail= TRUE;
break;
}
int temp= sample(available_days, 1, FALSE)[0];
res[i]= temp;
forbidden_days= unique(pmax(0, temp + forbidden_space));
available_days= setdiff(available_days, forbidden_days);
}
}
while(fail== TRUE);
std::sort(res.begin(), res.end());
return res;
}
/*** R
# R function
sample_r= function(n= 12, min_dist=5, seed= 42){
if(n*min_dist>= 365) stop("Infeasible.")
set.seed(seed)
repeat{
res= numeric(n)
fail= FALSE
available_days= seq(365)
for(i in seq(n)){
if(length(available_days) <= n-i){
fail= TRUE
break()
}
res[i]= sample(available_days, 1)
forbidden_days= res[i]+(-(min_dist-1):(min_dist-1))
available_days= setdiff(available_days, forbidden_days)
}
if(fail== FALSE) return(sort(res))
}
}
# Benchmark
library(rbenchmark)
benchmark(cpp= sample_cpp(n= 12, min_dist = 28),
r= sample_r(n= 12, min_dist = 28),
replications = 50)[,1:4]
*/
Benchmark:
test replications elapsed relative
1 cpp 50 0.643 1.475
2 r 50 0.436 1.000
You can optimize your R version by sampling the largest possible number of days in one shot.
The following code is faster than yours. Statistically I sample the majority of the days before the loop. The remaining days are sampled in the loop but the loop is likely to run only once. Maybe twice.
Moreover it is easy to rewrite with Rcpp.
sample_r2= function(n = 12, min_dist = 5, seed = 42)
{
available_days = seq(365)
res = sort(sample(available_days, n))
y = diff(res)
res = res[y >= min_dist]
while (length(res) < n)
{
forbidden_days = sapply(res, function(x){ x + -(min_dist-1):(min_dist-1) } )
available_days = setdiff(available_days, forbidden_days)
days = sample(available_days, n - length(res))
res = sort(c(res, days))
y = diff(res)
res = res[y >= min_dist]
}
return(res)
}
Btw maybe there are some issues in my code. But I think the idea is correct.
I've been trying for the past hour to get this binary search algorithm to work and by using an example of the algorithm as explained on khan academy, I still can't get it to work, it should output a number but nothing happens. The example on khan academy is like this:
Let min = 0 and max = n-1.
If max < min, then stop: target is not present in array. Return -1.
Compute guess as the average of max and min, rounded down (so that it is an integer).
If array[guess] equals target, then stop. You found it! Return guess.
If the guess was too low, that is, array[guess] < target, then set min = guess + 1.
Otherwise, the guess was too high. Set max = guess - 1.
Go back to step 2.
And the code I wrote according to the steps is:
#include <iostream>
int main() {
int arr[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 };
int min = 0;
int max = 24;
int guess;
int targetValue = 73;
while (max > min) {
guess = ((max + min) / 2);
if (arr[guess] == targetValue) {
std::cout << guess;
break;
}
else if (arr[guess] < targetValue) {
min = guess + 1;
}
else {
max = guess - 1;
}
}
return 0;
}
The binary search algorithm states
If L > R, the search terminates as unsuccessful.
In your implementation however, you terminate the search on the condition L >= R. In the case of L == R, the algorithm should do one more iteration, because it did not consider this position in the list yet.
In your case of target value 73, when the algorithm reaches the position of the target, 20, L == R. Your implementation terminates one step too early to recognize the target.
try this:
(max > min) to (max >= min)