多维数组中的数组排列保留键 PHP

发布于 2024-11-06 17:07:33 字数 3508 浏览 4 评论 0原文

这两天我一直在疯狂地尝试完成这个任务,也许你可以启发我。这是针对赛马投注排列的。每次用户玩游戏时,我都会得到一个多维数组(2 个级别)。第一级包含比赛 ID,第二级包含用户为该比赛选择的马匹。它看起来像这样:

$play = array
(
    '4' => array(7, 32),
    '8' => array(4),
    '2' => array(9),
    '12' => array('5'),
    '83' => array('10', '11', '12', ''),
    '9' => array('3'),
);

我需要知道该游戏的所有可能组合是什么。使用此函数可以轻松完成此操作:

function permutations(array $array)
{
    switch (count($array)) {
        case 1:
            return $array[0];
            break;
        case 0:
            throw new InvalidArgumentException('Requires at least one array');
            break;
    }

    $a = array_shift($array);
    $b = permutations($array);

    $return = array();
    foreach ($a as $key => $v) {
        if(is_numeric($v))
        {
            foreach ($b as $key2 => $v2) {
                $return[] = array_merge(array($v), (array) $v2);
            }
        }
    }

    return $return;
}

这将返回一个包含所有可能组合的数组。到目前为止一切顺利,结果如下所示:

Array
(
    [0] => Array
        (
            [0] => 7
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 10
            [5] => 3
        )

    [1] => Array
        (
            [0] => 7
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 11
            [5] => 3
        )

    [2] => Array
        (
            [0] => 7
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 12
            [5] => 3
        )

    [3] => Array
        (
            [0] => 32
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 10
            [5] => 3
        )

    [4] => Array
        (
            [0] => 32
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 11
            [5] => 3
        )

    [5] => Array
        (
            [0] => 32
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 12
            [5] => 3
        )

)

我的问题: 我需要每匹马的数组“key”作为“比赛 ID”,而不是 0,1,2,3。 我需要这样的结果

Array
(
    [0] => Array
        (
            [4] => 7
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 10
            [9] => 3
        )

    [1] => Array
        (
            [4] => 7
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 11
            [9] => 3
        )

    [2] => Array
        (
            [4] => 7
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 12
            [9] => 3
        )

    [3] => Array
        (
            [4] => 32
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 10
            [9] => 3
        )

    [4] => Array
        (
            [4] => 32
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 11
            [9] => 3
        )

    [5] => Array
        (
            [4] => 32
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 12
            [9] => 3
        )

)

我怎样才能做到这一点?我知道这是一篇很长的文章,但我需要用图表来表示。我在思考函数递归时遇到了问题,并且我完全迷失在每个循环中。

For two days I've been running crazy trying to accomplish this, maybe you can enlighten me. This is for a horse betting permutation. Every time a user plays, I get a multidimensional array (2 levels). The first level contains the race ID, the the second level contains thee horses selected by the user for that race. It looks like this:

$play = array
(
    '4' => array(7, 32),
    '8' => array(4),
    '2' => array(9),
    '12' => array('5'),
    '83' => array('10', '11', '12', ''),
    '9' => array('3'),
);

I need to know what are all the possible combinations for that play. Which is easily done with this function:

function permutations(array $array)
{
    switch (count($array)) {
        case 1:
            return $array[0];
            break;
        case 0:
            throw new InvalidArgumentException('Requires at least one array');
            break;
    }

    $a = array_shift($array);
    $b = permutations($array);

    $return = array();
    foreach ($a as $key => $v) {
        if(is_numeric($v))
        {
            foreach ($b as $key2 => $v2) {
                $return[] = array_merge(array($v), (array) $v2);
            }
        }
    }

    return $return;
}

This returns an array with all the possible combinations beautifully. So far so good, and the result looks like this:

Array
(
    [0] => Array
        (
            [0] => 7
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 10
            [5] => 3
        )

    [1] => Array
        (
            [0] => 7
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 11
            [5] => 3
        )

    [2] => Array
        (
            [0] => 7
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 12
            [5] => 3
        )

    [3] => Array
        (
            [0] => 32
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 10
            [5] => 3
        )

    [4] => Array
        (
            [0] => 32
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 11
            [5] => 3
        )

    [5] => Array
        (
            [0] => 32
            [1] => 4
            [2] => 9
            [3] => 5
            [4] => 12
            [5] => 3
        )

)

My problem: I need the array "key" for every horse to be the "race ID", not 0,1,2,3. I need the result to be like this:

Array
(
    [0] => Array
        (
            [4] => 7
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 10
            [9] => 3
        )

    [1] => Array
        (
            [4] => 7
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 11
            [9] => 3
        )

    [2] => Array
        (
            [4] => 7
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 12
            [9] => 3
        )

    [3] => Array
        (
            [4] => 32
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 10
            [9] => 3
        )

    [4] => Array
        (
            [4] => 32
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 11
            [9] => 3
        )

    [5] => Array
        (
            [4] => 32
            [8] => 4
            [2] => 9
            [12] => 5
            [83] => 12
            [9] => 3
        )

)

How can I accomplish this? I know its a long post but I needed to graph this. I am having problems to wrap my head around the function recursion and I get totally lost in each loop.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

青芜 2024-11-13 17:07:33

我也遇到了同样的问题,丹尼的解决方案对我来说并不好。
我管理数千个排列并将它们存储在内存中非常昂贵。

这是我的解决方案:

/**
 * Calculate permutation of multidimensional array. Without recursion!
 * Ex.
 * $array = array(
 *   key => array(value, value),
 *   key => array(value, value, value),
 *   key => array(value, value),
 * );
 *
 * @copyright Copyright (c) 2011, Matteo Baggio
 * @param array $anArray Multidimensional array
 * @param function $isValidCallback User function called to verify the permutation. function($permutationIndex, $permutationArray)
 * @return mixed Return valid permutation count in save memory configuration, otherwise it return an Array of all permutations
 */
function permutationOfMultidimensionalArray(array $anArray, $isValidCallback = false) {

    // Quick exit
    if (empty($anArray))
        return 0;

    // Amount of possible permutations: count(a[0]) * count(a[1]) * ... * count(a[N])
    $permutationCount = 1;
    // Store informations about every column of matrix: count and cumulativeCount
    $matrixInfo = array();
    $cumulativeCount = 1;

    foreach($anArray as $aColumn) {

        $columnCount       = count($aColumn);
        $permutationCount *= $columnCount;

        // this save a lot of time!
        $matrixInfo[] = array(
            'count'           => $columnCount,
            'cumulativeCount' => $cumulativeCount
        );

        $cumulativeCount  *= $columnCount;
    }

    // Save the array keys
    $arrayKeys = array_keys($anArray);
    // It needs numeric index to work
    $matrix = array_values($anArray);
    // Number of column
    $columnCount = count($matrix);
    // Number of valid permutation
    $validPermutationCount = 0;
    // Contain all permutations
    $permutations = array();

    // Iterate through all permutation numbers
    for ($currentPermutation = 0; $currentPermutation < $permutationCount; $currentPermutation++) {

        for ($currentColumnIndex = 0; $currentColumnIndex < $columnCount; $currentColumnIndex++) {
            // Here the magic!
            // I = int(P / (Count(c[K-1]) * ... * Count(c[0]))) % Count(c[K])
            // where:
            // I: the current column index
            // P: the current permutation number
            // c[]: array of the current column
            // K: number of the current column
            $index = intval($currentPermutation / $matrixInfo[$currentColumnIndex]['cumulativeCount']) % $matrixInfo[$currentColumnIndex]['count'];

            // Save column into current permutation
            $permutations[$currentPermutation][$currentColumnIndex] = $matrix[$currentColumnIndex][$index];
        }

        // Restore array keys
        $permutations[$currentPermutation] = array_combine($arrayKeys, $permutations[$currentPermutation]);

        // Callback validate
        if ($isValidCallback !== false) {
            if ($isValidCallback($currentPermutation, $permutations[$currentPermutation]))
                $validPermutationCount++;
            // *** Uncomment this lines if you want that this function return all
            //     permutations
            //else
            //  unset($permutations[$currentPermutation]);
        }
        else {
            $validPermutationCount++;
        }

        // Save memory!!
        // Use $isValidCallback to check permutation, store into DB, etc..
        // *** Comment this line if you want that function return all
        //     permutation. Memory warning!!
        unset($permutations[$currentPermutation]);
    }


    if (!empty($permutations))
        return $permutations;
    else
        return $validPermutationCount;
}


//
// How to?
//

$play = array(
    '4'  => array(7, 32),
    '8'  => array(4),
    '2'  => array(9),
    '12' => array('5'),
    '83' => array('10', '11', '12', ''), // <-- It accept all values, nested array too
    '9'  => array('3'),
);


$start = microtime(true);

// Anonymous function work with PHP 5.3.0
$validPermutationsCount = permutationOfMultidimensionalArray($play, function($permutationIndex, $permutationArray){

        // Here you can validate the permutation, print it, etc...
        // Using callback you can save memory and improve performance.
        // You don't need to cicle over all permutation after generation.
        printf('<p><strong>%d</strong>: %s</p>', $permutationIndex, implode(', ', $permutationArray));

        return true; // in this case always true
    });

$stop = microtime(true) - $start;

printf('<hr /><p><strong>Performance for %d permutations</strong><br />
       Execution time: %f sec<br/>
       Memory usage: %d Kb</p>',
       $validPermutationsCount,
       $stop,
       memory_get_peak_usage(true) / 1024);

如果有人有更好的主意,我就在这里!

I've got the same problem and Danny's solution wasn't good for me.
I manage thousand of permutation and store them in memory is damn expensive.

Here my solution:

/**
 * Calculate permutation of multidimensional array. Without recursion!
 * Ex.
 * $array = array(
 *   key => array(value, value),
 *   key => array(value, value, value),
 *   key => array(value, value),
 * );
 *
 * @copyright Copyright (c) 2011, Matteo Baggio
 * @param array $anArray Multidimensional array
 * @param function $isValidCallback User function called to verify the permutation. function($permutationIndex, $permutationArray)
 * @return mixed Return valid permutation count in save memory configuration, otherwise it return an Array of all permutations
 */
function permutationOfMultidimensionalArray(array $anArray, $isValidCallback = false) {

    // Quick exit
    if (empty($anArray))
        return 0;

    // Amount of possible permutations: count(a[0]) * count(a[1]) * ... * count(a[N])
    $permutationCount = 1;
    // Store informations about every column of matrix: count and cumulativeCount
    $matrixInfo = array();
    $cumulativeCount = 1;

    foreach($anArray as $aColumn) {

        $columnCount       = count($aColumn);
        $permutationCount *= $columnCount;

        // this save a lot of time!
        $matrixInfo[] = array(
            'count'           => $columnCount,
            'cumulativeCount' => $cumulativeCount
        );

        $cumulativeCount  *= $columnCount;
    }

    // Save the array keys
    $arrayKeys = array_keys($anArray);
    // It needs numeric index to work
    $matrix = array_values($anArray);
    // Number of column
    $columnCount = count($matrix);
    // Number of valid permutation
    $validPermutationCount = 0;
    // Contain all permutations
    $permutations = array();

    // Iterate through all permutation numbers
    for ($currentPermutation = 0; $currentPermutation < $permutationCount; $currentPermutation++) {

        for ($currentColumnIndex = 0; $currentColumnIndex < $columnCount; $currentColumnIndex++) {
            // Here the magic!
            // I = int(P / (Count(c[K-1]) * ... * Count(c[0]))) % Count(c[K])
            // where:
            // I: the current column index
            // P: the current permutation number
            // c[]: array of the current column
            // K: number of the current column
            $index = intval($currentPermutation / $matrixInfo[$currentColumnIndex]['cumulativeCount']) % $matrixInfo[$currentColumnIndex]['count'];

            // Save column into current permutation
            $permutations[$currentPermutation][$currentColumnIndex] = $matrix[$currentColumnIndex][$index];
        }

        // Restore array keys
        $permutations[$currentPermutation] = array_combine($arrayKeys, $permutations[$currentPermutation]);

        // Callback validate
        if ($isValidCallback !== false) {
            if ($isValidCallback($currentPermutation, $permutations[$currentPermutation]))
                $validPermutationCount++;
            // *** Uncomment this lines if you want that this function return all
            //     permutations
            //else
            //  unset($permutations[$currentPermutation]);
        }
        else {
            $validPermutationCount++;
        }

        // Save memory!!
        // Use $isValidCallback to check permutation, store into DB, etc..
        // *** Comment this line if you want that function return all
        //     permutation. Memory warning!!
        unset($permutations[$currentPermutation]);
    }


    if (!empty($permutations))
        return $permutations;
    else
        return $validPermutationCount;
}


//
// How to?
//

$play = array(
    '4'  => array(7, 32),
    '8'  => array(4),
    '2'  => array(9),
    '12' => array('5'),
    '83' => array('10', '11', '12', ''), // <-- It accept all values, nested array too
    '9'  => array('3'),
);


$start = microtime(true);

// Anonymous function work with PHP 5.3.0
$validPermutationsCount = permutationOfMultidimensionalArray($play, function($permutationIndex, $permutationArray){

        // Here you can validate the permutation, print it, etc...
        // Using callback you can save memory and improve performance.
        // You don't need to cicle over all permutation after generation.
        printf('<p><strong>%d</strong>: %s</p>', $permutationIndex, implode(', ', $permutationArray));

        return true; // in this case always true
    });

$stop = microtime(true) - $start;

printf('<hr /><p><strong>Performance for %d permutations</strong><br />
       Execution time: %f sec<br/>
       Memory usage: %d Kb</p>',
       $validPermutationsCount,
       $stop,
       memory_get_peak_usage(true) / 1024);

If someone has a better idea i'm here!

酒与心事 2024-11-13 17:07:33

这就是您所需要的。我已根据需要发表了评论:

function permutations(array $array)
{
    switch (count($array)) {
        case 1:
            // Return the array as-is; returning the first item
            // of the array was confusing and unnecessary
            return $array;
            break;
        case 0:
            throw new InvalidArgumentException('Requires at least one array');
            break;
    }

    // We 'll need these, as array_shift destroys them
    $keys = array_keys($array);

    $a = array_shift($array);
    $k = array_shift($keys); // Get the key that $a had
    $b = permutations($array);

    $return = array();
    foreach ($a as $v) {
        if(is_numeric($v))
        {
            foreach ($b as $v2) {
                // array($k => $v) re-associates $v (each item in $a)
                // with the key that $a originally had
                // array_combine re-associates each item in $v2 with
                // the corresponding key it had in the original array
                // Also, using operator+ instead of array_merge
                // allows us to not lose the keys once more
                $return[] = array($k => $v) + array_combine($keys, $v2);
            }
        }
    }

    return $return;
}

查看实际操作

顺便说一句,递归计算所有排列很简洁,但您可能不想在生产环境中执行此操作。您绝对应该考虑进行健全性检查,计算有多少排列,并且至少在它们超过某个限制时不允许继续处理。

Here's what you need. I have commented as necessary:

function permutations(array $array)
{
    switch (count($array)) {
        case 1:
            // Return the array as-is; returning the first item
            // of the array was confusing and unnecessary
            return $array;
            break;
        case 0:
            throw new InvalidArgumentException('Requires at least one array');
            break;
    }

    // We 'll need these, as array_shift destroys them
    $keys = array_keys($array);

    $a = array_shift($array);
    $k = array_shift($keys); // Get the key that $a had
    $b = permutations($array);

    $return = array();
    foreach ($a as $v) {
        if(is_numeric($v))
        {
            foreach ($b as $v2) {
                // array($k => $v) re-associates $v (each item in $a)
                // with the key that $a originally had
                // array_combine re-associates each item in $v2 with
                // the corresponding key it had in the original array
                // Also, using operator+ instead of array_merge
                // allows us to not lose the keys once more
                $return[] = array($k => $v) + array_combine($keys, $v2);
            }
        }
    }

    return $return;
}

See it in action.

By the way, calculating all the permutations recursively is neat, but you might not want to do it in a production environment. You should definitely consider a sanity check that calculates how many permutations there are and doesn't allow processing to continue if they are over some limit, at the very least.

南薇 2024-11-13 17:07:33

我通过将乔恩的算法与我最初的算法合并来改进乔恩的函数。我所做的是检查函数是否正在执行递归,如果是,我使用原始的 array_merge() (它正在工作),否则我使用 Jon 的 array_combine() (以保留数组键)。

我将乔恩的答案标记为正确,因为他提出了一个巧妙的解决方案来保持数组键完整。

function permutations(array $array, $inb=false)
{
    switch (count($array)) {
        case 1:
            // Return the array as-is; returning the first item
            // of the array was confusing and unnecessary
            return $array[0];
            break;
        case 0:
            throw new InvalidArgumentException('Requires at least one array');
            break;
    }

    // We 'll need these, as array_shift destroys them
    $keys = array_keys($array);

    $a = array_shift($array);
    $k = array_shift($keys); // Get the key that $a had
    $b = permutations($array, 'recursing');

    $return = array();
    foreach ($a as $v) {
        if(is_numeric($v))
        {
            foreach ($b as $v2) {
                // array($k => $v) re-associates $v (each item in $a)
                // with the key that $a originally had
                // array_combine re-associates each item in $v2 with
                // the corresponding key it had in the original array
                // Also, using operator+ instead of array_merge
                // allows us to not lose the keys once more
                if($inb == 'recursing')
                    $return[] = array_merge(array($v), (array) $v2);
                else
                    $return[] = array($k => $v) + array_combine($keys, $v2);
            }
        }
    }

    return $return;
}

已成功使用多种阵列组合进行测试。

I improved Jon's function by merging his algorithm with the one I had initially. What I did, was check if the function was doing a recursion, if so, I use the original array_merge() (which was working), else I use Jon's array_combine() (to keep the arrays keys).

I'm marking Jon's answer as correct since he proposed a slick solution to keep the array keys intact.

function permutations(array $array, $inb=false)
{
    switch (count($array)) {
        case 1:
            // Return the array as-is; returning the first item
            // of the array was confusing and unnecessary
            return $array[0];
            break;
        case 0:
            throw new InvalidArgumentException('Requires at least one array');
            break;
    }

    // We 'll need these, as array_shift destroys them
    $keys = array_keys($array);

    $a = array_shift($array);
    $k = array_shift($keys); // Get the key that $a had
    $b = permutations($array, 'recursing');

    $return = array();
    foreach ($a as $v) {
        if(is_numeric($v))
        {
            foreach ($b as $v2) {
                // array($k => $v) re-associates $v (each item in $a)
                // with the key that $a originally had
                // array_combine re-associates each item in $v2 with
                // the corresponding key it had in the original array
                // Also, using operator+ instead of array_merge
                // allows us to not lose the keys once more
                if($inb == 'recursing')
                    $return[] = array_merge(array($v), (array) $v2);
                else
                    $return[] = array($k => $v) + array_combine($keys, $v2);
            }
        }
    }

    return $return;
}

Tested successfully with several array combinations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文