Reputation: 9113
With the holidays slowly nearing, It's time to pick straws again. We always pick a piece of paper from a box containing everbody's name. However, this year I wanted to solve the issue of picking your own name from the box by using PHP.
I've got an array with names ex:
$names = [
'John',
'Jane',
'Joe',
'Matt',
'Steve',
'Anne',
'Karin'
];
For each of these names, I wanna pick 4 random names, so that at the end every person has 4 names for which they will have to buy a present.
Well, I'm stuck. I've thought of a million ways on how to do this but I just can't come up with anything.
array_rand()
The problem with these is that a person shouldn't be picking their own name and that everyone should be picked an even amount of times (4).
Some great answers already posted. I'm not exactly sure though if this is fair for everyone. Everyone should at the end get 4 presents from 4 different persons.
I was thinking of adding each name to the $names
array 4 times and then applying one of your answers. Am I on the right track with this?
What I've tried now:
function select_rand($array, $exclude_array) {
$diff = array_diff($array, $exclude_array);
return $diff[array_rand($diff, 1)];
}
$workArray = [
'0' => $names,
'1' => $names,
'2' => $names,
'3' => $names,
];
foreach ($names as $k => $name) {
$i = 0;
$new[$name] = array();
while ($i < 4) {
$value = select_rand($workArray[$i], array_merge($new[$name], array($name)));
if (($key = array_search($value, $workArray[$i])) !== false) {
unset($workArray[$i][$key]);
}
$new[$name][] = $value;
$i++;
}
}
This works only in a few caes.
Upvotes: 2
Views: 84
Reputation: 4318
I think if you want to have an even distribution of presents per person, you need to have more control over it. Anyhow, in this code I keep track of number of presents per person. I initialise it with zero: $presents = array_fill_keys($names, 0);
. Then after each selection I updated this number $presents[$key]++;
.
<?php
$names = array(
'John',
'Jane',
'Joe',
'Matt',
'Steve',
'Anne',
'Karin'
);
$presents_number = 4;
// initialization
$presents = array_fill_keys($names, 0);
$names_names = array();
foreach ($names as $i => $picker) {
$box = $names;
unset($box[$i]);
// filter out the people with maximum presents:
foreach ($presents as $key => $number) {
if (($presents[$key] > $presents_number-1) && in_array($key, $box)) {
$box = array_diff($box, array($key));
}
}
// shuffle the box and select 4 top
shuffle($box);
$selection = array_slice($box, 0, $presents_number);
foreach ($selection as $key) {
$presents[$key]++;
}
$names_names[$picker] = $selection;
}
echo "<pre>";
print_r($names_names);
echo "</pre>";
There can be more to be considered mathematically. Specially since loops can happen, this algorithm can go wrong. I haven't spend time on it. But as an example of 3 people and one present per person. (A, B, C)
correct answer:
A => {B}
B => {C}
C => {A}
wrong answer:
A => {B}
B => {A}
C => {}
Basically, the perfect algorithm should to avoid these wrong answers. Maybe later I fixed the problem as an update for this post.
Upvotes: 1
Reputation: 31739
This can help -
function select_rand($array, $exclude_array) {
$diff= array_diff($array, $exclude_array);
return $diff[array_rand($diff, 1)];
}
$names = array(
'John',
'Jane',
'Joe',
'Matt',
'Steve',
'Anne',
'Karin'
);
foreach($names as $name)
{
$i = 0;
$new[$name] = array();
while($i < 4) {
$new[$name][] = select_rand($names, array_merge($new[$name], array($name)));
$i++;
}
}
This will generate a new array
for each name
(as key
) in that array containing 4 unique names.
Update
$new[$name][] = select_rand($names, array_merge($new[$name], array($name, 'Karin')));
Upvotes: 1
Reputation: 21437
You can create custom function like as
$names = [
'John',
'Jane',
'Joe',
'Matt',
'Steve',
'Anne',
'Karin'
];
$my_name = "John";
$limit = 4;
function get_name($arr,$your_name,$limit){
$key = array_flip($arr)[$your_name];
unset($arr[$key]);
$rand_keys = array_rand($arr,$limit);
$result = array_intersect_key($arr, array_flip($rand_keys));
return implode(',',$result);
}
echo get_name($names,$my_name,$limit);
Upvotes: 1
Reputation: 20726
I would use shuffle, and array_slice for this job:
$names = [
'John',
'Jane',
'Joe',
'Matt',
'Steve',
'Anne',
'Karin'
];
foreach ($names as $name) {
// working array
$working = $names;
// remove current name from array,no one wants to buy presents for him/her self..
unset($working[$name]);
shuffle($working);
$people = array_slice($working, 0, 4);
echo $name . ' has to buy presents for:';
var_dump($people);
}
Upvotes: 3