Inverse, exponentially spaced representation of a count field? - php

var minCount = 10; // Used as 0, anything below 10 uses will be ignored
User hasMany Attributes
Attribute (id, value, count, worth)
// Count - number of users who associate with this attribute
// Worth - float 0.1 (or a better non-zero number?) to 1
An attribute with count of minCount has worth 1.
The attribute(s) with the highest count has the lowest possible worth.
The lesser used attributes are worth more - lower count -> higher worth.
The difference in worth should be exponential:
Commonly used attributes (high count) - smaller gap between worth
Uncommon attributes (low count) - larger gap between worth
I'll be using php for now, but psuedo code is equally acceptable - I'd rather just understand how to do it.

So what's the problem?
Use the following simple formula:
currentWorth = exp(minCount / currentCount - 1)
where:
exp - exponent of e;
currentCount - current attribute's count;
minCount - least attribute's count.
Brief explanation:
First consider formula without exponent application:
currentWorth = minCount / currentCount
For attribute having minimal count (currentCount = minCount):
currentWorth = minCount / minCount = 1
For attributes having any other count (obviously greater than minCount):
(currentWorth = minCount / currentCount) < 1
Now let's apply exponential law:
currentWorth = exp(minCount / currentCount - 1)
For attribute having minimal count (currentCount = minCount):
currentWorth = exp(minCount / minCount - 1) = exp(1 - 1) = exp(0) = 1
For attributes having any other count (currentCount > minCount):
currentWorth = exp(minCount / currentCount - 1)
Assuming:
t = 1 - minCount / currentCount > 0
We'll have:
(currentWorth = exp(-t) = 1 / exp(t)) < 1

Related

Get the percentage between 2 numbers

I am trying to figure out if there is a way to find the percentage between 2 numbers.
It's a progress / ranking system.
I want to find the percentage the $current_exp is between the $current_min and the $current_max values, is there a way to achieve this in PHP? So far I've got to this, but it doesn't work as you progress in ranks, it doesn't treat the $current_min as 0 so when you rank up, it says you are like 75% into your next rank progression when you're in fact 0. Does this make sense?
$currentProg = ($current_exp * 100) / $current_max;
Say the current minimum is 18750 and the current maximum is 25100, the current exp is 22000... What percentage from the min to the max is the current exp? This will change each rank as the $current_min and $current_max variables get set depending on the exp of the user.
The next rank is Current min is 25100 Current max is 34230
Currently, when you are at 26000 exp, the output is saying 75.956763073327% which is not correct, it should be like 1 or 2%?
Thanks in advance 🙏
Not a good mathematician, but it looks like it should be:
(Difference of rank - minimum) / (Difference of maximum - minimum) * 100
<?php
$x = 25100;
$z = 34230;
$y = 26000;
echo ($y - $x + 1) / ($z - $x + 1) * 100; // outputs 9.8674843938232 %
Online Demo
Note: + 1 is added to both numerator and denominator to avoid divide by zero errors.

PHP Generate number based on chance

What I want to do is generate a float between 1.00 to 200.00, let's call this number X. The X determines how much a user "wins", think of it like a multiplier on a casino. The user bets 10$, X is 23.21, the user wins 10*23.21.
If the house and the user should have the same odds (+/- 0) in the long run, the chance of X being, for example, 200.00 should be 1/199 - 1/200. This means Y=1/(X-1) - 1/X where Y = percentage of the chance of X.
The percentage Y should be randomized and I was thinking to do a frand(0, 100, 15). Where:
function frand($min, $max, $decimals = 0)
{
$scale = pow(10, $decimals);
return mt_rand($min * $scale, $max * $scale) / $scale;
}
This means we will know Y, and therefor the next step should be using Y=1/(X-1) - 1/X to determine X. However, I do not know how I can achieve this in PHP. Other ways for the same function:
Y = 1/(X - 1) - 1/X
X^2 - X = 1/Y
X(X - 1) = 1/
So, my question is; how do I solve the X OR is there a better way to achieve what I am trying?

Generate all possible 3 positive integers that sum to N

I am not good with math and I can't wrap my head around this, I want to generate EVERY possible 3 positive numbers there is that sum to N example 100, for instance:
0 - 0 - 100
0 - 1 - 99
1 - 1 - 98
You don't need to answer me with PHP code, just a general idea on how I can generate those numbers would be sufficient.
Thanks.
Brute force is an option in your case: you can just use 2 nested loops
and it takes less than 10000 tests only.
// pseudo code
for (i = 0; i <= 100; ++i)
for (j = 0; j <= 100; ++j) {
if ((i + j) > 100)
break;
k = 100 - i - j;
print(i, j, k);
}
If duplicates e.g. 0, 0, 100 and 0, 100, 0 should be excluded, you can use slightly modified code:
// pseudo code
for (i = 0; i <= 100; ++i)
for (j = i; j <= 100; ++j) {
if ((i + j) > 100)
break;
k = 100 - i - j;
if (j <= k)
print(i, j, k);
}
As for just an algorithm, consider first just pairs of numbers whose sum are less than or equal to 100. These should be easy to list. I.e
0 1 2 100
{{0,0}, {0,1}, {0,2},.........., {0,100}
{1,1}, {1,2},..., {1,99}
.
.
...............................{50,50}}
But then each of those pairs, taking their sums can also be paired with precisely one number such that the entire triplet sum is 100.
So to summarize; if you could make first a list of these pairs (would require a double loop i in [0,100], j in [0:50]); and then loop through all pairs in this list calculating the third number you should get all triplets without duplication. Furthermore, if done correctly you wouldn't actually need any lists at all, with proper loop indexing you could calculate them in position.
edit Noticed you wanted duplicates - (though you could permute each triplet).
another approach with slightly better time complexity.
n=int(input())
for i in range(0,int(n/2+1)):
for j in range(0,int(i/2+1)):
print(j," ",i-j," ",(int)(n-i))
l=n-i
for j in range(0,int((n-i)/2+1)):
print(j," ",l-j," ",(int)(i))
it is just the extension of this algorithm which produces two numbers whose sum is equal to n
n=int(input())
for i in range(0,int(n/2+1)):
print(i," ",n-i)
I see you need those duplicates also just change the limits to full in line number 2,3 and 6
n=int(input())
for i in range(0,n):
for j in range(0,i):
print(j," ",i-j," ",(int)(n-i))
l=n-i
for j in range(0,l):
print(j," ",l-j," ",(int)(i))

Mapping increasing rank to decreasing but non-negative points

I need to create a business logic or php function to compute the following: given some input $rank (which is the alexa ranking) I need to compute some $points in such a way that $points will be high for the top ranking website and will decrease with increasing $rank value.
I imagine something like this:
function($rank)
{
$points = x*$rank;
return $points;
}
How do I get $points in such a way that
if the rank is 1 then the points returned is maximum (e.g. 10000).
if rank is 2 then $points returned will be 9500 or nearby.
if rank is 4 then $points returned will be 6000 or nearby.
if rank is 200 $points returned will be 2 or whatever the function will return.
Rule: if $rank is less then $points should be more. Maximal value of $points is 10000 which is for $rank=1.
Now as the $rank increases the $points value should decrease accordingly.
There are many formulas which might satisfy your requiremements.
Nested powers
One possibility:
$points = 10000 * pow(0.993575964272119, pow($rank, 3.16332422407427) - 1)
This gives you the following results:
f(1) = 10000
f(2) = 9500
f(4) = 6000
f(9) = 12.065
f(10) = 0.84341
f(200) = 0
So the three values you fixed (1, 2 and 4) are all satisfied, but the result for 200 indicates that this might not be exactly what you're looking for. The curve looks like this:
By the way, I found this using python and mpmath, by fixing the form of the formula and determining the numbers with the many digits numerically:
>>> import mpmath
>>> print(mpmath.findroot((lambda a,b: 10000*a**(2**b - 1) - 9500,
... lambda a,b: 10000*a**(4**b - 1) - 6000),
... (0.995, 2.7)))
[0.993575964272119]
[ 3.16332422407427]
If you decide on a different form of the function, this approach might be adapted.
Exp of a polynomial
A possible different form with the desired properties would be this:
$points = exp(9.14265175282929 + $rank*(0.127179575914116 - $rank*0.0594909567672230))
This does not decrease quite as quickly as the one above:
f( 1) = 10000
f( 2) = 9500
f( 4) = 6000
f( 13) = 2.1002
f( 14) = 0.47852
f(200) = 0
It was obtained by solving this system of equations:
a + b + c = log(10000)
a + 2b + 4c = log( 9500)
a + 4b + 16c = log( 6000)
to obtain the coefficients a through c for the polynomial. One can add another degree to match f(200)=2 as well, but in that case, the last coefficient will become positive, which means that points will start to increase with rank for very large ranks.
If you want to match that f(200)=2 as well, you can do so using
$points = exp(max(8.86291000469285 - $rank*0.0408488141206645,
9.14265175282929 + $rank*(0.127179575914116 - $rank*0.0594909567672230)))
although this will result in a bend in your curve.
To compare these alternatives to the above:
function getPoints($rank)
{
$returnValue = -0.005 * $rank * $rank - 0.035 * $rank + 100.040;
if ($returnValue < 0) $returnValue = 0;
return $returnValue;
}
This was my thinking.
Function is not forking for large values:
it should atleast give some small value for large ranks...
like if rank is 2000000 then points will be 2.
Thnx btw

Calculating Floating Point Powers (PHP/BCMath)

I'm writing a wrapper for the bcmath extension, and bug #10116 regarding bcpow() is particularly annoying -- it casts the $right_operand ($exp) to an (native PHP, not arbitrary length) integer, so when you try to calculate the square root (or any other root higher than 1) of a number you always end up with 1 instead of the correct result.
I started searching for algorithms that would allow me to calculate the nth root of a number and I found this answer which looks pretty solid, I actually expanded the formula using WolframAlpha and I was able to improve it's speed by about 5% while keeping the accuracy of the results.
Here is a pure PHP implementation mimicking my BCMath implementation and its limitations:
function _pow($n, $exp)
{
$result = pow($n, intval($exp)); // bcmath casts $exp to (int)
if (fmod($exp, 1) > 0) // does $exp have a fracional part higher than 0?
{
$exp = 1 / fmod($exp, 1); // convert the modulo into a root (2.5 -> 1 / 0.5 = 2)
$x = 1;
$y = (($n * _pow($x, 1 - $exp)) / $exp) - ($x / $exp) + $x;
do
{
$x = $y;
$y = (($n * _pow($x, 1 - $exp)) / $exp) - ($x / $exp) + $x;
} while ($x > $y);
return $result * $x; // 4^2.5 = 4^2 * 4^0.5 = 16 * 2 = 32
}
return $result;
}
The above seems to work great except when 1 / fmod($exp, 1) doesn't yield an integer. For example, if $exp is 0.123456, its inverse will be 8.10005 and the outcome of pow() and _pow() will be a bit different (demo):
pow(2, 0.123456) = 1.0893412745953
_pow(2, 0.123456) = 1.0905077326653
_pow(2, 1 / 8) = _pow(2, 0.125) = 1.0905077326653
How can I achieve the same level of accuracy using "manual" exponential calculations?
The employed algorithm to find the nth root of a (positive) number a is the Newton algorithm for finding the zero of
f(x) = x^n - a.
That involves only powers with natural numbers as exponents, hence is straightforward to implement.
Calculating a power with an exponent 0 < y < 1 where y is not of the form 1/n with an integer n is more complicated. Doing the analogue, solving
x^(1/y) - a == 0
would again involve calculating a power with non-integral exponent, the very problem we're trying to solve.
If y = n/d is rational with small denominator d, the problem is easily solved by calculating
x^(n/d) = (x^n)^(1/d),
but for most rational 0 < y < 1, numerator and denominator are rather large, and the intermediate x^n would be huge, so the computation would use a lot of memory and take a (relatively) long time.
(For the example exponent of 0.123456 = 1929/15625, it's not too bad, but 0.1234567 would be rather taxing.)
One way to calculate the power for general rational 0 < y < 1 is to write
y = 1/a ± 1/b ± 1/c ± ... ± 1/q
with integers a < b < c < ... < q and to multiply/divide the individual x^(1/k). (Every rational 0 < y < 1 has such representations, and the shortest such representations generally don't involve many terms, e.g.
1929/15625 = 1/8 - 1/648 - 1/1265625;
using only additions in the decomposition leads to longer representations with larger denominators, e.g.
1929/15625 = 1/9 + 1/82 + 1/6678 + 1/46501020 + 1/2210396922562500,
so that would involve more work.)
Some improvement is possible by mixing the approaches, first find a close rational approximation to y with small denominator via the continued fraction expansion of y - for the example exponent 1929/15625 = [0;8,9,1,192] and using the first four partial quotients yields the approximation 10/81 = 0.123456790123... [note that 10/81 = 1/8 - 1/648, the partial sums of the shortest decomposition into pure fractions are convergents] - and then decompose the remainder into pure fractions.
However, in general that approach leads to calculating nth roots for large n, which also is slow and memory-intensive if the desired accuracy of the final result is high.
All in all, it is probably simpler and faster to implement exp and log and use
x^y = exp(y*log(x))

Categories