Convert arbitrary length to a value between -1.0 a 1.0? - math

How can I convert a length into a value in the range -1.0 to 1.0?
Example: my stage is 440px in length and accepts mouse events. I would like to click in the middle of the stage, and rather than an output of X = 220, I'd like it to be X = 0. Similarly, I'd like the real X = 0 to become X = -1.0 and the real X = 440 to become X = 1.0.
I don't have access to the stage, so i can't simply center-register it, which would make this process a lot easier. Also, it's not possible to dynamically change the actual size of my stage, so I'm looking for a formula that will translate the mouse's real X coordinate of the stage to evenly fit within a range from -1 to 1.

-1 + (2/440)*x
where x is the distance
So, to generalize it, if the minimum normalized value is a and the maximum normalized value is b (in your example a = -1.0, b = 1.0 and the maximum possible value is k (in your example k = 440):
a + x*(b-a)/k
where x is >= 0 and <= k

This is essentially two steps:
Center the range on 0, so for example a range from 400 to 800 moves so it's from -200 to 200. Do this by subtracting the center (average) of the min and max of the range
Divide by the absolute value of the range extremes to convert from a -n to n range to a -1 to 1 range. In the -200 to 200 example, you'd divide by 200

Doesn't answer your question, but for future googlers looking for a continuous monotone function that maps all real numbers to (-1, 1), any sigmoid curve will do, such as atan or a logistic curve:
f(x) = atan(x) / (pi/2)
f(x) = 2/(1+e-x) - 1

(x - 220) / 220 = new X
Is that what you're looking for?

You need to shift the origin and normalize the range. So the expression becomes
(XCoordinate - 220) / 220.0

handling arbitrary stage widths (no idea if you've got threads to consider, which might require mutexes or similar depending on your language?)
stageWidth = GetStageWidth(); // which may return 440 in your case
clickedX = MouseInput(); // should be 0 to 440
x = -1.0 + 2.0 * (clickedX / stageWidth); // scale to -1.0 to +1.0
you may also want to limit x to the range [-1,1] here?
if ( x < -1 ) x = -1.0;
if ( x > 1 ) x = 1.0;
or provide some kind of feedback/warning/error if its out of bounds (only if it really matters and simply clipping it to the range [-1,1] isn't good enough).

You have an interval [a,b] that you'd like to map to a new interval [c,d], and a value x in the original coordinates that you'd like to map to y in the new coordinates. Then:
y = c + (x-a)*(c-d)/(b-a)
And for your example with [a,b] = [0,440] and [c,d] = [-1,1], with x=220:
y = -1 + (220-0)*(1 - -1)/(440-0)
= 0
and so forth.
By the way, this works even if x is outside of [a,b]. So as long as you know any two values in both systems, you can convert any value in either direction.

Related

How to keep outputs of an operation between 2 set values? (limits,modulus)

I would like to write an operation that takes a number, that could take any value larger than -1, and only outputs a number between -1 and 0.5
Currently, I am able to ensure that the above happens and it always outputs a number between 0 and 1 by doing the following:
SupressedNumber = (Number)%1
And the following for values between -1 and 0:
SupressedNumber = (Number)%-1
And the following for values between -0.5 and 0:
SupressedNumber = (Number)%-0.5
However I would like to make it between -1 and <-0.5 (-0.49ish max). It doesn't have to use modulus but I feel like it's part of the solution. It just has to be doable in lua.
As far as I understand, you want to clamp a number between two values, but one of them is a supremum, not a maximum. Lets solve the first problem first:
To clamp a number between two values inclusivly (e.g. -1 <= number <= -0.5), you can use the standard lua functions math.min() and math.max() or even code it yourself, if you need pure lua:
local function clamp(min, value, max)
return math.max(min, math.min(max, value))
end
clamp() will return value, but if value is smaller than min, it returns min. If it is greater than max, it returns max, so the result never leaves [-1, -0.5].
Since your goal is: [-1, -0.5), you have to make a compromise. Computers store decimals with a finite amount of precision, so you can't get a number that is infinitly close to -0.5, but maybe a number, that is close enaugh. Let's create a variable epsilon which says, how close is close enough:
local epsilon = 0.00001
And now we can put both ideas together:
supressed_number = clamp(-1, number, -0.5 + epsilon)
This will clamp your number between -1 and a little bit less than -0.5:
-1 <= value <= −0,49999 < -0.5

Normalize number between 0 and 1 in user profile

I'm working on user-profile where each term exists in the user-profile have wight and the weight formulated from set of factors such as (duration, total number of visit ...etc) , I need to normalize the result of their summation to be number between 0 and 1, I performed this equation:
(x+y+z+......)/100
Where x, y and z are factors. I have suggested this equation to my self (I'm sorry I'm not very good in math :( ), but unfortunately it returns some value more than 1 , so is there any way that can be applied to limit the result of the summation between 0 and 1?
Many thanks in advance.
Ok, generally, to normalize, this is what you do:
Find the absolute minimum value, and subtract this from your number. (This may be 0, in which case you can skip this step.)
Find the absolute maximum value. Your total range after step 1 will be from 0..(maximum - minimum). Divide your number by this value, and everything will be in the range of 0..1.
To spin it back, you do the opposite: take your normalized number, multiply by the range (i.e. max - min), then add back the min.
The reason you're having a problem is because x + y + z + ... has a range that is not 100.
Example
If x has a range of 0-10, y has a range of 15-25 and z has a range of 10-25, and your specific values are x = 8, y = 17, z = 12:
x + y + z = 8 + 17 + 12 = 37
min = 0 + 15 + 10 = 25
max = 10 + 25 + 25 = 60
so your normalized value is calculated by doing:
(37 - 25) / (60 - 25) = (12 / 35) = 0.342857 (approximately).
To go back from normalized to a composite number, do the opposite:
0.342857 * 35 = 11.999995 = 12 once rounded.
12 + 25 = 37
If your variables are unbounded, nobody can reach the normalized value 1, because if someone achieved 1, another person with larger factors would exceed 1.
This said, you can transform every factor with a function that maps [0 +inf[ to [0 1[, like X/(X+a) or 1-2^(-X/a), where a is some scaling constant (chosen by you). You will apply this transform to the individual factors and average them, or just apply it to the global sum.

Screen coordinates to isometric coordinates

I'm struggling at converting mouse/screen coordinates to isometric tile index. I have tried about every formula I could find here or on internet but none of them seems to work or I am missing something.
Here is a picture, origin is in the top left corner and dimensions of one tile are 128x64px.
I would appreciate any help, thanks.
Basically, you need to apply a rotation matrix with a few other bits. Here's some sample code written in AWK which should be easy to port to any other language:
END {
PI = 3.1415;
x = 878.0;
y = 158.0;
# Translate one origin to the other
x1 = x - 128*5;
# Stretch the height so that it's the same as the width in the isometric
# This makes the rotation easier
# Invert the sign because y is upwards in math but downwards in graphics
y1 = y * -2;
# Apply a counter-clockwise rotation of 45 degrees
xr = cos(PI/4)*x1 - sin(PI/4)*y1;
yr = sin(PI/4)*x1 + cos(PI/4)*y1;
# The side of each isometric tile (which is now a square after the stretch)
diag = 64 * sqrt(2);
# Calculate which tile the coordinate belongs to
x2 = int(xr / diag);
# Don't forget to invert the sign again
y2 = int(yr * -1 / diag);
# See the final result
print x2, y2;
}
I tested it with a few different coordinates and the results seem correct.
I tried the solution by acfrancis and I found that the function has its limits when it comes to negative indices. Just in case someone else will tackle this issue:
Reason for issue: negative values like -0.1.... will be cast to 0 instead of -1.
Its the classic "there is only one zero" problem for arrays.
To solve it: before casting the x2, y2 values to int:
check if xr/diag < 0 and, if true, result = result - 1
(respectively for y2: yr * -1 / diag < 0 then result = result -1)
you then cast the result values to int like before.
Hope it helps.
Addition:
The translation of the origin by 128*5 seems to specific to a certain case so i guess this should be removed in order to generalize the function.

Avoiding bias in randomly generated subtraction problems

I'm writing a Python script to generate problems for mental arithmetic drills. The addition and multiplication ones were easy, but I'm running into trouble trying to generate unbiased problems for the subtraction ones.
I want to be able to specify a minimum and maximum value that the minuend (first number) will be -- e.g., for two-digit subtraction it should be between 20 and 99. The subtrahend should also have a range option (11-99, say). The answer needs to be positive and preferably also bounded by a minimum of, say, 10 for this situation.
So:
20 < Minuend < 99
11 < Subtrahend < 99
Answer = Minuend - Subtrahend
Answer >= 10
All the numeric values should be used as variables, of course.
I have these conditions met as follows:
ansMin, ansMax = 10, 99
subtrahendMin, minuendMax = 11,99
# the other max and min did not seem to be necessary here,
# and two ranges was the way I had the program set up
answer = randint(ansMin, ansMax)
subtrahend = randint(subtrahendMin, minuendMax - answer)
minuend = answer + subtrahend # rearranged subtraction equation
The problem here is that the minuend values wind up being nearly all over 50 because the answer and subtrahend were generated first and added together, and only the section of them that were both in the bottom 25% of the range will get the result below 50%. (Edit: that's not strictly true -- for instance, bottom 1% plus bottom 49% would work, and percentages are a bad way of describing it anyway, but I think the idea is clear.)
I also considered trying generating the minuend and subtrahend values both entirely randomly, then throwing out the answer if it didn't match the criteria (namely, that the minuend be greater than the subtrahend by a value at least greater than the answerMin and that they both be within the criteria listed above), but I figured that would result in a similar bias.
I don't care about it being perfectly even, but this is too far off. I'd like the minuend values to be fully random across the allowable range, and the subtrahend values random across the range allowed by the minuends (if I'm thinking about it right, this will be biased in favor of lower ones). I don't think I really care about the distribution of the answers (as long as it's not ridiculously biased). Is there a better way to calculate this?
There are several ways of defining what "not biased" means in this case. I assume that what you are looking for is that every possible subtraction problem from the allowed problem space is chosen with equal probability. Quick and dirty approach:
Pick random x in [x_min, x_max]
Pick random y in [y_min, y_max]
If x - y < answer_min, discard both x and y and start over.
Note the bold part. If you discard only y and keep the x, your problems will have an uniform distribution in x, not in the entire problem space. You need to ensure that for every valid x there is at least one valid y - this is not the case for your original choice of ranges, as we'll see later.
Now the long, proper approach. First we need to find out the actual size of the problem space.
The allowed set of subtrahends is determined by the minuend:
x in [21, 99]
y in [11, x-10]
or using symbolic constants:
x in [x_min, x_max]
y in [y_min, x - answer_min]
We can rewrite that as
x in [21, 99]
y = 11 + a
a in [0, x-21]
or again using symbolic constants
x in [x_min, x_max]
y = y_min + a
a in [0, x - (answer_min + y_min)].
From this, we see that valid problems exist only for x >= (answer_min + y_min), and for a given x there are x - (answer_min + y_min) + 1 possible subtrahents.
Now we assume that x_max does not impose any further constraints, e.g. that answer_min + y_min >= 0:
x in [21, 99], number of problems:
(99 - 21 + 1) * (1 + 78+1) / 2
x in [x_min, x_max], number of problems:
(x_max - x_min + 1) * (1 + x_max - (answer_min + y_min) + 1) / 2
The above is obtained using the formula for the sum of an arithmetic sequence. Therefore, you need to pick a random number in the range [1, 4740]. To transform this number into a subtraction problem, we need to define a mapping between the problem space and the integers. An example mapping is as follows:
1 <=> x = 21, y = 11
2 <=> x = 22, y = 12
3 <=> x = 22, y = 11
4 <=> x = 23, y = 13
5 <=> x = 23, y = 12
6 <=> x = 23, y = 11
and so on. Notice that x jumps by 1 when a triangular number is exceeded. To compute x and y from the random number r, find the lowest triangular number t greater than or equal to r, preferably by searching in a precomputed table; write this number as q*(q+1)/2. Then x = x_min + q-1 and y = y_min + t - r.
Complete program:
import random
x_min, x_max = (21, 99)
y_min = 11
answer_min = 10
triangles = [ (q*(q+1)/2, q) for q in range(1, x_max-x_min+2) ]
upper = (x_max-x_min+1) * (1 + x_max - (answer_min + y_min) + 1) / 2
for i in range(0, 20):
r = 1 + random.randrange(0, upper)
(t, q) = next(a for a in triangles if a[0] >= r)
x = x_min + q - 1
y = y_min + t - r
print "%d - %d = ?" % (x, y)
Note that for a majority of problems (around 75%), x will be above 60. This is correct, because for low values of the minuend there are fewer allowed values of the subtrahend.
I can see a couple of issues with your starting values - if you want the answer to always be greater than 10 - then you need to either increase MinuendMin, or decrease SubtrahendMin because 20-11 is less than 10... Also you have defined the answer min and max as 3,9 - which means the answer will never be more than 10...
Apart from that I managed to get a nice even distribution of values by selecting the minuend value first, then selecting the subtrahend value based on it and the answerMin:
ansMin = 10
minuendMin, minuendMax = 20,99
subtrahendMin = 9;
minuend = randint(minuendMin, minuendMax )
subtrahend = randint(subtrahendMin,(minuend-ansMin) )
answer = minuend - subtrahend
You say you've already got addition working properly. Assuming you have similar restrictions for the addends/sum you could rearrange the factors so that:
minuend <= sum
subtrahend <= first addend
answer <= second addend
A similar mapping can be made for multiplication/division, if required.

How to find 10 values, exponentially distributed, which sum to a value, x

I have a value, for example 2.8. I want to find 10 numbers which are on an exponential curve, which sum to this value.
That is, I want to end up with 10 numbers which sum to 2.8, and which, when plotted, look like the curve below (exponential decay). These 10 numbers should be equally spaced along the curve - that is, the 'x-step' between the values should be constant.
This value of 2.8 will be entered by the user, and therefore the way I calculate this needs to be some kind of algorithm that I can program (hence asking this on SO not Math.SE).
I have no idea where to start with this at all - any ideas?
You want to have 10 x values equally distributed, i.e. x_k = a + k * b. They shall fulfill sum(exp(-x_k)) = v with v being your target value (the 2.8). This means exp(-a) * sum(exp(-b)^k) = v.
Obviously, there is a solution for each choice of b if v is positive. Set b to an arbitrary value, and calculate a from it.
E.g. for v = 2.8 and b = 0.1, you get a = -log(v / sum(exp(-b)^k)) = -log(2.8/sum(0.90484^k)) = -log(2.8/6.6425) = -log(0.421526) = 0.86387.
So for this example, the x values would be 0.86387, 0.96387, ..., 1.76387 and the y values 0.421526, 0.381412, 0.345116, 0.312274, 0.282557, 0.255668, 0.231338, 0.209324, 0.189404, 0.171380.
Update:
As it has been clarified that the curve can be scaled arbitrarily and the xs are preferred to be 1, 2, 3 ... 9, this is much more simple.
Assuming the curve function is r*exp(-x), the 10 values would be r*exp(-1) ... r*exp(-9). Their sum is r*sum(exp(-x)) = r*0.58190489. So to reach a certain value (2.8) you just have to adjust the r accordingly:
r = 2.8/sum(exp(-x)) = 4.81178294
And you get the 10 values: 1.770156, 0.651204, 0.239565, 0.088131, 0.032422, 0.011927, 0.004388, 0.001614, 0.000594.
If I understand your question correctly then you want to find x which solves the equation
It can be solved as
(just sum numbers as geometric progression)
The equation under RootOf will always have 1 real square different from 1 for 2.8 or any other positive number. You can solve it using some root-finding algorithm (1 is always a root but it does not solve original task). For constant a you can choose any number you like.
After computing the x you can easily calculate 10 numbers as .
I'm going to generalize and assume you want N numbers summing to V.
Since your numbers are equally spaced on an exponential you can write your sum as
a + a*x + a*x^2 + ... + a*x^(N-1) = V
Where the first point has value a, and the second a*x etc.
You can take out a factor of a and get:
a ( 1 + x + x^2 + ... + x^(N-1) ) = V
If we're free to pick x then we can solve for a easily
a = V / ( 1 + x + x^2 + .. x^(N-1) )
= V*(x+1)/(x^N-1)
Substituting that back into
a, a*x, a*x^2, ..., a*x^(N-1)
gives the required sequence

Resources