I am doing some Codility coding practices and in the results, I got this "Detected time complexity: O(Y-X)". I do not understand what it actually means. It is because of my lousy and constant looping? How to enhance or improve the performance to get rid of this poor performance?
public static int Solution(int startPoint, int endPoint, int frogJumpPowerDistance)
{
int jumpCount = 0;
// AIM: return the number of jumps to reach endPoint
// CHECK: no jump needed.
if (startPoint == endPoint) return jumpCount;
int distanceReach = startPoint + frogJumpPowerDistance;
jumpCount++;
if (distanceReach < endPoint)
{
//enter checking loop
do
{
distanceReach += frogJumpPowerDistance;
jumpCount++;
}
while (distanceReach < endPoint);
}
return jumpCount;
}
I expect not to get a Timeout error. But I got it.
I am not sure how to improve my codes to solve the Timeout error.
For the input (5, 1000000000, 2) the solution exceeded the time limit.
For your information,
All inputs are within range [1 ... 1,000,000,000].
startPoint less than or equal to endPoint.
I think what "Detected time complexity: O(Y-X)" means is that it is saying your code takes longer to run when the start and end is farther apart. Specifically, the time it takes to run your code increases linearly with respect to the difference between start and end. Note that I am assuming Y is the end and X is the start.
You don't actually need a loop here. You can just do some maths to figure out how many jumps you need, and do this in constant time O(1).
First, you calculate the distance you have to jump by calculating the difference between start and end:
int distance = endPoint - startPoint;
Then, divide the distance by the jump power:
int jumps = distance / frogJumpPowerDistance;
If distance is divisible by frogJumpPowerDistance, then we can just return jumps. Otherwise, we still have some distance left, after we have jumped jumps times (this is integer division!). So we need one more jump to finish it off.
if (distance % frogJumpPowerDistance == 0) {
return jumps;
} else {
return jumps + 1;
}
EDIT:
As iakobski suggested in the comments, this can also be done with just one division, like so:
return (distance - 1) / frogJumpPowerDistance + 1;
Related
I'm receiving some inconsistent behavior from the Random.NextDouble().
Regularly, the console would freeze and the cpu usage would increase dramatically until I closed it down. I ran the debugger and found that the cause of the freezing was Random.NextDouble(). I added some lines for debugging purposes, but the code is as follows:
double generateCatenationRate()
{
double catenation = 999.999; //random value to see if it pops up down there
double uniformValue;
double int_covalence = covalence.original;
double dist = int_covalence - 4;
int counter = 0;
while (true)
{
counter++;
uniformValue = utils.getRandomDouble(); //goes bad here!
if (uniformValue <= 0.15)
{
catenation = Math.Abs(utils.normalize(dist, 0, 4)) + uniformValue;
if (catenation < 0 || catenation > 1)
{
if (counter > 10000)
{
Console.WriteLine("Went nuclear!");
break; //break so console doesn't stall out
}
continue;
}
else
{
break;
}
}
}
Console.WriteLine("Took "+counter+" iterations.");
return 1 - catenation;
}
And:
public static double getRandomDouble()
{
Init();
return random.NextDouble();
}
Lastly:
private static void Init()
{
if (random == null) random = new Random();
}
It typically does not stall out, but running it several times successively produces output such as:
Took 4 iterations.
Took 3 iterations
Took 3 iterations.
Took 23 iterations.
Took 12 iterations.
Took 4 iterations.
Went nuclear!
Took 10007 iterations.
Can anyone explain why Random.NextDouble() occasionally seems to create an infinite loop? Looking around, I suspect it has something to do with how the values are seeded, but any insight would be appreciated; would love to fix this issue.
Thank you!
EDIT: covalence.original is always an integer between 1 and 8 (inclusive). normalize() performs min-max normalization, producing a number from 0-1 based on an input and a range. Neither of these seem to contribute to the problem, however.
If I understand correctly then the value of dist and utils.normalize(dist, 0, 4) never changes.
So if int_covalence = 8 then dist = 4 and utils.normalize(dist, 0, 4) = 1, correct?
Since the chance of generating 0.0 is pretty small, that will make catenation virtually always greater than 1 and the check if (catenation < 0 || catenation > 1) always true.
why not just generate the samples directly rather than using rejection sampling?
public static double generateCatenationRate(Random rng, double coval_norm) {
double low = Math.abs(coval_norm) + 0.15;
double delta = 1. - low;
if (delta < 0) {
throw new IllegalArgumentException("impossible given covalence");
}
return low + delta * rng.nextDouble();
}
where coval_norm is whatever you get back from utils.normalize. if we write it this way we get visibility of the "impossible" condition and can do something about it, rather than just looping.
I'm not stuck with my code because I don't know how to write it but more because I'm split between two ways of doing and don't know which path to go.
I will first write my problem then post some code.
So I have a switch with 4 cases and then in my function called by the cases, I have two more cases (ifs). I have to rewrite the code used in all those cases but a little different in every one.
If I write all the code, I have less things to check therefor it's more performant but it makes the code less flexible.
private void FillDiagonalStartAndEnd(FastNoise noiseBiome, FastNoise noiseTransition)
{
switch (direction)
{
case DirectionOfBiomeCell.NE:
TransitionDiagonallyNorthEast(noiseBiome, noiseTransition); // Less checks
break;
case DirectionOfBiomeCell.SE:
towardsRight = 1;
TransitionDiagonallyED(noiseBiome, noiseTransition, new Vector2(spacePosX -1, spacePosZ + BIOME_CELL_SIZE), new Vector2(spacePosX + BIOME_CELL_SIZE - 1, spacePosZ)); // More flexible
break;
case DirectionOfBiomeCell.SW:
//TransitionLinearlySouth(noiseBiome, noiseTransition);
break;
case DirectionOfBiomeCell.NW:
//TransitionLinearlyWest(noiseBiome, noiseTransition);
break;
default:
Debug.Log("Default case in FillDiagonallyStartAndEnd");
break;
}
}
Above, I have the NE direction which is more performant I think and then SE direction which is flexible with parameters.
Then in the functions :
private void TransitionDiagonallyED(FastNoise noiseBiome, FastNoise noiseTransition, Vector2 start, Vector2 end)
{
//Variables for flexible function
int heightModificator;
//Start and ending values for diagonal line
int startValue = TerrainGen.GetNoise2D(noiseBiome, (int)start.x, (int)start.y, TerrainGen.min, TerrainGen.max);
int endValue = TerrainGen.GetNoise2D(noiseTransition, (int)end.x, (int)end.y, TerrainGen.min, TerrainGen.max);
//Step values
float endMinusStart = endValue - startValue;
float stepValue = endMinusStart / BIOME_CELL_SIZE;
//1 or 0 for height start of diagonal
heightModificator = (int)start.y > spacePosX ? 1 : 0;
for (int xz = 0; xz < BIOME_CELL_SIZE; xz++)
{
//Making diagonal and adjusting if it starts at the bottom or top of the square
transitionHeights[xz, Mathf.Abs((BIOME_CELL_SIZE - 1) * heightModificator - xz)] = Mathf.RoundToInt((startValue + stepValue * (xz + 1)) * towardsRight + (endValue - xz * stepValue) * towardsLeft);
}
}
I won't post all of the other function since it's very long but above you can see in the loop that I am using multiplication by 0 or 1. This is set in the heightModificator which doesn't exist in the other function with no parameters. This is very handy since it's flexible and set in the beginning of the class.
public int towardsRight = 0;
public int towardsLeft = 0;
Now the other function doesn't need this since it's only usable in the case the direction is NE
private void TransitionDiagonallyNorthEast(FastNoise noiseBiome, FastNoise noiseTransition)
{
float endMinusStart;
float stepValue;
if (position == PositionOfBiomeCell.outward)
{
//For diagonal
endMinusStart = valuesFromTransitionNoiseEND[BIOME_CELL_SIZE - 1] - valuesFromBiomeNoiseSTART[0];
stepValue = endMinusStart / BIOME_CELL_SIZE;
for (int xz = 0; xz < BIOME_CELL_SIZE; xz++)
{
//Filling diagonal
transitionHeights[xz, xz] = Mathf.RoundToInt(valuesFromBiomeNoiseSTART[0] + (xz + 1) * stepValue);
So, above you can see there is no multiplication by heightModificator. The functions aren't finished, so there would be two more loops of this kind in both of the function. I didn't want to write them since I don't know for which one to go. What is considered good practice, being more flexible but less performant or the opposite?
In my case this code will be executed A LOT of times. I would say in my Start() function about 2080 (because a lot of objects use this code). Furthermore, my two loops which loop around 496 times each will be nested in the first one.
Thanks for reading
Best regards.
What is considered good practice, being more flexible but less performant or the opposite?
Until you are sure there is a performance issue, don't look for a way to optimize it. There is a good chance that you're spending time on the wrong thing. For your example, a decent device won't even break a sweat with simple code like this being called 2000 times, even if you do the these calls every second.
On the other hand, having clean code will enable you to work faster and produce less bugs. And it's easier to optimize clean code to make it faster than to clean up unnecessary optimizations.
for(int i = 0; i < gos.Length; i++)
{
float randomspeed = (float)Math.Round (UnityEngine.Random.Range (1.0f, 15.0f));
floats.Add (randomspeed);
_animator [i].SetFloat ("Speed", randomspeed);
}
Now what i get is only round numbers between 1 and 15. I mean i'm not getting numbers like 1.0 or 5.4 or 9.8 or 14.5 is it logical to have speed values like this ? If so how can i make that the random numbers will include also floats ?
Second how can i make sure that there will be no the same numbers ?
gos Length is 15
As noted in the other answer, you aren't getting fractional values, because you call Math.Round(), which has the express purpose of rounding to the nearest whole number (when called the way you do).
As for preventing duplicates, I question the need to ensure against duplicates. First, the number of possible values within the range you're selecting is large enough that the chances of getting duplicates is very small. Second, it appears you are selecting random speeds for some game object, and it seems to me that in that scenario, it's entirely plausible that once in a while you would find a pair of game objects with the same speed.
That said, if you still want to do that, I would advise against the linear searches recommended by the other answers. Game logic should be reasonably efficient, and in this scenario that would mean using a hash set. For example:
HashSet<float> values = new HashSet<float>();
while (values.Count < gos.Length)
{
float randomSpeed = UnityEngine.Random.Range(1.0f, 15.0f);
// The Add() method returns "true" if the value _wasn't_ already in the set
if (values.Add(randomSpeed))
{
_animator[values.Count - 1].SetFloat("Speed, randomSpeed);
}
}
// it's not clear from your question whether you really need the list of
// floats at the end, but if you do, this is a way to convert the hash set
// to a list
floats = values.ToList();
The reason you're not getting any decimals is because you're using Math.Round, this will either raise the float to the next whole number or lower it.
As for if it's logical, it depends.As for your case animation speed is usually done by floats because it can smoothly speed up and down.
Also to answer your question on how to avoid duplicates of the same float.. which in itself is already very unlikely, try doing this instead :
for(int i = 0; i < gos.Length; i++)
{
float randomspeed = 0f;
// Keep repeating this until we find an unique randomspeed.
while(randomspeed == 0f || floats.Contains(randomspeed))
{
// Use this is you want round numbers
//randomspeed = Mathf.Round(Random.Range(1.0f, 15.0f));
randomspeed = Random.Range(1.0f, 15.0f);
}
floats.Add (randomspeed);
_animator [i].SetFloat ("Speed", randomspeed);
}
Your first problem: if you use Math.Round(), you'll never get numbers like 5.4...
Second question: you can check for existance of the number before you add the number:
private float GenerateRandomSpeed()
{ return (float)UnityEngine.Random.Range (1.0f, 15.0f);}
for(int i = 0; i < gos.Length; i++)
{
float randomspeed= GenerateRandomSpeed();
while (floats.any(x=>x==randomspeed))
randomspeed=GenerateRandomSpeed();
floats.Add (randomspeed);
_animator [i].SetFloat ("Speed", randomspeed);
}
I didn't test it but i hope it can direct you to the answer.
I am programming a video game, and in it, I would like to call a method that adds a player bonus life for every 2,000 points scored. I have no idea what operator(s) to use for such a thing.
If (score is divisible by 2000, each increment){
DoSomething();
}
I'm not even sure if I'm asking this question correctly. Basically when the player scores 2,000pts, 4,000pts, 6,000pts, etc, I want to give him/her a bonus life by calling a method. I already have the method created; I was just wondering how I can apply the conditions that call it.
I tried using this:
public int bonusTarget = 2000;
paddle = GameObject.Find("Paddle").GetComponent<Paddle>();
if(score >= bonusTarget){
paddle.Bonus();
bonusTarget += 2000;
}
but, it awarded more than one bonus life each increment. I need to award the bonus life only one time for each 2,000pts
"score is divisible by 2000"
if (score % 2000 == 0) DoSomething()
If You need to track score use property instead of varilable eg.:
private int _score;
public int Score
{
get
{
return _score;
}
set
{
var a = Math.Floor(_score / 2000)
var b = Math.Floor(value / 2000)
if (a < b) DoSomething();
_score = value;
}
}
Put the if(score is divisible by 2000) check inside the DoSomething() method.
There's no reason to keep this outside the method that increases the bonus lives, in the dozens of possible code paths that increase the score. You need ONE CENTRAL location that the score increases, and checks various conditions.
You need a centralized location to add to the score, like this:
public void AddToScore(int points)
{
score += points;
if (score >= bonusTarget)
{
bonusTarget += 2000;
paddle.Bonus();
}
}
I switched the two lines inside the conditional block in case paddle.Bonus() tries adding points itself, which could cause issues, possibly even infinite recursion.
If you make it so you always add to the score using this method, you can add any special handling you want. You might also want to write a ResetScore() and a GetScore() so you never access score directly elsewhere in your code.
I'm trying to make it so that after every say 200 of a value, it will increase a global variable by 1. As you can see below the problem I have is that one, it isn't in any way shape or form efficient and really doesn't work well.
An overall of how I'd like this to work would be so that when GV.TotalNumberValue hits past a certain number, let's say 200, GV.TotalLevel will increase by one and update the text and this will happen every time that GV.TotalNumberValue increases by 200.
Finally, if this is going to be checking what the number is on constantly, should I have this bound to an event such as a button click or a timer? Your help's greatly appreciated, thanks.
void LevelMod()
{
if (GV.TotalNumberValue >= 200)
{
GV.TotalLevel = GV.TotalLevel + 1;
lblLevel.Text = string.Format("{0}{1}", GV.LevelPrefix, GV.TotalLevel);
}
else if (GV.TotalNumberValue >= 400)
{
GV.TotalLevel = GV.TotalLevel + 1;
lblLevel.Text = string.Format("{0}{1}", GV.LevelPrefix, GV.TotalLevel);
}
else
{
return;
}
}
Well, you can use simple math:
Either deduce the level from the value, like this:
int totalLevel = value / 200;
This works because an integer division is always rounded down.
Or, if you know that value has just been incremented, you can detect a level boundary like this:
bool shouldLevelUp = (value % 200) == 0;
if (shouldLevelUp)
++totalLevel;
Perhaps use integer division similar to:
void LevelMod()
{
// I assume the level variables are integrals, so perform an integer division
if (GV.TotalNumberValue / 200 > GV.TotalLevel)
{
GV.TotalLevel = GV.TotalNumberValue / 200;
lblLevel.Text = string.Format("{0}{1}", GV.LevelPrefix, GV.TotalLevel);
}
}
Essentially, your TotalLevel is always TotalNumberValue / 200. This assumes that that GV.TotalNumberValue is an integral type which always rounds the result towards zero.