In my PHP (v5.2.17) script, I want to select a unique colour for the current user's entries, based on their IP address. I don't want to map the colour values from the hex codes, because I also want to fade the colours of each entry over time. The colour must always have one of the RGB values set to zero (it's like a set of bright, primary colours).
Is there a clever mathematical solution to do this?
I'd greatly appreciate if any math genuises reading this would share some insights. :-)
Are you really limiting yourself to just six "base" colors?
255 255 0
255 0 255
0 255 255
0 0 255
0 255 0
255 0 0
I presume you're going to apply a linear function to these colors to try to fade them out. This won't necessarily look as good as you think it might -- RGB as a representation isn't very linear. You can cheaply approximate a better "linear" representation by using an HSV or HSL representation instead. They surely aren't perfect but it will feel a little more natural than RGB.
As for mapping the IP address to a color, you could store these color combinations in an array and pick among the six elements by using a simple hash function. Something like this might be sufficient:
b1, b2, b3, b4 = <split the four bytes from an IP address>
index = (b1 * 17 + (b2 * 17 + (b3 * 17 + b4))) % 6
(I just picked the multiplier 17 out of the air -- its binary representation is 10001, which means the bits of each byte in the address get "smeared" over each other. There might be better values. Once you've gotten a few colors selected and a handful of IP addresses you can try changing the multiplier to e.g. 21 or 53 and see what makes most sense.)
Although this won't give you a result where one of {R,G,B} is always 0, a HSL representation might look good. As an example, let hue be a decimal value from 0 to 1, defined by
(float)(octet[0] + octet[1] << 8 + octet[2] << 16 + octet[3] << 24) / (2^32-1)
, where each octet[i] is an unsigned byte, and ^ is exponentiation). And then perhaps set lightness and saturation by hand, as per your preference. Just an idea!
As an added bonus, this makes fading the colours easy (just subtract some portion of "time" from saturation/lightness).
are you using a database to store the relations? you could always grab the user's IP Address
<?php
function userIP(){
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$userIp=$_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$userIp=$_SERVER['REMOTE_ADDR'];
}
return trim($userIp);
}
?>
Then use the function to set a usable variable of the IP:
<?php
$Users_IP_address = userIP();
?>
once you have that, you can assign a color that isn't in use and save the association for future reference.
Related
I need to create a function which takes a single integer as argument in the range 0-N and returns a seemingly random number in the same range.
Each input number should always have exactly one output and it should always be the same.
Such a function would produce something like this:
f(1) = 4
f(2) = 1
f(3) = 5
f(4) = 2
f(5) = 3
I believe this could be accomplished by some kind of a hashing algorithm? I don't need anything complex, just not something too simple like f(1) = 2, f(2) = 3 etc.
The biggest issue is that I need this to be reversible. E.g. the above table should be true left-to-right as well as right-to-left, using a different function for the right-to-left conversion is fine.
I know the easiest way is to create an array, shuffle it and just store the relations in a db or something, but as I need N to be quite large I'd like to avoid this if possible.
Edit: For my particular case N is a specific number, it's exactly 16777216 (64^4).
If the range is always a power of two -- like [0,16777216) -- then you can use exclusive-or just as #MarkBaker suggested. It just doesn't work so easily if your range is not a power of two.
You can use addition and subtraction modulo N, although these alone are too obvious, so you have to combine it with something else.
You can also do multiplication modulo-N, but reversing that is complicated. To make it simpler, we can isolate the bottom eight bits and multiply those and add them in a way that doesn't interfere with those bits so we can use them again to reverse the operation.
I don't know PHP so I'm going to give an example in C, instead. Maybe it's the same.
int enc(int x) {
x = x + 4799 * 256 * (x % 256);
x = x + 8896843;
x = x ^ 4777277;
return (x + 1073741824) % 16777216;
}
And to decode, play the operations back in reverse order:
int dec(int x) {
x = x + 1073741824;
x = x ^ 4777277;
x = x - 8896843;
x = x - 4799 * 256 * (x % 256);
return x % 16777216;
}
That 1073741824 must be a multiple of N, and 256 must be a factor of N, and if N is not a power of two then you can't (necessarily) use exclusive-or (^ is exclusive-or in C and I assume in PHP too). The other numbers you can fiddle with, and add and remove stages, at your leisure.
The addition of 1073741824 in both functions is to ensure that x stays positive; this is so that the modulo operation doesn't ever give a negative result, even after we've subtracted values from x which might have made it go negative in the interim.
I offered to describe how I "randomly" scramble up 9-digit SSNs when producing research data sets. This does not replace or hash an SSN. It re-orders the digits. It is difficult to put the digits back in the correct order if you don't know the order in which they were scrambled. I have a gut feeling that this is not what the questioner really wants. So, I am happy to delete this answer if it is deemed off-topic.
I know that I have 9 digits. So, I start with an array that has 9 index values in order:
$a = array(0,1,2,3,4,5,6,7,8);
Now, I need to turn a key that I can remember into a way to shuffle the array. The shuffling has to be the same order for the same key every time. I use a couple tricks. I use crc32 to turn a word into a number. I use srand/rand to get a predictable order of random values. Note: mt_rand no longer produces the same sequence of random digits with the same seed, so I have to use rand.
srand(crc32("My secret key"));
usort($a, function($a, $b) { return rand(-1,1); });
The array $a still has the digits 0 through 8, but they are shuffled. If I use the same keyword I will get the same shuffled order every time. That lets me repeat this every month and get the same result. Then, with a shuffled array, I can pick the digits off the SSN. First, I ensure it has 9 characters (some SSNs are sent as integers and a leading 0 is omitted). Then, I build a masked SSN by picking the digits using $a.
$ssn = str_pad($ssn, 9, '0', STR_PAD_LEFT);
$masked_ssn = '';
foreach($a as $i) $masked_ssn.= $ssn{$i};
$masked_ssn will now have all the digits in $ssn, but in a different order. Technically, there are keywords that make $a become the original ordered array after shuffling, but that is very very rare.
Hopefully this makes sense. If so, you can do it all much faster. If you turn the original string into an array of characters, you can shuffle the array of characters. You just need to reseed rand every time.
$ssn = "111223333"; // Assume I'm using a proper 9-digit SSN
$a = str_split($ssn);
srand(crc32("My secret key"));
usort($a, function($a, $b) { return rand(-1,1); });
$masked_ssn = implode('', $a);
This is not really faster in a runtime way because rand is a rather expensive function and you run rand a hell of lot more here. If you are masking thousands of values as I do, you will want to use an index array that is shuffled just once, not a shuffling for every value.
Now, how do I undo it? Assume I'm using the first method with the index array. It will be something like $a = {5, 3, 6, 1, 0, 2, 7, 8, 4}. Those are the indexes for the original SSN in the masked order. So, I can easily build the original SSN.
$ssn = '000000000'; // I like to define all 9 characters before I start
foreach($a as $i=>$j) $ssn[$j] = $masked_ssn{$i};
As you can see, $i counts from 0 to 8 across the masked SSN. $j counts 5, 3, 6... and puts each value from the masked SSN in the correct place in the original SSN.
Looks like you've got good answer, but still there is an alternative. Linear Congruential Generator (LCG) could provide 1-to-1 mapping and it is known to be a reversible using Euclid's algorithm. For 24bit
Xi = [(A * Xi-1) + C] Mod M
where M = 2^24 = 16,777,216
A = 16,598,013
C = 12,820,163
For LCG reversability take a look at Reversible pseudo-random sequence generator
I'm trying to generate a spiral galaxy in the form of xy (2D) coordinates -- but math is not my strong suit.
I've gleaned the following from an excellent source on spirals:
The radius r(t) and the angle t are proportional for the
simpliest spiral, the spiral of Archimedes. Therefore the equation is:
(3) Polar equation: r(t) = at [a is constant].
From this follows
(2) Parameter form: x(t) = at cos(t), y(t) = at sin(t),
(1) Central
equation: x²+y² = a²[arc tan (y/x)]².
This question sort of touched upon galaxy generation, but the responses were scattered and still overly complex for what I need (aka, my math-dumb mind can't understand them).
Essentially, what I need to do is loop through a spiral formula in PHP ~5000 times to generate points on a 513x513 XY grid. The size of the grid and the number of points needed may change in the future. Even better would be to weigh those points towards the origin of the spirals both in frequency and how far they can stray from the exact mathematical formula, similarly to how a galaxy actually looks.
This mathematical paper talks about a formula that describes the structure of spiral galaxies.
What completely loses me is how to translate a mathematical formula to something I can loop through in PHP!
// a is 5 here
function x($t){ return 5 * $t * cos($t); }
function y($t){ return 5 * $t * sin($t); }
for ($t = 0; $t < 50; $t += 0.01) {
$xyPoint = array(x($t), y($t));
// draw it
}
when you encounter parametric equations like this, its common for the parameter variable to be t, which means time. So you could think of plugging increasing values of t into the functions, and getting coordinates which gradually change as elapsed time increases.
you'll need to choose your own values for a, the range of t, and the increment step size of t. It just depends on your requirements. both cos() and sin() have a max value of 1, if that helps you figure out suitable values for a and t depending on your canvas size
the topic pretty much describes what we would like to accomplish.
a) start with a possible range of integers, for example, 1 to 10000.
b) take any md5 hash, run it thru this algo.
c) result that pops out will be an integer between 1 to 10000.
we are open to using another hashing method too.
the flow would ideally look like this:
string -> md5(string) -> algo(md5(string),range) -> resulting integer within range
is something like this possible?
final note: the range will always start with 1.
if you have an answer, feel free to post just the general idea, or if you so desire, php snippet works too :)
thanks!
Since MD5 (and SHA-1, etc.) will give you 128 bits of data (in PHP, you'll get it in hexadecimal string notation, so you need to convert it to an integer first). That number modulo 10000 will give you your integer.
Note however that many different hashes will convert to the same integer; this is unavoidable with any sort of conversion to your integer range, as the modulo operation essentially maps a larger set of numbers (in this case, 128 bits, that is numbers from 0 to 340,282,366,920,938,463,463,374,607,431,768,211,456) to a smaller set of numbers (less than 17 bits, numbers from 1 to 100,000).
since the range that we want will always start at 1, the following works great. all credit goes to Piskvor, as he was the one who provided the basic idea of how to go at this.
the code below seams to accomplish what we want. please chime in if this can be (not the code, its just for reference, but if the idea) improved at all. running the code below will result in 6305 / 10000 unique results. that in our case is good enough.
<?
$final=array();
$range=10000;
for($i=1;$i<=$range;$i++){
$string='this is my test string - attempt #'.$i;
echo 'initial string: '.$string.PHP_EOL;
$crc32=crc32($string);
echo 'crc32 of string: '.$crc32.PHP_EOL;
$postalgo=$crc32%$range;
echo 'post algo: '.$postalgo.PHP_EOL;
if(!in_array($postalgo,$final)){
$final[]=$postalgo;
}
}
echo 'unique results for '.($i-1).' attempts: '.count($final).PHP_EOL;
?>
enjoy!
Like the title suggests I need to do something like so...
$i++;//we all know this.
$value = 'a';
increment($value);// i need this functionality
//output
string [ b ]
///here are some more samples, to help you understand...
increment('b'); //output// c
increment('z'); //output// A [capital A not fussy but would be good :) ]
increment('9'); //output// a1
increment('a1'); //output// a2
increment('aa1'); //output// aa2
and so on...
UPDATE
well lets say I use numeric values
$id++;
I would end up with a massive number eventuall 1310743942525;
which can take alot more space than say `ab34c9" im trying to save length of characters to save on db ...
You try to treat it as a base 62 number:
http://www.pgregg.com/projects/php/base_conversion/base_conversion.php
with source code at
http://www.pgregg.com/projects/php/base_conversion/base_conversion.inc.phps
convert it to decimal, increment, and convert it back to base 62
UPDATE
From how I read the code, you could have a workflow like this:
$value = 'ab8Zb';
$value_base10 = base_base2dec($value, 62);
$value_base10++;
$value = base_dec2base($value_base10, 62); // should be 'ab8Zc'
If all you are trying to do is save database space, consider this.
In MySQL you can have a field with a type UNSIGNED BIGINT. The maximum size of this field is 18446744073709551615 and the storage space is only 8 bytes.
If you were to convert this number (1.844 x 10^19) to base-62, it would be represented as LygHa16AHYF. You would need a CHAR(11) (11 bytes) or a VARCHAR(11) (12 bytes) in order to store the converted number.
If you used VARCHAR for the field type, smaller numbers would take less space, but for the larger numbers it actually takes more. 8 bytes for a huge number is pretty minimal anyway. I would save the effort and just make the DB field a UNSIGNED BIGINT.
You can use ascii codes of each letter. This is just a simple example that will show you the idea, ofcourse it need a lot of modyfications if you want to increment 'aa1' into 'aa2' but impossible is nothig ;P You will just need to write few conditions.
function increment($value)
{
if(strlen($value)>1)
return false;
$asciiCode = ord($value);
return chr($asciiCode + 1);
}
http://www.asciitable.com/ - ASCII codes table :)
when you use the random(min,max) function in most languages, what is the distribution like ?
what if i want to produce a range of numbers for 20% of the time, and another range of numbers for 80% of the time, how can i generate series of random number that follows that ?
ex) i should get random frequency but the frequency of "1" must be higher by around 20% than the frequency of "0"
For most languages, the random number generated can be dependent on an algorithm within that language, or generated randomly based on the several factors such as time, processor, seed number.
The distribution is not normal. In fact say if the function returns 5 integers, all 5 integers have a fair chance of appearing in the next function call. This is also known as uniformed distribution.
So say if you wish to produce a number (say 7) for 20% of the time, and another number (say 13) for 80% of the time, you can do an array like this:
var arr = [7,13,13,13,13];
var picked = arr[Math.floor(Math.random()*arr.length)] ;
// since Math.random() returns a float from 0.0 to 1.0
So thus 7 has a 20% chance of appearing, and 13 has 80% chance.
This is one possible method:
ranges = [(10..15), (20..30)]
selector = [0, 0, 1,1,1,1,1,1,1,1] # 80:20 distribution array
# now select a range randomly
random_within_range(ranges(selector[random(10)]))
def random_within_range range
rand (range.last - range.begin - (range.exclude_end? ? 1 : 0)) + range.begin
end
Most pseudo random generators built-in programming languages produce a uniform distribution, i.e. each value within the range has the same probability of being produced as any other value in the range. Indeed in some cases this requirement is part of the language standard. Some languages such as Python or R support various of the common distributions.
If the language doesn't support it, you either have to use mathematical tricks to produce other distributions such as a normal distribution from a uniform one, or you can look for third-party libraries which perform this function.
Your problem seems much simpler however since the random variable is discrete (and of the simpler type thereof, i.e binary). The trick for these is to produce a random number form the uniform distribution, in a given range, say 0 to 999, and to split this range in the proportions associated with each value, in the case at hand this would be something like :
If (RandomNumber) < 200 // 20%
RandomVariable = 0
Else // 80%
RandomVariable = 1
This logic can of course be applied to n discrete variables.
Your question differs from your example quite a bit. So I'll answer both and you can figure out whichever answers what you're really looking for.
1) Your example (I don't know ruby or java, so bear with me)
First generate a random number from a uniform distribution from 0 to 1, we'll call it X.
You can then setup a if/else (i.e. if ( x < .2) {1} else {0})
2) Generating random numbers from a normal distribution with skew
You can look into skewed distributions such as a skewed student T's distribution with high degree of freedom.
You can also use the normal CDF and just pick off numbers that way.
Here's a paper which discusses how to do it with multiple random numbers from a uniform distribution
Finally, you can use a non-parametric approach which would involve kernal density estimation (I suspect you aren't looking for anything this sophisticated however).
Like anybody says, pseudo-random number generator on most languages implements the uniform distribution over (0,1).
If you have two responses categories (0,1) with p probability for 1, you have a Bernoulli distribution and can be emulated with
# returns 1 with p probability and 0 with (1-p) probability
def bernoulli(p)
rand()<p ? 1:0;
end
Simple as that.
Skewed normal distribution is a entirely different beast, made by the 'union' of pdf and cdf of a normal distribution to create the skew. You can read Azzalini's work here. Using gem distribution, you can generate the probability density function, with
# require 'distribution'
def sn_pdf(x,alpha)
sp = 2*Distribution::Normal.pdf(x)*Distribution::Normal.cdf(x*alpha)
end
Obtains the cdf is difficult, because there isn't an analytical solution, so you should integrate.
To obtain random numbers from a skewed normal, you could use the acceptation-rejection algorithm.
Most computer languages have a uniform distribution to their (pseudo) random integer generators. So each integer is equally likely.
For your example, suppose you want "1" 55% of the time and "0" 45% of the time.
To get unequal these frequencies, try generating a random number between 1 and 100. If the number generated is from 1 to 55, output "1"; otherwise output "0".
How about
var oneFreq = 80.0/100.0;
var output = 0;
if (Math.random() > oneFreq)
output = 1;
or, if you want 20% of the values to be between 0 and 100, and 80% to be between 100 and 200.
var oneFreq = 80.0/100.0;
var oneRange = 100;
var zeroRange = 100;
var output = Math.random();
if (output > oneFreq)
output = zeroRange + Math.floor(oneRange * (output - oneFreq));
else
output = Math.floor(zeroRange * output);
In ruby I would do it like this:
class DistributedRandom
def initialize(left, right = nil)
if right
#distribution = [0] * left + [1] * right
else
#distribution = left
end
end
def get
#distribution[rand #distribution.length]
end
end
Running a test with 80:20 distribution:
test = [0,0]
rnd = DistributedRandom.new 80, 20 # 80:20 distribution
10000.times { test[rnd.get] += 1 }; puts "Test 1", test
Running a test with 20% more distribution on the right side:
test = [0,0]
rnd = DistributedRandom.new 100, 120 # +20% distribution
10000.times { test[rnd.get] += 1 }; puts "Test 2", test
Running a test with custom distribution with a trigonometric function over 91 discrete values, output however does not fit very well into the previous tests:
test = [0,0]
rnd = DistributedRandom.new((0..90).map {|x| Math.sin(Math::PI * x / 180.0)})
10000.times { test[rnd.get] += 1 }; puts "Test 3", test
Have a look at this lecture if you want a good mathematical understanding.