I want to shorten long integers in PHP to a string in the same way as how hexadecimal works. I think Google also use this for there sort urls in YouTube.
I have a string of numbers and letters that i want to use:
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
where:
8 = 8
a = 10
f = 15
Z = 61
10 = 62
11 = 63
1a = 72
and zo on...
the purpose is to set an integer the sortest way in a parameter in an affiliate url subid for tracking.
I was looking for a good name to discripe my question i find out that there is a sexagesimal numeral system that uses sixty as its base.
Here you can find a function that is shorten urls and avoid fonfusion between similar characters like 0/o and i/1/l/L etc.
<?php
/**
* A pure PHP implementation of NewBase60. A base 60 numbering system designed for
* use with URL shortening. Limited/overlapping character set to avoid confusion
* between similar characters, eg o/0, i/l, etc.
*
* Q: Why not use PHP base_convert()?
* A: Because it only goes up to base 36, and doesn't support the NewBase60
*
* #see http://tantek.pbworks.com/w/page/19402946/NewBase60
* #see http://en.wikipedia.org/wiki/sexagesimal
*
* #author david#dsingleton.co.uk
*/
class NewBase60
{
protected static $characterSet = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz';
/**
* Convert a sexagesimal number to a decimal number
* #param string sexagesimal number to convert
* #return integer Decimal representation of sexagesimal number
*/
public static function toDecimal($sexNum)
{
// Return falsy and 0 values as is
if (!$sexNum) {
return $sexNum === '0' ? 0 : $sexNum;
}
$decNum = 0;
foreach(str_split($sexNum) as $chr) {
$ord = ord($chr);
if ($ord>=48 && $ord<=57) { $ord -= 48; } // 0 - 9
elseif ($ord>=65 && $ord<=72) { $ord -= 55; } // A - H
elseif ($ord==73 || $ord==108) { $ord = 1; } // Error correct typo: capital I, lowercase l to 1
elseif ($ord>=74 && $ord<=78) { $ord -= 56; } // J - N
elseif ($ord==79) { $ord = 0; } // Error correct typo: capital O to 0
elseif ($ord>=80 && $ord<=90) { $ord -= 57; } // P - Z
elseif ($ord==95) { $ord = 34; } // underscore
elseif ($ord>=97 && $ord<=107) { $ord -= 62; } // a - k
elseif ($ord>=109 && $ord<=122) { $ord -= 63; } // m - z
else { $ord = 0; } // treat all other noise as 0
$decNum = 60 *$decNum + $ord;
}
return $decNum;
}
/**
* Convert a decimal number to a sexagesimal number
* #param integer Decimal number to convert
* #return string sexagesimal representation of decimal
*/
public static function fromDecimal($decNum)
{
$decNum = (int) $decNum;
if (!$decNum) {
return $decNum === 0 ? '0' : $sexNum;
}
$aSexCharset = self::$characterSet;
$result = '';
while ($decNum > 0) {
$decRemainder = $decNum % 60;
$decNum = ($decNum - $decRemainder) / 60;
$result = $aSexCharset[$decRemainder] . $result;
}
return $result;
}
}
Copyright: https://github.com/dsingleton/new-base-60/blob/master/newbase60.class.php
Related
I got a set of different events, all with their own odds to happen. Is there a way to calculate the odds for them combined, so that I get a the odds for 0, 1 2 and so on to happen.
The math is easy, but the number of calculations grow quick, so I was hoping there was a function that could help me.
Example with three factors :
Event | Yes | No
A | 3% | 97%
B | 4% | 96%
C | 5% | 95%
0 Happening : $A[no] * $ B[no] * $c[no]
1 happening : $A[yes] * $ B[no] * $c[no] + $A[no] * $ B[yes] * $c[no] + $A[no] * $ B[no] * $c[yes]
2 happening = $A[yes] * $ B[yes] * $c[no] + $A[yes] * $ B[no] * $c[yes] + $A[no] * $ B[yes] * $c[yes]
3 happening : $A[yes] * $ B[yes] * $c[yes]
This is easy to write in php, my problem is how this scale up if i add more events. Just adding one more doubles the number of caclulations, and soon the code would be really long.
So is there an easier way to do this? I'll be grateful for any tips or ideas.
This is quite slow implementation.
Let's consider a case with 5 events.
Odds of 0 events happening is:
$no[0] * $no[1] * $no[2] * $no[3] * $no[4]
Odds of 1 event happening is:
$no[0] * $no[1] * $no[2] * $no[3] * $yes[4] +
$no[0] * $no[1] * $no[2] * $yes[3] * $no[4] +
$no[0] * $no[1] * $yes[2] * $no[3] * $no[4] +
...
where you go through all multiplications where there is exactly 1 'yes' choice.
Odds of 2 events happening is:
$no[0] * $no[1] * $no[2] * $yes[3] * $yes[4] +
$no[0] * $no[1] * $yes[2] * $no[3] * $yes[4] +
$no[0] * $no[1] * $yes[2] * $yes[3] * $no[4] +
...
where you go through all multiplications where there is exactly 2 'yes' choices.
This can be generalized: To calculate odds of N events happening you go through all multiplications where there is exactly N 'yes' choices.
Now when you need to calculate all odds from 0 to 5 events happening, you need to go through all possible combinations of yes/no choices and add each multiplication to $odds[$yesCount].
$no[0] * $no[1] * $no[2] * $no[3] * $no[4] ; added to $odds[0]
$no[0] * $no[1] * $no[2] * $no[3] * $yes[4] ; added to $odds[1]
$no[0] * $no[1] * $no[2] * $yes[3] * $no[4] ; added to $odds[1]
$no[0] * $no[1] * $no[2] * $yes[3] * $yes[4] ; added to $odds[2]
$no[0] * $no[1] * $yes[2] * $no[3] * $no[4] ; added to $odds[1]
...
$yes[0] * $yes[1] * $yes[2] * $yes[3] * $yes[4] ; added to $odds[5]
There are total of 2**5 = 32 different multiplications here, or generally 2**$eventCount.
It is easy to go through all these cases if we assign a number to each case from 0 to 2**$eventCount-1, and then use bits of this number to select whether 'yes' or 'no' choice of each event is included in multiplication, and finally add each multiplication result to $odds[$yesCount]:
// number of events
$eventCount = 5;
// odds of each event happening
$yes = [ 0.10, 0.50, 0.32, 0.66, 0.99 ];
// odds of each event not happening
$no = [];
for ($eventNumber = 0; $eventNumber < $eventCount; $eventNumber++) {
$no[$eventNumber] = 1 - $yes[$eventNumber];
}
// initialize combined $odds to zero
$odds = [];
for ($n = 0; $n <= $eventCount; $n++) {
$odds[$n] = 0;
}
// calculate combined odds
for ($case = 0; $case < 2 ** $eventCount; $case++) {
$multiplication = 1;
$yesCount = 0;
for ($eventNumber = 0; $eventNumber < $eventCount; $eventNumber++) {
if ($case & (1 << $eventNumber)) {
$yesCount++;
$multiplication *= $yes[$eventNumber];
} else {
$multiplication *= $no[$eventNumber];
}
}
$odds[$yesCount] += $multiplication;
}
// show combined odds
for ($n = 0; $n <= $eventCount; $n++) {
echo "Odds of " . $n . " events happening is " . $odds[$n] . "<br>\n";
}
Let's see what we actually need here. Let's assume, we have an array of chances:
$chances = [0.8, 0.3, 0.15];
What we need is to get those results with the following calculations:
// 0.119
$chance0 = (1-$array[0]) * (1-$array[1]) * (1-$array[2]);
// 0.548
$chance1 = $array[0] * (1-$array[1]) * (1-$array[2]) + (1-$array[0]) * $array[1] * (1-$array[2]) + (1-$array[0]) * (1-$array[1]) * $array[2];
// 0.297
$chance2 = $array[0] * $array[1] * (1-$array[2]) + $array[0] * (1-$array[1]) * $array[2] + (1-$array[0]) * $array[1] * $array[2];
// 0.036
$chance3 = $array[0] * $array[1] * $array[2];
What we actually need, is based on an integer that represents a number of successful chances, get every non-repeating combination from a pool of numbers, and make the rest "inversive", i.e. make them (1 - chance)
To achieve that, I used this answer and modified the code a bit. From the mentioned answer, we get non-repeating combinations of chances (meaning, that in case we get 2 successful chances, 0.8 and 0.15 are same as 0.15 and 0.8). Next, we use an array_diff to see what is left and assume those are failing chances. But since those are failing chances, we need to "reverse" them, calling array_map with the respective callback. The last thing is to calculate array_product to get the chance of the event happening.
And finally we just array_sum all the chances.
class Combinations implements Iterator
{
protected $c = null;
protected $s = null;
protected $n = 0;
protected $k = 0;
protected $pos = 0;
function __construct($s, $k) {
if(is_array($s)) {
$this->s = array_values($s);
$this->n = count($this->s);
} else {
$this->s = (string) $s;
$this->n = strlen($this->s);
}
$this->k = $k;
$this->rewind();
}
function key() {
return $this->pos;
}
function current() {
$r = array();
for($i = 0; $i < $this->k; $i++)
$r[] = $this->s[$this->c[$i]];
return is_array($this->s) ? $r : implode('', $r);
}
function next() {
if($this->_next())
$this->pos++;
else
$this->pos = -1;
}
function rewind() {
$this->c = range(0, $this->k);
$this->pos = 0;
}
function valid() {
return $this->pos >= 0;
}
protected function _next() {
$i = $this->k - 1;
while ($i >= 0 && $this->c[$i] == $this->n - $this->k + $i)
$i--;
if($i < 0)
return false;
$this->c[$i]++;
while($i++ < $this->k - 1)
$this->c[$i] = $this->c[$i - 1] + 1;
return true;
}
}
function getTotalChances($calculateFrom, $successCount)
{
$totalChance = [];
foreach(new Combinations($calculateFrom, $successCount) as $success)
{
$failure = array_map(function($element) {
return (1-$element);
}, array_diff($calculateFrom, $success));
$chance = array_product(array_merge($success, $failure));
$totalChance[] = $chance;
}
return(array_sum($totalChance));
}
$calculateFrom = [0.8, 0.3, 0.15];
var_dump(getTotalChances($calculateFrom, 2));
Now if you run the following, you will get what you need:
var_dump(getTotalChances($calculateFrom, 0)); // 0.119
var_dump(getTotalChances($calculateFrom, 1)); // 0.548
var_dump(getTotalChances($calculateFrom, 2)); // 0.297
var_dump(getTotalChances($calculateFrom, 3)); // 0.036
I want to create a function that checks if number is in range.
// ex. validate(10,"? >=0 && ? <= 30)
function validate($var,$range){
//check if $var is a number
{
//check if $range contains '?'
{
//replace '?' with $var in $range
// how to execute the if statement in $range to check it and return true or false?
}
}
}
That's my idea of it.. If you have something better in mind please make a suggestion.
This solutions seems to be more flexible. I've even documented it.
/**
* Validate a number between a given range
* #param int|double $num The number to validate
* #param int|double $min The lower boundary of the range
* #param int|double $max The upper boundary of the range
* #param boolean $inclusive Whether to include the boundary values as valid
* #return boolean
*/
function validate($num, $min, $max = PHP_INT_MAX, $inclusive = true) {
if ($inclusive) {
return $num >= $min && $num <= $max;
}
return $num > $min && $num < $max;
}
Hi I am writing a PHP class to implement Rabin-Karp algorithm. I have issue with re-hashing part. This code doesn't include matching part of the characters. I had to stop since it never matching hash codes due to the issue with re-hashing. Someone please help me to figure it out.
<?php
class RabinKarp
{
/**
* #var String
*/
private $pattern ;
private $patternHash ;
private $text ;
private $previousHash ;
/**
* #var Integer
*/
private $radix ;
private $prime ;
private $position ;
/**
* Constructor
*
* #param String $pattern - The pattern
*
*/
public function __construct($pattern)
{
$this->pattern = $pattern;
$this->radix = 256;
$this->prime = 100007;
$this->previousHash = "";
$this->position = 0;
$this->patternHash = $this->generateHash($pattern);
}
private function generateHash($key)
{
$charArray = str_split($key);
$hash = 0;
foreach($charArray as $char)
{
$hash = ($this->radix * $hash + ord($char)) % $this->prime;
}
return $hash;
}
public function search($character)
{
$this->text .= $character;
if(strlen($this->text) < strlen($this->pattern))
{
return false;
}
else
{
$txtHash = 0;
echo $this->previousHash . "<br/>";
if(empty($this->previousHash))
{
$txtHash = $this->generateHash($this->text);
$this->previousHash = $txtHash;
$this->position = 0;
}
else
{
// The issue is here
$charArray = str_split($this->text);
$txtHash = (($txtHash + $this->prime) - $this->radix * strlen($this->pattern) * ord($charArray[$this->position]) % $this->prime) % $this->prime;
$txtHash = ($txtHash * $this->radix + ord($character)) % $this->prime;
$this->previousHash = $txtHash;
}
if($txtHash == $this->patternHash)
{
echo "Hash Match found";
}
}
}
}
$x = new RabinKarp("ABC");
$x->search("Z");
$x->search("A");
$x->search("B");
$x->search("C");
?>
Thank you.
The value contributed to the hash by the character (c for shortness) you're removing is
ord(c) * radix^(length(pattern)-1)
since a character contributes ord(c) when it first enters the matching window, and the hash - therefore also its contribution - is multiplied with radix for each of the length(pattern)-1 characters entering the matching window until c finally leaves it.
But you're subtracting ord(c) * radix * length(pattern)
$charArray = str_split($this->text);
$txtHash = (($txtHash + $this->prime)
- $this->radix * strlen($this->pattern)
* ord($charArray[$this->position]) % $this->prime)
% $this->prime;
$txtHash = ($txtHash * $this->radix + ord($character)) % $this->prime;
Additionally, in the calculation you're using the variable $txtHash, which you've set to 0, that should be $this->previousHash, and you must increment the text position.
In principle,
$charArray = str_split($this->text);
$txtHash = (($this->previousHash + $this->prime)
- pow($this->radix, strlen($this->pattern)-1)
* ord($charArray[$this->position]) % $this->prime)
% $this->prime;
$txtHash = ($txtHash * $this->radix + ord($character)) % $this->prime;
$this->previousHash = $txtHash;
$this->position += 1;
is what you have to do.
But unless the pattern is very short, pow($this->radix,strlen($this->pattern)-1) will overflow, so you have to replace pow($this-radix, strlen($this->pattern)-1) with a modular exponentiation function
function mod_pow($base,$exponent,$modulus)
{
$aux = 1;
while($exponent > 0) {
if ($exponent % 2 == 1) {
$aux = ($aux * $base) % $modulus;
}
$base = ($base * $base) % $modulus;
$exponent = $exponent/2;
}
return $aux;
}
(this can still overflow if $modulus, that is $this->prime here, is too large). The relevant line of code becomes
$txtHash = (($this->previousHash + $this->prime)
- mod_pow($this->radix, strlen($this->pattern)-1, $this->prime)
* ord($charArray[$this->position]) % $this->prime)
% $this->prime;
Then you have a potentially huge inefficiency
$this->text .= $character;
...
$charArray = str_split($this->text);
If the string becomes long, the concatenation and the splitting may take a lot of time (not sure how PHP implements strings and string operations, but they're likely not constant time). You should probably keep only the relevant part of the string, i.e. drop the first character after recalculating the hash.
The function levenshtein in PHP works on strings with maximum length 255. What are good alternatives to compute a similarity score of sentences in PHP.
Basically I have a database of sentences, and I want to find approximate duplicates.
similar_text function is not giving me expected results. What is the easiest way for me to detect similar sentences like below:
$ss="Jack is a very nice boy, isn't he?";
$pp="jack is a very nice boy is he";
$ss=strtolower($ss); // convert to lower case as we dont care about case
$pp=strtolower($pp);
$score=similar_text($ss, $pp);
echo "$score %\n"; // Outputs just 29 %
$score=levenshtein ( $ss, $pp );
echo "$score\n"; // Outputs '5', which indicates they are very similar. But, it does not work for more than 255 chars :(
The levenshtein algorithm has a time complexity of O(n*m), where n and m are the lengths of the two input strings. This is pretty expensive and computing such a distance for long strings will take a long time.
For whole sentences, you might want to use a diff algorithm instead, see for example: Highlight the difference between two strings in PHP
Having said this, PHP also provides the similar_text function which has an even worse complexity (O(max(n,m)**3)) but seems to work on longer strings.
I've found the Smith Waterman Gotoh to be the best algorithm for comparing sentences. More info in this answer. Here is the PHP code example:
class SmithWatermanGotoh
{
private $gapValue;
private $substitution;
/**
* Constructs a new Smith Waterman metric.
*
* #param gapValue
* a non-positive gap penalty
* #param substitution
* a substitution function
*/
public function __construct($gapValue=-0.5,
$substitution=null)
{
if($gapValue > 0.0) throw new Exception("gapValue must be <= 0");
//if(empty($substitution)) throw new Exception("substitution is required");
if (empty($substitution)) $this->substitution = new SmithWatermanMatchMismatch(1.0, -2.0);
else $this->substitution = $substitution;
$this->gapValue = $gapValue;
}
public function compare($a, $b)
{
if (empty($a) && empty($b)) {
return 1.0;
}
if (empty($a) || empty($b)) {
return 0.0;
}
$maxDistance = min(mb_strlen($a), mb_strlen($b))
* max($this->substitution->max(), $this->gapValue);
return $this->smithWatermanGotoh($a, $b) / $maxDistance;
}
private function smithWatermanGotoh($s, $t)
{
$v0 = [];
$v1 = [];
$t_len = mb_strlen($t);
$max = $v0[0] = max(0, $this->gapValue, $this->substitution->compare($s, 0, $t, 0));
for ($j = 1; $j < $t_len; $j++) {
$v0[$j] = max(0, $v0[$j - 1] + $this->gapValue,
$this->substitution->compare($s, 0, $t, $j));
$max = max($max, $v0[$j]);
}
// Find max
for ($i = 1; $i < mb_strlen($s); $i++) {
$v1[0] = max(0, $v0[0] + $this->gapValue, $this->substitution->compare($s, $i, $t, 0));
$max = max($max, $v1[0]);
for ($j = 1; $j < $t_len; $j++) {
$v1[$j] = max(0, $v0[$j] + $this->gapValue, $v1[$j - 1] + $this->gapValue,
$v0[$j - 1] + $this->substitution->compare($s, $i, $t, $j));
$max = max($max, $v1[$j]);
}
for ($j = 0; $j < $t_len; $j++) {
$v0[$j] = $v1[$j];
}
}
return $max;
}
}
class SmithWatermanMatchMismatch
{
private $matchValue;
private $mismatchValue;
/**
* Constructs a new match-mismatch substitution function. When two
* characters are equal a score of <code>matchValue</code> is assigned. In
* case of a mismatch a score of <code>mismatchValue</code>. The
* <code>matchValue</code> must be strictly greater then
* <code>mismatchValue</code>
*
* #param matchValue
* value when characters are equal
* #param mismatchValue
* value when characters are not equal
*/
public function __construct($matchValue, $mismatchValue) {
if($matchValue <= $mismatchValue) throw new Exception("matchValue must be > matchValue");
$this->matchValue = $matchValue;
$this->mismatchValue = $mismatchValue;
}
public function compare($a, $aIndex, $b, $bIndex) {
return ($a[$aIndex] === $b[$bIndex] ? $this->matchValue
: $this->mismatchValue);
}
public function max() {
return $this->matchValue;
}
public function min() {
return $this->mismatchValue;
}
}
$str1 = "Jack is a very nice boy, isn't he?";
$str2 = "jack is a very nice boy is he";
$o = new SmithWatermanGotoh();
echo $o->compare($str1, $str2);
You could try using similar_text.
It can get quite slow with 20,000+ characters (3-5 seconds) but your example you mention using only sentences, this will work just fine for that usage.
One thing to note is when comparing string of different sizes you will not get 100%. For example if you compare "he" with "head" you would only get a 50% match.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
The community reviewed whether to reopen this question 10 months ago and left it closed:
Opinion-based Update the question so it can be answered with facts and citations by editing this post.
Improve this question
Is there any way to create unique keys like those used in YouTube video urls (ex: https://www.youtube.com/watch?v=nWChTnkVdKE)?
The idea is to convert a unique integer (such as current time) in an other mathematical base.
A very simple way in PHP :
// With this precision (microsecond) ID will looks like '2di2adajgq6h'
// From PHP 7.4.0 this is needed, otherwise a warning is displayed
$cleanNumber = preg_replace( '/[^0-9]/', '', microtime(false) );
$id = base_convert($cleanNumber, 10, 36);
// With less precision (second) ID will looks like 'niu7pj'
$id = base_convert(time(), 10, 36);
A small PHP class to generate YouTube-like hashes from one or many numbers. Use hashids when you do not want to expose your database ids to the user.
Source: https://github.com/ivanakimov/hashids.php
Use whichever you like :-)
// Generates Alphanumeric Output
function generateRandomID() {
// http://mohnish.in
$required_length = 11;
$limit_one = rand();
$limit_two = rand();
$randomID = substr(uniqid(sha1(crypt(md5(rand(min($limit_one, $limit_two), max($limit_one, $limit_two)))))), 0, $required_length);
return $randomID;
}
// Generates only alphabetic output
function anotherRandomIDGenerator() {
// Copyright: http://snippets.dzone.com/posts/show/3123
$len = 8;
$base='ABCDEFGHKLMNOPQRSTWXYZabcdefghjkmnpqrstwxyz';
$max=strlen($base)-1;
$activatecode='';
mt_srand((double)microtime()*1000000);
while (strlen($activatecode)<$len+1)
$activatecode.=$base{mt_rand(0,$max)};
return $activatecode;
}
For me best algorithm is this: Create Youtube-Like IDs With PHP/Python/Javascript/Java/SQL
<?php
/**
* Translates a number to a short alhanumeric version
*
* Translated any number up to 9007199254740992
* to a shorter version in letters e.g.:
* 9007199254740989 --> PpQXn7COf
*
* specifiying the second argument true, it will
* translate back e.g.:
* PpQXn7COf --> 9007199254740989
*
* this function is based on any2dec && dec2any by
* fragmer[at]mail[dot]ru
* see: http://nl3.php.net/manual/en/function.base-convert.php#52450
*
* If you want the alphaID to be at least 3 letter long, use the
* $pad_up = 3 argument
*
* In most cases this is better than totally random ID generators
* because this can easily avoid duplicate ID's.
* For example if you correlate the alpha ID to an auto incrementing ID
* in your database, you're done.
*
* The reverse is done because it makes it slightly more cryptic,
* but it also makes it easier to spread lots of IDs in different
* directories on your filesystem. Example:
* $part1 = substr($alpha_id,0,1);
* $part2 = substr($alpha_id,1,1);
* $part3 = substr($alpha_id,2,strlen($alpha_id));
* $destindir = "/".$part1."/".$part2."/".$part3;
* // by reversing, directories are more evenly spread out. The
* // first 26 directories already occupy 26 main levels
*
* more info on limitation:
* - http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/165372
*
* if you really need this for bigger numbers you probably have to look
* at things like: http://theserverpages.com/php/manual/en/ref.bc.php
* or: http://theserverpages.com/php/manual/en/ref.gmp.php
* but I haven't really dugg into this. If you have more info on those
* matters feel free to leave a comment.
*
* The following code block can be utilized by PEAR's Testing_DocTest
* <code>
* // Input //
* $number_in = 2188847690240;
* $alpha_in = "SpQXn7Cb";
*
* // Execute //
* $alpha_out = alphaID($number_in, false, 8);
* $number_out = alphaID($alpha_in, true, 8);
*
* if ($number_in != $number_out) {
* echo "Conversion failure, ".$alpha_in." returns ".$number_out." instead of the ";
* echo "desired: ".$number_in."\n";
* }
* if ($alpha_in != $alpha_out) {
* echo "Conversion failure, ".$number_in." returns ".$alpha_out." instead of the ";
* echo "desired: ".$alpha_in."\n";
* }
*
* // Show //
* echo $number_out." => ".$alpha_out."\n";
* echo $alpha_in." => ".$number_out."\n";
* echo alphaID(238328, false)." => ".alphaID(alphaID(238328, false), true)."\n";
*
* // expects:
* // 2188847690240 => SpQXn7Cb
* // SpQXn7Cb => 2188847690240
* // aaab => 238328
*
* </code>
*
* #author Kevin van Zonneveld <kevin#vanzonneveld.net>
* #author Simon Franz
* #author Deadfish
* #author SK83RJOSH
* #copyright 2008 Kevin van Zonneveld (http://kevin.vanzonneveld.net)
* #license http://www.opensource.org/licenses/bsd-license.php New BSD Licence
* #version SVN: Release: $Id: alphaID.inc.php 344 2009-06-10 17:43:59Z kevin $
* #link http://kevin.vanzonneveld.net/
*
* #param mixed $in String or long input to translate
* #param boolean $to_num Reverses translation when true
* #param mixed $pad_up Number or boolean padds the result up to a specified length
* #param string $pass_key Supplying a password makes it harder to calculate the original ID
*
* #return mixed string or long
*/
function alphaID($in, $to_num = false, $pad_up = false, $pass_key = null)
{
$out = '';
$index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$base = strlen($index);
if ($pass_key !== null) {
// Although this function's purpose is to just make the
// ID short - and not so much secure,
// with this patch by Simon Franz (http://blog.snaky.org/)
// you can optionally supply a password to make it harder
// to calculate the corresponding numeric ID
for ($n = 0; $n < strlen($index); $n++) {
$i[] = substr($index, $n, 1);
}
$pass_hash = hash('sha256',$pass_key);
$pass_hash = (strlen($pass_hash) < strlen($index) ? hash('sha512', $pass_key) : $pass_hash);
for ($n = 0; $n < strlen($index); $n++) {
$p[] = substr($pass_hash, $n, 1);
}
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
}
if ($to_num) {
// Digital number <<-- alphabet letter code
$len = strlen($in) - 1;
for ($t = $len; $t >= 0; $t--) {
$bcp = bcpow($base, $len - $t);
$out = $out + strpos($index, substr($in, $t, 1)) * $bcp;
}
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$out -= pow($base, $pad_up);
}
}
} else {
// Digital number -->> alphabet letter code
if (is_numeric($pad_up)) {
$pad_up--;
if ($pad_up > 0) {
$in += pow($base, $pad_up);
}
}
for ($t = ($in != 0 ? floor(log($in, $base)) : 0); $t >= 0; $t--) {
$bcp = bcpow($base, $t);
$a = floor($in / $bcp) % $base;
$out = $out . substr($index, $a, 1);
$in = $in - ($a * $bcp);
}
}
return $out;
}
Here is a small function that generates unique key randomly each time. It has very fewer chances to repeat same unique ID.
function uniqueKey($limit = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randstring = '';
for ($i = 0; $i < $limit; $i++) {
$randstring .= $characters[rand(0, strlen($characters))];
}
return $randstring;
}
I was looking for a very quick and simple solution (that was NOT intended to be secure in any way) that would replicate the actual hash code upon regeneration (unlike PHP's microtime unique id). This one did the trick.
http://php.net/manual/en/book.hash.php
So:
for($i=0;$i<count($myArray);$i++) {
$hashCode = "#".hash('crc32b', $myArray[$i]."-".$i);
}
Yields:
#179507fa
#8e9c5640
#f99b66d6
#67fff375
#10f8c3e3
etc
I won't pretend to know how all the different hash algorithms are used but for non-secure uses (like link #anchors) this is handy.
From comments on here:
<?php
function generateRandStr($length){
$randstr = "";
for($i=0; $i<$length; $i++){
$randnum = mt_rand(0,61);
if($randnum < 10){
$randstr .= chr($randnum+48);
}else if($randnum < 36){
$randstr .= chr($randnum+55);
}else{
$randstr .= chr($randnum+61);
}
}
return $randstr;
}
?>
Simply use:
generateRandStr(10);
Sample output: $%29zon(4f
You can mess around with this function to generate just alphanumeric, or just alphabetic characters.
For most purposes uniqid will suffice, if you need to make sure there's absolutely no clash, more convoluted measures are necessary.
all the methods above are good but make sure you know and not assume the generated string is unique. What I have done in the past is made a recursive function that checks the string against my database and if it is unique it returns the value, otherwise it just runs again. This will hardly happen though depending how long your unique string is. Its just good to know its unique.
<?php
function keygen(){
$chars = "bcdfghjklmnpqrstvwxyz";
$chars .= "BCDFGHJKLMNPQRSTVWXYZ";
$chars .= "0123456789";
while(1){
$key = '';
srand((double)microtime()*1000000);
for($i = 0; $i < 10; $i++){
$key .= substr($chars,(rand()%(strlen($chars))), 1);
}
break;
}
return $key;
}
//echo keygen();
?>