PHP Question about bitwise - php

I have a script like this
$job = array(
1 => 'User',
2 => 'Editor',
4 => 'Supervisor',
8 => 'Manager',
10 => 'President');
$value = 18;
foreach($job as $key => $val)
{
if($value & $key)
{
$job[] = $key;
echo $val.', ';
}
}
now what I want to achieve is that in the example above '18' system must only display the value as ' Manager, President, ' ( 10+8 = 18; )
But if you run the script, the system displays as Editor, President,
that's not all the arrays I've got, I have like 23 more array values
Sorry but I really can't explain it in english, hope you understand...

<?php
$flags = array(
1 => 'User', // 00000001 Note the place of the 1
2 => 'Editor', // 00000010
4 => 'Supervisor', // 00000100
8 => 'Manager', // 00001000
16 => 'President' // 00010000
);
// permission we're going for
$permissions = 16 + 8; // 00011000 again, note the 1's places.
$levels = array();
foreach ($flags as $level => $name)
{
if (($permissions & $level) == $level)
$levels[] = $name;
}
echo implode(', ', $levels);
Output:
Manager, President
You want your values in powers of 2 so that, in binary, the corresponding flag matches the permission you're specifying.
The bit-wise AND (&) is saying (when it reaches Manager):
$flags $Pemissions $flags
if (00001000 & 00011000) == 00001000
if (00001000) == 00001000 [true]
Basically, if the flag is set on both sides (both the value you're checking for and against) keep that bit. Then, make sure it still equals the value you're checking for. If it does, the user is set to that permission. An AND is only true when both the left and the right values have the bit(s) set.

The value for President is wrong. To use a bitfield all numbers must be powers of two, so President should be 16 and not 10.
Note that 16 (decimal) is 0x10 (hex) -- perhaps setting the President value to 10 (decimal) was a copy/paste error?

Your problem is that 10 is not a 2^y variable.
10 = 2+8 (2^1 and 2^3), and thus will match values 2 and 8 respectively.
If you want to allow bitwise selection like you do; you need to pick 2^y values for your indexes (1, 2, 4, 8, 16, 32, etc).
Then you can safely add those values as selectors (e.g. 16+4 (20), 2+32(34) etc).

you should change 10 to 16 (1,2,4,8,16,32, ..) or you may cannot understand some number. Is 10=10 or 10=2+8

Related

Get the sum of all carried digits while performing addition on 2 or more integers

I've got this task, which I honestly don't understand what exactly to do.
It my be because of my English level, or mathmatics level, but this is really something I can not make sense of. Could you help be at least to understand the task ?
My php knowledge is very well, at least I thought so...
The task is this :
"Carry" is a term of an elementary arithmetic. It's a digit that you transfer to column with higher significant digits when adding numbers.
This task is about getting the sum of all carried digits.
You will receive an array of two numbers, like in the example. The function should return the sum of all carried digits.
function carry($arr) {
// ...
}
carry([123, 456]); // 0
carry([555, 555]); // 3 (carry 1 from ones column, carry 1 from tens column, carry 1 from hundreds column)
carry([123, 594]); // 1 (carry 1 from tens column)
Support of arbitrary number of operands will be a plus:
carry([123, 123, 804]); // 2 (carry 1 from ones column, carry 1, carry 1 from hundreds column)
Background information on "carry": https://en.m.wikipedia.org/wiki/Carry_(arithmetic)
For this task, we don't actually need the numbers written under the equals line, just the numbers which are carried. Importantly, the carried numbers need to be used when calculating subsequent columns.
Before looping each column of integers, reverse the order of the columns so that looping from left-to-right also iterates the lowest unit column and progresses to higher unit columns (ones, then tens, then hundreds, etc).
For flexibility, my snippet is designed to handle numbers of dynamic length. If processing potential float numbers, you could merely multiply all number by a power of 10 to convert all values to integers. My snippet is not designed to handled signed integers.
Code: (Demo)
function sumCarries(array $array) {
$columns = ['carries' => []];
// prepare matrix of 1-digit integers in columns -- ones, tens, hundreds, etc
foreach ($array as $integer) {
$columns[] = str_split(strrev($integer));
}
// sum column values in ascending order and populate carry values
// subsequent column sums need to include carried value
for ($i = 0, $len = strlen(max($array)); $i < $len; ++$i) {
$columns['carries'][$i + 1] = (int)(array_sum(array_column($columns, $i)) / 10);
}
// sum all populated carry values
return array_sum($columns['carries']);
}
$tests = [
[123, 456], // no carries in any column
[555, 555], // 1 ones, 1 tens, 1 hundreds
[123, 594], // 1 tens
[123, 123, 804], // 1 ones, 1 hundreds
[99, 9, 99, 99, 99], // 4 ones, 4 hundreds
[9,9,9,9,9,9,9,9,9,9,9,9], // 10 ones
];
var_export(array_map('sumCarries', $tests));
Output:
array (
0 => 0,
1 => 3,
2 => 1,
3 => 2,
4 => 8,
5 => 10,
)
Since it's homework, I'm not going to fully answer the question, but explain the pieces you seem confused about so that you can put them together.
1 11 111 111 <- these are the carry digits
555 555 555 555 555
+ 555 -> + 555 -> + 555 -> + 555 -> + 555
----- ----- ----- ----- -----
0 10 110 1110
For a better example of two digits, let's use 6+6. To get the carry digit you can use the modulus operator where 12 % 10 == 2. So, (12 - (12 % 10)) / 10 == 1.
Thank you again. #Sammitch
I got it to make it work. Actually the problem was my English Math Level. The term "Carry digits" had no meaning at all for me. I was completely focusing on something else.
Here is my code : It may be far from perfect, but it does the job :)
function carry($arr) {
$sum_ones = 0;
$sum_tens = 0;
$sum_hunds = 0;
$arrCount = count($arr);
foreach($arr as $key){
$stri = (string)$key;
$foo[] = array(
"hunds" => $stri[0],
"tens" => $stri[1],
"ones" => $stri[2]
);
}
$fooCount = count($foo);
for($i=0; $i<$fooCount; $i++) {
$sum_ones+= $foo[$i]["ones"];
$sum_tens+= $foo[$i]["tens"];
$sum_hunds+= $foo[$i]["hunds"];
}
$sum1 = ($sum_ones - ($sum_ones % 10)) / 10;
$sum10 = ($sum_tens - ($sum_tens % 10)) / 10;
$sum100 = ($sum_hunds - ($sum_hunds % 10)) / 10;
return ($sum1 + $sum10 + $sum100);
}
$arr = array(555, 515, 111);
echo carry($arr);

Algorithm for a poker-style scoring system

What I need is to create five random integer (say rand(1,5)). Then, I generate a score based on these numbers. For instance, if I get a result of 1,2,3,4,5 then that would equal a zero score, but if I got 1,1,3,4,5 that would be 1 as we have a pair. Similar to a poker kind of scoring, so five of the same number would be a "full house" thus resulting in the highest score.
How would I go about the scoring system, even if it is just the mathematical equation?
More detail:
1-5 will hold separate images and then will be fought against "The House" which will have identical code to the user to determine the winner. Here's some example draws and the score they would receive:
1,2,3,4,5 = score 0
1,1,2,3,4 = score 1 (1 pair)
1,1,2,2,4 = score 2 (2 pair)
1,1,1,3,4 = score 3 (3 of a kind)
1,1,1,1,5 = score 4 (4 of a kind)
1,1,1,3,3 = score 5 (full house)
1,1,1,1,1 = score 6 (5 of a kind)
The combination of numbers is irreverent if they score 6 and the house scores 6, it's a tie.
if (isset($_POST['play'])) {
$rand1 = rand(1, 5);
$rand2 = rand(1, 5);
$rand3 = rand(1, 5);
$rand4 = rand(1, 5);
$rand5 = rand(1, 5);
if ($_POST['bet'] <= $user_data['coins']) {
if ($_POST['bet'] < 999999999) {
if ($_POST['bet'] > 0.99) {
if ($user_data['coins'] >= 1) {
$array = array($rand1,$rand2,$rand3,$rand4,$rand5);
print_r(array_count_values($array));
echo $rand1.', '.$rand2.', '.$rand3.', '.$rand4.', '.$rand5;
Array( // Here I don't understand
1 => 3,//
2 => 1,//
3 => 1 //
);
}
}
}
}
}
This outputs ; Array ( [5] => 2 [4] => 2 [1] => 1 ) 5, 5, 4, 4, 1
Use array_count_value function for this.
$array = array(1,1,1,2,5);
print_r(array_count_values($array));
Array(
1 => 3,
2 => 1,
3 => 1
);
Here's the approach I would consider, building on #Lele's answer. Warning: this is a bit confusing, so sit down with a cup of tea for this one.
Build a set of five buckets, [1] to [5], and scan a player's numbers, so that the count for each number is stored in the corresponding bucket
Then count the numbers you are left with into a new bucket system, with each position representing the number of counts you have for something.
So, if your score is this:
1 1 2 2 4
Then your first buckets are:
2 2 0 1 0
That's because you have two ones, two twos, and one four. And your second buckets are:
1 2 0 0 0
That's because you have two two-counts, and one one-count. Here, you disregard the first position (since a one-count for something does not score anything) and score for the others. So, test for two twos, and score that two.
If you score is this:
5 5 5 5 1
Then your first buckets are:
1 0 0 0 4
That's one one and four fives. So your second buckets are:
1 0 0 1 0
Your lookup table for this could be:
x 1 0 0 0 -> one pair
x 2 0 0 0 -> two pairs
x 0 1 0 0 -> three of a kind
x 1 1 0 0 -> full house
x 0 0 1 0 -> four of a kind
x 0 0 0 1 -> five of a kind
The 'x' means that you don't match on this. So, your lookup table matches four numbers to a score.
I was rather interested in this problem, so I have written some code to do the above. You'll still need to do the lookup table, but that is relatively trivial, and will be good practice for you. Here is a demo, with comments (run code here):
<?php
function counting(array $array) {
// Input figures
print_r($array);
// Run the figures twice through the bucket-counter
$firstBuckets = bucketCounter($array);
$secondBuckets = bucketCounter($firstBuckets);
// Ignore counts of 1
array_shift($secondBuckets);
// Output, just need to do the lookup now
echo ' converts to ';
print_r($secondBuckets);
echo "<br />";
}
/**
* Bucket counter
*/
function bucketCounter(array $array) {
$result = array(0, 0, 0, 0, 0, );
foreach($array as $value) {
if ($value > 0) {
$result[$value - 1]++;
}
}
return $result;
}
// Try some demos here!
counting(array(1, 2, 3, 4, 5));
counting(array(1, 1, 2, 4, 2));
counting(array(1, 1, 1, 1, 1));
?>
The demos I've included seem to work, but do hunt for bugs!
If the range is quite small, you can use counting sort approach. For each number, provide a "bucket" to count how many times a number appear. Scan once to fill in the buckets. Then another scan, but this time against the bucket to get the highest value. That's your score.

PHP Generate an unique hash from an IPADDRESS

I have a board and i want to disable registrations instead i want the users to be able to post freely with an unique ID based on their IPAddress
for example:
if "Joe" IP is 200.100.15.117, "Joe" nickname will become the hash function of 200.100.15.117
if "Joe" IP changes he will get another ID, that doesn't matter, i only want one unique ID per IPAddress
There are also two important things :
unique ID must be 8 characters long
hash should be secure, i don't want hackers be abble to crack my
users IPaddresses
I thought in using an MD5 function and then trim it to 8 chars, but how unique is that ? is there a better way ?
You can turn the ip address string to LONG(4 bytes) and do whatever you want next.
see the php function ip2long.
The thing with this is that you cant really diferentiate 2 persons on the same network, so if I post on there, and my dorm neighbour goes to the site, the site thinks he is me.
You should consider using cookies, these can easily be made unique, and AFAIK, can't be hacked remotely.
For the unique ID part, if you are storing the "users" in a database, you could just assign the primary key from your "user" table and use that in the cookie.
If it HAS to be 8 chars long, you could prepend 0's to the ID - e.g. 00000001, 00000002.... Atleast this way its unique, and 8 chars long-
EDIT According to OP comment on this answer.
Code below uses BC Math library to translate a MD5 hash into a Base-90 string. This converts a 32 char string into a 20 char one. This is far from desired 8 chars but it is the minimum string length using ASCII range (it is possible to increase the base to Base-94 by using chars ' " \ [space] but this does not affect to the string length and instead may cause problems while handling data).
$ip = '200.100.15.117';
$hash = md5($ip);
$chars16 = array(
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5,
'6' => 6, '7' => 7, '8' => 8, '9' => 9, 'a' => 10, 'b' => 11,
'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15
);
$base10 = '0';
for ($i = strlen($hash) - 1; $i > 0; $i--) {
$base10 = bcadd($base10, bcmul($chars16[$hash[$i]], bcpow(16, $i)));
}
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,;.:-_+*?!$%&##~^=/<>[](){}`';
$base = (string)strlen($chars);
$baseX = '';
while (bccomp($base10, $base) === 1 || bccomp($base10, $base) === 0) {
$baseX = substr($chars, bcmod($base10, $base), 1) . $baseX;
$base10 = preg_replace('/\.\d*$/', '', bcdiv($base10, $base));
}
$baseX = substr($chars, $base10, 1) . $baseX;
echo $baseX; // Shows: 1BS[JdZf/7J$J{ud&r5i
You can salt the ip address, e.g.
$salt = "e45re%$#";
$ip = "200.111.123.111";
$hash = md5($ip.$salt);
$hash = substr($hash,0,8);

How to pick random numbers from pot with some winning numbers?

i am trying to make a very simple game where i have 7 positions which are all hidden and within those there a 3 winning positions. I can pick randomly 3 times. I need to display whether the pick is a winning or not after every pick and store the result in a base.
Currently my thought where to generate an array of winning numbers on the first pick and then pick random number and check if it is in the winning array.
But i have a feeling that there is much more efficient way to do so.
Would appreciate if you would use PHP for coding examples, but pseudo code will do as-well.
EDIT
i am looking for the way to solve this without populating array with winning positions. maybe there is a way to do this with weights or probability percents.
Something like on first pick i have 3/7*100 percent chance to win. save result to base.
on second pick i have either 3/6*100 or 2/6*100 percent chance to win based weather i won in previous pick which i get from base.
Revised answer: this example does not require you to store the complete state of the game in a variable; instead, you just need to store the try count and won count:
$won = 0;
for($try = 0; $try < 3; $try++) {
$slots = array_fill(0, 7 - $try, 0); // blank slots
$lucky = array_fill(0, 3 - $won, 1); // lucky slots
$pot = array_replace($slots, $lucky); // make some of the slots lucky
$win = $pot[array_rand($pot)]; // randomly pick a slot
$won += $win == 1; // update won count
echo sprintf("Try %d: win=%d, total wins=%d\n", $try + 1, $win, $won);
}
Original answer:
$pot = array( // pot is (an associative) array; 0 = blank, 1 = win
"pos_1" => 0,
"pos_2" => 0,
"pos_3" => 0,
"pos_4" => 0,
"pos_5" => 0,
"pos_6" => 0,
"pos_7" => 0
);
$win = array_rand($pot, 3); // picks three indexes from the pot randomly
foreach($win as $w) {
$pot[$w] = 1; // set winning indicator
}
print_r($pot);
Output: array containing state of the pots.
Array
(
[pos_1] => 0
[pos_2] => 1
[pos_3] => 0
[pos_4] => 1
[pos_5] => 1
[pos_6] => 0
[pos_7] => 0
)
You can just save the positions of the winning numbers. This way you can always check their values using the [] operator for arrays. After all, you just pick the positions and not the numbers.
Update:
This way you even don't need to hide numbers. It's quite possible to have some more abstract "winning things" - characters, words, structures. However, it is important that you do not alter your array of hidden "things" in any way or at least update the stored winning positions accordingly if they stay the same. If that's not the case you'd naturally need to update the saved winning positions.
<?php
$arr = array(true, true, false, false, false, false, false);
shuffle($arr);
function pick($arr, $index) {
return isset($arr[$index]) && $arr[$index] === true;
}
var_dump($arr);
var_dump(pick($arr, 3));
var_dump(pick($arr, 5));
var_dump(pick($arr, 1));

Which data type is suitable for storing this situation?

For example, I have a user, and the user have different user right, for example, a user can have
-create file
-read file
-update file
-delete file
4 rights, I can use 4 BOOL to find the user right, but if the user have more right, I need create more and more BOOL to store the right. I don't think it is a good idea. And I think of getting a long integer for this... for example, the user can do all the stuff is 1111.
create file is 1000, read file is 100, update is 10, delete is 1. So, if the user only get read file right is 0100.
Is there any better ideas?? Thank you.
I suggest to convert privileges (rights) in binary-state-string and later, store it in database as long integer or hexadecimal string (VARCHAR; for storing a lot of rights).
Example
$privileges_list = array(
0 => 'create_file',
1 => 'read_file',
2 => 'update_file',
3 => 'delete_file',
4 => 'create_pool',
5 => 'vote_in_pool',
6 => 'create_gallery',
7 => 'upload_images',
8 => 'view_statistics'
);
So if you want to set create file, update file and view statistics rights to the user just put 1 on appropriate positions in string (0, 2, 8) and 0 for rest of them
$binary_string = "100000101";
Last character in this string is position 0, first is position 8.
Now you can convert this string to integer (261) or hex-number (105) and put it into database as privilege-set for that user (I prefer hex).
To convert this value back to privileges-list you can use something like this
function hexvalue2privileges($hexvalue, $plist) {
$res = array(); $res_assoc = array();
for ($i = strlen($hexvalue) - 1; $i >= 0; $i--) {
$bin = str_pad(decbin(hexdec(substr($hexvalue, $i, 1))), 4, '0', STR_PAD_LEFT);
$bin_array = array_reverse(str_split($bin, 1));
foreach ($bin_array as $bitstate) $res[] = $bitstate == '1' ? true : false;
}
foreach ($plist as $key => $id) {
$res_assoc[$id] = $res[$key];
}
return $res_assoc;
}
and call this function
print_r(hexvalue2privileges('105', $privileges_list));
Output will be
Array
(
[create_file] => 1 // true
[read_file] => // false
[update_file] => 1 // true
[delete_file] => // false
[create_pool] => // false
[vote_in_pool] => // false
[create_gallery] => // false
[upload_images] => // false
[view_statistics] => 1 // true
)
With every hexadecimal character you can store 4 rights so to calculate number of character needed use this formula
$chars_needed = floor((count($privileges_list)-1) / 4) + 1; // result 3
To get total length of binary-string
$binary_length = $chars_needed * 4; // result 12
To fix length of privileges set
$binary_string = "100000101";
$binary_string = str_pad($binary_string, $binary_length, '0', STR_PAD_LEFT);
// result '000100000101'
To convert $binary_string to hex
$binary_string = "000100000101";
$hexvalue = "";
$groups = str_split($binary_string, 4);
foreach ($groups as $group) $hexvalue .= dechex(bindec($group));
// result $hexvalue='105' (1=0001, 0=0000, 5=0101)
Also you can create groups of privileges and assign them to users by creating privileges-set for every group (administrators, moderators, guests, vip, etc).
Hope this helps
Use [flags CHAR(10) default '0000000000']
Whenever you need any digit, you can use it like
1st digit - New
2nd digit - Edit etc.
At the time of storing, you just need to change that bit and store like this 1100000000. That's it.

Categories