Working with array value instances - php

I'm making a simple yahtzee script in PHP, I've got up the final point where it checks the 5 dice for a result at the end.
The 5 dice sides are stored in an array, example $dice (2,5,2,7,8)
I'm not that experienced in working with arrays, but is there easier ways to compare each number, to like find instances of 2 the same, 3 the same, all the same etc?
array_search() ?

array_count_values() might be a function that's worth looking at. It will count the number of instances of a value in an array.
Example:
$dice = array(2,5,2,7,7);
$count = array_count_values($dice);
if($n = array_keys($count, 2))
{
// 2 of a kind
// $n = array(2, 7)
}
if(array_keys($count, 4))
{
// 4 of a kind
}
if(array_keys($count, 2) && array_keys($count, 3))
{
// Full House
}

Just count them.
for ($i=0; $i<count($dice); $i++)
$counter[$dice[$i]]++;

Use:
var_dump(array_count_values($array));

Related

Numbers to letters with logical sequence

I have this array which links numbers to letters at the moment like this:
1-26 = A-Z
But there is more, 27=AA and 28=AB etc...
so basically when I do this:
var_dump($array[2]); //shows B
var_dump($array[29]); //shows AC
Now this array I made myself but it's becoming way too long. Is there a way to actually get this going on till lets say 32? I know there is chr but I dont think I can use this.
Is there an easier way to actually get this without using this way too long of an array?
It's slower calculating it this way, but you can take advantage of the fact that PHP lets you increment letters in the same way as numbers, Perl style:
function excelColumnRange($number) {
$character = 'A';
while ($number > 1) {
++$character;
--$number;
}
return $character;
}
var_dump(excelColumnRange(2));
var_dump(excelColumnRange(29));
here is the code which you are looking for :
<?php
$start = "A";
$max = 50;
$result = array();
for($i=1; $i<=$max; $i++) {
$result[$i] = $start++;
}
print_r($result);
?>
Ref: http://www.xpertdeveloper.com/2011/01/php-strings-unusual-behaviour/
This should work for you:
Even without any loops. First I calculate how many times the alphabet (26) goes into the number. With this I define how many times it has to str_repleat() A. Then I simply subtract this number and calculate the number in the alphabet with the number which is left.
<?php
function numberToLetter($number) {
$fullSets = (($num = floor(($number-1) / 26)) < 0 ? 0 : $num);
return str_repeat("A", $fullSets) . (($v = ($number-$fullSets*26)) > 0 ? chr($v+64) : "");
}
echo numberToLetter(53);
?>
output:
AAA

Loop inside loop

I have this difficult task (for me at least).
This is abstract question, mathematical i'd say.
Let's assume I have 2 inputs:
a keyword (string)
a number (deepness level)
and submit button.
This keyword returns me 8 other keywords from a database that are similar to this string.
And for each of that 8 keywords I need to call same function that will return me another 8 similar keywords of all these 8 strings I have already returned.
Here comes the "level" number. I need to go deeper inside every of returned string depending on level number I entered.
For example: If the level number is 2, then we will call the function 9 times. First time for the original keyword, and 8 times for each returned keyword.
If the level number is 3, then the function will be called 73 times. Like in the previous example, but plus for another 8 keywords we have returned. I think there will be a couple of loops inside loops, but can't figure it out by myself. Will appreciate your suggestions.
Here's the main code that I have wrote which is probably unsufficient:
$keywords = preg_split('/$\R?^/m', trim($_POST['keyword']));
$keywords = array_map('trim', $keywords);
$level = $_POST['level'];
if (!$level || $level < 2) {
echo '<b>Level was either 1 or null</b>';
}
foreach ($keywords as $keyword) {
$results = getResults($keyword);
if ($level && $results) {
for ($i = 0; $i < sizeof($results); $i++) {
$results1 = getResults($results[$i]);
for ($j = 0; $j < $level; $j++) {
$results1 = getResults($results1[$i])
}
}
}
}
The output should be something like this:
1->
2
->
3
3
3
3
3
3
3
3
2->
2->
2->
2->
You need to understand what recursion means and how you can use it in your code. Basically you need to call the same function inside itself, n times where n is the deepness level of your request.
Start with some little examples like Fibonacci series, and you will find the way to implement your function.
All is based on a condition ($deepness > 0).
Here's a little suggestion (in pseudocode) based on what I understood.
function findSimilar($words,$deepness) {
if($deepness == 0) {
return similarWordsOf($words);
} else {
return similarWordsOf(findSimilar($words,$deepness -1));
}
}
As others have already pointed out, the key to the solution is to use a recursive function, i.e. a function that calls itself again for each of the similar words using a decreased deepness value.
My PHP is a bit rusty, so here's some pseudo code (aka Python):
def printSimilar(word, deepness=1, indent=0):
print (" " * indent) + word
if deepness > 0:
for similar in similarWords(word):
printSimilar(similar, deepness - 1, indent + 1)
Assuming that similarWords returns the list of similar words, this should result in the desired output.

Reading CHMOD like values with php

# Permission
7 read and write and execute (1+2+4)
6 read and write (2+4)
5 read and execute (1+4)
4 read only
3 write and execute (1+2)
2 write only
1 execute only
0 none
I like the pattern that you can store any combination of the options options in one integer number, and add options by doubling the last number (8, 16 ,32 etc).
I'd like to use this method, and I'd like to know if there's a name for it, and what is the fastest simplest method for turning numbers into results similar to this?
array(1=>false,2=>true,4=>true);//6
array(1=>true,2=>true,4=>true,8=>true);//15
Using bitwise operations as recommended. To get the array you are after.
This function will figure out the number of bit required for any value given and return the array in the format you suggested.
I've included a loop for 0 to 20 to verify.
<?php
function getBitArray($value) {
$numberOfBits = ceil(log($value + 1, 2));
$result = array();
$bit = 1;
for($i = 0; $i < $numberOfBits; $i++) {
$result[$bit] = ($value & $bit) == $bit;
$bit <<= 1;
}
return $result;
}
for($i = 0; $i < 20; $i++)
var_dump(getBitArray($i));
That's generally referred to as a bit field, and you can work with it using bitwise operators.
This method is known as bitwise operation, and is used in php like this. Here is a nice tutorial.

Project Euler || Question 10

I'm attempting to solve Project Euler in PHP and running into a problem with my for loop conditions inside the while loop. Could someone point me towards the right direction? Am I on the right track here?
The problem, btw, is to find the sums of all prime numbers below 2,000,000
Other note: The problem I'm encountering is that it seems to be a memory hog and besides implementing the sieve, I'm not sure how else to approach this. So, I'm wondering if I did something wrong in the implementation.
<?php
// The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
// Additional information:
// Sum below 100: 1060
// 1000: 76127
// (for testing)
// Find the sum of all the primes below 2,000,000.
// First, let's set n = 2 mill or the number we wish to find
// the primes under.
$n = 2000000;
// Then, let's set p = 2, the first prime number.
$p = 2;
// Now, let's create a list of all numbers from p to n.
$list = range($p, $n);
// Now the loop for Sieve of Eratosthenes.
// Also, let $i = 0 for a counter.
$i = 0;
while($p*$p < $n)
{
// Strike off all multiples of p less than or equal to n
for($k=0; $k < $n; $k++)
{
if($list[$k] % $p == 0)
{
unset($list[$k]);
}
}
// Re-initialize array
sort ($list);
// Find first number on list after p. Let that equal p.
$i = $i + 1;
$p = $list[$i];
}
echo array_sum($list);
?>
You can make a major optimization to your middle loop.
for($k=0; $k < $n; $k++)
{
if($list[$k] % $p == 0)
{
unset($list[$k]);
}
}
By beginning with 2*p and incrementing by $p instead of by 1. This eliminates the need for divisibility check as well as reducing the total iterations.
for($k=2*$p; $k < $n; $k += $p)
{
if (isset($list[k])) unset($list[$k]); //thanks matchu!
}
The suggestion above to check only odds to begin with (other than 2) is a good idea as well, although since the inner loop never gets off the ground for those cases I don't think its that critical. I also can't help but thinking the unsets are inefficient, tho I'm not 100% sure about that.
Here's my solution, using a 'boolean' array for the primes rather than actually removing the elements. I like using map,filters,reduce and stuff, but i figured id stick close to what you've done and this might be more efficient (although longer) anyway.
$top = 20000000;
$plist = array_fill(2,$top,1);
for ($a = 2 ; $a <= sqrt($top)+1; $a++)
{
if ($plist[$a] == 1)
for ($b = ($a+$a) ; $b <= $top; $b+=$a)
{
$plist[$b] = 0;
}
}
$sum = 0;
foreach ($plist as $k=>$v)
{
$sum += $k*$v;
}
echo $sum;
When I did this for project euler i used python, as I did for most. but someone who used PHP along the same lines as the one I did claimed it ran it 7 seconds (page 2's SekaiAi, for those who can look). I don't really care for his form (putting the body of a for loop into its increment clause!), or the use of globals and the function he has, but the main points are all there. My convenient means of testing PHP runs thru a server on a VMWareFusion local machine so its well slower, can't really comment from experience.
I've got the code to the point where it runs, and passes on small examples (17, for instance). However, it's been 8 or so minutes, and it's still running on my machine. I suspect that this algorithm, though simple, may not be the most effective, since it has to run through a lot of numbers a lot of times. (2 million tests on your first run, 1 million on your next, and they start removing less and less at a time as you go.) It also uses a lot of memory since you're, ya know, storing a list of millions of integers.
Regardless, here's my final copy of your code, with a list of the changes I made and why. I'm not sure that it works for 2,000,000 yet, but we'll see.
EDIT: It hit the right answer! Yay!
Set memory_limit to -1 to allow PHP to take as much memory as it wants for this very special case (very, very bad idea in production scripts!)
In PHP, use % instead of mod
The inner and outer loops can't use the same variable; PHP considers them to have the same scope. Use, maybe, $j for the inner loop.
To avoid having the prime strike itself off in the inner loop, start $j at $i + 1
On the unset, you used $arr instead of $list ;)
You missed a $ on the unset, so PHP interprets $list[j] as $list['j']. Just a typo.
I think that's all I did. I ran it with some progress output, and the highest prime it's reached by now is 599, so I'll let you know how it goes :)
My strategy in Ruby on this problem was just to check if every number under n was prime, looping through 2 and floor(sqrt(n)). It's also probably not an optimal solution, and takes a while to execute, but only about a minute or two. That could be the algorithm, or that could just be Ruby being better at this sort of job than PHP :/
Final code:
<?php
ini_set('memory_limit', -1);
// The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.
// Additional information:
// Sum below 100: 1060
// 1000: 76127
// (for testing)
// Find the sum of all the primes below 2,000,000.
// First, let's set n = 2 mill or the number we wish to find
// the primes under.
$n = 2000000;
// Then, let's set p = 2, the first prime number.
$p = 2;
// Now, let's create a list of all numbers from p to n.
$list = range($p, $n);
// Now the loop for Sieve of Eratosthenes.
// Also, let $i = 0 for a counter.
$i = 0;
while($p*$p < $n)
{
// Strike off all multiples of p less than or equal to n
for($j=$i+1; $j < $n; $j++)
{
if($list[$j] % $p == 0)
{
unset($list[$j]);
}
}
// Re-initialize array
sort ($list);
// Find first number on list after p. Let that equal p.
$i = $i + 1;
$p = $list[$i];
echo "$i: $p\n";
}
echo array_sum($list);
?>

Random ID/Number Generator in PHP

I am building a list of "agent id's" in my database with the following requirements:
The ID must be 9 digits long (numeric only)
The ID may not contain more than 3 of the same number.
The ID may not contain more than 2 of the same number consecutively (i.e. 887766551; cannot have 888..)
So far I have part 1 down solid but am struggling with 2 and 3 above. My code is below.
function createRandomAGTNO() {
srand ((double) microtime( )*1000000);
$random_agtno = rand(100000000,900000000);
return $random_agtno;
}
// Usage
$NEWAGTNO = createRandomAGTNO();
Any ideas?
Do not re-seed the RNG on every call like that, unless you want to completely blow the security of your random numbers.
Unless your PHP is very old, you probably don't need to re-seed the RNG at all, as PHP seeds it for you on startup and there are very few cases where you need to replace the seed with one of your own choosing.
If it's available to you, use mt_rand instead of rand. My example will use mt_rand.
As for the rest -- you could possibly come up with a very clever mapping of numbers from a linear range onto numbers of the form you want, but let's brute-force it instead. This is one of those things where yes, the theoretical upper bound on running time is infinite, but the expected running time is bounded and quite small, so don't worry too hard.
function createRandomAGTNO() {
do {
$agt_no = mt_rand(100000000,900000000);
$valid = true;
if (preg_match('/(\d)\1\1/', $agt_no))
$valid = false; // Same digit three times consecutively
elseif (preg_match('/(\d).*?\1.*?\1.*?\1/', $agt_no))
$valid = false; // Same digit four times in string
} while ($valid === false);
return $agt_no;
}
For second condition, you can create an array like this
$a = array( 0,0,1,1,2,2,3,3.....,9,9 );
and get random elements: array_rand() (see manual) to get digit, append it to your ID and remove value from source array by unsetting at index.
Generally, this solving also third condition, but this solution excludes all ID's with possible and acceptable three digits
The first solution that comes to mind is a recursive function that simply tests your three requirements and restarts if any three of them fail. Not the most efficient solution but it would work. I wrote an untested version of this below. May not run without errors but you should get the basic idea from it.
function createRandomAGTNO(){
srand ((double) microtime( )*1000000);
$random_agtno = rand(100000000,900000000);
$random_agtno_array = explode('', $random_agtno);
foreach($random_agtno_array as $raa_index => $raa){
if($raa == $random_agtno_array[$raa_index + 1] && raa == $random_agtno_array[$raa_index + 2]) createRandomAGTNO();
$dup_match = array_search($raa, $random_agtno_array);
if($dup_match){
unset($random_agtno_array[$dup_match]);
if(array_search($raa, $random_agtno_array)) createRandomAGTNO();
};
}
return $random_agtno;
}
Try this code:
<?php
function createRandomAGTNO() {
//srand ((double) microtime( )*1000000);
$digits = array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ,1, 2, 3, 4, 5, 6, 7, 8, 9, 0 );
shuffle($digits);
$random_agtno = 0;
for($i = 0; $i < 9; $i++)
{
if($i == 0)
{
while($digits[0] == 0)
shuffle($digits);
}
/*if($i >= 2)
{
while(($random_agtno % 100) == $digits[0])
shuffle($digits);
}*/
$random_agtno *= 10;
$random_agtno += $digits[0];
array_splice($digits, 0, 1);
}
return $random_agtno;
}
for($i = 0; $i < 1000; $i++)
{
$NEWAGTNO = createRandomAGTNO();
echo "<p>";
echo $NEWAGTNO;
echo "</p>";
}
?>
Good luck!
Edit:
Removed the call to srand() and commented-out the "if($i >= 2)" code, which is impossible anyway, here.

Categories