(PHP) Spreading array contents equally across a fixed number of columns - php

I am going nuts here - hope you can help me figure this out! What is supposed to be very simple math is utterly confusing me.
$columns = 3;
$items = range(1,20);
$total = count($items);
$col1 = ???; $col2 = ???; $col3 = ???;
// $col1 must be an array of (1,2,3,4...)
// $col2 must be an array of (8,9,10,11...)
// $col3 must be an array of (15,16,17,18...)
COL1 COL2 COL3
1 8 15
2 9 16
3 10 17
4 11 18
5 12 19
6 13 20
7 14
The above is a visual example of what I am trying to achieve. Basically, for any given number of items in an array and for any given number of columns, how do I produce n number of arrays (equalling number of columns) that are as equal length as possible. If equal length is not possible (as in the example above), they must be spread out as evenly as possible and the last array must be the shortest.
Any guidance on how I construct $col1/2/3 in the example above would be much appreciated!
Please ignore the fact that I used range and integers to generate the array - this is just to simplify the example. Assume the array will contain strings.
Thanks for any help!

You do want to use array_chunk, but you need to calculate the chunk size yourself:
list($col1, $col2, $col3) = array_chunk($items, ceil($total / $columns));

I think you might want array_chunk
list($col1,$col2,$col3)=array_chunk($arrofstrings,$colcount);

$itemsInColumn = ceil($total / $columns);

$columns = 3;
$items = range(1, 20);
$rows = array_chunk($items, $columns);
$columns = array();
foreach ($rows as $row) {
for ($i = 0; $i < $columns; $i++) {
$columns[$i][] = $row[$i];
}
}

Related

How can I sum groups of X elements together?

I need to get a valid timestamp from the following array:
$arrkeys = array(
"t" => [
1442238840,60,120,180,240,300,360,420,480,540,600,660,720,780,840,900,960,
1442239860,60,120,180,240,300,360,420,480,540,600,660,720,780,840,900,960,
1442240880,60,120,180,240,300,360,420,480,540,600,660,720,780,840,900,960
]
);
I need to sum the first valid timestamp 1442238840 with the following 17 numbers together, to get a correct timestamp, and then, the second timestamp 1442239860, etc...
Example:
1442238840 + 60;
1442238840 + 120;
1442238840 + 180;
etc...
I can't figure out how I could do this,
some things I've tried:
Attempt No. 1
//Pseudo-code
foreach($arrkeys["t"] as $t){
if(strlen($t) < 10){
//Search for the first valid timestamp and sum?
}
}
Attempt No. 2
//Not working.
$array_size = 17;
/* I know that every 17 (counting from 0) have a valid timestamp.
so that 0 = 1st valid timestamp, 20 = 2nd valid timestamp. */
for ($i = 0; $i < $array_size; $i++) {
do {
echo $i;
} while ($i > 0);
I don't know if I'm going the right way to solve this, I don't know how to do this efficiently, or at least do it.
Any suggestions?
I love that you tried different things. That's good!
You tried to solve the problem yourself and array_chunk() will help you a lot here, since you can chunk your array into groups of 17 elements and then use array_sum() to sum each group of 17 elements together, e.g.
$result = array_chunk($arrkeys["t"], 17);
$result = array_map("array_sum", $result);

How to create equally distributed groups of three?

For a website application I need to form random groups of three. The user him/herself cannot grade him/herself (so cannot be part of the group). There will always be a minimum of 4 users.
For example, if there were 5 users, I would have an array as such: Array(0,1,2,3,4) and I would want the output to be (where the key is the user and the data is the group of 3).
Array(
[0 : 1,2,3],
[1 : 0,2,4],
[2 : 1,4,3],
[3 : 0,1,4],
[4 : 0,2,3]
);
Notice the user is never in the group and every user is assigned exactly 3 times.
You cannot simply randomly select users to groups because it might be assigned more than 3 times and force some groups to have less than 3 users in the group.
I hope I explained this problem appropriately and someone will be able to help.
Here is some code that puts three random numbers in a dictionary and works fine for [0,1,2,3] but will (most likely) fail for anything larger because the number will not be equally distributed (and will continue in the while loop forever because there are no possible numbers left).
$rows = range(0,3);
$array_size = count($rows);
$peers_array = Array();
$number_count = array_fill(0, $array_size, 0);
foreach($rows as $index => $row){
$randomNumbers = Array();
for($x = 0; $x < 3; ++$x){
$randomNumber = rand(0, $array_size-1);
while($randomNumber==$index or in_array($randomNumber, $randomNumbers) or $number_count[$randomNumber]>2)
$randomNumber = rand(0, $array_size-1);
$number_count[$randomNumber]++;
$randomNumbers[] = $randomNumber;
}
$peers_array[$index] = $randomNumbers;
}
print_R( $peers_array);
Ok so I've come up with my own solution. It took a little thought but thanks to suggestions I was able to come up with this solution:
First it generates a range from 0 to the number of users - 1 (e.g. for 5 it would give [0,1,2,3,4]) then every time after that it shifts the list one (e.g. [0,1,2,3,4] becomes [4,0,1,2,3]) then it takes each element at a given index of the array and puts it into a group (so if I only wanted groups of 2 it would give 0:[0,4] 1:[0,1] 2:[2,1] and so on...). You then shift the order between the number of users - the size of the group, don't ask just trust me. This guarantees all numbers appear an equal number of times but still randomizes the order.
The lines below accomplishes this:
function getUsers($user_num, $groups_of){
$index_list = range(0,$user_num-1);
$random_indexes = range(0,$user_num-1);
shuffle($random_indexes);
$groups = array();
foreach($index_list as $user_num)
$groups[] = array($random_indexes[$user_num]);
for($i = 0; $i<($groups_of-1); ++$i){
array_unshift($index_list, array_pop($index_list)); //puts the last element first
foreach($index_list as $index => $user_num)
$groups[$index][] = $random_indexes[$user_num];
}
$shift_number = rand(1, ($len_users-$groups_of));
for($i = 0; $i<$shift_number; ++$i)
array_unshift($groups, array_pop($groups));
return $groups
}
I was thinking array_pop would be a good approach, it works well for the first part of the problem, easy to get the current item and make sure it isn't available for the next part, however then you end up having to keep track of too many moving parts.
In the end went for array_diff to remove the current row from the original array.
$rows = range(0,15);
$results = array();
foreach ($rows as $row) {
$others = array_diff($rows,array($row));
shuffle($others);
$results[$row] = array_slice(array_values($others),0,3);
}
var_dump($results);
Results:
array (size=16)
0 =>
array (size=3)
0 => int 9
1 => int 1
2 => int 10
1 =>
array (size=3)
0 => int 10
1 => int 11
2 => int 14
2 =>
array (size=3)
0 => int 3
1 => int 15
2 => int 14
3 =>
array (size=3)
0 => int 9
1 => int 4
2 => int 1
etc...
I think this:(to generalize)
function getUsers($users,$groups_of){
$result = array();
for($i = 0; $i < $users; $i++){
$usernums = array();
while(count($usernums) < $groups_of){
$randomNumber = rand(0, $users-1);
if($i != $randomNumber && !in_array($randomNumber, $usernums)){
$usernums[] = $randomNumber;
}
}
$result[$i] = $usernums;
}
return $result;
}
$users = 5;
$groups_of = 3;
print_r(getUsers($users,$groups_of));

fill 2 dimensional array random in a bingo way

I've got these two functions:
function drawNumber($drawnNumbers){
$unique = true;
while ($unique == true){
$number = mt_rand(10, 69);
if (!in_array($number, $drawnNumbers)){
return $number;
$unique = false;
}
}
}
fillCard(); ?>
It's a bingo game. The card gets filled with random Numbers. But I can't get it like this:
column column column column column column
row 10 13 16 14 16 19
row 24 26 28 29 23 21
row 36 33 39 30 31 35
row 46 48 42 45 43 47
row 59 56 51 52 58 50
row 60 65 68 62 61 67
So I would like to have the first row with numbers from 10 to 19
the second row from 20 to 29 and so on.
I tried like this
<?php drawnNumber(): $number = mt_rand(0,9);
fillArray(): $number = $row . $number; ?>
But that doesn't work, because there are double numbers in the card.
So before that I tried it in a different way,with in_array:
<?php
function fillCard(){
$card = array();
/* fill card with random numbers */
for($i = 0, $min = 10, $max = 19; $i < 6; $i++, $min+=10, $max += 10)
{
for($j = 0; $j < 6; $j++)
{
$number = mt_rand($min,$max) ;
if(!in_array($number, $card){
$card['row' . $i]['column' . $j]['number'] = $number;
$card['row' . $i]['column' . $j]['found'] = 0;
}
}
}
var_dump($card);
return $card;
} ?>
But there are still double random numbers in the card.
I tried a few other thinks in the last two weeks, but I just can't get it to work together.
If one thing succeeds the other thing fails.
I can get the random numbers but not unique random numbers in the card.
I hope someone can help me.
(for extra information: it's a bingo game. So drawnNumber() are the "balls", which get drawn
and stored in the array $drawnNumbers, they also are unique random numbers. fillCard() is the
function that fills the bingo card and checks if $drawNumber is in $card)
I would appreciate some help, if someone can tell me how to get it to work. Maybe in
an algorithm way or else some code?
Thank you in advance.
In general, you draw from some kind of box, right? So do the same, have an array with all available numbers and once you get a random number out of it, remove it, so the next time you search for a number, you will only pick from the remaining ones. Small example:
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
we pick a random number between 0 and 3 inclusive (0 and the length - 1 of a that is). Let's say we picked index 2, then a will look like:
a[0] = 1
a[1] = 2
a[2] = 4
Now if you draw a number between 0 and 2 (note that you take the length - 1 of a!), you won't re-pick the already chosen number in any way thus giving you unique numbers ALL the time.
P.S. this is a simplified version, but now if you can apply that to yourself and, for your example, create several arrays you will pick from.
The simplest way would be to have an additional flat array to keep track, and loop mt_rand
Here's an example of the meat of things:
$drawn = array();
// Loop around until you have a new number in the desired range
do {
$number = mt_rand($min,$max);
} while(in_array($number, $drawn));
// And save it to the $drawn array
$drawn[] = $rand;
To clarify, the above snippet (without the initialization of $drawn) is meant to replace the line
$number = mt_rand($min,$max) ;
in your code.
define('NB_ROWS', 6);
define('NB_COLS', 6);
$rows = range(10, NB_ROWS * 10, 10);
$bingo = array();
foreach($rows as $rowIndex)
{
$availNumbers = range(0, 9);
$line = array();
for($cellIndex = 0; $cellIndex < NB_COLS; $cellIndex++)
{
// pick a random value among remaining ones for current line
$numIndex = rand(0, count($availNumbers)-1);
list($value) = array_splice($availNumbers, $numIndex, 1);
$line[] = $value + $rowIndex;
}
$bingo[] = $line;
}
print_r($bingo);

How can i modify the php code to recieve 5 numbers without duplicates/repeating numbers?

i have this php code:
<?php
function GetRand($N, $min=1, $max=59) {
$Local = array();
mt_srand(time());
for ($i=0;$i<$N;$i++)
$LocalArr [] = mt_rand($min, $max);
return $LocalArr;
}
$A = GetRand(5);
foreach($A as $K=>$v) echo "$v ";
?>
The result is 5 numbers between 1 and 59. The problem is that sometimes i receive results like this:
43 9 13 9 7
In those 5 numbers, there is the number 9 twice. I would like to change the php code, so everytime when there is a number that repeats, this number should be skipped and instead of the repeated number should be represented another number, so that every time i have 5 numbers and no duplicates between them.
Thank you very much in Advance!
$numbers = range(1, 59);
shuffle($numbers);
var_dump(array_slice($numbers, 0, 5));
Try this (untested):
<?php
$randoms = array(rand(1,59));
while(sizeof($randoms) <= 5) {
$randoms[] = rand(1,59);
$randoms = array_unique($randoms);
}

Evenly represent all numbers in combinations subset

I would like to find out what is the most effective PHP script to evenly represent all numbers in a specific combinations subset.
Lottery problem example:
create 10 combinations each consisting of of 6 numbers
from set of number (1,2,3,4,5,6,7,8,9,10,11,12)
I know that from 12 numbers I can create 924 combinations each consisting of 6 numbers.
Since I can't afford to play 924 lines - I want to pick only 10 lines which represent evenly all my selected numbers.
So in this example it would be something like:
1-2-3-4-5-6
7-8-9-10-11-12
and 8 more lines
I'm trying to avoid combinations like:
1-2-3-4-5-6
1-2-3-4-5-7
1-2-3-4-5-8
... etc. which are almost the same; I want to evenly represent each number.
Hope that makes sense.
You can create a "pool" of the numbers you want to use, and randomly draw from that pool. For instance, if you want 10 combinations of 6 numbers each, that's a total of 60 numbers. But you want each of 1-12 represented evenly, so there will be 5 of each number. So start with an array containing 5 of each 1-12, and draw randomly from the array for each set of 6.
$pool = array();
for($i = 0; $i < 5; $i++)
for($x = 1; $x <= 12; $x++)
$pool[] = $x;
$result = array();
for($i = 0; $i < 10; $i++) {
$set = array();
for($x = 0; $x < 6; $x++) {
$key = array_rand($pool);
$set[] = $pool[$key];
unset($pool[$key]);
}
$result[] = $set;
}
// $result now contains 10 sets of 6 numbers each
Demo: http://ideone.com/NpO3h4
// how many numbers are in the set, starting from 1
$numbers = 12;
$set = array();
for ($i=1;$i>=$numbers;$i++)
{
array_push($set, $i);
}
// how many numbers in the subset
$count = 6;
$subSet = array();
while ($count > 0)
{
// get a random number from 1 to numbers in set
$rand=rand(0,$numbers-1);
array_push($subSet, $set[$rand]);
$count--;
}
// $subSet now contains a combination of 6 random numbers from 1 to 12
// keep refreshing
var_dump($subSet);
There you go, with explanations, is this what you wanted?
EDIT: I just noticed you said "the most effective way". This is not the most effective (in terms of memory used) way, but it's close.

Categories