【问题标题】:How to select randomly from array exluding one value?如何从不包括一个值的数组中随机选择?
【发布时间】:2015-10-19 06:37:05
【问题描述】:

随着假期的临近,又到了捡稻草的时候了。我们总是从一个包含每个人名字的盒子里挑选一张纸。然而,今年我想通过使用 PHP 解决从框中选择自己名字的问题。

我有一个名称为 ex 的数组:

$names = [
    'John',
    'Jane',
    'Joe',
    'Matt',
    'Steve',
    'Anne',
    'Karin'
];

对于这些名字中的每一个,我想随机选择 4 个名字,这样最后每个人就有 4 个名字,他们必须为这些名字买礼物。

问题

好吧,我被困住了。我已经想出了一百万种方法来做到这一点,但我就是想不出任何办法。

到目前为止我已经尝试过什么

  • 使用 foreach
  • 使用array_rand()
  • 移动数组

这些问题是一个人不应该选择自己的名字,每个人都应该被选择的次数相同 (4)。

更新

已经发布了一些很棒的答案。我不确定这是否对每个人都公平。最后,每个人都应该从 4 个不同的人那里得到 4 个礼物。

我正在考虑将每个名称添加到 $names 数组 4 次,然后应用您的答案之一。我在这方面走在正确的轨道上吗?

更新 2

我现在尝试过的:

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++;
    }
}

这仅在少数情况下有效。

【问题讨论】:

  • array_rand(), array_diff()

标签: php arrays


【解决方案1】:

我会使用 shuffle 和 array_slice 来完成这项工作:

$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);
}

【讨论】:

  • 这看起来很棒,而且非常接近。但是每个人都被分配了 4 个名字。这 4 个必须是唯一的。虽然名称数组包含 30 人而不是 7 人,但如果这有用的话。
【解决方案2】:

您可以创建自定义函数,如 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);

Demo

【讨论】:

    【解决方案3】:

    这可以帮助 -

    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++;
        }
    }
    

    这将为包含4个唯一名称的数组中的每个name(如key)生成一个新的array

    FIDDLE

    更新

    $new[$name][] = select_rand($names, array_merge($new[$name], array($name, 'Karin')));
    

    【讨论】:

    • 效果很好,但这将如何阻止卡琳从每个人那里收到礼物?
    • @Peter 检查更新。只需将 Karin 添加到要排除的数组中。 :)
    • 添加 Karin 不会解决我的问题吧?其他人仍然可以得到超过 4 份礼物。
    • 那么应该得到多少礼物呢?每个名称的限制应该是多少?
    • 每个人应该有 4 个名字。但是每个人应该只能被选中 4 次。
    【解决方案4】:

    我认为,如果您希望每人平均分配礼物,则需要对其进行更多控制。无论如何,在这段代码中,我记录了每人的礼物数量。我用零初始化它:$presents = array_fill_keys($names, 0);。然后在每次选择后我更新了这个号码$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>";
    

    在数学上可以考虑更多。特别是由于可能发生循环,该算法可能会出错。我没有花时间在上面。但以 3 人和每人一份礼物为例。 (A, B, C)

    正确答案:

    A => {B}

    B => {C}

    C => {A}

    错误答案:

    A => {B}

    B => {A}

    C => {}

    基本上,完美的算法应该避免这些错误的答案。也许稍后我将这个问题作为这篇文章的更新来解决。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      相关资源
      最近更新 更多