How does this code generate the proper sequence of characters? - php

The code in question comes from MathGuard, a PHP anti-spam CAPTCHA script that requires the user to answer a simple math problem. It displays the digits and operator symbols as 3x5 matrices of random characters. I understand how the code works in the sense that I can follow the code and understand what it's doing; I just don't understand how one would come to this solution.
This function takes an integer that describes one line of the 3x5 matrix and converts it into a line of random characters:
function decToBin($dec) {
$pattern = "123456789ABCDEFGHIJKLMNOPQRTSTUWXYZ";
$output = " ";
$i = 0;
do {
if ($dec % 2) {
$rand = rand() % 34;
$output { 2 - $i } = $pattern { $rand };
} else {
$output { 2 - $i } = " ";
}
$dec = (int) ($dec / 2);
$i++;
} while ($dec > 0);
$output = str_replace(" ", " ", $output);
return $output;
}
Here are the digit descriptors:
$number = array (
array ( 7, 5, 5, 5, 7 ), // 0
array ( 2, 6, 2, 2, 7 ), // 1
array ( 7, 1, 7, 4, 7 ), // 2
array ( 7, 1, 7, 1, 7 ), // 3
array ( 4, 5, 7, 1, 1 ), // 4
array ( 7, 4, 7, 1, 7 ), // 5
array ( 7, 4, 7, 5, 7 ), // 6
array ( 7, 1, 1, 1, 1 ), // 7
array ( 7, 5, 7, 5, 7 ), // 8
array ( 7, 5, 7, 1, 7 ) // 9
);
My question is: how does one come to this conclusion and method of generation and know that, for example, 7 will generate a full line of random characters, and 5 only the outermost characters?
Is this just a form of code obfuscation? What makes this method better than, say, storing the digits as a string (111101101101111 as 0, for example) and replacing each 1 with a random character?

Looks like a simple bitmap to me.
7 = 1 1 1
5 = 1 0 1
5 = 1 0 1
5 = 1 0 1
7 = 1 1 1
2 = 0 1 0
6 = 1 1 0
2 = 0 1 0
2 = 0 1 0
7 = 1 1 1

Related

How to right align numbers from an array in php?

I have a problem with aligning numbers from a multidimensional array. I want to print the following result:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
And I want all of the numbers to be aligned with the second digit of the next rows. However my result is that:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
I did this in C# by using:
for (int col = 0; col < matrix.GetLength(1); col++)
{
Console.Write("{0,4}", matrix[row, col]);
}
But how can I receive this result in PHP?
You can use str_pad
$arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
$test = '';
foreach ($arr as $key => $value) {
if ($key % 4 == 0) {
$test .= "\n";
}
$test .= str_pad($value, 4, ' ', STR_PAD_LEFT);
}
echo "<pre>$test</pre>";
The result would be:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

Advancing index keys in an array

I have an array with numerical incrementing keys starting at 0, like 0 1 2 3 4 5 ....etc. I need to assign new keys to the array in the following manner
The first three keys keep their index number
Every 4th key gets incremented by 4
The two keys after the fourth gets incremented by 1again
I know I need to use a foreach loop ( seems the simplest way anyway ) to build a new array with newly assigned keys. My problem is the calculation of the keys
HERE IS WHAT I HAVE SEEN IN RELATION
Here is my current array keys
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
and this is what I need them to be
0 1 2 6 7 8 12 13 14 18 19 20 24 25 26 30
Lets break this down into 3 key groups with the old array key on the top and the new array key at the bottom
1st group
0 1 2
0 1 2
2nd group
3 4 5
6 7 8
3rd group
6 7 8
12 13 14
4th group
9 10 11
18 19 20
etc etc......
When you look at the relationship between the two array keys, you'll see the following:
The old array keys 0, 1 and 2 keep their key value
The old array keys 3, 6, 9 etc, if multiplied by 2, gives you the new array key value
The old array keys 4, 7, 10 etc, if multiplied by 2 and you subtract 1 from that total, you get the new array key for that specific key
The old array keys 5, 8, 11 etc, if multiplied by 2 and you subtract 2 from the total, you get the new array key for that specific key
Alternatively, if you subtract the old key from the new key, you get the following answer
0 for the 1st group
3 for the 2nd group
6 for the 3rd group
9 for the 4th group
etc etc.....
POSSIBLE SOLUTION
The only solution I can think of is to use the modulus operator (inside my foreach loop), check the current key against the modulus result and then calculate my new key
Example:
if ( $key%3 == 0 && $key != 0 ) {
$new_key = $key * 2;
} elseif ( ( $key - 1 ) %3 == 0 && $key != 1 ) {
$new_key = ( $key * 2 ) - 1;
} elseif ( ( $key - 2 ) %3 == 0 && $key != 2 ) {
$new_key = ( $key * 2 ) - 2;
} else {
$new_key = $key;
}
$new_array[$new_key] = $value;
MY QUESTION
Isn't there a smarter more mathematical way of doing this?
Try the following:
$new_key = floor($old_key / 3) * 3 + $old_key
If you use modulous you can generate a sequence with items missing. The below code demonstrates how to do that.
for ($x = 0; $x < 40; $x++)
{
echo ("$x, " . ($x % 6) . ", " . ($x % 6 < 3? "true": "false") . "\n");
}
The output is:
0, 0, true
1, 1, true
2, 2, true
3, 3, false
4, 4, false
5, 5, false
6, 0, true
7, 1, true
8, 2, true
9, 3, false
10, 4, false
11, 5, false
12, 0, true
13, 1, true
14, 2, true
15, 3, false
16, 4, false
17, 5, false
18, 0, true
19, 1, true
20, 2, true
21, 3, false
22, 4, false
23, 5, false
24, 0, true
25, 1, true
26, 2, true
27, 3, false
28, 4, false
29, 5, false
30, 0, true
31, 1, true
32, 2, true
33, 3, false
34, 4, false
35, 5, false
36, 0, true
37, 1, true
38, 2, true
39, 3, false
If you use $x as a key only when $x % 6 < 3 you can get the sequence you want.
$array = range(1,15);
For ($i=0; $i<count($array); $i++) echo $keys[] = $i + floor($i/3)*3;
$new_array = array_combine($keys, $array);
var_dump($new_array);

How to make a graphic using numbers with PHP [closed]

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 8 years ago.
Improve this question
I need to break a number in a table field and turn it into an array.
input_table
+------+---------+
| id | number |
+------+---------+
| 1 | 7 |
| 2 | 8 |
+------+---------+
for the id 1 (which has 7 number) the output what I need in php is:
1 2 3 4 5 6 7
2 3 4 5 6
3 4 5
4
How to do this?
for ($q=1; $q <= $obj->number ; $q++) {
echo "$q";
//This only turn 1, 2, 3, 4, 5, 6, 7
}
try something like this.
$set = array( 7, 8 );
echo '<pre>';
foreach( $set as $number ){
//assuming number is your INT
$array = range( 1, $number );
while( count( $array ) ){
echo "\n";
var_export( $array );
//remove first element
array_shift( $array );
//remove last element
array_pop($array);
}
}
Outputs:
For 7
array (
0 => 1,
1 => 2,
2 => 3,
3 => 4,
4 => 5,
5 => 6,
6 => 7,
)
array (
0 => 2,
1 => 3,
2 => 4,
3 => 5,
4 => 6,
)
array (
0 => 3,
1 => 4,
2 => 5,
)
array (
0 => 4,
)
For 8
array (
0 => 1,
1 => 2,
2 => 3,
3 => 4,
4 => 5,
5 => 6,
6 => 7,
7 => 8,
)
array (
0 => 2,
1 => 3,
2 => 4,
3 => 5,
4 => 6,
5 => 7,
)
array (
0 => 3,
1 => 4,
2 => 5,
3 => 6,
)
array (
0 => 4,
1 => 5,
)
I'll leave it to you to build a multi-dimensional array out of that.
If you just want the output, than use echo implode(' ', $array ); in place of var_export(). Such as this:
$set = array( 7, 8 );
echo '<div style="text-align:center">';
foreach( $set as $number ){
//assuming number is your INT
$array = range( 1, $number );
while( count( $array ) ){
echo implode(' ', $array );
//remove first element
array_shift($array);
//remove last element
array_pop($array);
echo '<br>';
}
echo '<br>';
}
echo '</div>';
Outputs:
1 2 3 4 5 6 7
2 3 4 5 6
3 4 5
4
1 2 3 4 5 6 7 8
2 3 4 5 6 7
3 4 5 6
4 5
<?php
$n = 7; //or whatever you want
echo '<div style="text-align:center">';
for($i=0;$i<=round($n/2,0);$i++){
for($j=$i; $j<$n-$i;$j++){
echo ($j+1).' ';
}
echo "<br />\n";
}
echo '</div>';

Randomise array of numbers with disqualifying conditions

I wish to create an array with random sequences that correspond to my conditions.
The sequence should be composed of 8 different numbers from 1-8.
I should not have more than 2 successive numbers in the first 4 or last 4 numbers. Ex: 1,2,3 is not good nor 5,3,4 because they are successive if sorted (3,4,5).
This is a good example: 1,4,5,7, | 8,6,3,2
This is NOT a good example: 1,3,2,6, | 5,7,8,4 because 1,3,2 are successive numbers if sorted (1,2,3) in the first 4 digits
I made this:
$sequences = array();
while(count($sequences) < 100){
//Random 8 numbers sequence from 1-8
$sequence = array();
while(count($sequence) < 8){
$rand = rand(1,8);
if(!in_array($rand, $sequence)){
array_push($sequence, $rand);
}
}
//Insert if numbers are not successive.
//Struggling here
if(?????){
array_push($sequences, $sequence);
}
}
print_r($sequences);
It's working for the generation part of it but I can't figure how to insert sequences that do not contain successive numbers. Any thoughts?
You can represent the two groups of digits as a 1 or 0 and pick which elements go in each set by picking one of the 8 bit binary strings with four 1s and 0s and no sequences of 3 consecutive 1s or 0s. Each of these binary strings representes by one of the following integers:
43 45 51 53 54 75 77 83 85 86 89 90 101 102 105 106 108
147 149 150 153 154 165 166 169 170 172 178 180 201 202 204 210 212
The second half are all symmetric with the first half, so we can just pick a number from the first half and then do some processing to pick the whole set:
$combinations = array(43,45,51,53,54,75,77,83,85,86,89,90,101,102,105,106,108);
// Pick a random combination
$combination = $combinations[array_rand($combinations)];
if (mt_rand() & 1)
$combination = 255 - $combination;
$combination = str_split(str_pad(decbin($combination), 8, '0', STR_PAD_LEFT));
// Get the first four values
$first = array_keys(array_filter($combination, function($x){ return $x == '0'; }));
shuffle($first); // Permute them
// Get the last four values
$last = array_keys(array_filter($combination, function($x){ return $x == '1'; }));
shuffle($last); // Permute them
$result = array_map(function($x){ return $x + 1; }, array_merge($first, $last));
This will a random sequence with your constraints uniformly at random and should be quite efficient. Some sample outputs:
[6, 2, 3, 8, 1, 4, 5, 7]
[1, 6, 2, 4, 3, 5, 8, 7]
[6, 3, 1, 8, 2, 4, 5, 7]
[5, 8, 4, 2, 7, 1, 6, 3]
[5, 2, 8, 1, 3, 6, 7, 4]
[8, 6, 2, 4, 7, 1, 3, 5]
[7, 1, 5, 4, 8, 3, 2, 6]
[5, 1, 3, 6, 2, 7, 8, 4]
[1, 7, 2, 5, 3, 8, 4, 6]
[5, 6, 3, 8, 2, 7, 4, 1]
This is probably an ineffective way (in terms of resources) and could be more considered as a hack, but;
$sequence = array(1,2,4,5,8,9,5,7);
usort($sequence, function($a, $b) {
//if $a + 1 = $b, then $b + 1;
return ($a + 1) == $b ? $b = $b + 1 : 1;
});
print_r($sequence);
This would return;
Array ( [0] => 5 [1] => 8 [2] => 2 [3] => 1 [4] => 4 [5] => 9 [6] => 5 [7] => 7 )
It's not really a beautiful code, I could shorten it but still, it's working.
$sequence = array();
$sequences = array();
$loops = 0;
while($loops < 10000){
$loops++;
//Get a sequence
while(count($sequence) < 8){
$rand = rand(1,8);
if(!in_array($rand, $sequence)){
array_push($sequence, $rand);
}
}
//Checks if sequence meet requirements
//This part could be summarized in a function
$first = array($sequence[0], $sequence[1], $sequence[2],$sequence[3]);
$last = array($sequence[4], $sequence[5], $sequence[6],$sequence[7]);
sort($first);
sort($last);
if($first[1] != $first[0]+1 || $first[1] !=$first[2]-1){
if($first[2] != $first[1]+1 || $first[2] !=$first[3]-1){
if($last[1] != $last[0]+1 || $last[1] !=$last[2]-1){
if($last[2] != $last[1]+1 || $last[2] !=$last[3]-1){
$sequence = array_merge($first, $last);
if(!in_array($sequence, $sequences)){
array_push($sequences, $sequence);
}
}
}
}
}
$sequence = array();
}
print_r($sequences);

PHP Specific sort of numbers

I have the following type of array :
$foo = array(
"a" => 1,
"b" => 1,
"c" => 2,
"d" => 2,
"e" => 3,
"f" => 3,
"g" => 4,
"h" => 4,
"i" => 5,
"j" => 5,
"k" => 10,
"l" => 12,
"m" => 15,
"n" => 20
);
I need to sort the array in this way :
$foo = array(1,2,3,4,5,12,20,15,10,5,4,3,2,1);
As you can see, the best value needs to be in the middle of the array. And the min-values in the start/end of array. The key needs to be linked to the original value.
Thank's for the help! sorry for my English.
If the input is already sorted, you can use two loops to first push every item with an odd offset in ascending order and then every item with an even offset in descending order into your array:
$keys = array_keys($foo);
$n = count($keys);
$result = array();
for ($i=0; $i<$n; $i+=2) {
$result[$keys[$i]] = $foo[$keys[$i]];
}
for ($i=$n-$n%2-1; $i>0; $i-=2) {
$result[$keys[$i]] = $foo[$keys[$i]];
}
First arrange all the integers in ascending order.
Lets say $foo_as is the new array you got in ascending order.
$a=a new empty array of size $n
$n=number of integers.
$c=0;
for ($i=$n-1;$i>0;$i--){
if (i%2==0){
$a(i/2)+c=$foo_as($n-$c)
}
else{
$a(i+1/2)-c=$foo_as($n-$c)
}
$c++
}
Explanation: I'm putting the largest integer in the middle. After that I'm filling the alternate indexes around the middle one with the integers in descending order.
- - - - - 11 - - - - -
- - - - 10 11 - - - - -
- - - - 10 11 9 - - - -
- - - 8 10 11 9 - - - -
- - - 8 10 11 9 7 - - -
- - 6 8 10 11 9 7 - - -
- - 6 8 10 11 9 7 5 - -
- 4 6 8 10 11 9 7 5 - -
- 4 6 8 10 11 9 7 5 3 -
2 4 6 8 10 11 9 7 5 3 -
2 4 6 8 10 11 9 7 5 3 1
I have used a basic algorithm to demonstrate this. If at some point, my php syntax is incorrect, please omit that.
A overkill version:
<?php
$foo = array(
"a" => 1,
"b" => 1,
"c" => 2,
"d" => 2,
"e" => 3,
"f" => 3,
"g" => 4,
"h" => 4,
"i" => 5,
"j" => 5,
"k" => 10,
"l" => 12,
"m" => 15,
"n" => 20
);
$arrayKeys = array_keys($foo);
$arrayValues = array_values($foo);
$array_count = count($foo);
for ($idx =0; $idx < round($array_count/2); $idx+=2) {
$tmpA = $arrayKeys[$idx];
$arrayKeys[$idx] = $arrayKeys[$array_count - $idx -1];
$arrayKeys[$array_count - $idx -1] = $tmpA;
$tmpB = $arrayValues[$idx];
$arrayValues[$idx] = $arrayValues[$array_count - $idx -1];
$arrayValues[$array_count - $idx -1] = $tmpB;
}
$tmpArray = array_combine($arrayKeys, $arrayValues);
$ascent = array_slice($tmpArray, 0, round($array_count/2));
$descent = array_slice($tmpArray, round($array_count/2));
asort($ascent);
arsort($descent);
$foobar = array_merge($ascent, $descent);
var_dump($foo, $foobar);
?>

Categories