I have to pass an index to a function and from that index return a string containing from 1 to 4 chars.
I have:
$string = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
So if I say index(20) the function should return lower "k" because it is its index in the $string variable.
But...
If I type indexes
62 it should return 10
63 .. 11
That is because from index 62 it should start the string from 1, not 0, and loop until the next index, so they will be 2 length string
If I type indexes
3843 .. ZZ
That is because from index 3843 all the possibilities from [0-9a-zA-Z][0-9a-zA-Z] have ended and now the string starts with 3 length.
3844 .. 100
...
9999 .. 2Bh
All the possibilities and at [0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z] but I only need until index 9999
Your question is a bit hard to understand, not sure if this is what you want:
$string = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
function t($n)
{
global $string;
$ret = $string{$n%62};
if($n>=62)
$ret = t(floor($n/62)).$ret;
return $ret;
}
echo t(9999);
What you're asking for is basically a number base converter, going from base 10 (decimal) numbers to base 64 (using your string as the 64 digits in counting system).
PHP does provide a built-in base_convert() function, however while it works well for converting between say hexadecimal and decimal, it doesn't work for very high bases like this. This is partly because no-one generally has any need for them, and partly because the digits of such a base are not an agreed standard.
There are several examples in the comments of the base_convert() page linked above where people have written functions that do attempt to work for high bases. I can't vouch for any of them though.
Related
I am learning php myself with short exercises and I came through this small excercise to generate random 11 character string
public function Random_string()
{
$number = rand(10e12, 10e16);
echo base_convert($number, 10, 36);
//Sample output=9n4jfyh18v9
}
I thought the function generated string itself but when i use following values then it does not generate any string.
<?php
$number = "0977567";
echo base_convert($number,8,10);
//output=32631
?>
As far as i know rand() function generates random numbers while base_convert() function converts a number from one number base to another. In this process how does a string gets generated? It was very hard for me to understand this. It would be very nice if some could shed light on it. Thank you.
P.S. The first function shows error in PHP 7 but it completely works in PHP 5
You are right, rand(int $min, int $max) generates a random integer number between $min and $max, see documentation.
And base_convert(string $num, int $from_base, int $to_base) converts $num from $from_base to $to_base, but $num here is a string, because hexadecimal and other numbers above base 10 can contain characters as well, not only numbers, see documentation. That's also the reason why this functions returns a string, even if in some cases it won't actually contain any letters.
PHP also converts string to number if needed, for example next code will output 124 as int:
$a = "123";
var_dump($a+1);
In your first example, even if $number is an integer, PHP does the favour for you that it converts it into string, when you invoke base_convert from base 10 to 36.
In your second example, there is a problem, because the input $number="0977567" contains digit 9, and you want to convert it from base 8 to 10. But digit 9 does not exist in a base 8 number, only digits from 0 to 7. In this case PHP ignores invalid character 9, and converts only 077567 from base 8 to base 10, which happens to be 32631.
Please always check PHP warnings to catch issues like this. While learning and testing it is a good idea to set error_reporting(E_ALL); so you will get every message. Check documentation.
I made this function. It seemed it's working but when it comes to 20 digits number, the return value was 19. I'm wondering why this problem happen..
My function
function sumDigits($n) {
return strlen($n);
}
echo sumDigits(100); //3
echo sumDigits(1000); //4
echo sumDigits(12345); //5
echo sumDigits(1000000000); //10
echo sumDigits(145874589632); //12
echo sumDigits(0); //1
echo sumDigits(12345698745254856320); //19 <-- Why not 20?
Can you please somebody explain for me?
Thank you so much.
First, I would point out that the name of your function is misleading, as you are not really summing the values of the digits, but are counting the digits. So I would call your function countDigits instead of sumDigits.
The reason why it doesn't work for large numbers, is that the string representation will switch to scientific notation, so you're actually getting the length of "1.2345698745255E+19" not of "12345698745254856320"
If you are only interested in integers, you will get better results with the logarithm:
function countDigits($n) {
return ceil(log10($n));
}
For numbers that have decimals, there is no good solution, since the precision of 64-bit floating pointing point numbers is limited to about 16 significant digits, so even if you provide more digits, the trailing decimals will be dropped -- this has nothing to do with your function, but with the precision of the number itself. For instance, you'll find that these two literals are equal:
if (1.123456789123456789123456789 == 1.12345678912345678) echo "equal";
Because you function parameter is an integer, exceeding the limit.
If you dump it, it actually shows the following:
1.2345698745255E+19 - which is 19 letters.
If you would do the following, it will return 20 - mind the quotes, which declares the input as string.
echo sumDigits("12345698745254856320"); //19 <-- Why not 20? -> now will be 20
As per documentation, strlen() expects a string so a cast happens. With default settings you get 1.2345698745255E+19:
var_dump((string)12345698745254856320);
string(19) "1.2345698745255E+19"
The root issue is that PHP converts your integer literal to float because it exceeds PHP_INT_MAX so it cannot be represented as integer:
var_dump(12345698745254856320, PHP_INT_MAX);
In 64-bit PHP:
float(1.2345698745254857E+19)
int(9223372036854775807)
You could change display settings to avoid E notation but you've already lost precision at this point.
Computer languages that store integers as a fixed amount of bytes do not allow arbitrary precision. Your best chance is to switch to strings:
var_dump('12345698745254856320', strlen('12345698745254856320'));
string(20) "12345698745254856320"
int(20)
... and optionally use an arbitrary precision library such as BCMath or GMP if you need actual maths.
It's also important to consider that this kind of issues is sometimes a symptom that your input data is not really meant to be an integer but just a very long digit-only string.
In php is there a way to give a unique hash from a string, but that the hash was made up from numbers only?
example:
return md5(234); // returns 098f6bcd4621d373cade4e832627b4f6
but I need
return numhash(234); // returns 00978902923102372190
(20 numbers only)
the problem here is that I want the hashing to be short.
edit:
OK let me explain the back story here.
I have a site that has a ID for every registered person, also I need a ID for the person to use and exchange (hence it can't be too long), so far the ID numbering has been 00001, 00002, 00003 etc...
this makes some people look more important
this reveals application info that I don't want to reveal.
To fix point 1 and 2 I need to "hide" the number while keeping it unique.
Edit + SOLUTION:
Numeric hash function based on the code by https://stackoverflow.com/a/23679870/175071
/**
* Return a number only hash
* https://stackoverflow.com/a/23679870/175071
* #param $str
* #param null $len
* #return number
*/
public function numHash($str, $len=null)
{
$binhash = md5($str, true);
$numhash = unpack('N2', $binhash);
$hash = $numhash[1] . $numhash[2];
if($len && is_int($len)) {
$hash = substr($hash, 0, $len);
}
return $hash;
}
// Usage
numHash(234, 20); // always returns 6814430791721596451
An MD5 or SHA1 hash in PHP returns a hexadecimal number, so all you need to do is convert bases. PHP has a function that can do this for you:
$bignum = hexdec( md5("test") );
or
$bignum = hexdec( sha1("test") );
PHP Manual for hexdec
Since you want a limited size number, you could then use modular division to put it in a range you want.
$smallnum = $bignum % [put your upper bound here]
EDIT
As noted by Artefacto in the comments, using this approach will result in a number beyond the maximum size of an Integer in PHP, and the result after modular division will always be 0. However, taking a substring of the hash that contains the first 16 characters doesn't have this problem. Revised version for calculating the initial large number:
$bignum = hexdec( substr(sha1("test"), 0, 15) );
You can try crc32(). See the documentation at: http://php.net/manual/en/function.crc32.php
$checksum = crc32("The quick brown fox jumped over the lazy dog.");
printf("%u\n", $checksum); // prints 2191738434
With that said, crc should only be used to validate the integrity of data.
There are some good answers but for me the approaches seem silly.
They first force php to create a Hex number, then convert this back (hexdec) in a BigInteger and then cut it down to a number of letters... this is much work!
Instead why not
Read the hash as binary:
$binhash = md5('[input value]', true);
then using
$numhash = unpack('N2', $binhash); //- or 'V2' for little endian
to cast this as two INTs ($numhash is an array of two elements). Now you can reduce the number of bits in the number simply using an AND operation. e.g:
$result = $numhash[1] & 0x000FFFFF; //- to get numbers between 0 and 1048575
But be warned of collisions! Reducing the number means increasing the probability of two different [input value] with the same output.
I think that the much better way would be the use of "ID-Crypting" with a Bijectiv function. So no collisions could happen! For the simplest kind just use an Affine_cipher
Example with max input value range from 0 to 25:
function numcrypt($a)
{
return ($a * 15) % 26;
}
function unnumcrypt($a)
{
return ($a * 7) % 26;
}
Output:
numcrypt(1) : 15
numcrypt(2) : 4
numcrypt(3) : 19
unnumcrypt(15) : 1
unnumcrypt(4) : 2
unnumcrypt(19) : 3
e.g.
$id = unnumcrypt($_GET('userid'));
... do something with the ID ...
echo ' go ';
of course this is not secure, but if no one knows the method used for your encryption then there are no security reasons then this way is faster and collision safe.
The problem of cut off the hash are the collisions, to avoid it try:
return hexdec(crc32("Hello World"));
The crc32():
Generates the cyclic redundancy checksum polynomial of 32-bit lengths
of the str. This is usually used to validate the integrity of data
being transmitted.
That give us an integer of 32 bit, negative in 32 bits installation, or positive in the 64 bits. This integer could be store like an ID in a database. This don´t have collision problems, because it fits into 32bits variable, once you convert it to decimal with the hexdec() function.
First of all, md5 is basically compromised, so you shouldn't be using it for anything but non-critical hashing.
PHP5 has the hash() function, see http://www.php.net/manual/en/function.hash.php.
Setting the last parameter to true will give you a string of binary data. Alternatively, you could split the resulting hexadecimal hash into pieces of 2 characters and convert them to integers individually, but I'd expect that to be much slower.
Try hashid.
It hash a number into format you can define. The formats include how many character, and what character included.
Example:
$hashids->encode(1);
Will return "28630" depends on your format,
Just use my manual hash method below:
Divide the number (e.g. 6 digit) by prime values, 3,5,7.
And get the first 6 values that are in the decimal places as the ID to be used. Do a check on uniqueness before actual creation of the ID, if a collision exists, increase the last digit by +1 until a non collision.
E.g. 123456 gives you 771428
123457 gives you 780952
123458 gives you 790476.
My website use various resources from a single domain, for example:
http://static.example.com/javascript/common.js
http://static.example.com/javascript/common.css
http://static.example.com/javascript/menu/menu.js
http://static.example.com/javascript/menu/menu.css
http://static.example.com/images/1804/logo/02000100.jpg
http://static.example.com/images/1804/headers/main/09400060.png
http://static.example.com/images/1804/headers/home/1101/06900200-01.jpg
http://static.example.com/images/1804/headers/home/1101/06900200-02.jpg
I need a very simple string hashing function that maps these URLs to numbers, the numbers being 0, 1, 2 and 3. The algorithm should be deterministic and uniform. I have tagged the question PHP but a generic answer is acceptable.
You might have guessed why I need this; I plan to change the URLs to, for example:
http://0.static.example.com/javascript/common.js
http://2.static.example.com/javascript/common.css
I prefer doing a crc32 hash of the string, and taking its modulo with the limit.
Code:
function numeric_hash($str, $range) {
return sprintf("%u", crc32($str)) % $range;
}
Usage:
$str = "http://static.example.com/javascript/common.js
http://static.example.com/javascript/common.css
http://static.example.com/javascript/menu/menu.js
http://static.example.com/javascript/menu/menu.css
http://static.example.com/images/1804/logo/02000100.jpg
http://static.example.com/images/1804/headers/main/09400060.png
http://static.example.com/images/1804/headers/home/1101/06900200-01.jpg
http://static.example.com/images/1804/headers/home/1101/06900200-02.jpg";
$urls = explode("\n", $str);
foreach($urls as $url) {
echo numeric_hash($url, 4) . "\n";
}
Output:
1
3
3
3
1
3
1
3
If you have lots of URLs you should just a strong hashing and then take mod <noBuckets>
MD5(URL) % 4
If you have few URLs or you have uneven size or call frequency a "random" distribution might be bad and you should just create four lists and statically assign your URLs to each list, either manually or using some heuristic based on number of requests per URL.
I need a function in php that can generate a 5 char long string with numbers and a-z
What should I look into?
Other's have already provided you with correct answers, but here's a one-liner, just for the sake of it:
$code = substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyz'), 0, 5);
str_shuffle randomizes the above string, then substr takes the first 5 letters of that shuffled string. Simple.
As noted in comments for this answer, this function only generates strings that have only unique characters. If one would like to have the strings where even "aaaaa" is possible, here's a little function that allows just that:
function generate($len) {
return substr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstuvwxyz', $len)), 0, $len);
}
echo generate(5);
str_repeat repeats the 0-9a-z string $len times, so every letter has an almost equal possibility for every position. (Read the comments on why only "almost")
For kicks, here's an alternate approach:
//create a random base 36 string
$str = base_convert(rand(), 10, 36);
substr and concatenate as necessary to satisfy length requirements.
This will not give unique characters (e.g., 'aa11a' would be a possible output) -- which may or may not be what the OP wants. Also, the fact that you may need to run the function multiple times to get a string of the requested length means performance may not be spectacular, but if you're only calling this function once or twice, it won't matter.
Here's a more complete implementation:
function randstr($len) {
$currLen = 0;
$value = '';
while($currLen < $len) {
$new = base_convert(rand(), 10, 36);
$value .= $new;
$currLen += strlen($new);
}
//$value may be longer than the requested $len
return substr($value, 0, $len);
}
It's also worth noting that this string will be of less-than-perfect randomness -- the first char of each string output by base_convert will have a bias toward the lower end of the spectrum (as rand() will not completely fill a whole char's worth of bits every time). Ideally, you want a number of bits out of rand that will exactly fill some number of base-36 chars.
Using a source of entropy that gives you more bits than you need for the string in the first place (like /dev/urandom) would resolve this issue. But for most applications, the loss of entropy won't matter enough to justify the overhead of reading /dev/urandom.
Alternately, you could simply throw away the first char of each base_convert() call.
Here’s some example generator:
$length = 5;
$charset = '0123456789abcdefghijklmnopqrstuvwxyz';
$str = '';
while ($length--) {
$str .= $charset[rand() % count($charset)];
}
Do you mean a random string?
If so, simply create a string containing the 36 characters, generate 5 random numbers and create the string based on the character positions (pseudo-code):
string src = "0123456789abcdefghijklmnopqrstuvwxyz"
string dst = ""
for i = 1 to 5:
dst = dst + src[random(len(src))]
If you want 5 unique characters, you do the same thing but with one slight difference.
Generate the first random number in the range 0 through 35, the second in the range 0 through 34 and so on.
Then, as you add the character from the source string to your own string, replace the used character in the source string with the last character in the source string. This will prevent the same character from being selected twice:
string src = "0123456789abcdefghijklmnopqrstuvwxyz"
int srclen = len(src)
string dst = ""
for i = 1 to 5:
idx = randon(len(src))
dst = dst + src[idx]
src[idx] = src[srclen-1]
srclen = srclen - 1
Aside: #Tatu has provided a simple solution using str_shuffle which is a more elegant way of doing that last method (unique characters) but I'm not convinced it's the most efficient way since it's likely to involve a lot of swaps to get a decent shuffle. The method here seems to me to be more likely to be faster.
Keep that in mind if performance is important, but also keep in mind that I haven't tested how good it is - it may be fast enough - it may even blow my solution out of the water :-) As with all performance-related things, measure, don't guess.
http://php.net/manual/en/function.chr.php, in case you dislike hardcoding the alphabet.
Yet another approach, for funsies. Could be shortened - here it's a bit verbose, for readability.
function rand_string($length=5) {
$values = str_split("abcdefghijklmnopqrstuvwxyz0123456789"));
shuffle($values);
$values = array_flip($values);
$string = implode(array_rand($lenght));
return $string;
}